]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[bofm] Add core BOFM library
authorMichael Brown <mcb30@ipxe.org>
Thu, 10 Feb 2011 13:47:38 +0000 (13:47 +0000)
committerMichael Brown <mcb30@ipxe.org>
Wed, 2 Mar 2011 23:58:43 +0000 (23:58 +0000)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/Makefile
src/include/ipxe/bofm.h [new file with mode: 0644]
src/include/ipxe/errfile.h
src/interface/bofm/bofm.c [new file with mode: 0644]

index 1a878ef8f76f2d9bcbc7915ca58c3088037b76bf..8b691a7159bead0ca67baf8dac8fd8adc4d3e310 100644 (file)
@@ -75,6 +75,7 @@ SRCDIRS               += drivers/nvs
 SRCDIRS                += drivers/bitbash
 SRCDIRS                += drivers/infiniband
 SRCDIRS                += interface/pxe interface/efi interface/smbios
+SRCDIRS                += interface/bofm
 SRCDIRS                += tests
 SRCDIRS                += crypto crypto/axtls crypto/matrixssl
 SRCDIRS                += hci hci/commands hci/tui
diff --git a/src/include/ipxe/bofm.h b/src/include/ipxe/bofm.h
new file mode 100644 (file)
index 0000000..2fe1e90
--- /dev/null
@@ -0,0 +1,341 @@
+#ifndef _IPXE_BOFM_H
+#define _IPXE_BOFM_H
+
+/**
+ * @file
+ *
+ * IBM BladeCenter Open Fabric Manager (BOFM)
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <ipxe/list.h>
+#include <ipxe/pci.h>
+
+/** 'IBM ' signature
+ *
+ * Present in %edi when the BIOS initialisation entry point is called,
+ * with the BOFM table pointer in %esi.
+ *
+ * Defined in section 4.1.2 of the POST/BIOS BOFM I/O Address
+ * Re-Assignment Architecture document.
+ */
+#define IBMs_SIGNATURE ( ( 'I' << 24 ) + ( 'B' << 16 ) + ( 'M' << 8 ) + ' ' )
+
+/** ' IBM' signature
+ *
+ * Returned in %edi from the BIOS initialisation entry point, with the
+ * return code in %dl.
+ *
+ * Defined in section 4.1.2 of the POST/BIOS BOFM I/O Address
+ * Re-Assignment Architecture document.
+ */
+#define sIBM_SIGNATURE ( ( ' ' << 24 ) + ( 'I' << 16 ) + ( 'B' << 8 ) + 'M' )
+
+/** @defgroup bofmrc BOFM return codes
+ *
+ * Defined in section 4.1.3 of the POST/BIOS BOFM I/O Address
+ * Re-Assignment Architecture document.
+ *
+ * @{
+ */
+
+/** Successful */
+#define BOFM_SUCCESS 0x00
+
+/** Invalid action string */
+#define BOFM_ERR_INVALID_ACTION 0x01
+
+/** Unsupported parameter structure version */
+#define BOFM_ERR_UNSUPPORTED 0x02
+
+/** Device error prohibited MAC/WWN update */
+#define BOFM_ERR_DEVICE_ERROR 0x03
+
+/** PCI reset required (may be combined with another return code) */
+#define BOFM_PCI_RESET 0x80
+
+/** @} */
+
+/** Skip option ROM initialisation
+ *
+ * A BOFM BIOS may call the initialisation entry point multiple times;
+ * only the last call should result in actual initialisation.
+ *
+ * This flag is internal to iPXE.
+ */
+#define BOFM_SKIP_INIT 0x80000000UL
+
+/** BOFM table header
+ *
+ * Defined in section 4.1 of the Open Fabric Manager Parameter
+ * Specification document.
+ */
+struct bofm_global_header {
+       /** Signature */
+       uint32_t magic;
+       /** Subsignature (action string) */
+       uint32_t action;
+       /** Data structure version */
+       uint8_t version;
+       /** Data structure level */
+       uint8_t level;
+       /** Data structure length */
+       uint16_t length;
+       /** Data structure checksum */
+       uint8_t checksum;
+       /** Data structure profile */
+       char profile[32];
+       /** Data structure global options */
+       uint32_t options;
+       /** Data structure sequence stamp */
+       uint32_t sequence;
+} __attribute__ (( packed ));
+
+/** BOFM table header signature
+ *
+ * Defined in section 4.1.2 of the POST/BIOS BOFM I/O Address
+ * Re-Assignment Architecture document.
+ */
+#define BOFM_IOAA_MAGIC         ( 'I' + ( 'O' << 8 ) + ( 'A' << 16 ) + ( 'A' << 24 ) )
+
+/** @defgroup bofmaction BOFM header subsignatures (action strings)
+ *
+ * Defined in section 4.1.2 of the POST/BIOS BOFM I/O Address
+ * Re-Assignment Architecture document.
+ *
+ * @{
+ */
+
+/** Update MAC/WWN */
+#define BOFM_ACTION_UPDT ( 'U' + ( 'P' << 8 ) + ( 'D' << 16 ) + ( 'T' << 24 ) )
+
+/** Restore MAC/WWN to factory default */
+#define BOFM_ACTION_DFLT ( 'D' + ( 'F' << 8 ) + ( 'L' << 16 ) + ( 'T' << 24 ) )
+
+/** Harvest MAC/WWN */
+#define BOFM_ACTION_HVST ( 'H' + ( 'V' << 8 ) + ( 'S' << 16 ) + ( 'T' << 24 ) )
+
+/** Update MAC/WWN and initialise device */
+#define BOFM_ACTION_PARM ( 'P' + ( 'A' << 8 ) + ( 'R' << 16 ) + ( 'M' << 24 ) )
+
+/** Just initialise the device */
+#define BOFM_ACTION_NONE ( 'N' + ( 'O' << 8 ) + ( 'N' << 16 ) + ( 'E' << 24 ) )
+
+/** @} */
+
+/** BOFM section header
+ *
+ * Defined in section 4.2 of the Open Fabric Manager Parameter
+ * Specification document.
+ */
+struct bofm_section_header {
+       /** Signature */
+       uint32_t magic;
+       /** Length */
+       uint16_t length;
+} __attribute__ (( packed ));
+
+/** @defgroup bofmsections BOFM section header signatures
+ *
+ * Defined in section 4.2 of the Open Fabric Manager Parameter
+ * Specification document.
+ *
+ * @{
+ */
+
+/** EN start marker */
+#define BOFM_EN_MAGIC    ( ' ' + ( ' ' << 8 ) + ( 'E' << 16 ) + ( 'N' << 24 ) )
+
+/** End marker */
+#define BOFM_DONE_MAGIC         ( 'D' + ( 'O' << 8 ) + ( 'N' << 16 ) + ( 'E' << 24 ) )
+
+/** @} */
+
+/** BOFM Ethernet parameter entry
+ *
+ * Defined in section 5.1 of the Open Fabric Manager Parameter
+ * Specification document.
+ */
+struct bofm_en {
+       /** Options */
+       uint16_t options;
+       /** PCI bus:dev.fn
+        *
+        * Valid only if @c options indicates @c BOFM_EN_MAP_PFA
+        */
+       uint16_t busdevfn;
+       /** Slot or mezzanine number
+        *
+        * Valid only if @c options indicates @c BOFM_EN_MAP_SLOT_PORT
+        */
+       uint8_t slot;
+       /** Port number
+        *
+        * Valid only if @c options indicates @c BOFM_EN_MAP_SLOT_PORT
+        */
+       uint8_t port;
+       /** Multi-port index */
+       uint8_t mport;
+       /** VLAN tag for MAC address A */
+       uint16_t vlan_a;
+       /** MAC address A
+        *
+        * MAC address A is the sole MAC address, or the lower
+        * (inclusive) bound of a range of MAC addresses.
+        */
+       uint8_t mac_a[6];
+       /** VLAN tag for MAC address B */
+       uint16_t vlan_b;
+       /** MAC address B
+        *
+        * MAC address B is unset, or the upper (inclusive) bound of a
+        * range of MAC addresses
+        */
+       uint8_t mac_b[6];
+} __attribute__ (( packed ));
+
+/** @defgroup bofmenopts BOFM Ethernet parameter entry options
+ *
+ * Defined in section 5.1 of the Open Fabric Manager Parameter
+ * Specification document.
+ *
+ * @{
+ */
+
+/** Port mapping mask */
+#define BOFM_EN_MAP_MASK       0x0001
+
+/** Port mapping is by PCI bus:dev.fn */
+#define BOFM_EN_MAP_PFA                        0x0000
+
+/** Port mapping is by slot/port */
+#define BOFM_EN_MAP_SLOT_PORT          0x0001
+
+/** MAC address B is present */
+#define BOFM_EN_EN_B           0x0002
+
+/** VLAN tag for MAC address B is present */
+#define BOFM_EN_VLAN_B         0x0004
+
+/** MAC address A is present */
+#define BOFM_EN_EN_A           0x0008
+
+/** VLAN tag for MAC address A is present */
+#define BOFM_EN_VLAN_A         0x0010
+
+/** Entry consumption indicator mask */
+#define BOFM_EN_CSM_MASK       0x00c0
+
+/** Entry has not been used */
+#define BOFM_EN_CSM_UNUSED             0x0000
+
+/** Entry has been used successfully */
+#define BOFM_EN_CSM_SUCCESS            0x0040
+
+/** Entry has been used but failed */
+#define BOFM_EN_CSM_FAILED             0x0080
+
+/** Consumed entry change mask */
+#define BOFM_EN_CHG_MASK       0x0100
+
+/** Consumed entry is same as previous active entry */
+#define BOFM_EN_CHG_UNCHANGED          0x0000
+
+/** Consumed entry is different than previous active entry */
+#define BOFM_EN_CHG_CHANGED            0x0100
+
+/** Ignore values - it's harvest time */
+#define BOFM_EN_USAGE_HARVEST  0x1000
+
+/** Use entry values for assignment */
+#define BOFM_EN_USAGE_ENTRY    0x0800
+
+/** Use factory default values */
+#define BOFM_EN_USAGE_DEFAULT  0x0400
+
+/** Harvest complete */
+#define BOFM_EN_HVST           0x2000
+
+/** Harvest request mask */
+#define BOFM_EN_RQ_HVST_MASK   0xc000
+
+/** Do not harvest */
+#define BOFM_EN_RQ_HVST_NONE           0x0000
+
+/** Harvest factory default values */
+#define BOFM_EN_RQ_HVST_DEFAULT                0x4000
+
+/** Harvest active values */
+#define BOFM_EN_RQ_HVST_ACTIVE         0xc000
+
+/** @} */
+
+/** BOFM magic value debug message format */
+#define BOFM_MAGIC_FMT "'%c%c%c%c'"
+
+/** BOFM magic value debug message arguments */
+#define BOFM_MAGIC_ARGS( magic )                                       \
+       ( ( (magic) >> 0 ) & 0xff ), ( ( (magic) >> 8 ) & 0xff ),       \
+       ( ( (magic) >> 16 ) & 0xff ), ( ( (magic) >> 24 ) & 0xff )
+
+/** A BOFM device */
+struct bofm_device {
+       /** Underlying PCI device */
+       struct pci_device *pci;
+       /** BOFM device operations */
+       struct bofm_operations *op;
+       /** List of BOFM devices */
+       struct list_head list;
+};
+
+/** BOFM device operations */
+struct bofm_operations {
+       /** Harvest Ethernet MAC
+        *
+        * @v bofm              BOFM device
+        * @v mport             Multi-port index
+        * @v mac               MAC to fill in
+        * @ret rc              Return status code
+        */
+       int ( * harvest ) ( struct bofm_device *bofm, unsigned int mport,
+                           uint8_t *mac );
+       /** Update Ethernet MAC
+        *
+        * @v bofm              BOFM device
+        * @v mport             Multi-port index
+        * @v mac               New MAC
+        * @ret rc              Return status code
+        */
+       int ( * update ) ( struct bofm_device *bofm, unsigned int mport,
+                          const uint8_t *mac );
+};
+
+/** BOFM driver table */
+#define BOFM_DRIVERS __table ( struct pci_driver, "bofm_drivers" )
+
+/** Declare a BOFM driver */
+#define __bofm_driver __table_entry ( BOFM_DRIVERS, 01 )
+
+/**
+ * Initialise BOFM device
+ *
+ * @v bofm             BOFM device
+ * @v pci              PCI device
+ * @v op               BOFM device operations
+ */
+static inline __attribute__ (( always_inline )) void
+bofm_init ( struct bofm_device *bofm, struct pci_device *pci,
+           struct bofm_operations *op ) {
+       bofm->pci = pci;
+       bofm->op = op;
+}
+
+extern int bofm_register ( struct bofm_device *bofm );
+extern void bofm_unregister ( struct bofm_device *bofm );
+extern int bofm_find_driver ( struct pci_device *pci );
+extern int bofm ( userptr_t bofmtab, struct pci_device *pci );
+
+#endif /* _IPXE_BOFM_H */
index 24d5b310cd76592d082cea9a529480336881fac5..01de5600eb7adf93f0601e0c121da70e18dd764d 100644 (file)
@@ -236,6 +236,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #define ERRFILE_fcmgmt_cmd           ( ERRFILE_OTHER | 0x001e0000 )
 #define ERRFILE_gdbstub_cmd          ( ERRFILE_OTHER | 0x001f0000 )
 #define ERRFILE_sanboot_cmd          ( ERRFILE_OTHER | 0x00200000 )
