iPXE
bofm.c
Go to the documentation of this file.
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 
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 ) );
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 ) );
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 ) ? "" : "(?)" ), 0,
318  PCI_FUNC ( en.busdevfn ), en.mport );
320  if ( ! bofm ) {
321  DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0,
323  PCI_FUNC ( en.busdevfn ), en.mport );
324  continue;
325  }
326  if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
328  } else {
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 }
#define PCI_FUNC(busdevfn)
Definition: pci.h:258
struct bofm_en en
Definition: bofm_test.c:45
#define PCI_BUS(busdevfn)
Definition: pci.h:256
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
A PCI driver.
Definition: pci.h:224
static void bofm_remove(struct pci_device *pci)
Remove PCI device.
Definition: bofm.c:132
uint8_t slot
Slot or mezzanine number.
Definition: bofm.h:175
#define list_add(new, head)
Add a new entry to the head of a list.
Definition: list.h:69
unsigned int id_count
Number of entries in PCI ID table.
Definition: pci.h:228
Error codes.
#define BOFM_EN_CSM_FAILED
Entry has been used but failed.
Definition: bofm.h:240
uint32_t magic
Signature.
Definition: bofm.h:79
struct pci_device_id * ids
PCI ID table.
Definition: pci.h:226
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
static void pci_set_driver(struct pci_device *pci, struct pci_driver *driver, struct pci_device_id *id)
Set PCI driver.
Definition: pci.h:324
#define BOFM_EN_EN_A
MAC address A is present.
Definition: bofm.h:225
#define BOFM_EN_USAGE_HARVEST
Ignore values - it's harvest time.
Definition: bofm.h:252
#define BOFM_EN_MAGIC
EN start marker.
Definition: bofm.h:151
uint32_t magic
Magic signature.
Definition: fdt.h:12
#define ENOENT
No such file or directory.
Definition: errno.h:514
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
static LIST_HEAD(bofmdevs)
List of BOFM devices.
struct pci_device * pci
Underlying PCI device.
Definition: bofm.h:288
uint8_t mac[ETH_ALEN]
MAC address.
Definition: ena.h:24
#define BOFM_SUCCESS
Successful.
Definition: bofm.h:47
Access to external ("user") memory.
struct list_head list
List of BOFM devices.
Definition: bofm.h:292
#define BOFM_ACTION_UPDT
Update MAC/WWN.
Definition: bofm.h:114
static int bofm_en(struct bofm_device *bofm, struct bofm_en *en)
Process BOFM Ethernet parameter entry.
Definition: bofm.c:183
uint8_t mport
Multi-port index.
Definition: bofm.h:182
#define list_del(list)
Delete an entry from a list.
Definition: list.h:119
void * memcpy(void *dest, const void *src, size_t len) __nonnull
#define BOFM_EN_CHG_CHANGED
Consumed entry is different than previous active entry.
Definition: bofm.h:249
IBM BladeCenter Open Fabric Manager (BOFM)
uint16_t device
Device ID.
Definition: pci.h:204
Ethernet protocol.
#define list_for_each_entry(pos, head, member)
Iterate over entries in a list.
Definition: list.h:420
#define BOFM_MAGIC_FMT
BOFM magic value debug message format.
Definition: bofm.h:278
uint32_t magic
Signature.
Definition: bofm.h:137
uint16_t length
Data structure length.
Definition: bofm.h:87
uint16_t length
Length.
Definition: bofm.h:139
#define BOFM_SKIP_INIT
Skip option ROM initialisation.
Definition: bofm.h:70
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
#define BOFM_EN_MAP_PFA
Port mapping is by PCI bus:dev.fn.
Definition: bofm.h:213
uint32_t action
Subsignature (action string)
Definition: bofm.h:81
#define BOFM_ACTION_NONE
Just initialise the device.
Definition: bofm.h:126
uint16_t busdevfn
PCI bus:dev.fn.
Definition: bofm.h:170
#define BOFM_IOAA_MAGIC
BOFM table header signature.
Definition: bofm.h:103
static int bofm_probe(struct pci_device *pci)
Probe PCI device for BOFM driver.
Definition: bofm.c:114
#define BOFM_DONE_MAGIC
End marker.
Definition: bofm.h:154
uint16_t options
Options.
Definition: bofm.h:165
uint8_t id
Request identifier.
Definition: ena.h:12
Linked lists.
#define BOFM_ERR_DEVICE_ERROR
Device error prohibited MAC/WWN update.
Definition: bofm.h:56
BOFM table header.
Definition: bofm.h:77
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
#define PCI_FMT
PCI device debug message format.
Definition: pci.h:287
#define DBG2_HDA(...)
Definition: compiler.h:516
#define PCI_SLOT(busdevfn)
Definition: pci.h:257
A PCI device.
Definition: pci.h:187
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition: ethernet.c:175
#define BOFM_EN_HVST
Harvest complete.
Definition: bofm.h:261
static __always_inline void copy_to_user(userptr_t dest, off_t dest_off, const void *src, size_t len)
Copy data to user buffer.
Definition: uaccess.h:324
#define BOFM_MAGIC_ARGS(magic)
BOFM magic value debug message arguments.
Definition: bofm.h:281
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition: tables.h:358
A BOFM device.
Definition: bofm.h:286
void pci_remove(struct pci_device *pci)
Remove a PCI device.
Definition: pci.c:308
static struct bofm_device * bofm_find_busdevfn(unsigned int busdevfn)
Find BOFM device matching PCI bus:dev.fn address.
Definition: bofm.c:74
#define BOFM_EN_RQ_HVST_MASK
Harvest request mask.
Definition: bofm.h:264
#define BOFM_ACTION_PARM
Update MAC/WWN and initialise device.
Definition: bofm.h:123
unsigned char uint8_t
Definition: stdint.h:10
BOFM section header.
Definition: bofm.h:135
#define BOFM_EN_USAGE_ENTRY
Use entry values for assignment.
Definition: bofm.h:255
int bofm_register(struct bofm_device *bofm)
Register BOFM device.
Definition: bofm.c:49
A PCI device ID list entry.
Definition: pci.h:151
unsigned int uint32_t
Definition: stdint.h:12
uint16_t vendor
Vendor ID.
Definition: pci.h:202
uint8_t port
Port number.
Definition: bofm.h:180
static size_t bofm_locate_section(userptr_t bofmtab, size_t len, uint32_t magic, struct bofm_section_header *bofmsec)
Locate BOFM table section.
Definition: bofm.c:159
uint8_t mac_a[6]
MAC address A.
Definition: bofm.h:190
#define BOFM_EN_MAP_MASK
Port mapping mask.
Definition: bofm.h:210
uint32_t len
Length.
Definition: ena.h:14
int bofm(userptr_t bofmtab, struct pci_device *pci)
Process BOFM table.
Definition: bofm.c:238
BOFM Ethernet parameter entry.
Definition: bofm.h:163
#define PCI_ARGS(pci)
PCI device debug message arguments.
Definition: pci.h:290
#define BOFM_ERR_INVALID_ACTION
Invalid action string.
Definition: bofm.h:50
int bofm_find_driver(struct pci_device *pci)
Find BOFM driver for PCI device.
Definition: bofm.c:90
void bofm_unregister(struct bofm_device *bofm)
Unregister BOFM device.
Definition: bofm.c:62
#define BOFM_DRIVERS
BOFM driver table.
Definition: bofm.h:318
#define BOFM_EN_CSM_SUCCESS
Entry has been used successfully.
Definition: bofm.h:237
#define DBG(...)
Print a debugging message.
Definition: compiler.h:498
#define BOFM_ACTION_HVST
Harvest MAC/WWN.
Definition: bofm.h:120
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition: string.c:113
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
int pci_probe(struct pci_device *pci)
Probe a PCI device.
Definition: pci.c:286
char profile[32]
Data structure profile.
Definition: bofm.h:91
String functions.
#define BOFM_ACTION_DFLT
Restore MAC/WWN to factory default.
Definition: bofm.h:117
unsigned long userptr_t
A pointer to a user buffer.
Definition: uaccess.h:33
#define DBG2(...)
Definition: compiler.h:515