123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- /*
- * 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., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #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 " mport %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 " mport %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 " mport %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 " mport %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 " mport %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 + 1 ) );
- continue;
- }
- DBG ( "BOFM: slot %d port %d%s is " PCI_FMT " mport %d\n",
- en.slot, ( en.port + 1 ),
- ( ( en.slot || en.port ) ? "" : "(?)" ), 0,
- PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
- PCI_FUNC ( en.busdevfn ), en.mport );
- bofm = bofm_find_busdevfn ( en.busdevfn );
- if ( ! bofm ) {
- DBG ( "BOFM: " PCI_FMT " mport %d ignored\n", 0,
- PCI_BUS ( en.busdevfn ), PCI_SLOT ( en.busdevfn ),
- PCI_FUNC ( en.busdevfn ), en.mport );
- 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;
- }
|