Added quick and dirty commands for testing the new NVO code.
[people/xl0/gpxe.git] / src / core / nvo.c
index 9bd9252..0e88b8a 100644 (file)
  *
  */
 
-static size_t nvo_options_len ( struct nvs_options *nvo ) {
-       struct dhcp_option *option;
-       uint8_t sum;
+#warning "Temporary hack"
+struct nvo_block *ugly_nvo_hack = NULL;
+
+/**
+ * Calculate checksum over non-volatile stored options
+ *
+ * @v nvo              Non-volatile options block
+ * @ret sum            Checksum
+ */
+static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
+       uint8_t *data = nvo->options->data;
+       uint8_t sum = 0;
        unsigned int i;
-       size_t len;
 
-       for ( sum = 0, i = 0 ; i < nvo->nvs->size ; i++ ) {
-               sum += * ( ( uint8_t * ) ( nvo->options->data + i ) );
+       for ( i = 0 ; i < nvo->total_len ; i++ ) {
+               sum += *(data++);
+       }
+       return sum;
+}
+
+/**
+ * Load non-volatile stored options from non-volatile storage device
+ *
+ * @v nvo              Non-volatile options block
+ * @ret rc             Return status code
+ */
+static int nvo_load ( struct nvo_block *nvo ) {
+       void *data = nvo->options->data;
+       struct nvo_fragment *fragment;
+       int rc;
+
+       /* Read data a fragment at a time */
+       for ( fragment = nvo->fragments ; 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;
        }
-       if ( sum != 0 ) {
+       
+       return 0;
+}
+
+/**
+ * Save non-volatile stored options back to non-volatile storage device
+ *
+ * @v nvo              Non-volatile options block
+ * @ret rc             Return status code
+ */
+int nvo_save ( struct nvo_block *nvo ) {
+       void *data = nvo->options->data;
+       uint8_t *checksum = ( data + nvo->total_len - 1 );
+       struct nvo_fragment *fragment;
+       int rc;
+
+       /* Recalculate checksum */
+       *checksum -= nvo_checksum ( nvo );
+
+       /* Write data a fragment at a time */
+       for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
+               if ( ( rc = nvs_write ( nvo->nvs, fragment->address,
+                                       data, fragment->len ) ) != 0 ) {
+                       DBG ( "NVO %p could not write %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
+ *
+ * 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 ) {
+       struct dhcp_option_block *options = nvo->options;
+       struct dhcp_option *option;
+
+       /* Steal one byte for the checksum */
+       options->max_len = ( nvo->total_len - 1 );
+
+       /* Verify checksum over whole block */
+       if ( nvo_checksum ( nvo ) != 0 ) {
                DBG ( "NVO %p has bad checksum %02x; assuming empty\n",
-                     nvo, sum );
-               return 0;
+                     nvo, nvo_checksum ( nvo ) );
+               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 ) {
+       struct nvo_fragment *fragment = nvo->fragments;
        int rc;
 
-       nvo->options = alloc_dhcp_options ( nvo->nvs->size );
+       /* Calculate total length of all fragments */
+       nvo->total_len = 0;
+       for ( fragment = nvo->fragments ; fragment->len ; fragment++ ) {
+               nvo->total_len += fragment->len;
+       }
+
+       /* Allocate memory for options and read in from NVS */
+       nvo->options = alloc_dhcp_options ( nvo->total_len );
        if ( ! nvo->options ) {
                DBG ( "NVO %p could not allocate %zd bytes\n",
-                     nvo, nvo->nvs->size );
+                     nvo, 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_load ( 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 );
        register_dhcp_options ( nvo->options );
 
+       ugly_nvo_hack = nvo;
+
        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 );
                nvo->options = NULL;
        }
+
+       ugly_nvo_hack = NULL;
 }