]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[pci] Auto-resize VPD fields used for non-volatile storage
authorMichael Brown <mcb30@ipxe.org>
Mon, 10 Jan 2011 03:35:34 +0000 (03:35 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 19 Jan 2011 13:52:56 +0000 (13:52 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/drivers/nvs/nvsvpd.c
src/include/ipxe/errfile.h
src/include/ipxe/nvsvpd.h

index b53829e844c5840284e6f6ff8131e7f9b6f9791e..a22ec825f927b44699df6d0131febb6cc174ae3a 100644 (file)
 FILE_LICENCE ( GPL2_OR_LATER );
 
 #include <stdio.h>
+#include <errno.h>
 #include <ipxe/nvs.h>
 #include <ipxe/pci.h>
 #include <ipxe/pcivpd.h>
+#include <ipxe/nvo.h>
 #include <ipxe/nvsvpd.h>
 
 /** @file
@@ -31,24 +33,50 @@ FILE_LICENCE ( GPL2_OR_LATER );
  */
 
 /**
- * Read from VPD
+ * Read from VPD field
  *
  * @v nvs              NVS device
- * @v address          Starting address
- * @v buf              Data buffer
+ * @v field            VPD field descriptor
+ * @v data             Data buffer
  * @v len              Length of data buffer
  * @ret rc             Return status code
  */
-static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address,
+static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int field,
                          void *data, size_t len ) {
        struct nvs_vpd_device *nvsvpd =
                container_of ( nvs, struct nvs_vpd_device, nvs );
+       struct pci_device *pci = nvsvpd->vpd.pci;
+       unsigned int address;
+       size_t max_len;
        int rc;
 
-       if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, ( nvsvpd->address + address ),
-                                  data, len ) ) != 0 ) {
-               DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not read "
-                      "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ),
+       /* Allow reading non-existent field */
+       if ( len == 0 )
+               return 0;
+
+       /* Locate VPD field */
+       if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
+                                  &max_len ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD could not locate field "
+                      PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ),
+                      PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) );
+               return rc;
+       }
+
+       /* Sanity check */
+       if ( len > max_len ) {
+               DBGC ( pci, PCI_FMT " NVS VPD cannot read %#02zx bytes "
+                      "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
+                      PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ),
+                      address, ( address + max_len ) );
+               return -ENXIO;
+       }
+
+       /* Read from VPD field */
+       if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, address, data, len ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD could not read field "
+                      PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n",
+                      PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
                       address, ( address + len ), strerror ( rc ) );
                return rc;
        }
@@ -57,24 +85,47 @@ static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int address,
 }
 
 /**
- * Write to VPD
+ * Write to VPD field
  *
  * @v nvs              NVS device
- * @v address          Starting address
- * @v buf              Data buffer
+ * @v field            VPD field descriptor
+ * @v data             Data buffer
  * @v len              Length of data buffer
  * @ret rc             Return status code
  */
-static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address,
+static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int field,
                           const void *data, size_t len ) {
        struct nvs_vpd_device *nvsvpd =
                container_of ( nvs, struct nvs_vpd_device, nvs );
+       struct pci_device *pci = nvsvpd->vpd.pci;
+       unsigned int address;
+       size_t max_len;
        int rc;
 
-       if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, ( nvsvpd->address + address ),
-                                   data, len ) ) != 0 ) {
-               DBGC ( nvsvpd->vpd.pci, PCI_FMT " NVS could not write "
-                      "[%04x,%04zx): %s\n", PCI_ARGS ( nvsvpd->vpd.pci ),
+       /* Locate VPD field */
+       if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
+                                  &max_len ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD could not locate field "
+                      PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ),
+                      PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) );
+               return rc;
+       }
+
+       /* Sanity check */
+       if ( len > max_len ) {
+               DBGC ( pci, PCI_FMT " NVS VPD cannot write %#02zx bytes "
+                      "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
+                      PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ),
+                      address, ( address + max_len ) );
+               return -ENXIO;
+       }
+
+       /* Write field */
+       if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, address, data,
+                                   len ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD could not write field "
+                      PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n",
+                      PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
                       address, ( address + len ), strerror ( rc ) );
                return rc;
        }
@@ -82,6 +133,36 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address,
        return 0;
 }
 
