Add the concept of a fragment list for non-volatile stored options.
authorMichael Brown <mcb30@etherboot.org>
Mon, 4 Dec 2006 21:46:13 +0000 (21:46 +0000)
committerMichael Brown <mcb30@etherboot.org>
Mon, 4 Dec 2006 21:46:13 +0000 (21:46 +0000)
src/core/nvo.c
src/drivers/net/etherfabric.c
src/include/gpxe/nvo.h

index 9bd9252..a130329 100644 (file)
  *
  */
 
-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 );
index 1789766..b6ffc70 100644 (file)
@@ -23,6 +23,7 @@
 #include <gpxe/bitbash.h>
 #include <gpxe/i2c.h>
 #include <gpxe/spi.h>
+#include <gpxe/nvo.h>
 #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;
index b51431a..f766508 100644 (file)
@@ -7,15 +7,37 @@
  *
  */
 
+#include <stdint.h>
+
 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 */