iPXE
bofm.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2011 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 <stdint.h>
00027 #include <string.h>
00028 #include <errno.h>
00029 #include <ipxe/uaccess.h>
00030 #include <ipxe/list.h>
00031 #include <ipxe/ethernet.h>
00032 #include <ipxe/bofm.h>
00033 
00034 /** @file
00035  *
00036  * IBM BladeCenter Open Fabric Manager (BOFM)
00037  *
00038  */
00039 
00040 /** List of BOFM devices */
00041 static LIST_HEAD ( bofmdevs );
00042 
00043 /**
00044  * Register BOFM device
00045  *
00046  * @v bofm              BOFM device
00047  * @ret rc              Return status code
00048  */
00049 int bofm_register ( struct bofm_device *bofm ) {
00050 
00051         list_add ( &bofm->list, &bofmdevs );
00052         DBG ( "BOFM: " PCI_FMT " registered using driver \"%s\"\n",
00053               PCI_ARGS ( bofm->pci ), bofm->pci->id->name );
00054         return 0;
00055 }
00056 
00057 /**
00058  * Unregister BOFM device
00059  *
00060  * @v bofm              BOFM device
00061  */
00062 void bofm_unregister ( struct bofm_device *bofm ) {
00063 
00064         list_del ( &bofm->list );
00065         DBG ( "BOFM: " PCI_FMT " unregistered\n", PCI_ARGS ( bofm->pci ) );
00066 }
00067 
00068 /**
00069  * Find BOFM device matching PCI bus:dev.fn address
00070  *
00071  * @v busdevfn          PCI bus:dev.fn address
00072  * @ret bofm            BOFM device, or NULL
00073  */
00074 static struct bofm_device * bofm_find_busdevfn ( unsigned int busdevfn ) {
00075         struct bofm_device *bofm;
00076 
00077         list_for_each_entry ( bofm, &bofmdevs, list ) {
00078                 if ( bofm->pci->busdevfn == busdevfn )
00079                         return bofm;
00080         }
00081         return NULL;
00082 }
00083 
00084 /**
00085  * Find BOFM driver for PCI device
00086  *
00087  * @v pci               PCI device
00088  * @ret rc              Return status code
00089  */
00090 int bofm_find_driver ( struct pci_device *pci ) {
00091         struct pci_driver *driver;
00092         struct pci_device_id *id;
00093         unsigned int i;
00094 
00095         for_each_table_entry ( driver, BOFM_DRIVERS ) {
00096                 for ( i = 0 ; i < driver->id_count ; i++ ) {
00097                         id = &driver->ids[i];
00098                         if ( ( id->vendor == pci->vendor ) &&
00099                              ( id->device == pci->device ) ) {
00100                                 pci_set_driver ( pci, driver, id );
00101                                 return 0;
00102                         }
00103                 }
00104         }
00105         return -ENOENT;
00106 }
00107 
00108 /**
00109  * Probe PCI device for BOFM driver
00110  *
00111  * @v pci               PCI device
00112  * @ret rc              Return status code
00113  */
00114 static int bofm_probe ( struct pci_device *pci ) {
00115         int rc;
00116 
00117         /* Probe device */
00118         if ( ( rc = pci_probe ( pci ) ) != 0 ) {
00119                 DBG ( "BOFM: " PCI_FMT " could not load driver: %s\n",
00120                       PCI_ARGS ( pci ), strerror ( rc ) );
00121                 return rc;
00122         }
00123 
00124         return 0;
00125 }
00126 
00127 /**
00128  * Remove PCI device
00129  *
00130  * @v pci               PCI device
00131  */
00132 static void bofm_remove ( struct pci_device *pci ) {
00133 
00134         /* Note that the IBM BIOS may re-read the expansion ROM after
00135          * the BOFM initialisation call.  The BOFM driver must ensure
00136          * that the card is left in a state in which expansion ROM
00137          * reads will succeed.  (For example, if a card contains an
00138          * embedded CPU that may issue reads to the same underlying
00139          * flash device, and these reads are not locked against reads
00140          * via the expansion ROM BAR, then the CPU must be stopped.)
00141          *
00142          * If this is not done, then occasional corrupted reads from
00143          * the expansion ROM will be seen, and the BIOS may complain
00144          * about a ROM checksum error.
00145          */
00146         pci_remove ( pci );
00147         DBG ( "BOFM: " PCI_FMT " removed\n", PCI_ARGS ( pci ) );
00148 }
00149 
00150 /**
00151  * Locate BOFM table section
00152  *
00153  * @v bofmtab           BOFM table
00154  * @v len               Length of BOFM table
00155  * @v magic             Section magic
00156  * @v bofmsec           BOFM section header to fill in
00157  * @ret offset          Offset to section, or 0 if not found
00158  */
00159 static size_t bofm_locate_section ( userptr_t bofmtab, size_t len,
00160                                     uint32_t magic,
00161                                     struct bofm_section_header *bofmsec ) {
00162         size_t offset = sizeof ( struct bofm_global_header );
00163 
00164         while ( offset < len ) {
00165                 copy_from_user ( bofmsec, bofmtab, offset,
00166                                  sizeof ( *bofmsec ) );
00167                 if ( bofmsec->magic == magic )
00168                         return offset;
00169                 if ( bofmsec->magic == BOFM_DONE_MAGIC )
00170                         break;
00171                 offset += ( sizeof ( *bofmsec ) + bofmsec->length );
00172         }
00173         return 0;
00174 }
00175 
00176 /**
00177  * Process BOFM Ethernet parameter entry
00178  *
00179  * @v bofm              BOFM device
00180  * @v en                EN parameter entry
00181  * @ret rc              Return status code
00182  */
00183 static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) {
00184         uint8_t mac[6];
00185         int rc;
00186 
00187         /* Retrieve current MAC address */
00188         if ( ( rc = bofm->op->harvest ( bofm, en->mport, mac ) ) != 0 ) {
00189                 DBG ( "BOFM: " PCI_FMT " mport %d could not harvest: %s\n",
00190                       PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
00191                 return rc;
00192         }
00193 
00194         /* Harvest MAC address if necessary */
00195         if ( en->options & BOFM_EN_RQ_HVST_MASK ) {
00196                 DBG ( "BOFM: " PCI_FMT " mport %d harvested MAC %s\n",
00197                       PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
00198                 memcpy ( en->mac_a, mac, sizeof ( en->mac_a ) );
00199                 en->options |= ( BOFM_EN_EN_A | BOFM_EN_HVST );
00200         }
00201 
00202         /* Mark as changed if necessary */
00203         if ( ( en->options & BOFM_EN_EN_A ) &&
00204              ( memcmp ( en->mac_a, mac, sizeof ( en->mac_a ) ) != 0 ) ) {
00205                 DBG ( "BOFM: " PCI_FMT " mport %d MAC %s",
00206                       PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
00207                 DBG ( " changed to %s\n", eth_ntoa ( en->mac_a ) );
00208                 en->options |= BOFM_EN_CHG_CHANGED;
00209         }
00210 
00211         /* Apply MAC address if necessary */
00212         if ( ( en->options & BOFM_EN_EN_A ) &&
00213              ( en->options & BOFM_EN_USAGE_ENTRY ) &&
00214              ( ! ( en->options & BOFM_EN_USAGE_HARVEST ) ) ) {
00215                 DBG ( "BOFM: " PCI_FMT " mport %d applied MAC %s\n",
00216                       PCI_ARGS ( bofm->pci ), en->mport,
00217                       eth_ntoa ( en->mac_a ) );
00218                 memcpy ( mac, en->mac_a, sizeof ( mac ) );
00219         }
00220 
00221         /* Store MAC address */
00222         if ( ( rc = bofm->op->update ( bofm, en->mport, mac ) ) != 0 ) {
00223                 DBG ( "BOFM: " PCI_FMT " mport %d could not update: %s\n",
00224                       PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
00225                 return rc;
00226         }
00227 
00228         return 0;
00229 }
00230 
00231 /**
00232  * Process BOFM table
00233  *
00234  * @v bofmtab           BOFM table
00235  * @v pci               PCI device
00236  * @ret bofmrc          BOFM return status
00237  */
00238 int bofm ( userptr_t bofmtab, struct pci_device *pci ) {
00239         struct bofm_global_header bofmhdr;
00240         struct bofm_section_header bofmsec;
00241         struct bofm_en en;
00242         struct bofm_device *bofm;
00243         size_t en_region_offset;
00244         size_t en_offset;
00245         int skip;
00246         int rc;
00247         int bofmrc;
00248 
00249         /* Read BOFM structure */
00250         copy_from_user ( &bofmhdr, bofmtab, 0, sizeof ( bofmhdr ) );
00251         if ( bofmhdr.magic != BOFM_IOAA_MAGIC ) {
00252                 DBG ( "BOFM: invalid table signature " BOFM_MAGIC_FMT "\n",
00253                       BOFM_MAGIC_ARGS ( bofmhdr.magic ) );
00254                 bofmrc = BOFM_ERR_INVALID_ACTION;
00255                 goto err_bad_signature;
00256         }
00257         DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n",
00258               BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile );
00259 
00260         /* Determine whether or not we should skip normal POST
00261          * initialisation.
00262          */
00263         switch ( bofmhdr.action ) {
00264         case BOFM_ACTION_UPDT:
00265         case BOFM_ACTION_DFLT:
00266         case BOFM_ACTION_HVST:
00267                 skip = BOFM_SKIP_INIT;
00268                 break;
00269         case BOFM_ACTION_PARM:
00270         case BOFM_ACTION_NONE:
00271                 skip = 0;
00272                 break;
00273         default:
00274                 DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n",
00275                       BOFM_MAGIC_ARGS ( bofmhdr.action ) );
00276                 bofmrc = BOFM_ERR_INVALID_ACTION;
00277                 goto err_bad_action;
00278         }
00279 
00280         /* Find BOFM driver */
00281         if ( ( rc = bofm_find_driver ( pci ) ) != 0 ) {
00282                 DBG ( "BOFM: " PCI_FMT " has no driver\n", PCI_ARGS ( pci ) );
00283                 bofmrc = BOFM_ERR_DEVICE_ERROR;
00284                 goto err_find_driver;
00285         }
00286 
00287         /* Probe driver for PCI device */
00288         if ( ( rc = bofm_probe ( pci ) ) != 0 ) {
00289                 bofmrc = BOFM_ERR_DEVICE_ERROR;
00290                 goto err_probe;
00291         }
00292 
00293         /* Locate EN section, if present */
00294         en_region_offset = bofm_locate_section ( bofmtab, bofmhdr.length,
00295                                                  BOFM_EN_MAGIC, &bofmsec );
00296         if ( ! en_region_offset ) {
00297                 DBG ( "BOFM: No EN section found\n" );
00298                 bofmrc = ( BOFM_SUCCESS | skip );
00299                 goto err_no_en_section;
00300         }
00301 
00302         /* Iterate through EN entries */
00303         for ( en_offset = ( en_region_offset + sizeof ( bofmsec ) ) ;
00304               en_offset < ( en_region_offset + sizeof ( bofmsec ) +
00305                             bofmsec.length ) ; en_offset += sizeof ( en ) ) {
00306                 copy_from_user ( &en, bofmtab, en_offset, sizeof ( en ) );
00307                 DBG2 ( "BOFM: EN entry found:\n" );
00308                 DBG2_HDA ( en_offset, &en, sizeof ( en ) );
00309                 if ( ( en.options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) {
00310                         DBG ( "BOFM: slot %d port %d has no PCI mapping\n",
00311                               en.slot, ( en.port + 1 ) );
00312                         continue;
00313                 }
00314                 DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n",
00315                       en.slot, ( en.port + 1 ),
00316                       ( ( en.slot || en.port ) ? "" : "(?)" ), 0,
00317                       PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
00318                       PCI_FUNC ( en.busdevfn ), en.mport );
00319                 bofm = bofm_find_busdevfn ( en.busdevfn );
00320                 if ( ! bofm ) {
00321                         DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0,
00322                               PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
00323                               PCI_FUNC ( en.busdevfn ), en.mport );
00324                         continue;
00325                 }
00326                 if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
00327                         en.options |= BOFM_EN_CSM_SUCCESS;
00328                 } else {
00329                         en.options |= BOFM_EN_CSM_FAILED;
00330                 }
00331                 DBG2 ( "BOFM: EN entry after processing:\n" );
00332                 DBG2_HDA ( en_offset, &en, sizeof ( en ) );
00333                 copy_to_user ( bofmtab, en_offset, &en, sizeof ( en ) );
00334         }
00335 
00336         bofmrc = ( BOFM_SUCCESS | skip );
00337 
00338  err_no_en_section:
00339         bofm_remove ( pci );
00340  err_probe:
00341  err_find_driver:
00342  err_bad_action:
00343  err_bad_signature:
00344         return bofmrc;
00345 }