iPXE
|
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 }