summaryrefslogtreecommitdiffstats
path: root/src/interface/bofm
diff options
context:
space:
mode:
authorMichael Brown2011-02-10 14:47:38 +0100
committerMichael Brown2011-03-03 00:58:43 +0100
commit5597d52c21377872387866e8cf9ee351b7a41a01 (patch)
tree8805382ac5267e7aff87bd5a3df613802ec54b48 /src/interface/bofm
parent[autoboot] Allow a SAN boot as a fallback if a filename boot returns (diff)
downloadipxe-5597d52c21377872387866e8cf9ee351b7a41a01.tar.gz
ipxe-5597d52c21377872387866e8cf9ee351b7a41a01.tar.xz
ipxe-5597d52c21377872387866e8cf9ee351b7a41a01.zip
[bofm] Add core BOFM library
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/interface/bofm')
-rw-r--r--src/interface/bofm/bofm.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/src/interface/bofm/bofm.c b/src/interface/bofm/bofm.c
new file mode 100644
index 00000000..dfa76d32
--- /dev/null
+++ b/src/interface/bofm/bofm.c
@@ -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;
+}