|  | @@ -28,79 +28,146 @@
 | 
		
	
		
			
			| 28 | 28 |   *
 | 
		
	
		
			
			| 29 | 29 |   */
 | 
		
	
		
			
			| 30 | 30 |  
 | 
		
	
		
			
			| 31 |  | -static size_t nvo_options_len ( struct nvs_options *nvo ) {
 | 
		
	
		
			
			|  | 31 | +/**
 | 
		
	
		
			
			|  | 32 | + * Calculate total length of non-volatile stored options
 | 
		
	
		
			
			|  | 33 | + *
 | 
		
	
		
			
			|  | 34 | + * @v nvo		Non-volatile options block
 | 
		
	
		
			
			|  | 35 | + * @ret total_len	Total length of all fragments
 | 
		
	
		
			
			|  | 36 | + */
 | 
		
	
		
			
			|  | 37 | +static size_t nvo_total_len ( struct nvo_block *nvo ) {
 | 
		
	
		
			
			|  | 38 | +	struct nvo_fragment *fragment = nvo->fragments;
 | 
		
	
		
			
			|  | 39 | +	size_t total_len = 0;
 | 
		
	
		
			
			|  | 40 | +
 | 
		
	
		
			
			|  | 41 | +	for ( ; fragment->len ; fragment++ ) {
 | 
		
	
		
			
			|  | 42 | +		total_len += fragment->len;
 | 
		
	
		
			
			|  | 43 | +	}
 | 
		
	
		
			
			|  | 44 | +
 | 
		
	
		
			
			|  | 45 | +	return total_len;
 | 
		
	
		
			
			|  | 46 | +}
 | 
		
	
		
			
			|  | 47 | +
 | 
		
	
		
			
			|  | 48 | +/**
 | 
		
	
		
			
			|  | 49 | + * Read non-volatile stored options from non-volatile storage device
 | 
		
	
		
			
			|  | 50 | + *
 | 
		
	
		
			
			|  | 51 | + * @v nvo		Non-volatile options block
 | 
		
	
		
			
			|  | 52 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 53 | + */
 | 
		
	
		
			
			|  | 54 | +static int nvo_read ( struct nvo_block *nvo ) {
 | 
		
	
		
			
			|  | 55 | +	struct nvo_fragment *fragment = nvo->fragments;
 | 
		
	
		
			
			|  | 56 | +	void *data = nvo->options->data;
 | 
		
	
		
			
			|  | 57 | +	int rc;
 | 
		
	
		
			
			|  | 58 | +
 | 
		
	
		
			
			|  | 59 | +	for ( ; fragment->len ; fragment++ ) {
 | 
		
	
		
			
			|  | 60 | +		if ( ( rc = nvs_read ( nvo->nvs, fragment->address,
 | 
		
	
		
			
			|  | 61 | +				       data, fragment->len ) ) != 0 ) {
 | 
		
	
		
			
			|  | 62 | +			DBG ( "NVO %p could not read %zd bytes at %#04x\n",
 | 
		
	
		
			
			|  | 63 | +			      nvo, fragment->len, fragment->address );
 | 
		
	
		
			
			|  | 64 | +			return rc;
 | 
		
	
		
			
			|  | 65 | +		}
 | 
		
	
		
			
			|  | 66 | +		data += fragment->len;
 | 
		
	
		
			
			|  | 67 | +	}
 | 
		
	
		
			
			|  | 68 | +	
 | 
		
	
		
			
			|  | 69 | +	return 0;
 | 
		
	
		
			
			|  | 70 | +}
 | 
		
	
		
			
			|  | 71 | +
 | 
		
	
		
			
			|  | 72 | +/**
 | 
		
	
		
			
			|  | 73 | + * Parse stored options
 | 
		
	
		
			
			|  | 74 | + *
 | 
		
	
		
			
			|  | 75 | + * @v nvo		Non-volatile options block
 | 
		
	
		
			
			|  | 76 | + * @v total_len		Total length of options data
 | 
		
	
		
			
			|  | 77 | + *
 | 
		
	
		
			
			|  | 78 | + * Verifies that the options data is valid, and configures the DHCP
 | 
		
	
		
			
			|  | 79 | + * options block.  If the data is not valid, it is replaced with an
 | 
		
	
		
			
			|  | 80 | + * empty options block.
 | 
		
	
		
			
			|  | 81 | + */
 | 
		
	
		
			
			|  | 82 | +static void nvo_init_dhcp ( struct nvo_block *nvo, size_t total_len ) {
 | 
		
	
		
			
			|  | 83 | +	struct dhcp_option_block *options = nvo->options;
 | 
		
	
		
			
			| 32 | 84 |  	struct dhcp_option *option;
 | 
		
	
		
			
			| 33 | 85 |  	uint8_t sum;
 | 
		
	
		
			
			| 34 | 86 |  	unsigned int i;
 | 
		
	
		
			
			| 35 |  | -	size_t len;
 | 
		
	
		
			
			| 36 | 87 |  
 | 
		
	
		
			
			| 37 |  | -	for ( sum = 0, i = 0 ; i < nvo->nvs->size ; i++ ) {
 | 
		
	
		
			
			| 38 |  | -		sum += * ( ( uint8_t * ) ( nvo->options->data + i ) );
 | 
		
	
		
			
			|  | 88 | +	/* Steal one byte for the checksum */
 | 
		
	
		
			
			|  | 89 | +	options->max_len = ( total_len - 1 );
 | 
		
	
		
			
			|  | 90 | +
 | 
		
	
		
			
			|  | 91 | +	/* Verify checksum over whole block */
 | 
		
	
		
			
			|  | 92 | +	for ( sum = 0, i = 0 ; i < total_len ; i++ ) {
 | 
		
	
		
			
			|  | 93 | +		sum += * ( ( uint8_t * ) ( options->data + i ) );
 | 
		
	
		
			
			| 39 | 94 |  	}
 | 
		
	
		
			
			| 40 | 95 |  	if ( sum != 0 ) {
 | 
		
	
		
			
			| 41 | 96 |  		DBG ( "NVO %p has bad checksum %02x; assuming empty\n",
 | 
		
	
		
			
			| 42 | 97 |  		      nvo, sum );
 | 
		
	
		
			
			| 43 |  | -		return 0;
 | 
		
	
		
			
			|  | 98 | +		goto empty;
 | 
		
	
		
			
			| 44 | 99 |  	}
 | 
		
	
		
			
			| 45 | 100 |  
 | 
		
	
		
			
			| 46 |  | -	option = nvo->options->data;
 | 
		
	
		
			
			|  | 101 | +	/* Check that we don't just have a block full of zeroes */
 | 
		
	
		
			
			|  | 102 | +	option = options->data;
 | 
		
	
		
			
			| 47 | 103 |  	if ( option->tag == DHCP_PAD ) {
 | 
		
	
		
			
			| 48 | 104 |  		DBG ( "NVO %p has bad start; assuming empty\n", nvo );
 | 
		
	
		
			
			| 49 |  | -		return 0;
 | 
		
	
		
			
			|  | 105 | +		goto empty;
 | 
		
	
		
			
			| 50 | 106 |  	}
 | 
		
	
		
			
			| 51 | 107 |  	
 | 
		
	
		
			
			| 52 |  | -	option = find_dhcp_option ( nvo->options, DHCP_END );
 | 
		
	
		
			
			|  | 108 | +	/* Search for the DHCP_END tag */
 | 
		
	
		
			
			|  | 109 | +	options->len = options->max_len;
 | 
		
	
		
			
			|  | 110 | +	option = find_dhcp_option ( options, DHCP_END );
 | 
		
	
		
			
			| 53 | 111 |  	if ( ! option ) {
 | 
		
	
		
			
			| 54 | 112 |  		DBG ( "NVO %p has no end tag; assuming empty\n", nvo );
 | 
		
	
		
			
			| 55 |  | -		return 0;
 | 
		
	
		
			
			|  | 113 | +		goto empty;
 | 
		
	
		
			
			| 56 | 114 |  	}
 | 
		
	
		
			
			| 57 | 115 |  
 | 
		
	
		
			
			| 58 |  | -	len = ( ( void * ) option - nvo->options->data + 1 );
 | 
		
	
		
			
			|  | 116 | +	/* Set correct length of DHCP options */
 | 
		
	
		
			
			|  | 117 | +	options->len = ( ( void * ) option - options->data + 1 );
 | 
		
	
		
			
			| 59 | 118 |  	DBG ( "NVO %p contains %zd bytes of options (maximum %zd)\n",
 | 
		
	
		
			
			| 60 |  | -	      nvo, len, nvo->nvs->size );
 | 
		
	
		
			
			|  | 119 | +	      nvo, options->len, options->max_len );
 | 
		
	
		
			
			|  | 120 | +	return;
 | 
		
	
		
			
			| 61 | 121 |  
 | 
		
	
		
			
			| 62 |  | -	return len;
 | 
		
	
		
			
			|  | 122 | + empty:
 | 
		
	
		
			
			|  | 123 | +	/* No options found; initialise an empty options block */
 | 
		
	
		
			
			|  | 124 | +	option = options->data;
 | 
		
	
		
			
			|  | 125 | +	option->tag = DHCP_END;
 | 
		
	
		
			
			|  | 126 | +	options->len = 1;
 | 
		
	
		
			
			|  | 127 | +	return;
 | 
		
	
		
			
			| 63 | 128 |  }
 | 
		
	
		
			
			| 64 | 129 |  
 | 
		
	
		
			
			| 65 |  | -int nvo_register ( struct nvs_options *nvo ) {
 | 
		
	
		
			
			| 66 |  | -	struct dhcp_option *option;
 | 
		
	
		
			
			|  | 130 | +/**
 | 
		
	
		
			
			|  | 131 | + * Register non-volatile stored options
 | 
		
	
		
			
			|  | 132 | + *
 | 
		
	
		
			
			|  | 133 | + * @v nvo		Non-volatile options block
 | 
		
	
		
			
			|  | 134 | + * @ret rc		Return status code
 | 
		
	
		
			
			|  | 135 | + */
 | 
		
	
		
			
			|  | 136 | +int nvo_register ( struct nvo_block *nvo ) {
 | 
		
	
		
			
			|  | 137 | +	size_t total_len;
 | 
		
	
		
			
			| 67 | 138 |  	int rc;
 | 
		
	
		
			
			| 68 | 139 |  
 | 
		
	
		
			
			| 69 |  | -	nvo->options = alloc_dhcp_options ( nvo->nvs->size );
 | 
		
	
		
			
			|  | 140 | +	/* Allocate memory for options and read in from NVS */
 | 
		
	
		
			
			|  | 141 | +	total_len = nvo_total_len ( nvo );
 | 
		
	
		
			
			|  | 142 | +	nvo->options = alloc_dhcp_options ( total_len );
 | 
		
	
		
			
			| 70 | 143 |  	if ( ! nvo->options ) {
 | 
		
	
		
			
			| 71 | 144 |  		DBG ( "NVO %p could not allocate %zd bytes\n",
 | 
		
	
		
			
			| 72 |  | -		      nvo, nvo->nvs->size );
 | 
		
	
		
			
			|  | 145 | +		      nvo, total_len );
 | 
		
	
		
			
			| 73 | 146 |  		rc = -ENOMEM;
 | 
		
	
		
			
			| 74 | 147 |  		goto err;
 | 
		
	
		
			
			| 75 | 148 |  	}
 | 
		
	
		
			
			| 76 | 149 |  
 | 
		
	
		
			
			| 77 |  | -	if ( ( rc = nvo->nvs->read ( nvo->nvs, 0, nvo->options->data,
 | 
		
	
		
			
			| 78 |  | -				     nvo->nvs->size ) ) != 0 ) {
 | 
		
	
		
			
			| 79 |  | -		DBG ( "NVO %p could not read [0,%zd)\n",
 | 
		
	
		
			
			| 80 |  | -		      nvo, nvo->nvs->size );
 | 
		
	
		
			
			|  | 150 | +	if ( ( rc = nvo_read ( nvo ) ) != 0 )
 | 
		
	
		
			
			| 81 | 151 |  		goto err;
 | 
		
	
		
			
			| 82 |  | -	}
 | 
		
	
		
			
			| 83 |  | -
 | 
		
	
		
			
			| 84 |  | -	nvo->options->len = nvo->options->max_len;
 | 
		
	
		
			
			| 85 |  | -	nvo->options->len = nvo_options_len ( nvo );
 | 
		
	
		
			
			| 86 |  | -	if ( ! nvo->options->len ) {
 | 
		
	
		
			
			| 87 |  | -		option = nvo->options->data;
 | 
		
	
		
			
			| 88 |  | -		option->tag = DHCP_END;
 | 
		
	
		
			
			| 89 |  | -		nvo->options->len = 1;
 | 
		
	
		
			
			| 90 |  | -	}
 | 
		
	
		
			
			| 91 | 152 |  
 | 
		
	
		
			
			|  | 153 | +	/* Verify and register options */
 | 
		
	
		
			
			|  | 154 | +	nvo_init_dhcp ( nvo, total_len );
 | 
		
	
		
			
			| 92 | 155 |  	register_dhcp_options ( nvo->options );
 | 
		
	
		
			
			| 93 | 156 |  
 | 
		
	
		
			
			| 94 | 157 |  	return 0;
 | 
		
	
		
			
			| 95 | 158 |  	
 | 
		
	
		
			
			| 96 | 159 |   err:
 | 
		
	
		
			
			| 97 |  | -	
 | 
		
	
		
			
			| 98 | 160 |  	free_dhcp_options ( nvo->options );
 | 
		
	
		
			
			| 99 | 161 |  	nvo->options = NULL;
 | 
		
	
		
			
			| 100 | 162 |  	return rc;
 | 
		
	
		
			
			| 101 | 163 |  }
 | 
		
	
		
			
			| 102 | 164 |  
 | 
		
	
		
			
			| 103 |  | -void nvo_unregister ( struct nvs_options *nvo ) {
 | 
		
	
		
			
			|  | 165 | +/**
 | 
		
	
		
			
			|  | 166 | + * Unregister non-volatile stored options
 | 
		
	
		
			
			|  | 167 | + *
 | 
		
	
		
			
			|  | 168 | + * @v nvo		Non-volatile options block
 | 
		
	
		
			
			|  | 169 | + */
 | 
		
	
		
			
			|  | 170 | +void nvo_unregister ( struct nvo_block *nvo ) {
 | 
		
	
		
			
			| 104 | 171 |  	if ( nvo->options ) {
 | 
		
	
		
			
			| 105 | 172 |  		unregister_dhcp_options ( nvo->options );
 | 
		
	
		
			
			| 106 | 173 |  		free_dhcp_options ( nvo->options );
 |