+/**
+ * Resize VPD field
+ *
+ * @v nvs              NVS device
+ * @v field            VPD field descriptor
+ * @v data             Data buffer
+ * @v len              Length of data buffer
+ * @ret rc             Return status code
+ */
+static int nvs_vpd_resize ( struct nvs_device *nvs, unsigned int field,
+                           size_t len ) {
+       struct nvs_vpd_device *nvsvpd =
+               container_of ( nvs, struct nvs_vpd_device, nvs );
+       struct pci_device *pci = nvsvpd->vpd.pci;
+       unsigned int address;
+       int rc;
+
+       /* Resize field */
+       if ( ( rc = pci_vpd_resize ( &nvsvpd->vpd, field, len,
+                                    &address ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD could not resize field "
+                      PCI_VPD_FIELD_FMT " to %#02zx bytes: %s\n",
+                      PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
+                      len, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
 /**
  * Initialise NVS VPD device
  *
@@ -89,9 +170,7 @@ static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int address,
  * @v pci              PCI device
  * @ret rc             Return status code
  */
-int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci,
-                  unsigned int field ) {
-       size_t len;
+int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci ) {
        int rc;
 
        /* Initialise VPD device */
@@ -101,23 +180,54 @@ int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci,
                return rc;
        }
 
-       /* Locate VPD field */
-       if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &nvsvpd->address,
-                                  &len ) ) != 0 ) {
-               DBGC ( pci, PCI_FMT " NVS could not locate VPD field "
-                      PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ),
-                      PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) );
-               return rc;
-       }
-
        /* Initialise NVS device */
-       nvsvpd->nvs.size = len;
        nvsvpd->nvs.read = nvs_vpd_read;
        nvsvpd->nvs.write = nvs_vpd_write;
 
-       DBGC ( pci, PCI_FMT " NVS using VPD field " PCI_VPD_FIELD_FMT " at "
-              "[%04x,%04x)\n", PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
-              nvsvpd->address, ( nvsvpd->address + nvsvpd->nvs.size ) );
+       return 0;
+}
+
+/**
+ * Resize non-volatile option storage within NVS VPD device
+ *
+ * @v nvo              Non-volatile options block
+ * @v len              New length
+ * @ret rc             Return status code
+ */
+static int nvs_vpd_nvo_resize ( struct nvo_block *nvo, size_t len ) {
+       int rc;
+
+       /* Resize VPD field */
+       if ( ( rc = nvs_vpd_resize ( nvo->nvs, nvo->address, len ) ) != 0 )
+               return rc;
 
        return 0;
 }
+
+/**
+ * Initialise non-volatile option storage within NVS VPD device
+ *
+ * @v nvsvpd           NVS VPD device
+ * @v field            VPD field descriptor
+ * @v nvo              Non-volatile options block
+ * @v refcnt           Containing object reference counter, or NULL
+ */
+void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, unsigned int field,
+                       struct nvo_block *nvo, struct refcnt *refcnt ) {
+       struct pci_device *pci = nvsvpd->vpd.pci;
+       unsigned int address;
+       size_t len;
+       int rc;
+
+       /* Locate VPD field, if present */
+       if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
+                                  &len ) ) != 0 ) {
+               DBGC ( pci, PCI_FMT " NVS VPD field " PCI_VPD_FIELD_FMT
+                      " not present; assuming empty\n",
+                      PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ) );
+               len = 0;
+       }
+
+       /* Initialise non-volatile options block */
+       nvo_init ( nvo, &nvsvpd->nvs, field, len, nvs_vpd_nvo_resize, refcnt );
+}
index 2d0422e46a89c4662562fb36290327a9c5752e67..f3a8efb0331fae00f9fa6ef23e6d7b993eead378 100644 (file)
@@ -73,6 +73,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_spi                 ( ERRFILE_DRIVER | 0x00110000 )
 #define ERRFILE_i2c_bit                     ( ERRFILE_DRIVER | 0x00120000 )
 #define ERRFILE_spi_bit                     ( ERRFILE_DRIVER | 0x00130000 )
+#define ERRFILE_nvsvpd              ( ERRFILE_DRIVER | 0x00140000 )
 
 #define ERRFILE_3c509               ( ERRFILE_DRIVER | 0x00200000 )
 #define ERRFILE_bnx2                ( ERRFILE_DRIVER | 0x00210000 )
index 5f80844e72b1233aa3dfc3fc27c3244ed18d098d..3450e5c7121788ee62d453b53af69108b25b5520 100644 (file)
@@ -13,21 +13,21 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/nvs.h>
 #include <ipxe/pcivpd.h>
 
+struct nvo_block;
+struct refcnt;
+
 /** An NVS VPD device */
 struct nvs_vpd_device {
        /** NVS device */
        struct nvs_device nvs;
        /** PCI VPD device */
        struct pci_vpd vpd;
-       /** Starting address
-        *
-        * This address is added to the NVS address to form the VPD
-        * address.
-        */
-       unsigned int address;
 };
 
-extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci,
-                         unsigned int field );
+extern int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd,
+                         struct pci_device *pci );
+extern void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd,
+                              unsigned int field, struct nvo_block *nvo,
+                              struct refcnt *refcnt );
 
 #endif /* IPXE_NVSVPD_H */