]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[nvo] Allow resizing of non-volatile stored option blocks
authorMichael Brown <mcb30@ipxe.org>
Tue, 11 Jan 2011 00:53:50 +0000 (00:53 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 19 Jan 2011 13:52:48 +0000 (13:52 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/core/nvo.c
src/drivers/net/etherfabric.c
src/drivers/net/myri10ge.c
src/drivers/net/natsemi.c
src/drivers/net/rtl8139.c
src/include/ipxe/dhcpopts.h
src/include/ipxe/nvo.h
src/net/dhcpopts.c

index c5968c6b11b18c48f1a8504931cc98ea5a3db41e..f4da407aa3900327de0e9da9c31be261fbd6c965 100644 (file)
@@ -49,6 +49,73 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
        return sum;
 }
 
+/**
+ * Reallocate non-volatile stored options block
+ *
+ * @v nvo              Non-volatile options block
+ * @v len              New length
+ * @ret rc             Return status code
+ */
+static int nvo_realloc ( struct nvo_block *nvo, size_t len ) {
+       void *new_data;
+
+       /* Reallocate data */
+       new_data = realloc ( nvo->data, len );
+       if ( ! new_data ) {
+               DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
+                      nvo, len );
+               return -ENOMEM;
+       }
+       nvo->data = new_data;
+       nvo->len = len;
+
+       /* Update DHCP option block */
+       if ( len ) {
+               nvo->dhcpopts.data = ( nvo->data + 1 /* checksum */ );
+               nvo->dhcpopts.alloc_len = ( len - 1 /* checksum */ );
+       } else {
+               nvo->dhcpopts.data = NULL;
+               nvo->dhcpopts.used_len = 0;
+               nvo->dhcpopts.alloc_len = 0;
+       }
+
+       return 0;
+}
+
+/**
+ * Reallocate non-volatile stored options DHCP option block
+ *
+ * @v options          DHCP option block
+ * @v len              New length
+ * @ret rc             Return status code
+ */
+static int nvo_realloc_dhcpopt ( struct dhcp_options *options, size_t len ) {
+       struct nvo_block *nvo =
+               container_of ( options, struct nvo_block, dhcpopts );
+       int rc;
+
+       /* Refuse to reallocate if we have no way to resize the block */
+       if ( ! nvo->resize )
+               return dhcpopt_no_realloc ( options, len );
+
+       /* Allow one byte for the checksum (if any data is present) */
+       if ( len )
+               len += 1;
+
+       /* Resize underlying non-volatile options block */
+       if ( ( rc = nvo->resize ( nvo, len ) ) != 0 ) {
+               DBGC ( nvo, "NVO %p could not resize to %zd bytes: %s\n",
+                      nvo, len, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Reallocate in-memory options block */
+       if ( ( rc = nvo_realloc ( nvo, len ) ) != 0 )
+               return rc;
+
+       return 0;
+}
+
 /**
  * Load non-volatile stored options from non-volatile storage device
  *
@@ -56,8 +123,15 @@ static unsigned int nvo_checksum ( struct nvo_block *nvo ) {
  * @ret rc             Return status code
  */
 static int nvo_load ( struct nvo_block *nvo ) {
+       uint8_t *options_data = nvo->dhcpopts.data;
        int rc;
 
+       /* Skip reading zero-length NVO fields */
+       if ( nvo->len == 0 ) {
+               DBGC ( nvo, "NVO %p is empty; skipping load\n", nvo );
+               return 0;
+       }
+
        /* Read data */
        if ( ( rc = nvs_read ( nvo->nvs, nvo->address, nvo->data,
                               nvo->len ) ) != 0 ) {
@@ -66,6 +140,20 @@ static int nvo_load ( struct nvo_block *nvo ) {
                return rc;
        }
 
+       /* If checksum fails, or options data starts with a zero,
+        * assume the whole block is invalid.  This should capture the
+        * case of random initial contents.
+        */
+       if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
+               DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
+                      "assuming empty\n", nvo, nvo_checksum ( nvo ),
+                      options_data[0] );
+               memset ( nvo->data, 0, nvo->len );
+       }
+
+       /* Rescan DHCP option block */
+       dhcpopt_update_used_len ( &nvo->dhcpopts );
+
        DBGC ( nvo, "NVO %p loaded from non-volatile storage\n", nvo );
        return 0;
 }
@@ -80,8 +168,9 @@ static int nvo_save ( struct nvo_block *nvo ) {
        uint8_t *checksum = nvo->data;
        int rc;
 
-       /* Recalculate checksum */
-       *checksum -= nvo_checksum ( nvo );
+       /* Recalculate checksum, if applicable */
+       if ( nvo->len > 0 )
+               *checksum -= nvo_checksum ( nvo );
 
        /* Write data */
        if ( ( rc = nvs_write ( nvo->nvs, nvo->address, nvo->data,
@@ -95,38 +184,6 @@ static int nvo_save ( struct nvo_block *nvo ) {
        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_dhcpopts ( struct nvo_block *nvo ) {
-       uint8_t *options_data;
-       size_t options_len;
-
-       /* Steal one byte for the checksum */
-       options_data = ( nvo->data + 1 );
-       options_len = ( nvo->len - 1 );
-
-       /* If checksum fails, or options data starts with a zero,
-        * assume the whole block is invalid.  This should capture the
-        * case of random initial contents.
-        */
-       if ( ( nvo_checksum ( nvo ) != 0 ) || ( options_data[0] == 0 ) ) {
-               DBGC ( nvo, "NVO %p has checksum %02x and initial byte %02x; "
-                      "assuming empty\n", nvo, nvo_checksum ( nvo ),
-                      options_data[0] );
-               memset ( nvo->data, 0, nvo->len );
-       }
-
-       dhcpopt_init ( &nvo->dhcpopts, options_data, options_len,
-                      dhcpopt_no_realloc );
-}
-
 /**
  * Store value of NVO setting
  *
@@ -190,13 +247,18 @@ static struct settings_operations nvo_settings_operations = {
  * @v nvs              Underlying non-volatile storage device
  * @v address          Address within NVS device
  * @v len              Length of non-volatile options data
+ * @v resize           Resize method
  * @v refcnt           Containing object reference counter, or NULL
  */
 void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
-               size_t address, size_t len, struct refcnt *refcnt ) {
+               size_t address, size_t len,
+               int ( * resize ) ( struct nvo_block *nvo, size_t len ),
+               struct refcnt *refcnt ) {
        nvo->nvs = nvs;
        nvo->address = address;
        nvo->len = len;
+       nvo->resize = resize;
+       dhcpopt_init ( &nvo->dhcpopts, NULL, 0, nvo_realloc_dhcpopt );
        settings_init ( &nvo->settings, &nvo_settings_operations, refcnt, 0 );
 }
 
@@ -211,20 +273,14 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
        int rc;
 
        /* Allocate memory for options */
-       nvo->data = zalloc ( nvo->len );
-       if ( ! nvo->data ) {
-               DBGC ( nvo, "NVO %p could not allocate %zd bytes\n",
-                      nvo, nvo->len );
-               rc = -ENOMEM;
-               goto err_malloc;
-       }
+       if ( ( rc = nvo_realloc ( nvo, nvo->len ) ) != 0 )
+               goto err_realloc;
 
        /* Read data from NVS */
        if ( ( rc = nvo_load ( nvo ) ) != 0 )
                goto err_load;
 
-       /* Verify and register options */
-       nvo_init_dhcpopts ( nvo );
+       /* Register settings */
        if ( ( rc = register_settings ( &nvo->settings, parent, "nvo" ) ) != 0 )
                goto err_register;
 
@@ -233,9 +289,8 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
        
  err_register:
  err_load:
-       free ( nvo->data );
-       nvo->data = NULL;
- err_malloc:
+       nvo_realloc ( nvo, 0 );
+ err_realloc:
        return rc;
 }
 
@@ -246,7 +301,6 @@ int register_nvo ( struct nvo_block *nvo, struct settings *parent ) {
  */
 void unregister_nvo ( struct nvo_block *nvo ) {
        unregister_settings ( &nvo->settings );
-       free ( nvo->data );
-       nvo->data = NULL;
+       nvo_realloc ( nvo, 0 );
        DBGC ( nvo, "NVO %p unregistered\n", nvo );
 }
index 77d21247de145de3289ef6c2498b997119bc0d26..836b85a9a2b7a79c7143fb1df66376a69b29f678 100644 (file)
@@ -3273,7 +3273,7 @@ falcon_probe_spi ( struct efab_nic *efab )
        /* If the device has EEPROM attached, then advertise NVO space */
        if ( has_eeprom ) {
                nvo_init ( &efab->nvo, &efab->spi_eeprom.nvs, 0x100, 0xf0,
-                          &efab->netdev->refcnt );
+                          NULL, &efab->netdev->refcnt );
        }
 
        return 0;
index bc730ea98364fdcce5a0a7bea923e930d7fe0ff5..c7b9dfa248b912d658746c0d9e6772baa2e42f47 100644 (file)
@@ -732,6 +732,7 @@ static int myri10ge_nv_init ( struct myri10ge_private *priv )
        nvo_init ( &priv->nvo,
                   &priv->nvs,
                   nvo_fragment_pos, 0x200,
+                  NULL,
                   & myri10ge_netdev (priv) -> refcnt );
        rc = register_nvo ( &priv->nvo,
                            netdev_settings ( myri10ge_netdev ( priv ) ) );
index 61073b59b2ded1087ee56be560e1c4683440a31b..da2f0886f5f5e52517959ab28217acd50c11a9d9 100644 (file)
@@ -154,7 +154,7 @@ static void natsemi_init_eeprom ( struct natsemi_private *np ) {
         * this region.  Currently it is not working. But with some
         * efforts it can.
         */
-       nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL );
+       nvo_init ( &np->nvo, &np->eeprom.nvs, 0x0c, 0x68, NULL, NULL );
 }
 
 /**
index e97829f069b90e49aad6d2c338cbe4cf513d77a6..7cc1de2f86d5125c9c04b9cc9fcf00a7f5670ffa 100644 (file)
@@ -288,7 +288,7 @@ static void rtl_init_eeprom ( struct net_device *netdev ) {
                DBGC ( rtl, "rtl8139 %p EEPROM in use for VPD; cannot use "
                       "for options\n", rtl );
        } else {
-               nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40,
+               nvo_init ( &rtl->nvo, &rtl->eeprom.nvs, 0x20, 0x40, NULL,
                           &netdev->refcnt );
        }
 }
index fe07d903d3cd836ad4bbcc86294049008d9d5483..8fb3d2d79a3e17d67d806fadd36544a5e42a2cff 100644 (file)
@@ -36,6 +36,7 @@ extern void dhcpopt_init ( struct dhcp_options *options,
                           void *data, size_t alloc_len,
                           int ( * realloc ) ( struct dhcp_options *options,
                                               size_t len ) );
+extern void dhcpopt_update_used_len ( struct dhcp_options *options );
 extern int dhcpopt_no_realloc ( struct dhcp_options *options, size_t len );
 
 #endif /* _IPXE_DHCPOPTS_H */
index 1fdc12ccb6848abd7b8e04e0d7126e317e12196d..995afd749901bf2f120f39da78b598274c8da036 100644 (file)
@@ -30,12 +30,22 @@ struct nvo_block {
        size_t len;
        /** Option-containing data */
        void *data;
+       /**
+        * Resize non-volatile stored option block
+        *
+        * @v nvo               Non-volatile options block
+        * @v len               New size
+        * @ret rc              Return status code
+        */
+       int ( * resize ) ( struct nvo_block *nvo, size_t len );
        /** DHCP options block */
        struct dhcp_options dhcpopts;
 };
 
 extern void nvo_init ( struct nvo_block *nvo, struct nvs_device *nvs,
-                      size_t address, size_t len, struct refcnt *refcnt );
+                      size_t address, size_t len,
+                      int ( * resize ) ( struct nvo_block *nvo, size_t len ),
+                      struct refcnt *refcnt );
 extern int register_nvo ( struct nvo_block *nvo, struct settings *parent );
 extern void unregister_nvo ( struct nvo_block *nvo );
 
index d1330eae50d319901317a4c35cfe8e784c02eaeb..f04b8e712e1298575ca964a0068a888f1ad139d4 100644 (file)
@@ -402,7 +402,7 @@ int dhcpopt_fetch ( struct dhcp_options *options, unsigned int tag,
  * The "used length" field will be updated based on scanning through
  * the block to find the end of the options.
  */
-static void dhcpopt_update_used_len ( struct dhcp_options *options ) {
+void dhcpopt_update_used_len ( struct dhcp_options *options ) {
        struct dhcp_option *option;
        int offset = 0;
        ssize_t remaining = options->alloc_len;