These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / interface / bofm / bofm.c
1 /*
2  * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
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., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
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.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 #include <stdint.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <ipxe/uaccess.h>
30 #include <ipxe/list.h>
31 #include <ipxe/ethernet.h>
32 #include <ipxe/bofm.h>
33
34 /** @file
35  *
36  * IBM BladeCenter Open Fabric Manager (BOFM)
37  *
38  */
39
40 /** List of BOFM devices */
41 static LIST_HEAD ( bofmdevs );
42
43 /**
44  * Register BOFM device
45  *
46  * @v bofm              BOFM device
47  * @ret rc              Return status code
48  */
49 int bofm_register ( struct bofm_device *bofm ) {
50
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 );
54         return 0;
55 }
56
57 /**
58  * Unregister BOFM device
59  *
60  * @v bofm              BOFM device
61  */
62 void bofm_unregister ( struct bofm_device *bofm ) {
63
64         list_del ( &bofm->list );
65         DBG ( "BOFM: " PCI_FMT " unregistered\n", PCI_ARGS ( bofm->pci ) );
66 }
67
68 /**
69  * Find BOFM device matching PCI bus:dev.fn address
70  *
71  * @v busdevfn          PCI bus:dev.fn address
72  * @ret bofm            BOFM device, or NULL
73  */
74 static struct bofm_device * bofm_find_busdevfn ( unsigned int busdevfn ) {
75         struct bofm_device *bofm;
76
77         list_for_each_entry ( bofm, &bofmdevs, list ) {
78                 if ( bofm->pci->busdevfn == busdevfn )
79                         return bofm;
80         }
81         return NULL;
82 }
83
84 /**
85  * Find BOFM driver for PCI device
86  *
87  * @v pci               PCI device
88  * @ret rc              Return status code
89  */
90 int bofm_find_driver ( struct pci_device *pci ) {
91         struct pci_driver *driver;
92         struct pci_device_id *id;
93         unsigned int i;
94
95         for_each_table_entry ( driver, BOFM_DRIVERS ) {
96                 for ( i = 0 ; i < driver->id_count ; i++ ) {
97                         id = &driver->ids[i];
98                         if ( ( id->vendor == pci->vendor ) &&
99                              ( id->device == pci->device ) ) {
100                                 pci_set_driver ( pci, driver, id );
101                                 return 0;
102                         }
103                 }
104         }
105         return -ENOENT;
106 }
107
108 /**
109  * Probe PCI device for BOFM driver
110  *
111  * @v pci               PCI device
112  * @ret rc              Return status code
113  */
114 static int bofm_probe ( struct pci_device *pci ) {
115         int rc;
116
117         /* Probe device */
118         if ( ( rc = pci_probe ( pci ) ) != 0 ) {
119                 DBG ( "BOFM: " PCI_FMT " could not load driver: %s\n",
120                       PCI_ARGS ( pci ), strerror ( rc ) );
121                 return rc;
122         }
123
124         return 0;
125 }
126
127 /**
128  * Remove PCI device
129  *
130  * @v pci               PCI device
131  */
132 static void bofm_remove ( struct pci_device *pci ) {
133
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.)
141          *
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.
145          */
146         pci_remove ( pci );
147         DBG ( "BOFM: " PCI_FMT " removed\n", PCI_ARGS ( pci ) );
148 }
149
150 /**
151  * Locate BOFM table section
152  *
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
158  */
159 static size_t bofm_locate_section ( userptr_t bofmtab, size_t len,
160                                     uint32_t magic,
161                                     struct bofm_section_header *bofmsec ) {
162         size_t offset = sizeof ( struct bofm_global_header );
163
164         while ( offset < len ) {
165                 copy_from_user ( bofmsec, bofmtab, offset,
166                                  sizeof ( *bofmsec ) );
167                 if ( bofmsec->magic == magic )
168                         return offset;
169                 if ( bofmsec->magic == BOFM_DONE_MAGIC )
170                         break;
171                 offset += ( sizeof ( *bofmsec ) + bofmsec->length );
172         }
173         return 0;
174 }
175
176 /**
177  * Process BOFM Ethernet parameter entry
178  *
179  * @v bofm              BOFM device
180  * @v en                EN parameter entry
181  * @ret rc              Return status code
182  */
183 static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) {
184         uint8_t mac[6];
185         int rc;
186
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 ) );
191                 return rc;
192         }
193
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 );
200         }
201
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;
209         }
210
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 ) );
219         }
220
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 ) );
225                 return rc;
226         }
227
228         return 0;
229 }
230
231 /**
232  * Process BOFM table
233  *
234  * @v bofmtab           BOFM table
235  * @v pci               PCI device
236  * @ret bofmrc          BOFM return status
237  */
238 int bofm ( userptr_t bofmtab, struct pci_device *pci ) {
239         struct bofm_global_header bofmhdr;
240         struct bofm_section_header bofmsec;
241         struct bofm_en en;
242         struct bofm_device *bofm;
243         size_t en_region_offset;
244         size_t en_offset;
245         int skip;
246         int rc;
247         int bofmrc;
248
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;
256         }
257         DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n",
258               BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile );
259
260         /* Determine whether or not we should skip normal POST
261          * initialisation.
262          */
263         switch ( bofmhdr.action ) {
264         case BOFM_ACTION_UPDT:
265         case BOFM_ACTION_DFLT:
266         case BOFM_ACTION_HVST:
267                 skip = BOFM_SKIP_INIT;
268                 break;
269         case BOFM_ACTION_PARM:
270         case BOFM_ACTION_NONE:
271                 skip = 0;
272                 break;
273         default:
274                 DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n",
275                       BOFM_MAGIC_ARGS ( bofmhdr.action ) );
276                 bofmrc = BOFM_ERR_INVALID_ACTION;
277                 goto err_bad_action;
278         }
279
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;
285         }
286
287         /* Probe driver for PCI device */
288         if ( ( rc = bofm_probe ( pci ) ) != 0 ) {
289                 bofmrc = BOFM_ERR_DEVICE_ERROR;
290                 goto err_probe;
291         }
292
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;
300         }
301
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 ) );
312                         continue;
313                 }
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 );
320                 if ( ! bofm ) {
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 );
324                         continue;
325                 }
326                 if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
327                         en.options |= BOFM_EN_CSM_SUCCESS;
328                 } else {
329                         en.options |= BOFM_EN_CSM_FAILED;
330                 }
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 ) );
334         }
335
336         bofmrc = ( BOFM_SUCCESS | skip );
337
338  err_no_en_section:
339         bofm_remove ( pci );
340  err_probe:
341  err_find_driver:
342  err_bad_action:
343  err_bad_signature:
344         return bofmrc;
345 }