iPXE
nvsvpd.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 #include <stdio.h>
00027 #include <errno.h>
00028 #include <ipxe/nvs.h>
00029 #include <ipxe/pci.h>
00030 #include <ipxe/pcivpd.h>
00031 #include <ipxe/nvo.h>
00032 #include <ipxe/nvsvpd.h>
00033 
00034 /** @file
00035  *
00036  * Non-Volatile Storage using Vital Product Data
00037  *
00038  */
00039 
00040 /**
00041  * Read from VPD field
00042  *
00043  * @v nvs               NVS device
00044  * @v field             VPD field descriptor
00045  * @v data              Data buffer
00046  * @v len               Length of data buffer
00047  * @ret rc              Return status code
00048  */
00049 static int nvs_vpd_read ( struct nvs_device *nvs, unsigned int field,
00050                           void *data, size_t len ) {
00051         struct nvs_vpd_device *nvsvpd =
00052                 container_of ( nvs, struct nvs_vpd_device, nvs );
00053         struct pci_device *pci = nvsvpd->vpd.pci;
00054         unsigned int address;
00055         size_t max_len;
00056         int rc;
00057 
00058         /* Allow reading non-existent field */
00059         if ( len == 0 )
00060                 return 0;
00061 
00062         /* Locate VPD field */
00063         if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
00064                                    &max_len ) ) != 0 ) {
00065                 DBGC ( pci, PCI_FMT " NVS VPD could not locate field "
00066                        PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ),
00067                        PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) );
00068                 return rc;
00069         }
00070 
00071         /* Sanity check */
00072         if ( len > max_len ) {
00073                 DBGC ( pci, PCI_FMT " NVS VPD cannot read %#02zx bytes "
00074                        "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
00075                        PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ),
00076                        address, ( address + max_len ) );
00077                 return -ENXIO;
00078         }
00079 
00080         /* Read from VPD field */
00081         if ( ( rc = pci_vpd_read ( &nvsvpd->vpd, address, data, len ) ) != 0 ) {
00082                 DBGC ( pci, PCI_FMT " NVS VPD could not read field "
00083                        PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n",
00084                        PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
00085                        address, ( address + len ), strerror ( rc ) );
00086                 return rc;
00087         }
00088 
00089         return 0;
00090 }
00091 
00092 /**
00093  * Write to VPD field
00094  *
00095  * @v nvs               NVS device
00096  * @v field             VPD field descriptor
00097  * @v data              Data buffer
00098  * @v len               Length of data buffer
00099  * @ret rc              Return status code
00100  */
00101 static int nvs_vpd_write ( struct nvs_device *nvs, unsigned int field,
00102                            const void *data, size_t len ) {
00103         struct nvs_vpd_device *nvsvpd =
00104                 container_of ( nvs, struct nvs_vpd_device, nvs );
00105         struct pci_device *pci = nvsvpd->vpd.pci;
00106         unsigned int address;
00107         size_t max_len;
00108         int rc;
00109 
00110         /* Locate VPD field */
00111         if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
00112                                    &max_len ) ) != 0 ) {
00113                 DBGC ( pci, PCI_FMT " NVS VPD could not locate field "
00114                        PCI_VPD_FIELD_FMT ": %s\n", PCI_ARGS ( pci ),
00115                        PCI_VPD_FIELD_ARGS ( field ), strerror ( rc ) );
00116                 return rc;
00117         }
00118 
00119         /* Sanity check */
00120         if ( len > max_len ) {
00121                 DBGC ( pci, PCI_FMT " NVS VPD cannot write %#02zx bytes "
00122                        "beyond field " PCI_VPD_FIELD_FMT " at [%04x,%04zx)\n",
00123                        PCI_ARGS ( pci ), len, PCI_VPD_FIELD_ARGS ( field ),
00124                        address, ( address + max_len ) );
00125                 return -ENXIO;
00126         }
00127 
00128         /* Write field */
00129         if ( ( rc = pci_vpd_write ( &nvsvpd->vpd, address, data,
00130                                     len ) ) != 0 ) {
00131                 DBGC ( pci, PCI_FMT " NVS VPD could not write field "
00132                        PCI_VPD_FIELD_FMT " at [%04x,%04zx): %s\n",
00133                        PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
00134                        address, ( address + len ), strerror ( rc ) );
00135                 return rc;
00136         }
00137 
00138         return 0;
00139 }
00140 
00141 /**
00142  * Resize VPD field
00143  *
00144  * @v nvs               NVS device
00145  * @v field             VPD field descriptor
00146  * @v data              Data buffer
00147  * @v len               Length of data buffer
00148  * @ret rc              Return status code
00149  */
00150 static int nvs_vpd_resize ( struct nvs_device *nvs, unsigned int field,
00151                             size_t len ) {
00152         struct nvs_vpd_device *nvsvpd =
00153                 container_of ( nvs, struct nvs_vpd_device, nvs );
00154         struct pci_device *pci = nvsvpd->vpd.pci;
00155         unsigned int address;
00156         int rc;
00157 
00158         /* Resize field */
00159         if ( ( rc = pci_vpd_resize ( &nvsvpd->vpd, field, len,
00160                                      &address ) ) != 0 ) {
00161                 DBGC ( pci, PCI_FMT " NVS VPD could not resize field "
00162                        PCI_VPD_FIELD_FMT " to %#02zx bytes: %s\n",
00163                        PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ),
00164                        len, strerror ( rc ) );
00165                 return rc;
00166         }
00167 
00168         return 0;
00169 }
00170 
00171 /**
00172  * Initialise NVS VPD device
00173  *
00174  * @v nvsvpd            NVS VPD device
00175  * @v pci               PCI device
00176  * @ret rc              Return status code
00177  */
00178 int nvs_vpd_init ( struct nvs_vpd_device *nvsvpd, struct pci_device *pci ) {
00179         int rc;
00180 
00181         /* Initialise VPD device */
00182         if ( ( rc = pci_vpd_init ( &nvsvpd->vpd, pci ) ) != 0 ) {
00183                 DBGC ( pci, PCI_FMT " NVS could not initialise "
00184                        "VPD: %s\n", PCI_ARGS ( pci ), strerror ( rc ) );
00185                 return rc;
00186         }
00187 
00188         /* Initialise NVS device */
00189         nvsvpd->nvs.read = nvs_vpd_read;
00190         nvsvpd->nvs.write = nvs_vpd_write;
00191 
00192         return 0;
00193 }
00194 
00195 /**
00196  * Resize non-volatile option storage within NVS VPD device
00197  *
00198  * @v nvo               Non-volatile options block
00199  * @v len               New length
00200  * @ret rc              Return status code
00201  */
00202 static int nvs_vpd_nvo_resize ( struct nvo_block *nvo, size_t len ) {
00203         int rc;
00204 
00205         /* Resize VPD field */
00206         if ( ( rc = nvs_vpd_resize ( nvo->nvs, nvo->address, len ) ) != 0 )
00207                 return rc;
00208 
00209         return 0;
00210 }
00211 
00212 /**
00213  * Initialise non-volatile option storage within NVS VPD device
00214  *
00215  * @v nvsvpd            NVS VPD device
00216  * @v field             VPD field descriptor
00217  * @v nvo               Non-volatile options block
00218  * @v refcnt            Containing object reference counter, or NULL
00219  */
00220 void nvs_vpd_nvo_init ( struct nvs_vpd_device *nvsvpd, unsigned int field,
00221                         struct nvo_block *nvo, struct refcnt *refcnt ) {
00222         struct pci_device *pci = nvsvpd->vpd.pci;
00223         unsigned int address;
00224         size_t len;
00225         int rc;
00226 
00227         /* Locate VPD field, if present */
00228         if ( ( rc = pci_vpd_find ( &nvsvpd->vpd, field, &address,
00229                                    &len ) ) != 0 ) {
00230                 DBGC ( pci, PCI_FMT " NVS VPD field " PCI_VPD_FIELD_FMT
00231                        " not present; assuming empty\n",
00232                        PCI_ARGS ( pci ), PCI_VPD_FIELD_ARGS ( field ) );
00233                 len = 0;
00234         }
00235 
00236         /* Initialise non-volatile options block */
00237         nvo_init ( nvo, &nvsvpd->nvs, field, len, nvs_vpd_nvo_resize, refcnt );
00238 }