+#define ERRFILE_bofm                 ( ERRFILE_OTHER | 0x00210000 )
 
 /** @} */
 
diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c
new file mode 100644 (file)
index 0000000..dfa76d3
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/list.h>
+#include <ipxe/ethernet.h>
+#include <ipxe/bofm.h>
+
+/** @file
+ *
+ * IBM BladeCenter Open Fabric Manager (BOFM)
+ *
+ */
+
+/** List of BOFM devices */
+static LIST_HEAD ( bofmdevs );
+
+/**
+ * Register BOFM device
+ *
+ * @v bofm             BOFM device
+ * @ret rc             Return status code
+ */
+int bofm_register ( struct bofm_device *bofm ) {
+
+       list_add ( &bofm->list, &bofmdevs );
+       DBG ( "BOFM: " PCI_FMT " registered using driver \"%s\"\n",
+             PCI_ARGS ( bofm->pci ), bofm->pci->id->name );
+       return 0;
+}
+
+/**
+ * Unregister BOFM device
+ *
+ * @v bofm             BOFM device
+ */
+void bofm_unregister ( struct bofm_device *bofm ) {
+
+       list_del ( &bofm->list );
+       DBG ( "BOFM: " PCI_FMT " unregistered\n", PCI_ARGS ( bofm->pci ) );
+}
+
+/**
+ * Find BOFM device matching PCI bus:dev.fn address
+ *
+ * @v busdevfn         PCI bus:dev.fn address
+ * @ret bofm           BOFM device, or NULL
+ */
+static struct bofm_device * bofm_find_busdevfn ( unsigned int busdevfn ) {
+       struct bofm_device *bofm;
+
+       list_for_each_entry ( bofm, &bofmdevs, list ) {
+               if ( bofm->pci->busdevfn == busdevfn )
+                       return bofm;
+       }
+       return NULL;
+}
+
+/**
+ * Find BOFM driver for PCI device
+ *
+ * @v pci              PCI device
+ * @ret rc             Return status code
+ */
+int bofm_find_driver ( struct pci_device *pci ) {
+       struct pci_driver *driver;
+       struct pci_device_id *id;
+       unsigned int i;
+
+       for_each_table_entry ( driver, BOFM_DRIVERS ) {
+               for ( i = 0 ; i < driver->id_count ; i++ ) {
+                       id = &driver->ids[i];
+                       if ( ( id->vendor == pci->vendor ) &&
+                            ( id->device == pci->device ) ) {
+                               pci_set_driver ( pci, driver, id );
+                               return 0;
+                       }
+               }
+       }
+       return -ENOENT;
+}
+
+/**
+ * Probe PCI device for BOFM driver
+ *
+ * @v pci              PCI device
+ * @ret rc             Return status code
+ */
+static int bofm_probe ( struct pci_device *pci ) {
+       int rc;
+
+       /* Probe device */
+       if ( ( rc = pci_probe ( pci ) ) != 0 ) {
+               DBG ( "BOFM: " PCI_FMT " could not load driver: %s\n",
+                     PCI_ARGS ( pci ), strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci              PCI device
+ */
+static void bofm_remove ( struct pci_device *pci ) {
+
+       /* Note that the IBM BIOS may re-read the expansion ROM after
+        * the BOFM initialisation call.  The BOFM driver must ensure
+        * that the card is left in a state in which expansion ROM
+        * reads will succeed.  (For example, if a card contains an
+        * embedded CPU that may issue reads to the same underlying
+        * flash device, and these reads are not locked against reads
+        * via the expansion ROM BAR, then the CPU must be stopped.)
+        *
+        * If this is not done, then occasional corrupted reads from
+        * the expansion ROM will be seen, and the BIOS may complain
+        * about a ROM checksum error.
+        */
+       pci_remove ( pci );
+       DBG ( "BOFM: " PCI_FMT " removed\n", PCI_ARGS ( pci ) );
+}
+
+/**
+ * Locate BOFM table section
+ *
+ * @v bofmtab          BOFM table
+ * @v len              Length of BOFM table
+ * @v magic            Section magic
+ * @v bofmsec          BOFM section header to fill in
+ * @ret offset         Offset to section, or 0 if not found
+ */
+static size_t bofm_locate_section ( userptr_t bofmtab, size_t len,
+                                   uint32_t magic,
+                                   struct bofm_section_header *bofmsec ) {
+       size_t offset = sizeof ( struct bofm_global_header );
+
+       while ( offset < len ) {
+               copy_from_user ( bofmsec, bofmtab, offset,
+                                sizeof ( *bofmsec ) );
+               if ( bofmsec->magic == magic )
+                       return offset;
+               if ( bofmsec->magic == BOFM_DONE_MAGIC )
+                       break;
+               offset += ( sizeof ( *bofmsec ) + bofmsec->length );
+       }
+       return 0;
+}
+
+/**
+ * Process BOFM Ethernet parameter entry
+ *
+ * @v bofm             BOFM device
+ * @v en               EN parameter entry
+ * @ret rc             Return status code
+ */
+static int bofm_en ( struct bofm_device *bofm, struct bofm_en *en ) {
+       uint8_t mac[6];
+       int rc;
+
+       /* Retrieve current MAC address */
+       if ( ( rc = bofm->op->harvest ( bofm, en->mport, mac ) ) != 0 ) {
+               DBG ( "BOFM: " PCI_FMT " port %d could not harvest: %s\n",
+                     PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Harvest MAC address if necessary */
+       if ( en->options & BOFM_EN_RQ_HVST_MASK ) {
+               DBG ( "BOFM: " PCI_FMT " port %d harvested MAC %s\n",
+                     PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
+               memcpy ( en->mac_a, mac, sizeof ( en->mac_a ) );
+               en->options |= ( BOFM_EN_EN_A | BOFM_EN_HVST );
+       }
+
+       /* Mark as changed if necessary */
+       if ( ( en->options & BOFM_EN_EN_A ) &&
+            ( memcmp ( en->mac_a, mac, sizeof ( en->mac_a ) ) != 0 ) ) {
+               DBG ( "BOFM: " PCI_FMT " port %d MAC %s",
+                     PCI_ARGS ( bofm->pci ), en->mport, eth_ntoa ( mac ) );
+               DBG ( " changed to %s\n", eth_ntoa ( en->mac_a ) );
+               en->options |= BOFM_EN_CHG_CHANGED;
+       }
+
+       /* Apply MAC address if necessary */
+       if ( ( en->options & BOFM_EN_EN_A ) &&
+            ( en->options & BOFM_EN_USAGE_ENTRY ) &&
+            ( ! ( en->options & BOFM_EN_USAGE_HARVEST ) ) ) {
+               DBG ( "BOFM: " PCI_FMT " port %d applied MAC %s\n",
+                     PCI_ARGS ( bofm->pci ), en->mport,
+                     eth_ntoa ( en->mac_a ) );
+               memcpy ( mac, en->mac_a, sizeof ( mac ) );
+       }
+
+       /* Store MAC address */
+       if ( ( rc = bofm->op->update ( bofm, en->mport, mac ) ) != 0 ) {
+               DBG ( "BOFM: " PCI_FMT " port %d could not update: %s\n",
+                     PCI_ARGS ( bofm->pci ), en->mport, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Process BOFM table
+ *
+ * @v bofmtab          BOFM table
+ * @v pci              PCI device
+ * @ret bofmrc         BOFM return status
+ */
+int bofm ( userptr_t bofmtab, struct pci_device *pci ) {
+       struct bofm_global_header bofmhdr;
+       struct bofm_section_header bofmsec;
+       struct bofm_en en;
+       struct bofm_device *bofm;
+       size_t en_region_offset;
+       size_t en_offset;
+       int skip;
+       int rc;
+       int bofmrc;
+
+       /* Read BOFM structure */
+       copy_from_user ( &bofmhdr, bofmtab, 0, sizeof ( bofmhdr ) );
+       if ( bofmhdr.magic != BOFM_IOAA_MAGIC ) {
+               DBG ( "BOFM: invalid table signature " BOFM_MAGIC_FMT "\n",
+                     BOFM_MAGIC_ARGS ( bofmhdr.magic ) );
+               bofmrc = BOFM_ERR_INVALID_ACTION;
+               goto err_bad_signature;
+       }
+       DBG ( "BOFM: " BOFM_MAGIC_FMT " (profile \"%s\")\n",
+             BOFM_MAGIC_ARGS ( bofmhdr.action ), bofmhdr.profile );
+
+       /* Determine whether or not we should skip normal POST
+        * initialisation.
+        */
+       switch ( bofmhdr.action ) {
+       case BOFM_ACTION_UPDT:
+       case BOFM_ACTION_DFLT:
+       case BOFM_ACTION_HVST:
+               skip = BOFM_SKIP_INIT;
+               break;
+       case BOFM_ACTION_PARM:
+       case BOFM_ACTION_NONE:
+               skip = 0;
+               break;
+       default:
+               DBG ( "BOFM: invalid action " BOFM_MAGIC_FMT "\n",
+                     BOFM_MAGIC_ARGS ( bofmhdr.action ) );
+               bofmrc = BOFM_ERR_INVALID_ACTION;
+               goto err_bad_action;
+       }
+
+       /* Find BOFM driver */
+       if ( ( rc = bofm_find_driver ( pci ) ) != 0 ) {
+               DBG ( "BOFM: " PCI_FMT " has no driver\n", PCI_ARGS ( pci ) );
+               bofmrc = BOFM_ERR_DEVICE_ERROR;
+               goto err_find_driver;
+       }
+
+       /* Probe driver for PCI device */
+       if ( ( rc = bofm_probe ( pci ) ) != 0 ) {
+               bofmrc = BOFM_ERR_DEVICE_ERROR;
+               goto err_probe;
+       }
+
+       /* Locate EN section, if present */
+       en_region_offset = bofm_locate_section ( bofmtab, bofmhdr.length,
+                                                BOFM_EN_MAGIC, &bofmsec );
+       if ( ! en_region_offset ) {
+               DBG ( "BOFM: No EN section found\n" );
+               bofmrc = ( BOFM_SUCCESS | skip );
+               goto err_no_en_section;
+       }
+
+       /* Iterate through EN entries */
+       for ( en_offset = ( en_region_offset + sizeof ( bofmsec ) ) ;
+             en_offset < ( en_region_offset + sizeof ( bofmsec ) +
+                           bofmsec.length ) ; en_offset += sizeof ( en ) ) {
+               copy_from_user ( &en, bofmtab, en_offset, sizeof ( en ) );
+               DBG2 ( "BOFM: EN entry found:\n" );
+               DBG2_HDA ( en_offset, &en, sizeof ( en ) );
+               if ( ( en.options & BOFM_EN_MAP_MASK ) != BOFM_EN_MAP_PFA ) {
+                       DBG ( "BOFM: slot %d port %d has no PCI mapping\n",
+                             en.slot, en.port );
+                       continue;
+               }
+               bofm = bofm_find_busdevfn ( en.busdevfn );
+               if ( ! bofm ) {
+                       DBG ( "BOFM: " PCI_FMT " ignored\n",
+                             PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
+                             PCI_FUNC ( en.busdevfn ) );
+                       continue;
+               }
+               if ( ( rc = bofm_en ( bofm, &en ) ) == 0 ) {
+                       en.options |= BOFM_EN_CSM_SUCCESS;
+               } else {
+                       en.options |= BOFM_EN_CSM_FAILED;
+               }
+               DBG2 ( "BOFM: EN entry after processing:\n" );
+               DBG2_HDA ( en_offset, &en, sizeof ( en ) );
+               copy_to_user ( bofmtab, en_offset, &en, sizeof ( en ) );
+       }
+
+       bofmrc = ( BOFM_SUCCESS | skip );
+
+ err_no_en_section:
+       bofm_remove ( pci );
+ err_probe:
+ err_find_driver:
+ err_bad_action:
+ err_bad_signature:
+       return bofmrc;
+}