2 * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 * You can also choose to distribute this program under the terms of
20 * the Unmodified Binary Distribution Licence (as given in the file
21 * COPYING.UBDL), provided that you have satisfied its requirements.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
29 #include <ipxe/uaccess.h>
30 #include <ipxe/list.h>
31 #include <ipxe/ethernet.h>
32 #include <ipxe/bofm.h>
36 * IBM BladeCenter Open Fabric Manager (BOFM)
40 /** List of BOFM devices */
41 static LIST_HEAD ( bofmdevs );
44 * Register BOFM device
47 * @ret rc Return status code
49 int bofm_register ( struct bofm_device *bofm ) {
51 list_add ( &bofm->list, &bofmdevs );
52 DBG ( "BOFM: " PCI_FMT " registered using driver \"%s\"\n",
53 PCI_ARGS ( bofm->pci ), bofm->pci->id->name );
58 * Unregister BOFM device
62 void bofm_unregister ( struct bofm_device *bofm ) {
64 list_del ( &bofm->list );
65 DBG ( "BOFM: " PCI_FMT " unregistered\n", PCI_ARGS ( bofm->pci ) );
69 * Find BOFM device matching PCI bus:dev.fn address
71 * @v busdevfn PCI bus:dev.fn address
72 * @ret bofm BOFM device, or NULL
74 static struct bofm_device * bofm_find_busdevfn ( unsigned int busdevfn ) {
75 struct bofm_device *bofm;
77 list_for_each_entry ( bofm, &bofmdevs, list ) {
78 if ( bofm->pci->busdevfn == busdevfn )
85 * Find BOFM driver for PCI device
88 * @ret rc Return status code
90 int bofm_find_driver ( struct pci_device *pci ) {
91 struct pci_driver *driver;
92 struct pci_device_id *id;
95 for_each_table_entry ( driver, BOFM_DRIVERS ) {
96 for ( i = 0 ; i < driver->id_count ; i++ ) {
98 if ( ( id->vendor == pci->vendor ) &&
99 ( id->device == pci->device ) ) {
100 pci_set_driver ( pci, driver, id );
109 * Probe PCI device for BOFM driver
112 * @ret rc Return status code
114 static int bofm_probe ( struct pci_device *pci ) {
118 if ( ( rc = pci_probe ( pci ) ) != 0 ) {
119 DBG ( "BOFM: " PCI_FMT " could not load driver: %s\n",
120 PCI_ARGS ( pci ), strerror ( rc ) );
132 static void bofm_remove ( struct pci_device *pci ) {
134 /* Note that the IBM BIOS may re-read the expansion ROM after
135 * the BOFM initialisation call. The BOFM driver must ensure
136 * that the card is left in a state in which expansion ROM
137 * reads will succeed. (For example, if a card contains an
138 * embedded CPU that may issue reads to the same underlying
139 * flash device, and these reads are not locked against reads
140 * via the expansion ROM BAR, then the CPU must be stopped.)
142 * If this is not done, then occasional corrupted reads from
143 * the expansion ROM will be seen, and the BIOS may complain
144 * about a ROM checksum error.
147 DBG ( "BOFM: " PCI_FMT " removed\n", PCI_ARGS ( pci ) );
151 * Locate BOFM table section
153 * @v bofmtab BOFM table
154 * @v len Length of BOFM table
155 * @v magic Section magic
156 * @v bofmsec BOFM section header to fill in
157 * @ret offset Offset to section, or 0 if not found
159 static size_t bofm_locate_section ( userptr_t bofmtab, size_t len,
161 struct bofm_section_header *bofmsec ) {
162 size_t offset = sizeof ( struct bofm_global_header );
164 while ( offset < len ) {
165 copy_from_user ( bofmsec, bofmtab, offset,
166 sizeof ( *bofmsec ) );
167 if ( bofmsec->magic == magic )
169 if ( bofmsec->magic == BOFM_DONE_MAGIC )
171 offset += ( sizeof ( *bofmsec ) + bofmsec->length );
177 * Process BOFM Ethernet parameter entry
179 * @v bofm BOFM device
180 * @v en EN parameter entry
181 * @ret rc Return status code
183 static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) {
187 /* Retrieve current MAC address */
188 if ( ( rc = bofm->op->harvest ( bofm, en->mport, mac ) ) != 0 ) {
189 DBG ( "BOFM: " PCI_FMT " mport %d could not harvest: %s\n",
190 PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
194 /* Harvest MAC address if necessary */
195 if ( en->options & BOFM_EN_RQ_HVST_MASK ) {
196 DBG ( "BOFM: " PCI_FMT " mport %d harvested MAC %s\n",
197 PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
198 memcpy ( en->mac_a, mac, sizeof ( en->mac_a ) );
199 en->options |= ( BOFM_EN_EN_A | BOFM_EN_HVST );
202 /* Mark as changed if necessary */
203 if ( ( en->options & BOFM_EN_EN_A ) &&
204 ( memcmp ( en->mac_a, mac, sizeof ( en->mac_a ) ) != 0 ) ) {
205 DBG ( "BOFM: " PCI_FMT " mport %d MAC %s",
206 PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
207 DBG ( " changed to %s\n", eth_ntoa ( en->mac_a ) );
208 en->options |= BOFM_EN_CHG_CHANGED;
211 /* Apply MAC address if necessary */
212 if ( ( en->options & BOFM_EN_EN_A ) &&
213 ( en->options & BOFM_EN_USAGE_ENTRY ) &&
214 ( ! ( en->options & BOFM_EN_USAGE_HARVEST ) ) ) {
215 DBG ( "BOFM: " PCI_FMT " mport %d applied MAC %s\n",
216 PCI_ARGS ( bofm->pci ), en->mport,
217 eth_ntoa ( en->mac_a ) );
218 memcpy ( mac, en->mac_a, sizeof ( mac ) );
221 /* Store MAC address */
222 if ( ( rc = bofm->op->update ( bofm, en->mport, mac ) ) != 0 ) {
223 DBG ( "BOFM: " PCI_FMT " mport %d could not update: %s\n",
224 PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
234 * @v bofmtab BOFM table
236 * @ret bofmrc BOFM return status
238 int bofm ( userptr_t bofmtab, struct pci_device *pci ) {
239 struct bofm_global_header bofmhdr;
240 struct bofm_section_header bofmsec;
242 struct bofm_device *bofm;
243 size_t en_region_offset;
249 /* Read BOFM structure */
250 copy_from_user ( &bofmhdr, bofmtab, 0, sizeof ( bofmhdr ) );
251 if ( bofmhdr.magic != BOFM_IOAA_MAGIC ) {
252 DBG ( "BOFM: invalid table signature " BOFM_MAGIC_FMT "\n",
253 BOFM_MAGIC_ARGS ( bofmhdr.magic ) );
254 bofmrc = BOFM_ERR_INVALID_ACTION;
255 goto err_bad_signature;
257 DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n",
258 BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile );
260 /* Determine whether or not we should skip normal POST
263 switch ( bofmhdr.action ) {
264 case BOFM_ACTION_UPDT:
265 case BOFM_ACTION_DFLT:
266 case BOFM_ACTION_HVST:
267 skip = BOFM_SKIP_INIT;
269 case BOFM_ACTION_PARM:
270 case BOFM_ACTION_NONE:
274 DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n",
275 BOFM_MAGIC_ARGS ( bofmhdr.action ) );
276 bofmrc = BOFM_ERR_INVALID_ACTION;
280 /* Find BOFM driver */
281 if ( ( rc = bofm_find_driver ( pci ) ) != 0 ) {
282 DBG ( "BOFM: " PCI_FMT " has no driver\n", PCI_ARGS ( pci ) );
283 bofmrc = BOFM_ERR_DEVICE_ERROR;
284 goto err_find_driver;
287 /* Probe driver for PCI device */
288 if ( ( rc = bofm_probe ( pci ) ) != 0 ) {
289 bofmrc = BOFM_ERR_DEVICE_ERROR;
293 /* Locate EN section, if present */
294 en_region_offset = bofm_locate_section ( bofmtab, bofmhdr.length,
295 BOFM_EN_MAGIC, &bofmsec );
296 if ( ! en_region_offset ) {
297 DBG ( "BOFM: No EN section found\n" );
298 bofmrc = ( BOFM_SUCCESS | skip );
299 goto err_no_en_section;
302 /* Iterate through EN entries */
303 for ( en_offset = ( en_region_offset + sizeof ( bofmsec ) ) ;
304 en_offset < ( en_region_offset + sizeof ( bofmsec ) +
305 bofmsec.length ) ; en_offset += sizeof ( en ) ) {
306 copy_from_user ( &en, bofmtab, en_offset, sizeof ( en ) );
307 DBG2 ( "BOFM: EN entry found:\n" );
308 DBG2_HDA ( en_offset, &en, sizeof ( en ) );
309 if ( ( en.options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) {
310 DBG ( "BOFM: slot %d port %d has no PCI mapping\n",
311 en.slot, ( en.port + 1 ) );
314 DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n",
315 en.slot, ( en.port + 1 ),
316 ( ( en.slot || en.port ) ? "" : "(?)" ),
317 PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
318 PCI_FUNC ( en.busdevfn ), en.mport );
319 bofm = bofm_find_busdevfn ( en.busdevfn );
321 DBG ( "BOFM: " PCI_FMT " mport %d ignored\n",
322 PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
323 PCI_FUNC ( en.busdevfn ), en.mport );
326 if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
327 en.options |= BOFM_EN_CSM_SUCCESS;
329 en.options |= BOFM_EN_CSM_FAILED;
331 DBG2 ( "BOFM: EN entry after processing:\n" );
332 DBG2_HDA ( en_offset, &en, sizeof ( en ) );
333 copy_to_user ( bofmtab, en_offset, &en, sizeof ( en ) );
336 bofmrc = ( BOFM_SUCCESS | skip );