|
|
1.1 root 1: /*
2: * Copyright (C) 2011 Michael Brown <[email protected]>.
3: *
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.
8: *
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.
13: *
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., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: FILE_LICENCE ( GPL2_OR_LATER );
20:
21: #include <stdint.h>
22: #include <string.h>
23: #include <errno.h>
24: #include <ipxe/uaccess.h>
25: #include <ipxe/list.h>
26: #include <ipxe/ethernet.h>
27: #include <ipxe/bofm.h>
28:
29: /** @file
30: *
31: * IBM BladeCenter Open Fabric Manager (BOFM)
32: *
33: */
34:
35: /** List of BOFM devices */
36: static LIST_HEAD ( bofmdevs );
37:
38: /**
39: * Register BOFM device
40: *
41: * @v bofm BOFM device
42: * @ret rc Return status code
43: */
44: int bofm_register ( struct bofm_device *bofm ) {
45:
46: list_add ( &bofm->list, &bofmdevs );
47: DBG ( "BOFM: " PCI_FMT " registered using driver \"%s\"\n",
48: PCI_ARGS ( bofm->pci ), bofm->pci->id->name );
49: return 0;
50: }
51:
52: /**
53: * Unregister BOFM device
54: *
55: * @v bofm BOFM device
56: */
57: void bofm_unregister ( struct bofm_device *bofm ) {
58:
59: list_del ( &bofm->list );
60: DBG ( "BOFM: " PCI_FMT " unregistered\n", PCI_ARGS ( bofm->pci ) );
61: }
62:
63: /**
64: * Find BOFM device matching PCI bus:dev.fn address
65: *
66: * @v busdevfn PCI bus:dev.fn address
67: * @ret bofm BOFM device, or NULL
68: */
69: static struct bofm_device * bofm_find_busdevfn ( unsigned int busdevfn ) {
70: struct bofm_device *bofm;
71:
72: list_for_each_entry ( bofm, &bofmdevs, list ) {
73: if ( bofm->pci->busdevfn == busdevfn )
74: return bofm;
75: }
76: return NULL;
77: }
78:
79: /**
80: * Find BOFM driver for PCI device
81: *
82: * @v pci PCI device
83: * @ret rc Return status code
84: */
85: int bofm_find_driver ( struct pci_device *pci ) {
86: struct pci_driver *driver;
87: struct pci_device_id *id;
88: unsigned int i;
89:
90: for_each_table_entry ( driver, BOFM_DRIVERS ) {
91: for ( i = 0 ; i < driver->id_count ; i++ ) {
92: id = &driver->ids[i];
93: if ( ( id->vendor == pci->vendor ) &&
94: ( id->device == pci->device ) ) {
95: pci_set_driver ( pci, driver, id );
96: return 0;
97: }
98: }
99: }
100: return -ENOENT;
101: }
102:
103: /**
104: * Probe PCI device for BOFM driver
105: *
106: * @v pci PCI device
107: * @ret rc Return status code
108: */
109: static int bofm_probe ( struct pci_device *pci ) {
110: int rc;
111:
112: /* Probe device */
113: if ( ( rc = pci_probe ( pci ) ) != 0 ) {
114: DBG ( "BOFM: " PCI_FMT " could not load driver: %s\n",
115: PCI_ARGS ( pci ), strerror ( rc ) );
116: return rc;
117: }
118:
119: return 0;
120: }
121:
122: /**
123: * Remove PCI device
124: *
125: * @v pci PCI device
126: */
127: static void bofm_remove ( struct pci_device *pci ) {
128:
129: /* Note that the IBM BIOS may re-read the expansion ROM after
130: * the BOFM initialisation call. The BOFM driver must ensure
131: * that the card is left in a state in which expansion ROM
132: * reads will succeed. (For example, if a card contains an
133: * embedded CPU that may issue reads to the same underlying
134: * flash device, and these reads are not locked against reads
135: * via the expansion ROM BAR, then the CPU must be stopped.)
136: *
137: * If this is not done, then occasional corrupted reads from
138: * the expansion ROM will be seen, and the BIOS may complain
139: * about a ROM checksum error.
140: */
141: pci_remove ( pci );
142: DBG ( "BOFM: " PCI_FMT " removed\n", PCI_ARGS ( pci ) );
143: }
144:
145: /**
146: * Locate BOFM table section
147: *
148: * @v bofmtab BOFM table
149: * @v len Length of BOFM table
150: * @v magic Section magic
151: * @v bofmsec BOFM section header to fill in
152: * @ret offset Offset to section, or 0 if not found
153: */
154: static size_t bofm_locate_section ( userptr_t bofmtab, size_t len,
155: uint32_t magic,
156: struct bofm_section_header *bofmsec ) {
157: size_t offset = sizeof ( struct bofm_global_header );
158:
159: while ( offset < len ) {
160: copy_from_user ( bofmsec, bofmtab, offset,
161: sizeof ( *bofmsec ) );
162: if ( bofmsec->magic == magic )
163: return offset;
164: if ( bofmsec->magic == BOFM_DONE_MAGIC )
165: break;
166: offset += ( sizeof ( *bofmsec ) + bofmsec->length );
167: }
168: return 0;
169: }
170:
171: /**
172: * Process BOFM Ethernet parameter entry
173: *
174: * @v bofm BOFM device
175: * @v en EN parameter entry
176: * @ret rc Return status code
177: */
178: static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) {
179: uint8_t mac[6];
180: int rc;
181:
182: /* Retrieve current MAC address */
183: if ( ( rc = bofm->op->harvest ( bofm, en->mport, mac ) ) != 0 ) {
184: DBG ( "BOFM: " PCI_FMT " port %d could not harvest: %s\n",
185: PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
186: return rc;
187: }
188:
189: /* Harvest MAC address if necessary */
190: if ( en->options & BOFM_EN_RQ_HVST_MASK ) {
191: DBG ( "BOFM: " PCI_FMT " port %d harvested MAC %s\n",
192: PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
193: memcpy ( en->mac_a, mac, sizeof ( en->mac_a ) );
194: en->options |= ( BOFM_EN_EN_A | BOFM_EN_HVST );
195: }
196:
197: /* Mark as changed if necessary */
198: if ( ( en->options & BOFM_EN_EN_A ) &&
199: ( memcmp ( en->mac_a, mac, sizeof ( en->mac_a ) ) != 0 ) ) {
200: DBG ( "BOFM: " PCI_FMT " port %d MAC %s",
201: PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
202: DBG ( " changed to %s\n", eth_ntoa ( en->mac_a ) );
203: en->options |= BOFM_EN_CHG_CHANGED;
204: }
205:
206: /* Apply MAC address if necessary */
207: if ( ( en->options & BOFM_EN_EN_A ) &&
208: ( en->options & BOFM_EN_USAGE_ENTRY ) &&
209: ( ! ( en->options & BOFM_EN_USAGE_HARVEST ) ) ) {
210: DBG ( "BOFM: " PCI_FMT " port %d applied MAC %s\n",
211: PCI_ARGS ( bofm->pci ), en->mport,
212: eth_ntoa ( en->mac_a ) );
213: memcpy ( mac, en->mac_a, sizeof ( mac ) );
214: }
215:
216: /* Store MAC address */
217: if ( ( rc = bofm->op->update ( bofm, en->mport, mac ) ) != 0 ) {
218: DBG ( "BOFM: " PCI_FMT " port %d could not update: %s\n",
219: PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
220: return rc;
221: }
222:
223: return 0;
224: }
225:
226: /**
227: * Process BOFM table
228: *
229: * @v bofmtab BOFM table
230: * @v pci PCI device
231: * @ret bofmrc BOFM return status
232: */
233: int bofm ( userptr_t bofmtab, struct pci_device *pci ) {
234: struct bofm_global_header bofmhdr;
235: struct bofm_section_header bofmsec;
236: struct bofm_en en;
237: struct bofm_device *bofm;
238: size_t en_region_offset;
239: size_t en_offset;
240: int skip;
241: int rc;
242: int bofmrc;
243:
244: /* Read BOFM structure */
245: copy_from_user ( &bofmhdr, bofmtab, 0, sizeof ( bofmhdr ) );
246: if ( bofmhdr.magic != BOFM_IOAA_MAGIC ) {
247: DBG ( "BOFM: invalid table signature " BOFM_MAGIC_FMT "\n",
248: BOFM_MAGIC_ARGS ( bofmhdr.magic ) );
249: bofmrc = BOFM_ERR_INVALID_ACTION;
250: goto err_bad_signature;
251: }
252: DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n",
253: BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile );
254:
255: /* Determine whether or not we should skip normal POST
256: * initialisation.
257: */
258: switch ( bofmhdr.action ) {
259: case BOFM_ACTION_UPDT:
260: case BOFM_ACTION_DFLT:
261: case BOFM_ACTION_HVST:
262: skip = BOFM_SKIP_INIT;
263: break;
264: case BOFM_ACTION_PARM:
265: case BOFM_ACTION_NONE:
266: skip = 0;
267: break;
268: default:
269: DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n",
270: BOFM_MAGIC_ARGS ( bofmhdr.action ) );
271: bofmrc = BOFM_ERR_INVALID_ACTION;
272: goto err_bad_action;
273: }
274:
275: /* Find BOFM driver */
276: if ( ( rc = bofm_find_driver ( pci ) ) != 0 ) {
277: DBG ( "BOFM: " PCI_FMT " has no driver\n", PCI_ARGS ( pci ) );
278: bofmrc = BOFM_ERR_DEVICE_ERROR;
279: goto err_find_driver;
280: }
281:
282: /* Probe driver for PCI device */
283: if ( ( rc = bofm_probe ( pci ) ) != 0 ) {
284: bofmrc = BOFM_ERR_DEVICE_ERROR;
285: goto err_probe;
286: }
287:
288: /* Locate EN section, if present */
289: en_region_offset = bofm_locate_section ( bofmtab, bofmhdr.length,
290: BOFM_EN_MAGIC, &bofmsec );
291: if ( ! en_region_offset ) {
292: DBG ( "BOFM: No EN section found\n" );
293: bofmrc = ( BOFM_SUCCESS | skip );
294: goto err_no_en_section;
295: }
296:
297: /* Iterate through EN entries */
298: for ( en_offset = ( en_region_offset + sizeof ( bofmsec ) ) ;
299: en_offset < ( en_region_offset + sizeof ( bofmsec ) +
300: bofmsec.length ) ; en_offset += sizeof ( en ) ) {
301: copy_from_user ( &en, bofmtab, en_offset, sizeof ( en ) );
302: DBG2 ( "BOFM: EN entry found:\n" );
303: DBG2_HDA ( en_offset, &en, sizeof ( en ) );
304: if ( ( en.options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) {
305: DBG ( "BOFM: slot %d port %d has no PCI mapping\n",
306: en.slot, en.port );
307: continue;
308: }
309: bofm = bofm_find_busdevfn ( en.busdevfn );
310: if ( ! bofm ) {
311: DBG ( "BOFM: " PCI_FMT " ignored\n",
312: PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
313: PCI_FUNC ( en.busdevfn ) );
314: continue;
315: }
316: if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
317: en.options |= BOFM_EN_CSM_SUCCESS;
318: } else {
319: en.options |= BOFM_EN_CSM_FAILED;
320: }
321: DBG2 ( "BOFM: EN entry after processing:\n" );
322: DBG2_HDA ( en_offset, &en, sizeof ( en ) );
323: copy_to_user ( bofmtab, en_offset, &en, sizeof ( en ) );
324: }
325:
326: bofmrc = ( BOFM_SUCCESS | skip );
327:
328: err_no_en_section:
329: bofm_remove ( pci );
330: err_probe:
331: err_find_driver:
332: err_bad_action:
333: err_bad_signature:
334: return bofmrc;
335: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.