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