| /* | |
| * 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 <string.h> | |
| #include <errno.h> | |
| #include <ipxe/bofm.h> | |
| #include <ipxe/efi/efi.h> | |
| #include <ipxe/efi/efi_pci.h> | |
| #include <ipxe/efi/efi_driver.h> | |
| /** @file | |
| * | |
| * IBM BladeCenter Open Fabric Manager (BOFM) EFI interface | |
| * | |
| */ | |
| /*************************************************************************** | |
| * | |
| * EFI BOFM definitions | |
| * | |
| *************************************************************************** | |
| * | |
| * Taken from the BOFM UEFI Vendor Specification document | |
| * | |
| */ | |
| #define IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL_GUID \ | |
| { 0x03207ce2, 0xd9c7, 0x11dc, \ | |
| { 0xa9, 0x4d, 0x00, 0x19, 0x7d, 0x89, 0x02, 0x38 } } | |
| #define IBM_BOFM_DRIVER_CONFIGURATION2_PROTOCOL_GUID \ | |
| { 0xe82a9763, 0x0584, 0x4e41, \ | |
| { 0xbb, 0x39, 0xe0, 0xcd, 0xb8, 0xc1, 0xf0, 0xfc } } | |
| typedef struct { | |
| UINT8 Id; | |
| UINT8 ResultByte; | |
| } __attribute__ (( packed )) BOFM_EPID_Results_t; | |
| typedef struct { | |
| UINT8 Version; | |
| UINT8 Level; | |
| UINT16 Length; | |
| UINT8 Checksum; | |
| UINT8 Profile[32]; | |
| UINT8 GlobalOption0; | |
| UINT8 GlobalOption1; | |
| UINT8 GlobalOption2; | |
| UINT8 GlobalOption3; | |
| UINT32 SequenceStamp; | |
| UINT8 Regions[911]; // For use by BOFM Driver | |
| UINT32 Reserved1; | |
| } __attribute__ (( packed )) BOFM_Parameters_t; | |
| typedef struct { | |
| UINT32 Reserved1; | |
| UINT8 Version; | |
| UINT8 Level; | |
| UINT8 Checksum; | |
| UINT32 SequenceStamp; | |
| UINT8 SUIDResults; | |
| UINT8 EntryResults[32]; | |
| UINT8 Reserved2; | |
| UINT8 Reserved3; | |
| UINT8 FCTgtResults[2]; | |
| UINT8 SASTgtResults[2]; | |
| BOFM_EPID_Results_t EPIDResults[2]; | |
| UINT8 Results4[10]; | |
| } __attribute__ (( packed )) BOFM_Results_t; | |
| typedef struct { | |
| UINT32 Signature; | |
| UINT32 SubSignature; | |
| BOFM_Parameters_t Parameters; | |
| BOFM_Results_t Results; | |
| } __attribute__ (( packed )) BOFM_DataStructure_t; | |
| #define IBM_BOFM_TABLE BOFM_DataStructure_t | |
| typedef struct _IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL; | |
| typedef struct _IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2; | |
| typedef EFI_STATUS ( EFIAPI *IBM_BOFM_DRIVER_CONFIGURATION_SUPPORT ) ( | |
| IN IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *This, | |
| EFI_HANDLE ControllerHandle, | |
| UINT8 SupporttedOptions, | |
| UINT8 iSCSI_Parameter_Version, | |
| UINT8 BOFM_Parameter_Version | |
| ); | |
| typedef EFI_STATUS ( EFIAPI *IBM_BOFM_DRIVER_CONFIGURATION_STATUS ) ( | |
| IN IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *This, | |
| EFI_HANDLE ControllerHandle, | |
| BOOLEAN ResetRequired, | |
| UINT8 BOFMReturnCode | |
| ); | |
| typedef EFI_STATUS ( EFIAPI *IBM_BOFM_DRIVER_CONFIGURATION_STATUS2 ) ( | |
| IN IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 *This, | |
| EFI_HANDLE ControllerHandle, | |
| BOOLEAN ResetRequired, | |
| UINT8 BOFMReturnCode | |
| ); | |
| struct _IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL { | |
| IBM_BOFM_TABLE BofmTable; | |
| IBM_BOFM_DRIVER_CONFIGURATION_STATUS SetStatus; | |
| IBM_BOFM_DRIVER_CONFIGURATION_SUPPORT RegisterSupport; | |
| }; | |
| struct _IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 { | |
| UINT32 Signature; | |
| UINT32 Reserved1; | |
| UINT64 Reserved2; | |
| IBM_BOFM_DRIVER_CONFIGURATION_STATUS2 SetStatus; | |
| IBM_BOFM_DRIVER_CONFIGURATION_SUPPORT RegisterSupport; | |
| IBM_BOFM_TABLE BofmTable; | |
| }; | |
| /*************************************************************************** | |
| * | |
| * EFI BOFM interface | |
| * | |
| *************************************************************************** | |
| */ | |
| /** BOFM1 protocol GUID */ | |
| static EFI_GUID bofm1_protocol_guid = | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL_GUID; | |
| /** BOFM2 protocol GUID */ | |
| static EFI_GUID bofm2_protocol_guid = | |
| IBM_BOFM_DRIVER_CONFIGURATION2_PROTOCOL_GUID; | |
| /** | |
| * Check if device is supported | |
| * | |
| * @v device EFI device handle | |
| * @ret rc Return status code | |
| */ | |
| static int efi_bofm_supported ( EFI_HANDLE device ) { | |
| EFI_BOOT_SERVICES *bs = efi_systab->BootServices; | |
| struct efi_pci_device efipci; | |
| union { | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; | |
| void *interface; | |
| } bofm1; | |
| EFI_STATUS efirc; | |
| int rc; | |
| /* Get PCI device information */ | |
| if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) | |
| return rc; | |
| /* Look for a BOFM driver */ | |
| if ( ( rc = bofm_find_driver ( &efipci.pci ) ) != 0 ) { | |
| DBGCP ( device, "EFIBOFM %s has no driver\n", | |
| efi_handle_name ( device ) ); | |
| return rc; | |
| } | |
| /* Locate BOFM protocol */ | |
| if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, | |
| &bofm1.interface ) ) != 0 ) { | |
| rc = -EEFI ( efirc ); | |
| DBGC ( device, "EFIBOFM %s cannot find BOFM protocol\n", | |
| efi_handle_name ( device ) ); | |
| return rc; | |
| } | |
| /* Register support for this device */ | |
| if ( ( efirc = bofm1.bofm1->RegisterSupport ( bofm1.bofm1, device, | |
| 0x04 /* Can change MAC */, | |
| 0x00 /* No iSCSI */, | |
| 0x02 /* Version */ ))!=0){ | |
| rc = -EEFI ( efirc ); | |
| DBGC ( device, "EFIBOFM %s could not register support: %s\n", | |
| efi_handle_name ( device ), strerror ( rc ) ); | |
| return rc; | |
| } | |
| DBGC ( device, "EFIBOFM %s has driver \"%s\"\n", | |
| efi_handle_name ( device ), efipci.pci.id->name ); | |
| return 0; | |
| } | |
| /** | |
| * Attach driver to device | |
| * | |
| * @v efidev EFI device | |
| * @ret rc Return status code | |
| */ | |
| static int efi_bofm_start ( struct efi_device *efidev ) { | |
| EFI_BOOT_SERVICES *bs = efi_systab->BootServices; | |
| EFI_HANDLE device = efidev->device; | |
| union { | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1; | |
| void *interface; | |
| } bofm1; | |
| union { | |
| IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL2 *bofm2; | |
| void *interface; | |
| } bofm2; | |
| struct efi_pci_device efipci; | |
| IBM_BOFM_TABLE *bofmtab; | |
| IBM_BOFM_TABLE *bofmtab2; | |
| int bofmrc; | |
| EFI_STATUS efirc; | |
| int rc; | |
| /* Get PCI device information */ | |
| if ( ( rc = efipci_info ( device, &efipci ) ) != 0 ) { | |
| DBGC ( device, "EFIBOFM %s cannot get PCI information: %s\n", | |
| efi_handle_name ( device ), strerror ( rc ) ); | |
| goto err_info; | |
| } | |
| /* Open PCI I/O protocol */ | |
| if ( ( rc = efi_open_unsafe ( device, &efi_pci_io_protocol_guid, | |
| &efipci.io ) ) != 0 ) { | |
| DBGC ( device, "EFIBOFM %s cannot open PCI device: %s\n", | |
| efi_handle_name ( device ), strerror ( rc ) ); | |
| goto err_open; | |
| } | |
| /* Locate BOFM protocol */ | |
| if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL, | |
| &bofm1.interface ) ) != 0 ) { | |
| rc = -EEFI ( efirc ); | |
| DBGC ( device, "EFIBOFM %s cannot find BOFM protocol\n", | |
| efi_handle_name ( device ) ); | |
| goto err_locate_bofm; | |
| } | |
| bofmtab = &bofm1.bofm1->BofmTable; | |
| DBGC ( device, "EFIBOFM %s found version 1 BOFM table at %p+%04x\n", | |
| efi_handle_name ( device ), bofmtab, bofmtab->Parameters.Length); | |
| /* Locate BOFM2 protocol, if available */ | |
| if ( ( efirc = bs->LocateProtocol ( &bofm2_protocol_guid, NULL, | |
| &bofm2.interface ) ) == 0 ) { | |
| bofmtab2 = &bofm2.bofm2->BofmTable; | |
| DBGC ( device, "EFIBOFM %s found version 2 BOFM table at " | |
| "%p+%04x\n", efi_handle_name ( device ), bofmtab2, | |
| bofmtab2->Parameters.Length ); | |
| assert ( bofm2.bofm2->RegisterSupport == | |
| bofm1.bofm1->RegisterSupport ); | |
| } else { | |
| DBGC ( device, "EFIBOFM %s cannot find BOFM2 protocol\n", | |
| efi_handle_name ( device ) ); | |
| /* Not a fatal error; may be a BOFM1-only system */ | |
| bofmtab2 = NULL; | |
| } | |
| /* Process BOFM table */ | |
| DBGC2 ( device, "EFIBOFM %s version 1 before processing:\n", | |
| efi_handle_name ( device ) ); | |
| DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); | |
| if ( bofmtab2 ) { | |
| DBGC2 ( device, "EFIBOFM %s version 2 before processing:\n", | |
| efi_handle_name ( device ) ); | |
| DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); | |
| } | |
| bofmrc = bofm ( ( bofmtab2 ? bofmtab2 : bofmtab ), &efipci.pci ); | |
| DBGC ( device, "EFIBOFM %s status %08x\n", | |
| efi_handle_name ( device ), bofmrc ); | |
| DBGC2 ( device, "EFIBOFM %s version 1 after processing:\n", | |
| efi_handle_name ( device ) ); | |
| DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length ); | |
| if ( bofmtab2 ) { | |
| DBGC2 ( device, "EFIBOFM %s version 2 after processing:\n", | |
| efi_handle_name ( device ) ); | |
| DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length ); | |
| } | |
| /* Return BOFM status */ | |
| if ( bofmtab2 ) { | |
| if ( ( efirc = bofm2.bofm2->SetStatus ( bofm2.bofm2, device, | |
| FALSE, bofmrc ) ) != 0){ | |
| rc = -EEFI ( efirc ); | |
| DBGC ( device, "EFIBOFM %s could not set BOFM2 " | |
| "status: %s\n", efi_handle_name ( device ), | |
| strerror ( rc ) ); | |
| goto err_set_status; | |
| } | |
| } else { | |
| if ( ( efirc = bofm1.bofm1->SetStatus ( bofm1.bofm1, device, | |
| FALSE, bofmrc ) ) != 0){ | |
| rc = -EEFI ( efirc ); | |
| DBGC ( device, "EFIBOFM %s could not set BOFM " | |
| "status: %s\n", efi_handle_name ( device ), | |
| strerror ( rc ) ); | |
| goto err_set_status; | |
| } | |
| } | |
| /* BOFM (ab)uses the "start" method to mean "process and exit" */ | |
| rc = -EAGAIN; | |
| err_set_status: | |
| err_locate_bofm: | |
| efi_close_unsafe ( device, &efi_pci_io_protocol_guid ); | |
| err_open: | |
| err_info: | |
| return rc; | |
| } | |
| /** | |
| * Detach driver from device | |
| * | |
| * @v device EFI device | |
| */ | |
| static void efi_bofm_stop ( struct efi_device *efidev __unused ) { | |
| /* Should never happen */ | |
| assert ( 0 ); | |
| } | |
| /** EFI BOFM driver */ | |
| struct efi_driver efi_bofm_driver __efi_driver ( EFI_DRIVER_EARLY ) = { | |
| .name = "BOFM", | |
| .supported = efi_bofm_supported, | |
| .start = efi_bofm_start, | |
| .stop = efi_bofm_stop, | |
| }; |