diff --git a/src/core/nvo.c b/src/core/nvo.c index 9bd92526..a130329f 100644 --- a/src/core/nvo.c +++ b/src/core/nvo.c @@ -28,79 +28,146 @@ * */ -static size_t nvo_options_len ( struct nvs_options *nvo ) { +/** + * Calculate total length of non-volatile stored options + * + * @v nvo Non-volatile options block + * @ret total_len Total length of all fragments + */ +static size_t nvo_total_len ( struct nvo_block *nvo ) { + struct nvo_fragment *fragment = nvo->fragments; + size_t total_len = 0; + + for ( ; fragment->len ; fragment++ ) { + total_len += fragment->len; + } + + return total_len; +} + +/** + * Read non-volatile stored options from non-volatile storage device + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +static int nvo_read ( struct nvo_block *nvo ) { + struct nvo_fragment *fragment = nvo->fragments; + void *data = nvo->options->data; + int rc; + + for ( ; fragment->len ; fragment++ ) { + if ( ( rc = nvs_read ( nvo->nvs, fragment->address, + data, fragment->len ) ) != 0 ) { + DBG ( "NVO %p could not read %zd bytes at %#04x\n", + nvo, fragment->len, fragment->address ); + return rc; + } + data += fragment->len; + } + + return 0; +} + +/** + * Parse stored options + * + * @v nvo Non-volatile options block + * @v total_len Total length of options data + * + * Verifies that the options data is valid, and configures the DHCP + * options block. If the data is not valid, it is replaced with an + * empty options block. + */ +static void nvo_init_dhcp ( struct nvo_block *nvo, size_t total_len ) { + struct dhcp_option_block *options = nvo->options; struct dhcp_option *option; uint8_t sum; unsigned int i; - size_t len; - for ( sum = 0, i = 0 ; i < nvo->nvs->size ; i++ ) { - sum += * ( ( uint8_t * ) ( nvo->options->data + i ) ); + /* Steal one byte for the checksum */ + options->max_len = ( total_len - 1 ); + + /* Verify checksum over whole block */ + for ( sum = 0, i = 0 ; i < total_len ; i++ ) { + sum += * ( ( uint8_t * ) ( options->data + i ) ); } if ( sum != 0 ) { DBG ( "NVO %p has bad checksum %02x; assuming empty\n", nvo, sum ); - return 0; + goto empty; } - option = nvo->options->data; + /* Check that we don't just have a block full of zeroes */ + option = options->data; if ( option->tag == DHCP_PAD ) { DBG ( "NVO %p has bad start; assuming empty\n", nvo ); - return 0; + goto empty; } - option = find_dhcp_option ( nvo->options, DHCP_END ); + /* Search for the DHCP_END tag */ + options->len = options->max_len; + option = find_dhcp_option ( options, DHCP_END ); if ( ! option ) { DBG ( "NVO %p has no end tag; assuming empty\n", nvo ); - return 0; + goto empty; } - len = ( ( void * ) option - nvo->options->data + 1 ); + /* Set correct length of DHCP options */ + options->len = ( ( void * ) option - options->data + 1 ); DBG ( "NVO %p contains %zd bytes of options (maximum %zd)\n", - nvo, len, nvo->nvs->size ); + nvo, options->len, options->max_len ); + return; - return len; + empty: + /* No options found; initialise an empty options block */ + option = options->data; + option->tag = DHCP_END; + options->len = 1; + return; } -int nvo_register ( struct nvs_options *nvo ) { - struct dhcp_option *option; +/** + * Register non-volatile stored options + * + * @v nvo Non-volatile options block + * @ret rc Return status code + */ +int nvo_register ( struct nvo_block *nvo ) { + size_t total_len; int rc; - nvo->options = alloc_dhcp_options ( nvo->nvs->size ); + /* Allocate memory for options and read in from NVS */ + total_len = nvo_total_len ( nvo ); + nvo->options = alloc_dhcp_options ( total_len ); if ( ! nvo->options ) { DBG ( "NVO %p could not allocate %zd bytes\n", - nvo, nvo->nvs->size ); + nvo, total_len ); rc = -ENOMEM; goto err; } - if ( ( rc = nvo->nvs->read ( nvo->nvs, 0, nvo->options->data, - nvo->nvs->size ) ) != 0 ) { - DBG ( "NVO %p could not read [0,%zd)\n", - nvo, nvo->nvs->size ); + if ( ( rc = nvo_read ( nvo ) ) != 0 ) goto err; - } - - nvo->options->len = nvo->options->max_len; - nvo->options->len = nvo_options_len ( nvo ); - if ( ! nvo->options->len ) { - option = nvo->options->data; - option->tag = DHCP_END; - nvo->options->len = 1; - } + /* Verify and register options */ + nvo_init_dhcp ( nvo, total_len ); register_dhcp_options ( nvo->options ); return 0; err: - free_dhcp_options ( nvo->options ); nvo->options = NULL; return rc; } -void nvo_unregister ( struct nvs_options *nvo ) { +/** + * Unregister non-volatile stored options + * + * @v nvo Non-volatile options block + */ +void nvo_unregister ( struct nvo_block *nvo ) { if ( nvo->options ) { unregister_dhcp_options ( nvo->options ); free_dhcp_options ( nvo->options ); diff --git a/src/drivers/net/etherfabric.c b/src/drivers/net/etherfabric.c index 17897666..b6ffc70c 100644 --- a/src/drivers/net/etherfabric.c +++ b/src/drivers/net/etherfabric.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "timer.h" #define dma_addr_t unsigned long #include "etherfabric.h" @@ -217,6 +218,9 @@ struct efab_nic { struct spi_bus spi; struct spi_device falcon_flash; struct spi_device falcon_eeprom; + + /** Non-volatile options */ + struct nvo_block nvo; }; /************************************************************************** @@ -2304,6 +2308,11 @@ static void falcon_init_spi ( struct efab_nic *efab ) { /** Offset of MAC address within EEPROM or Flash */ #define FALCON_MAC_ADDRESS_OFFSET(port) ( 0x310 + 0x08 * (port) ) +static struct nvo_fragment falcon_eeprom_fragments[] = { + { 0, 0x100 }, + { 0, 0 } +}; + /** * Read MAC address from EEPROM * @@ -2972,12 +2981,10 @@ static int falcon_init_nic ( struct efab_nic *efab ) { /* Register non-volatile storage */ if ( efab->has_eeprom ) { - /* - efab->nvs.op = &falcon_nvs_operations; - efab->nvs.len = 0x100; - if ( nvs_register ( &efab->nvs ) != 0 ) + efab->nvo.nvs = &efab->falcon_eeprom.nvs; + efab->nvo.fragments = falcon_eeprom_fragments; + if ( nvo_register ( &efab->nvo ) != 0 ) return 0; - */ } return 1; diff --git a/src/include/gpxe/nvo.h b/src/include/gpxe/nvo.h index b51431aa..f766508c 100644 --- a/src/include/gpxe/nvo.h +++ b/src/include/gpxe/nvo.h @@ -7,15 +7,37 @@ * */ +#include + struct nvs_device; struct dhcp_option_block; -struct nvs_options { +/** + * A fragment of a non-volatile storage device used for stored options + */ +struct nvo_fragment { + /** Starting address of fragment within NVS device */ + unsigned int address; + /** Length of fragment */ + size_t len; +}; + +/** + * A block of non-volatile stored options + */ +struct nvo_block { + /** Underlying non-volatile storage device */ struct nvs_device *nvs; + /** List of option-containing fragments + * + * The list is terminated by a fragment with a length of zero. + */ + struct nvo_fragment *fragments; + /** DHCP options block */ struct dhcp_option_block *options; }; -extern int nvo_register ( struct nvs_options *nvo ); -extern void nvo_unregister ( struct nvs_options *nvo ); +extern int nvo_register ( struct nvo_block *nvo ); +extern void nvo_unregister ( struct nvo_block *nvo ); #endif /* _GPXE_NVO_H */