aboutsummaryrefslogtreecommitdiffstats
path: root/roms/ipxe/src/interface/efi
diff options
context:
space:
mode:
Diffstat (limited to 'roms/ipxe/src/interface/efi')
-rw-r--r--roms/ipxe/src/interface/efi/efi_autoboot.c67
-rw-r--r--roms/ipxe/src/interface/efi/efi_bofm.c334
-rw-r--r--roms/ipxe/src/interface/efi/efi_console.c360
-rw-r--r--roms/ipxe/src/interface/efi/efi_debug.c661
-rw-r--r--roms/ipxe/src/interface/efi/efi_download.c240
-rw-r--r--roms/ipxe/src/interface/efi/efi_driver.c517
-rw-r--r--roms/ipxe/src/interface/efi/efi_file.c714
-rw-r--r--roms/ipxe/src/interface/efi/efi_guid.c205
-rw-r--r--roms/ipxe/src/interface/efi/efi_hii.c577
-rw-r--r--roms/ipxe/src/interface/efi/efi_init.c226
-rw-r--r--roms/ipxe/src/interface/efi/efi_pci.c357
-rw-r--r--roms/ipxe/src/interface/efi/efi_reboot.c61
-rw-r--r--roms/ipxe/src/interface/efi/efi_smbios.c67
-rw-r--r--roms/ipxe/src/interface/efi/efi_snp.c1209
-rw-r--r--roms/ipxe/src/interface/efi/efi_snp_hii.c722
-rw-r--r--roms/ipxe/src/interface/efi/efi_strings.c148
-rw-r--r--roms/ipxe/src/interface/efi/efi_timer.c124
-rw-r--r--roms/ipxe/src/interface/efi/efi_uaccess.c40
-rw-r--r--roms/ipxe/src/interface/efi/efi_umalloc.c104
-rw-r--r--roms/ipxe/src/interface/efi/efi_utils.c218
-rw-r--r--roms/ipxe/src/interface/efi/efi_wrap.c315
21 files changed, 7266 insertions, 0 deletions
diff --git a/roms/ipxe/src/interface/efi/efi_autoboot.c b/roms/ipxe/src/interface/efi/efi_autoboot.c
new file mode 100644
index 00000000..ab0f3654
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_autoboot.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_autoboot.h>
+#include <ipxe/efi/Protocol/SimpleNetwork.h>
+#include <usr/autoboot.h>
+
+/** @file
+ *
+ * EFI autoboot device
+ *
+ */
+
+/**
+ * Identify autoboot device
+ *
+ */
+void efi_set_autoboot ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_SIMPLE_NETWORK_PROTOCOL *snp;
+ void *interface;
+ } snp;
+ EFI_SIMPLE_NETWORK_MODE *mode;
+ EFI_STATUS efirc;
+
+ /* Look for an SNP instance on the image's device handle */
+ if ( ( efirc = bs->OpenProtocol ( efi_loaded_image->DeviceHandle,
+ &efi_simple_network_protocol_guid,
+ &snp.interface, efi_image_handle,
+ NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ DBGC ( efi_loaded_image, "EFI found no autoboot device\n" );
+ return;
+ }
+
+ /* Record autoboot device */
+ mode = snp.snp->Mode;
+ set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize );
+ DBGC ( efi_loaded_image, "EFI found autoboot link-layer address:\n" );
+ DBGC_HDA ( efi_loaded_image, 0, &mode->CurrentAddress,
+ mode->HwAddressSize );
+
+ /* Close protocol */
+ bs->CloseProtocol ( efi_loaded_image->DeviceHandle,
+ &efi_simple_network_protocol_guid,
+ efi_image_handle, NULL );
+}
diff --git a/roms/ipxe/src/interface/efi/efi_bofm.c b/roms/ipxe/src/interface/efi/efi_bofm.c
new file mode 100644
index 00000000..bdb70519
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_bofm.c
@@ -0,0 +1,334 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#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 pci_device pci;
+ union {
+ IBM_BOFM_DRIVER_CONFIGURATION_PROTOCOL *bofm1;
+ void *interface;
+ } bofm1;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Get PCI device information */
+ if ( ( rc = efipci_info ( device, &pci ) ) != 0 )
+ return rc;
+
+ /* Look for a BOFM driver */
+ if ( ( rc = bofm_find_driver ( &pci ) ) != 0 ) {
+ DBGCP ( device, "EFIBOFM %p %s has no driver\n",
+ device, 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 %p %s cannot find BOFM protocol\n",
+ device, 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 %p %s could not register support: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ DBGC ( device, "EFIBOFM %p %s has driver \"%s\"\n",
+ device, efi_handle_name ( device ), 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 pci_device pci;
+ IBM_BOFM_TABLE *bofmtab;
+ IBM_BOFM_TABLE *bofmtab2;
+ int bofmrc;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Open PCI device, if possible */
+ if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL,
+ &pci ) ) != 0 )
+ goto err_open;
+
+ /* Locate BOFM protocol */
+ if ( ( efirc = bs->LocateProtocol ( &bofm1_protocol_guid, NULL,
+ &bofm1.interface ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIBOFM %p %s cannot find BOFM protocol\n",
+ device, efi_handle_name ( device ) );
+ goto err_locate_bofm;
+ }
+ bofmtab = &bofm1.bofm1->BofmTable;
+ DBGC ( device, "EFIBOFM %p %s found version 1 BOFM table at %p+%04x\n",
+ device, 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 %p %s found version 2 BOFM table at "
+ "%p+%04x\n", device, efi_handle_name ( device ),
+ bofmtab2, bofmtab2->Parameters.Length );
+ assert ( bofm2.bofm2->RegisterSupport ==
+ bofm1.bofm1->RegisterSupport );
+ } else {
+ DBGC ( device, "EFIBOFM %p %s cannot find BOFM2 protocol\n",
+ device, efi_handle_name ( device ) );
+ /* Not a fatal error; may be a BOFM1-only system */
+ bofmtab2 = NULL;
+ }
+
+ /* Process BOFM table */
+ DBGC2 ( device, "EFIBOFM %p %s version 1 before processing:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length );
+ if ( bofmtab2 ) {
+ DBGC2 ( device, "EFIBOFM %p %s version 2 before processing:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_HD ( device, bofmtab2, bofmtab2->Parameters.Length );
+ }
+ bofmrc = bofm ( virt_to_user ( bofmtab2 ? bofmtab2 : bofmtab ), &pci );
+ DBGC ( device, "EFIBOFM %p %s status %08x\n",
+ device, efi_handle_name ( device ), bofmrc );
+ DBGC2 ( device, "EFIBOFM %p %s version 1 after processing:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_HD ( device, bofmtab, bofmtab->Parameters.Length );
+ if ( bofmtab2 ) {
+ DBGC2 ( device, "EFIBOFM %p %s version 2 after processing:\n",
+ device, 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 %p %s could not set BOFM2 "
+ "status: %s\n", device,
+ 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 %p %s could not set BOFM "
+ "status: %s\n", device,
+ 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:
+ efipci_close ( device );
+ err_open:
+ 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,
+};
diff --git a/roms/ipxe/src/interface/efi/efi_console.c b/roms/ipxe/src/interface/efi/efi_console.c
new file mode 100644
index 00000000..3b30f309
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_console.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h>
+#include <ipxe/ansiesc.h>
+#include <ipxe/console.h>
+#include <ipxe/init.h>
+#include <config/console.h>
+
+#define ATTR_BOLD 0x08
+
+#define ATTR_FCOL_MASK 0x07
+#define ATTR_FCOL_BLACK 0x00
+#define ATTR_FCOL_BLUE 0x01
+#define ATTR_FCOL_GREEN 0x02
+#define ATTR_FCOL_CYAN 0x03
+#define ATTR_FCOL_RED 0x04
+#define ATTR_FCOL_MAGENTA 0x05
+#define ATTR_FCOL_YELLOW 0x06
+#define ATTR_FCOL_WHITE 0x07
+
+#define ATTR_BCOL_MASK 0x70
+#define ATTR_BCOL_BLACK 0x00
+#define ATTR_BCOL_BLUE 0x10
+#define ATTR_BCOL_GREEN 0x20
+#define ATTR_BCOL_CYAN 0x30
+#define ATTR_BCOL_RED 0x40
+#define ATTR_BCOL_MAGENTA 0x50
+#define ATTR_BCOL_YELLOW 0x60
+#define ATTR_BCOL_WHITE 0x70
+
+#define ATTR_DEFAULT ATTR_FCOL_WHITE
+
+/* Set default console usage if applicable */
+#if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) )
+#undef CONSOLE_EFI
+#define CONSOLE_EFI ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
+#endif
+
+/** Current character attribute */
+static unsigned int efi_attr = ATTR_DEFAULT;
+
+/** Console control protocol */
+static EFI_CONSOLE_CONTROL_PROTOCOL *conctrl;
+EFI_REQUEST_PROTOCOL ( EFI_CONSOLE_CONTROL_PROTOCOL, &conctrl );
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params[0] Row (1 is top)
+ * @v params[1] Column (1 is left)
+ */
+static void efi_handle_cup ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused, int params[] ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ int cx = ( params[1] - 1 );
+ int cy = ( params[0] - 1 );
+
+ if ( cx < 0 )
+ cx = 0;
+ if ( cy < 0 )
+ cy = 0;
+
+ conout->SetCursorPosition ( conout, cx, cy );
+}
+
+/**
+ * Handle ANSI ED (erase in page)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params[0] Region to erase
+ */
+static void efi_handle_ed ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+
+ /* We assume that we always clear the whole screen */
+ assert ( params[0] == ANSIESC_ED_ALL );
+
+ conout->ClearScreen ( conout );
+}
+
+/**
+ * Handle ANSI SGR (set graphics rendition)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void efi_handle_sgr ( struct ansiesc_context *ctx __unused,
+ unsigned int count, int params[] ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ static const uint8_t efi_attr_fcols[10] = {
+ ATTR_FCOL_BLACK, ATTR_FCOL_RED, ATTR_FCOL_GREEN,
+ ATTR_FCOL_YELLOW, ATTR_FCOL_BLUE, ATTR_FCOL_MAGENTA,
+ ATTR_FCOL_CYAN, ATTR_FCOL_WHITE,
+ ATTR_FCOL_WHITE, ATTR_FCOL_WHITE /* defaults */
+ };
+ static const uint8_t efi_attr_bcols[10] = {
+ ATTR_BCOL_BLACK, ATTR_BCOL_RED, ATTR_BCOL_GREEN,
+ ATTR_BCOL_YELLOW, ATTR_BCOL_BLUE, ATTR_BCOL_MAGENTA,
+ ATTR_BCOL_CYAN, ATTR_BCOL_WHITE,
+ ATTR_BCOL_BLACK, ATTR_BCOL_BLACK /* defaults */
+ };
+ unsigned int i;
+ int aspect;
+
+ for ( i = 0 ; i < count ; i++ ) {
+ aspect = params[i];
+ if ( aspect == 0 ) {
+ efi_attr = ATTR_DEFAULT;
+ } else if ( aspect == 1 ) {
+ efi_attr |= ATTR_BOLD;
+ } else if ( aspect == 22 ) {
+ efi_attr &= ~ATTR_BOLD;
+ } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) {
+ efi_attr &= ~ATTR_FCOL_MASK;
+ efi_attr |= efi_attr_fcols[ aspect - 30 ];
+ } else if ( ( aspect >= 40 ) && ( aspect <= 49 ) ) {
+ efi_attr &= ~ATTR_BCOL_MASK;
+ efi_attr |= efi_attr_bcols[ aspect - 40 ];
+ }
+ }
+
+ conout->SetAttribute ( conout, efi_attr );
+}
+
+/**
+ * Handle ANSI DECTCEM set (show cursor)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void efi_handle_dectcem_set ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+
+ conout->EnableCursor ( conout, TRUE );
+}
+
+/**
+ * Handle ANSI DECTCEM reset (hide cursor)
+ *
+ * @v ctx ANSI escape sequence context
+ * @v count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void efi_handle_dectcem_reset ( struct ansiesc_context *ctx __unused,
+ unsigned int count __unused,
+ int params[] __unused ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+
+ conout->EnableCursor ( conout, FALSE );
+}
+
+/** EFI console ANSI escape sequence handlers */
+static struct ansiesc_handler efi_ansiesc_handlers[] = {
+ { ANSIESC_CUP, efi_handle_cup },
+ { ANSIESC_ED, efi_handle_ed },
+ { ANSIESC_SGR, efi_handle_sgr },
+ { ANSIESC_DECTCEM_SET, efi_handle_dectcem_set },
+ { ANSIESC_DECTCEM_RESET, efi_handle_dectcem_reset },
+ { 0, NULL }
+};
+
+/** EFI console ANSI escape sequence context */
+static struct ansiesc_context efi_ansiesc_ctx = {
+ .handlers = efi_ansiesc_handlers,
+};
+
+/**
+ * Print a character to EFI console
+ *
+ * @v character Character to be printed
+ */
+static void efi_putchar ( int character ) {
+ EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
+ wchar_t wstr[] = { character, 0 };
+
+ /* Intercept ANSI escape sequences */
+ character = ansiesc_process ( &efi_ansiesc_ctx, character );
+ if ( character < 0 )
+ return;
+
+ conout->OutputString ( conout, wstr );
+}
+
+/**
+ * Pointer to current ANSI output sequence
+ *
+ * While we are in the middle of returning an ANSI sequence for a
+ * special key, this will point to the next character to return. When
+ * not in the middle of such a sequence, this will point to a NUL
+ * (note: not "will be NULL").
+ */
+static const char *ansi_input = "";
+
+/** Mapping from EFI scan codes to ANSI escape sequences */
+static const char *ansi_sequences[] = {
+ [SCAN_UP] = "[A",
+ [SCAN_DOWN] = "[B",
+ [SCAN_RIGHT] = "[C",
+ [SCAN_LEFT] = "[D",
+ [SCAN_HOME] = "[H",
+ [SCAN_END] = "[F",
+ [SCAN_INSERT] = "[2~",
+ /* EFI translates an incoming backspace via the serial console
+ * into a SCAN_DELETE. There's not much we can do about this.
+ */
+ [SCAN_DELETE] = "[3~",
+ [SCAN_PAGE_UP] = "[5~",
+ [SCAN_PAGE_DOWN] = "[6~",
+ /* EFI translates some (but not all) incoming escape sequences
+ * via the serial console into equivalent scancodes. When it
+ * doesn't recognise a sequence, it helpfully(!) translates
+ * the initial ESC and passes the remainder through verbatim.
+ * Treating SCAN_ESC as equivalent to an empty escape sequence
+ * works around this bug.
+ */
+ [SCAN_ESC] = "",
+};
+
+/**
+ * Get ANSI escape sequence corresponding to EFI scancode
+ *
+ * @v scancode EFI scancode
+ * @ret ansi_seq ANSI escape sequence, if any, otherwise NULL
+ */
+static const char * scancode_to_ansi_seq ( unsigned int scancode ) {
+ if ( scancode < ( sizeof ( ansi_sequences ) /
+ sizeof ( ansi_sequences[0] ) ) ) {
+ return ansi_sequences[scancode];
+ }
+ return NULL;
+}
+
+/**
+ * Get character from EFI console
+ *
+ * @ret character Character read from console
+ */
+static int efi_getchar ( void ) {
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+ const char *ansi_seq;
+ EFI_INPUT_KEY key;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* If we are mid-sequence, pass out the next byte */
+ if ( *ansi_input )
+ return *(ansi_input++);
+
+ /* Read key from real EFI console */
+ if ( ( efirc = conin->ReadKeyStroke ( conin, &key ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not read keystroke: %s\n", strerror ( rc ) );
+ return 0;
+ }
+ DBG2 ( "EFI read key stroke with unicode %04x scancode %04x\n",
+ key.UnicodeChar, key.ScanCode );
+
+ /* If key has a Unicode representation, return it */
+ if ( key.UnicodeChar )
+ return key.UnicodeChar;
+
+ /* Otherwise, check for a special key that we know about */
+ if ( ( ansi_seq = scancode_to_ansi_seq ( key.ScanCode ) ) ) {
+ /* Start of escape sequence: return ESC (0x1b) */
+ ansi_input = ansi_seq;
+ return 0x1b;
+ }
+
+ return 0;
+}
+
+/**
+ * Check for character ready to read from EFI console
+ *
+ * @ret True Character available to read
+ * @ret False No character available to read
+ */
+static int efi_iskey ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
+ EFI_STATUS efirc;
+
+ /* If we are mid-sequence, we are always ready */
+ if ( *ansi_input )
+ return 1;
+
+ /* Check to see if the WaitForKey event has fired */
+ if ( ( efirc = bs->CheckEvent ( conin->WaitForKey ) ) == 0 )
+ return 1;
+
+ return 0;
+}
+
+/** EFI console driver */
+struct console_driver efi_console __console_driver = {
+ .putchar = efi_putchar,
+ .getchar = efi_getchar,
+ .iskey = efi_iskey,
+ .usage = CONSOLE_EFI,
+};
+
+/**
+ * Initialise EFI console
+ *
+ */
+static void efi_console_init ( void ) {
+ EFI_CONSOLE_CONTROL_SCREEN_MODE mode;
+
+ /* On some older EFI 1.10 implementations, we must use the
+ * (now obsolete) EFI_CONSOLE_CONTROL_PROTOCOL to switch the
+ * console into text mode.
+ */
+ if ( conctrl ) {
+ conctrl->GetMode ( conctrl, &mode, NULL, NULL );
+ if ( mode != EfiConsoleControlScreenText ) {
+ conctrl->SetMode ( conctrl,
+ EfiConsoleControlScreenText );
+ }
+ }
+}
+
+/**
+ * EFI console initialisation function
+ */
+struct init_fn efi_console_init_fn __init_fn ( INIT_EARLY ) = {
+ .initialise = efi_console_init,
+};
diff --git a/roms/ipxe/src/interface/efi/efi_debug.c b/roms/ipxe/src/interface/efi/efi_debug.c
new file mode 100644
index 00000000..d2396014
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_debug.c
@@ -0,0 +1,661 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * EFI debugging utilities
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/uuid.h>
+#include <ipxe/base16.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/Protocol/ComponentName.h>
+#include <ipxe/efi/Protocol/ComponentName2.h>
+#include <ipxe/efi/Protocol/DevicePathToText.h>
+#include <ipxe/efi/IndustryStandard/PeImage.h>
+
+/** Device path to text protocol */
+static EFI_DEVICE_PATH_TO_TEXT_PROTOCOL *efidpt;
+EFI_REQUEST_PROTOCOL ( EFI_DEVICE_PATH_TO_TEXT_PROTOCOL, &efidpt );
+
+/** Iscsi4Dxe module GUID */
+static EFI_GUID efi_iscsi4_dxe_guid = {
+ 0x4579b72d, 0x7ec4, 0x4dd4,
+ { 0x84, 0x86, 0x08, 0x3c, 0x86, 0xb1, 0x82, 0xa7 }
+};
+
+/** VlanConfigDxe module GUID */
+static EFI_GUID efi_vlan_config_dxe_guid = {
+ 0xe4f61863, 0xfe2c, 0x4b56,
+ { 0xa8, 0xf4, 0x08, 0x51, 0x9b, 0xc4, 0x39, 0xdf }
+};
+
+/** A well-known GUID */
+struct efi_well_known_guid {
+ /** GUID */
+ EFI_GUID *guid;
+ /** Name */
+ const char *name;
+};
+
+/** Well-known GUIDs */
+static struct efi_well_known_guid efi_well_known_guids[] = {
+ { &efi_arp_protocol_guid,
+ "Arp" },
+ { &efi_arp_service_binding_protocol_guid,
+ "ArpSb" },
+ { &efi_block_io_protocol_guid,
+ "BlockIo" },
+ { &efi_bus_specific_driver_override_protocol_guid,
+ "BusSpecificDriverOverride" },
+ { &efi_component_name_protocol_guid,
+ "ComponentName" },
+ { &efi_component_name2_protocol_guid,
+ "ComponentName2" },
+ { &efi_device_path_protocol_guid,
+ "DevicePath" },
+ { &efi_driver_binding_protocol_guid,
+ "DriverBinding" },
+ { &efi_dhcp4_protocol_guid,
+ "Dhcp4" },
+ { &efi_dhcp4_service_binding_protocol_guid,
+ "Dhcp4Sb" },
+ { &efi_disk_io_protocol_guid,
+ "DiskIo" },
+ { &efi_graphics_output_protocol_guid,
+ "GraphicsOutput" },
+ { &efi_hii_config_access_protocol_guid,
+ "HiiConfigAccess" },
+ { &efi_ip4_protocol_guid,
+ "Ip4" },
+ { &efi_ip4_config_protocol_guid,
+ "Ip4Config" },
+ { &efi_ip4_service_binding_protocol_guid,
+ "Ip4Sb" },
+ { &efi_iscsi4_dxe_guid,
+ "IScsi4Dxe" },
+ { &efi_load_file_protocol_guid,
+ "LoadFile" },
+ { &efi_load_file2_protocol_guid,
+ "LoadFile2" },
+ { &efi_loaded_image_protocol_guid,
+ "LoadedImage" },
+ { &efi_loaded_image_device_path_protocol_guid,
+ "LoadedImageDevicePath"},
+ { &efi_managed_network_protocol_guid,
+ "ManagedNetwork" },
+ { &efi_managed_network_service_binding_protocol_guid,
+ "ManagedNetworkSb" },
+ { &efi_mtftp4_protocol_guid,
+ "Mtftp4" },
+ { &efi_mtftp4_service_binding_protocol_guid,
+ "Mtftp4Sb" },
+ { &efi_nii_protocol_guid,
+ "Nii" },
+ { &efi_nii31_protocol_guid,
+ "Nii31" },
+ { &efi_pci_io_protocol_guid,
+ "PciIo" },
+ { &efi_pci_root_bridge_io_protocol_guid,
+ "PciRootBridgeIo" },
+ { &efi_pxe_base_code_protocol_guid,
+ "PxeBaseCode" },
+ { &efi_simple_file_system_protocol_guid,
+ "SimpleFileSystem" },
+ { &efi_simple_network_protocol_guid,
+ "SimpleNetwork" },
+ { &efi_tcg_protocol_guid,
+ "Tcg" },
+ { &efi_tcp4_protocol_guid,
+ "Tcp4" },
+ { &efi_tcp4_service_binding_protocol_guid,
+ "Tcp4Sb" },
+ { &efi_udp4_protocol_guid,
+ "Udp4" },
+ { &efi_udp4_service_binding_protocol_guid,
+ "Udp4Sb" },
+ { &efi_vlan_config_protocol_guid,
+ "VlanConfig" },
+ { &efi_vlan_config_dxe_guid,
+ "VlanConfigDxe" },
+};
+
+/**
+ * Convert GUID to a printable string
+ *
+ * @v guid GUID
+ * @ret string Printable string
+ */
+const char * efi_guid_ntoa ( EFI_GUID *guid ) {
+ union {
+ union uuid uuid;
+ EFI_GUID guid;
+ } u;
+ unsigned int i;
+
+ /* Sanity check */
+ if ( ! guid )
+ return NULL;
+
+ /* Check for a match against well-known GUIDs */
+ for ( i = 0 ; i < ( sizeof ( efi_well_known_guids ) /
+ sizeof ( efi_well_known_guids[0] ) ) ; i++ ) {
+ if ( memcmp ( guid, efi_well_known_guids[i].guid,
+ sizeof ( *guid ) ) == 0 ) {
+ return efi_well_known_guids[i].name;
+ }
+ }
+
+ /* Convert GUID to standard endianness */
+ memcpy ( &u.guid, guid, sizeof ( u.guid ) );
+ uuid_mangle ( &u.uuid );
+ return uuid_ntoa ( &u.uuid );
+}
+
+/**
+ * Name protocol open attributes
+ *
+ * @v attributes Protocol open attributes
+ * @ret name Protocol open attributes name
+ *
+ * Returns a (static) string with characters for each set bit
+ * corresponding to BY_(H)ANDLE_PROTOCOL, (G)ET_PROTOCOL,
+ * (T)EST_PROTOCOL, BY_(C)HILD_CONTROLLER, BY_(D)RIVER, and
+ * E(X)CLUSIVE.
+ */
+static const char * efi_open_attributes_name ( unsigned int attributes ) {
+ static char attribute_chars[] = "HGTCDX";
+ static char name[ sizeof ( attribute_chars ) ];
+ char *tmp = name;
+ unsigned int i;
+
+ for ( i = 0 ; i < ( sizeof ( attribute_chars ) - 1 ) ; i++ ) {
+ if ( attributes & ( 1 << i ) )
+ *(tmp++) = attribute_chars[i];
+ }
+ *tmp = '\0';
+
+ return name;
+}
+
+/**
+ * Print list of openers of a given protocol on a given handle
+ *
+ * @v handle EFI handle
+ * @v protocol Protocol GUID
+ */
+void dbg_efi_openers ( EFI_HANDLE handle, EFI_GUID *protocol ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *openers;
+ EFI_OPEN_PROTOCOL_INFORMATION_ENTRY *opener;
+ UINTN count;
+ unsigned int i;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Sanity check */
+ if ( ( ! handle ) || ( ! protocol ) ) {
+ printf ( "EFI could not retrieve openers for %s on %p\n",
+ efi_guid_ntoa ( protocol ), handle );
+ return;
+ }
+
+ /* Retrieve list of openers */
+ if ( ( efirc = bs->OpenProtocolInformation ( handle, protocol, &openers,
+ &count ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ printf ( "EFI could not retrieve openers for %s on %p: %s\n",
+ efi_guid_ntoa ( protocol ), handle, strerror ( rc ) );
+ return;
+ }
+
+ /* Dump list of openers */
+ for ( i = 0 ; i < count ; i++ ) {
+ opener = &openers[i];
+ printf ( "HANDLE %p %s %s opened %dx (%s)",
+ handle, efi_handle_name ( handle ),
+ efi_guid_ntoa ( protocol ), opener->OpenCount,
+ efi_open_attributes_name ( opener->Attributes ) );
+ printf ( " by %p %s", opener->AgentHandle,
+ efi_handle_name ( opener->AgentHandle ) );
+ if ( opener->ControllerHandle == handle ) {
+ printf ( "\n" );
+ } else {
+ printf ( " for %p %s\n", opener->ControllerHandle,
+ efi_handle_name ( opener->ControllerHandle ) );
+ }
+ }
+
+ /* Free list */
+ bs->FreePool ( openers );
+}
+
+/**
+ * Print list of protocol handlers attached to a handle
+ *
+ * @v handle EFI handle
+ */
+void dbg_efi_protocols ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_GUID **protocols;
+ EFI_GUID *protocol;
+ UINTN count;
+ unsigned int i;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Sanity check */
+ if ( ! handle ) {
+ printf ( "EFI could not retrieve protocols for %p\n", handle );
+ return;
+ }
+
+ /* Retrieve list of protocols */
+ if ( ( efirc = bs->ProtocolsPerHandle ( handle, &protocols,
+ &count ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ printf ( "EFI could not retrieve protocols for %p: %s\n",
+ handle, strerror ( rc ) );
+ return;
+ }
+
+ /* Dump list of protocols */
+ for ( i = 0 ; i < count ; i++ ) {
+ protocol = protocols[i];
+ printf ( "HANDLE %p %s %s supported\n",
+ handle, efi_handle_name ( handle ),
+ efi_guid_ntoa ( protocol ) );
+ dbg_efi_openers ( handle, protocol );
+ }
+
+ /* Free list */
+ bs->FreePool ( protocols );
+}
+
+/**
+ * Get textual representation of device path
+ *
+ * @v path Device path
+ * @ret text Textual representation of device path, or NULL
+ */
+const char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ static char text[256];
+ void *start;
+ void *end;
+ size_t max_len;
+ size_t len;
+ CHAR16 *wtext;
+
+ /* Sanity checks */
+ if ( ! path ) {
+ DBG ( "[NULL DevicePath]" );
+ return NULL;
+ }
+
+ /* If we have no DevicePathToText protocol then use a raw hex string */
+ if ( ! efidpt ) {
+ DBG ( "[No DevicePathToText]" );
+ start = path;
+ end = efi_devpath_end ( path );
+ len = ( end - start );
+ max_len = ( ( sizeof ( text ) - 1 /* NUL */ ) / 2 /* "xx" */ );
+ if ( len > max_len )
+ len = max_len;
+ base16_encode ( start, len, text );
+ return text;
+ }
+
+ /* Convert path to a textual representation */
+ wtext = efidpt->ConvertDevicePathToText ( path, TRUE, FALSE );
+ if ( ! wtext )
+ return NULL;
+
+ /* Store path in buffer */
+ snprintf ( text, sizeof ( text ), "%ls", wtext );
+
+ /* Free path */
+ bs->FreePool ( wtext );
+
+ return text;
+}
+
+/**
+ * Get driver name
+ *
+ * @v wtf Component name protocol
+ * @ret name Driver name, or NULL
+ */
+static const char * efi_driver_name ( EFI_COMPONENT_NAME_PROTOCOL *wtf ) {
+ static char name[64];
+ CHAR16 *driver_name;
+ EFI_STATUS efirc;
+
+ /* Sanity check */
+ if ( ! wtf ) {
+ DBG ( "[NULL ComponentName]" );
+ return NULL;
+ }
+
+ /* Try "eng" first; if that fails then try the first language */
+ if ( ( ( efirc = wtf->GetDriverName ( wtf, "eng",
+ &driver_name ) ) != 0 ) &&
+ ( ( efirc = wtf->GetDriverName ( wtf, wtf->SupportedLanguages,
+ &driver_name ) ) != 0 ) ) {
+ return NULL;
+ }
+
+ /* Convert name from CHAR16 to char */
+ snprintf ( name, sizeof ( name ), "%ls", driver_name );
+ return name;
+}
+
+/**
+ * Get driver name
+ *
+ * @v wtf Component name protocol
+ * @ret name Driver name, or NULL
+ */
+static const char * efi_driver_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *wtf ) {
+ static char name[64];
+ CHAR16 *driver_name;
+ EFI_STATUS efirc;
+
+ /* Sanity check */
+ if ( ! wtf ) {
+ DBG ( "[NULL ComponentName2]" );
+ return NULL;
+ }
+
+ /* Try "en" first; if that fails then try the first language */
+ if ( ( ( efirc = wtf->GetDriverName ( wtf, "en",
+ &driver_name ) ) != 0 ) &&
+ ( ( efirc = wtf->GetDriverName ( wtf, wtf->SupportedLanguages,
+ &driver_name ) ) != 0 ) ) {
+ return NULL;
+ }
+
+ /* Convert name from CHAR16 to char */
+ snprintf ( name, sizeof ( name ), "%ls", driver_name );
+ return name;
+}
+
+/**
+ * Get PE/COFF debug filename
+ *
+ * @v loaded Loaded image
+ * @ret name PE/COFF debug filename, or NULL
+ */
+static const char *
+efi_pecoff_debug_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) {
+ static char buf[32];
+ EFI_IMAGE_DOS_HEADER *dos;
+ EFI_IMAGE_OPTIONAL_HEADER_UNION *pe;
+ EFI_IMAGE_OPTIONAL_HEADER32 *opt32;
+ EFI_IMAGE_OPTIONAL_HEADER64 *opt64;
+ EFI_IMAGE_DATA_DIRECTORY *datadir;
+ EFI_IMAGE_DEBUG_DIRECTORY_ENTRY *debug;
+ EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY *codeview_nb10;
+ EFI_IMAGE_DEBUG_CODEVIEW_RSDS_ENTRY *codeview_rsds;
+ EFI_IMAGE_DEBUG_CODEVIEW_MTOC_ENTRY *codeview_mtoc;
+ uint16_t dos_magic;
+ uint32_t pe_magic;
+ uint16_t opt_magic;
+ uint32_t codeview_magic;
+ size_t max_len;
+ char *name;
+ char *tmp;
+
+ /* Sanity check */
+ if ( ! loaded ) {
+ DBG ( "[NULL LoadedImage]" );
+ return NULL;
+ }
+
+ /* Parse DOS header */
+ dos = loaded->ImageBase;
+ if ( ! dos ) {
+ DBG ( "[Missing DOS header]" );
+ return NULL;
+ }
+ dos_magic = dos->e_magic;
+ if ( dos_magic != EFI_IMAGE_DOS_SIGNATURE ) {
+ DBG ( "[Bad DOS signature %#04x]", dos_magic );
+ return NULL;
+ }
+ pe = ( loaded->ImageBase + dos->e_lfanew );
+
+ /* Parse PE header */
+ pe_magic = pe->Pe32.Signature;
+ if ( pe_magic != EFI_IMAGE_NT_SIGNATURE ) {
+ DBG ( "[Bad PE signature %#08x]", pe_magic );
+ return NULL;
+ }
+ opt32 = &pe->Pe32.OptionalHeader;
+ opt64 = &pe->Pe32Plus.OptionalHeader;
+ opt_magic = opt32->Magic;
+ if ( opt_magic == EFI_IMAGE_NT_OPTIONAL_HDR32_MAGIC ) {
+ datadir = opt32->DataDirectory;
+ } else if ( opt_magic == EFI_IMAGE_NT_OPTIONAL_HDR64_MAGIC ) {
+ datadir = opt64->DataDirectory;
+ } else {
+ DBG ( "[Bad optional header signature %#04x]", opt_magic );
+ return NULL;
+ }
+
+ /* Parse data directory entry */
+ if ( ! datadir[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress ) {
+ DBG ( "[Empty debug directory entry]" );
+ return NULL;
+ }
+ debug = ( loaded->ImageBase +
+ datadir[EFI_IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress );
+
+ /* Parse debug directory entry */
+ if ( debug->Type != EFI_IMAGE_DEBUG_TYPE_CODEVIEW ) {
+ DBG ( "[Not a CodeView debug directory entry (type %d)]",
+ debug->Type );
+ return NULL;
+ }
+ codeview_nb10 = ( loaded->ImageBase + debug->RVA );
+ codeview_rsds = ( loaded->ImageBase + debug->RVA );
+ codeview_mtoc = ( loaded->ImageBase + debug->RVA );
+ codeview_magic = codeview_nb10->Signature;
+
+ /* Parse CodeView entry */
+ if ( codeview_magic == CODEVIEW_SIGNATURE_NB10 ) {
+ name = ( ( void * ) ( codeview_nb10 + 1 ) );
+ } else if ( codeview_magic == CODEVIEW_SIGNATURE_RSDS ) {
+ name = ( ( void * ) ( codeview_rsds + 1 ) );
+ } else if ( codeview_magic == CODEVIEW_SIGNATURE_MTOC ) {
+ name = ( ( void * ) ( codeview_mtoc + 1 ) );
+ } else {
+ DBG ( "[Bad CodeView signature %#08x]", codeview_magic );
+ return NULL;
+ }
+
+ /* Sanity check - avoid scanning endlessly through memory */
+ max_len = EFI_PAGE_SIZE; /* Reasonably sane */
+ if ( strnlen ( name, max_len ) == max_len ) {
+ DBG ( "[Excessively long or invalid CodeView name]" );
+ return NULL;
+ }
+
+ /* Skip any directory components. We cannot modify this data
+ * or create a temporary buffer, so do not use basename().
+ */
+ while ( ( ( tmp = strchr ( name, '/' ) ) != NULL ) ||
+ ( ( tmp = strchr ( name, '\\' ) ) != NULL ) ) {
+ name = ( tmp + 1 );
+ }
+
+ /* Copy base name to buffer */
+ snprintf ( buf, sizeof ( buf ), "%s", name );
+
+ /* Strip file suffix, if present */
+ if ( ( tmp = strrchr ( name, '.' ) ) != NULL )
+ *tmp = '\0';
+
+ return name;
+}
+
+/**
+ * Get initial loaded image name
+ *
+ * @v loaded Loaded image
+ * @ret name Initial loaded image name, or NULL
+ */
+static const char *
+efi_first_loaded_image_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) {
+
+ /* Sanity check */
+ if ( ! loaded ) {
+ DBG ( "[NULL LoadedImage]" );
+ return NULL;
+ }
+
+ return ( ( loaded->ParentHandle == NULL ) ? "DxeCore(?)" : NULL );
+}
+
+/**
+ * Get loaded image name from file path
+ *
+ * @v loaded Loaded image
+ * @ret name Loaded image name, or NULL
+ */
+static const char *
+efi_loaded_image_filepath_name ( EFI_LOADED_IMAGE_PROTOCOL *loaded ) {
+
+ /* Sanity check */
+ if ( ! loaded ) {
+ DBG ( "[NULL LoadedImage]" );
+ return NULL;
+ }
+
+ return efi_devpath_text ( loaded->FilePath );
+}
+
+/** An EFI handle name type */
+struct efi_handle_name_type {
+ /** Protocol */
+ EFI_GUID *protocol;
+ /**
+ * Get name
+ *
+ * @v interface Protocol interface
+ * @ret name Name of handle, or NULL on failure
+ */
+ const char * ( * name ) ( void *interface );
+};
+
+/**
+ * Define an EFI handle name type
+ *
+ * @v protocol Protocol interface
+ * @v name Method to get name
+ * @ret type EFI handle name type
+ */
+#define EFI_HANDLE_NAME_TYPE( protocol, name ) { \
+ (protocol), \
+ ( const char * ( * ) ( void * ) ) (name), \
+ }
+
+/** EFI handle name types */
+static struct efi_handle_name_type efi_handle_name_types[] = {
+ /* Device path */
+ EFI_HANDLE_NAME_TYPE ( &efi_device_path_protocol_guid,
+ efi_devpath_text ),
+ /* Driver name (for driver image handles) */
+ EFI_HANDLE_NAME_TYPE ( &efi_component_name2_protocol_guid,
+ efi_driver_name2 ),
+ /* Driver name (via obsolete original ComponentName protocol) */
+ EFI_HANDLE_NAME_TYPE ( &efi_component_name_protocol_guid,
+ efi_driver_name ),
+ /* PE/COFF debug filename (for image handles) */
+ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid,
+ efi_pecoff_debug_name ),
+ /* Loaded image device path (for image handles) */
+ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_device_path_protocol_guid,
+ efi_devpath_text ),
+ /* First loaded image name (for the DxeCore image) */
+ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid,
+ efi_first_loaded_image_name ),
+ /* Handle's loaded image file path (for image handles) */
+ EFI_HANDLE_NAME_TYPE ( &efi_loaded_image_protocol_guid,
+ efi_loaded_image_filepath_name ),
+};
+
+/**
+ * Get name of an EFI handle
+ *
+ * @v handle EFI handle
+ * @ret text Name of handle, or NULL
+ */
+const char * efi_handle_name ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_handle_name_type *type;
+ unsigned int i;
+ void *interface;
+ const char *name;
+ EFI_STATUS efirc;
+
+ /* Fail immediately for NULL handles */
+ if ( ! handle )
+ return NULL;
+
+ /* Try each name type in turn */
+ for ( i = 0 ; i < ( sizeof ( efi_handle_name_types ) /
+ sizeof ( efi_handle_name_types[0] ) ) ; i++ ) {
+ type = &efi_handle_name_types[i];
+ DBG2 ( "<%d", i );
+
+ /* Try to open the applicable protocol */
+ efirc = bs->OpenProtocol ( handle, type->protocol, &interface,
+ efi_image_handle, handle,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL );
+ if ( efirc != 0 ) {
+ DBG2 ( ">" );
+ continue;
+ }
+
+ /* Try to get name from this protocol */
+ DBG2 ( "-" );
+ name = type->name ( interface );
+ DBG2 ( "%c", ( name ? ( name[0] ? 'Y' : 'E' ) : 'N' ) );
+
+ /* Close protocol */
+ bs->CloseProtocol ( handle, type->protocol,
+ efi_image_handle, handle );
+ DBG2 ( ">" );
+
+ /* Use this name, if possible */
+ if ( name && name[0] )
+ return name;
+ }
+
+ return "UNKNOWN";
+}
diff --git a/roms/ipxe/src/interface/efi/efi_download.c b/roms/ipxe/src/interface/efi/efi_download.c
new file mode 100644
index 00000000..1218852e
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_download.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2010 VMware, Inc. All Rights Reserved.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/open.h>
+#include <ipxe/process.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/xfer.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_snp.h>
+#include <ipxe/efi/efi_download.h>
+
+/** iPXE download protocol GUID */
+static EFI_GUID ipxe_download_protocol_guid
+ = IPXE_DOWNLOAD_PROTOCOL_GUID;
+
+/** A single in-progress file */
+struct efi_download_file {
+ /** Data transfer interface that provides downloaded data */
+ struct interface xfer;
+
+ /** Current file position */
+ size_t pos;
+
+ /** Data callback */
+ IPXE_DOWNLOAD_DATA_CALLBACK data_callback;
+
+ /** Finish callback */
+ IPXE_DOWNLOAD_FINISH_CALLBACK finish_callback;
+
+ /** Callback context */
+ void *context;
+};
+
+/* xfer interface */
+
+/**
+ * Transfer finished or was aborted
+ *
+ * @v file Data transfer file
+ * @v rc Reason for close
+ */
+static void efi_download_close ( struct efi_download_file *file, int rc ) {
+
+ file->finish_callback ( file->context, EFIRC ( rc ) );
+
+ intf_shutdown ( &file->xfer, rc );
+
+ efi_snp_release();
+}
+
+/**
+ * Process received data
+ *
+ * @v file Data transfer file
+ * @v iobuf I/O buffer
+ * @v meta Data transfer metadata
+ * @ret rc Return status code
+ */
+static int efi_download_deliver_iob ( struct efi_download_file *file,
+ struct io_buffer *iobuf,
+ struct xfer_metadata *meta ) {
+ EFI_STATUS efirc;
+ size_t len = iob_len ( iobuf );
+ int rc;
+
+ /* Calculate new buffer position */
+ if ( meta->flags & XFER_FL_ABS_OFFSET )
+ file->pos = 0;
+ file->pos += meta->offset;
+
+ /* Call out to the data handler */
+ if ( ( efirc = file->data_callback ( file->context, iobuf->data,
+ len, file->pos ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ goto err_callback;
+ }
+
+ /* Update current buffer position */
+ file->pos += len;
+
+ /* Success */
+ rc = 0;
+
+ err_callback:
+ free_iob ( iobuf );
+ return rc;
+}
+
+/** Data transfer interface operations */
+static struct interface_operation efi_xfer_operations[] = {
+ INTF_OP ( xfer_deliver, struct efi_download_file *, efi_download_deliver_iob ),
+ INTF_OP ( intf_close, struct efi_download_file *, efi_download_close ),
+};
+
+/** EFI download data transfer interface descriptor */
+static struct interface_descriptor efi_download_file_xfer_desc =
+ INTF_DESC ( struct efi_download_file, xfer, efi_xfer_operations );
+
+/**
+ * Start downloading a file, and register callback functions to handle the
+ * download.
+ *
+ * @v This iPXE Download Protocol instance
+ * @v Url URL to download from
+ * @v DataCallback Callback that will be invoked when data arrives
+ * @v FinishCallback Callback that will be invoked when the download ends
+ * @v Context Context passed to the Data and Finish callbacks
+ * @v File Token that can be used to abort the download
+ * @ret Status EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_download_start ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
+ CHAR8 *Url,
+ IPXE_DOWNLOAD_DATA_CALLBACK DataCallback,
+ IPXE_DOWNLOAD_FINISH_CALLBACK FinishCallback,
+ VOID *Context,
+ IPXE_DOWNLOAD_FILE *File ) {
+ struct efi_download_file *file;
+ int rc;
+
+ file = malloc ( sizeof ( struct efi_download_file ) );
+ if ( file == NULL ) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ intf_init ( &file->xfer, &efi_download_file_xfer_desc, NULL );
+ rc = xfer_open ( &file->xfer, LOCATION_URI_STRING, Url );
+ if ( rc ) {
+ free ( file );
+ return EFIRC ( rc );
+ }
+
+ efi_snp_claim();
+ file->pos = 0;
+ file->data_callback = DataCallback;
+ file->finish_callback = FinishCallback;
+ file->context = Context;
+ *File = file;
+ return EFI_SUCCESS;
+}
+
+/**
+ * Forcibly abort downloading a file that is currently in progress.
+ *
+ * It is not safe to call this function after the Finish callback has executed.
+ *
+ * @v This iPXE Download Protocol instance
+ * @v File Token obtained from Start
+ * @v Status Reason for aborting the download
+ * @ret Status EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_download_abort ( IPXE_DOWNLOAD_PROTOCOL *This __unused,
+ IPXE_DOWNLOAD_FILE File,
+ EFI_STATUS Status ) {
+ struct efi_download_file *file = File;
+
+ efi_download_close ( file, -EEFI ( Status ) );
+ return EFI_SUCCESS;
+}
+
+/**
+ * Poll for more data from iPXE. This function will invoke the registered
+ * callbacks if data is available or if downloads complete.
+ *
+ * @v This iPXE Download Protocol instance
+ * @ret Status EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_download_poll ( IPXE_DOWNLOAD_PROTOCOL *This __unused ) {
+ step();
+ return EFI_SUCCESS;
+}
+
+/** Publicly exposed iPXE download protocol */
+static IPXE_DOWNLOAD_PROTOCOL ipxe_download_protocol_interface = {
+ .Start = efi_download_start,
+ .Abort = efi_download_abort,
+ .Poll = efi_download_poll
+};
+
+/**
+ * Install iPXE download protocol
+ *
+ * @v handle EFI handle
+ * @ret rc Return status code
+ */
+int efi_download_install ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ efirc = bs->InstallMultipleProtocolInterfaces (
+ &handle,
+ &ipxe_download_protocol_guid,
+ &ipxe_download_protocol_interface,
+ NULL );
+ if ( efirc ) {
+ rc = -EEFI ( efirc );
+ DBG ( "Could not install download protocol: %s\n",
+ strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Uninstall iPXE download protocol
+ *
+ * @v handle EFI handle
+ */
+void efi_download_uninstall ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ bs->UninstallMultipleProtocolInterfaces (
+ handle,
+ &ipxe_download_protocol_guid,
+ &ipxe_download_protocol_interface, NULL );
+}
diff --git a/roms/ipxe/src/interface/efi/efi_driver.c b/roms/ipxe/src/interface/efi/efi_driver.c
new file mode 100644
index 00000000..ba7784cd
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_driver.c
@@ -0,0 +1,517 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/version.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/DriverBinding.h>
+#include <ipxe/efi/Protocol/ComponentName2.h>
+#include <ipxe/efi/Protocol/DevicePath.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_driver.h>
+
+/** @file
+ *
+ * EFI driver interface
+ *
+ */
+
+static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding;
+
+/** List of controlled EFI devices */
+static LIST_HEAD ( efi_devices );
+
+/**
+ * Find EFI device
+ *
+ * @v device EFI device handle
+ * @ret efidev EFI device, or NULL if not found
+ */
+static struct efi_device * efidev_find ( EFI_HANDLE device ) {
+ struct efi_device *efidev;
+
+ /* Look for an existing EFI device */
+ list_for_each_entry ( efidev, &efi_devices, dev.siblings ) {
+ if ( efidev->device == device )
+ return efidev;
+ }
+
+ return NULL;
+}
+
+/**
+ * Get parent EFI device
+ *
+ * @v dev Generic device
+ * @ret efidev Parent EFI device, or NULL
+ */
+struct efi_device * efidev_parent ( struct device *dev ) {
+ struct device *parent = dev->parent;
+ struct efi_device *efidev;
+
+ /* Check that parent exists and is an EFI device */
+ if ( ! parent )
+ return NULL;
+ if ( parent->desc.bus_type != BUS_TYPE_EFI )
+ return NULL;
+
+ /* Get containing EFI device */
+ efidev = container_of ( parent, struct efi_device, dev );
+ return efidev;
+}
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v child Path to child device, if any
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_driver_supported ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
+ EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) {
+ struct efi_driver *efidrv;
+ int rc;
+
+ DBGCP ( device, "EFIDRV %p %s DRIVER_SUPPORTED",
+ device, efi_handle_name ( device ) );
+ if ( child )
+ DBGCP ( device, " (child %s)", efi_devpath_text ( child ) );
+ DBGCP ( device, "\n" );
+
+ /* Do nothing if we are already driving this device */
+ if ( efidev_find ( device ) != NULL ) {
+ DBGCP ( device, "EFIDRV %p %s is already started\n",
+ device, efi_handle_name ( device ) );
+ return EFI_ALREADY_STARTED;
+ }
+
+ /* Look for a driver claiming to support this device */
+ for_each_table_entry ( efidrv, EFI_DRIVERS ) {
+ if ( ( rc = efidrv->supported ( device ) ) == 0 ) {
+ DBGC ( device, "EFIDRV %p %s has driver \"%s\"\n",
+ device, efi_handle_name ( device ),
+ efidrv->name );
+ return 0;
+ }
+ }
+ DBGCP ( device, "EFIDRV %p %s has no driver\n",
+ device, efi_handle_name ( device ) );
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * Attach driver to device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v child Path to child device, if any
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_driver_start ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
+ EFI_HANDLE device, EFI_DEVICE_PATH_PROTOCOL *child ) {
+ struct efi_driver *efidrv;
+ struct efi_device *efidev;
+ EFI_STATUS efirc;
+ int rc;
+
+ DBGC ( device, "EFIDRV %p %s DRIVER_START",
+ device, efi_handle_name ( device ) );
+ if ( child )
+ DBGC ( device, " (child %s)", efi_devpath_text ( child ) );
+ DBGC ( device, "\n" );
+
+ /* Do nothing if we are already driving this device */
+ efidev = efidev_find ( device );
+ if ( efidev ) {
+ DBGCP ( device, "EFIDRV %p %s is already started\n",
+ device, efi_handle_name ( device ) );
+ efirc = EFI_ALREADY_STARTED;
+ goto err_already_started;
+ }
+
+ /* Allocate and initialise structure */
+ efidev = zalloc ( sizeof ( *efidev ) );
+ if ( ! efidev ) {
+ efirc = EFI_OUT_OF_RESOURCES;
+ goto err_alloc;
+ }
+ efidev->device = device;
+ efidev->dev.desc.bus_type = BUS_TYPE_EFI;
+ INIT_LIST_HEAD ( &efidev->dev.children );
+ list_add ( &efidev->dev.siblings, &efi_devices );
+
+ /* Try to start this device */
+ for_each_table_entry ( efidrv, EFI_DRIVERS ) {
+ if ( ( rc = efidrv->supported ( device ) ) != 0 ) {
+ DBGC ( device, "EFIDRV %p %s is not supported by "
+ "driver \"%s\": %s\n", device,
+ efi_handle_name ( device ), efidrv->name,
+ strerror ( rc ) );
+ continue;
+ }
+ if ( ( rc = efidrv->start ( efidev ) ) == 0 ) {
+ efidev->driver = efidrv;
+ DBGC ( device, "EFIDRV %p %s using driver \"%s\"\n",
+ device, efi_handle_name ( device ),
+ efidev->driver->name );
+ return 0;
+ }
+ DBGC ( device, "EFIDRV %p %s could not start driver \"%s\": "
+ "%s\n", device, efi_handle_name ( device ),
+ efidrv->name, strerror ( rc ) );
+ }
+ efirc = EFI_UNSUPPORTED;
+
+ list_del ( &efidev->dev.siblings );
+ free ( efidev );
+ err_alloc:
+ err_already_started:
+ return efirc;
+}
+
+/**
+ * Detach driver from device
+ *
+ * @v driver EFI driver
+ * @v device EFI device
+ * @v pci PCI device
+ * @v num_children Number of child devices
+ * @v children List of child devices
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_driver_stop ( EFI_DRIVER_BINDING_PROTOCOL *driver __unused,
+ EFI_HANDLE device, UINTN num_children,
+ EFI_HANDLE *children ) {
+ struct efi_driver *efidrv;
+ struct efi_device *efidev;
+ UINTN i;
+
+ DBGC ( device, "EFIDRV %p %s DRIVER_STOP",
+ device, efi_handle_name ( device ) );
+ for ( i = 0 ; i < num_children ; i++ ) {
+ DBGC ( device, "%s%p %s", ( i ? ", " : " child " ),
+ children[i], efi_handle_name ( children[i] ) );
+ }
+ DBGC ( device, "\n" );
+
+ /* Do nothing unless we are driving this device */
+ efidev = efidev_find ( device );
+ if ( ! efidev ) {
+ DBGCP ( device, "EFIDRV %p %s is not started\n",
+ device, efi_handle_name ( device ) );
+ return 0;
+ }
+
+ /* Stop this device */
+ efidrv = efidev->driver;
+ assert ( efidrv != NULL );
+ efidrv->stop ( efidev );
+ list_del ( &efidev->dev.siblings );
+ free ( efidev );
+
+ return 0;
+}
+
+/** EFI driver binding protocol */
+static EFI_DRIVER_BINDING_PROTOCOL efi_driver_binding = {
+ .Supported = efi_driver_supported,
+ .Start = efi_driver_start,
+ .Stop = efi_driver_stop,
+};
+
+/**
+ * Look up driver name
+ *
+ * @v wtf Component name protocol
+ * @v language Language to use
+ * @v driver_name Driver name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
+ CHAR8 *language __unused, CHAR16 **driver_name ) {
+ const wchar_t *name;
+
+ name = ( product_wname[0] ? product_wname : build_wname );
+ *driver_name = ( ( wchar_t * ) name );
+ return 0;
+}
+
+/**
+ * Look up controller name
+ *
+ * @v wtf Component name protocol
+ * @v device Device
+ * @v child Child device, or NULL
+ * @v language Language to use
+ * @v driver_name Device name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_driver_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *wtf __unused,
+ EFI_HANDLE device, EFI_HANDLE child,
+ CHAR8 *language, CHAR16 **controller_name ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_COMPONENT_NAME2_PROTOCOL *name2;
+ void *interface;
+ } name2;
+ EFI_STATUS efirc;
+
+ /* Delegate to the EFI_COMPONENT_NAME2_PROTOCOL instance
+ * installed on child handle, if present.
+ */
+ if ( ( child != NULL ) &&
+ ( ( efirc = bs->OpenProtocol (
+ child, &efi_component_name2_protocol_guid,
+ &name2.interface, NULL, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) == 0 ) ) {
+ return name2.name2->GetControllerName ( name2.name2, device,
+ child, language,
+ controller_name );
+ }
+
+ /* Otherwise, let EFI use the default Device Path Name */
+ return EFI_UNSUPPORTED;
+}
+
+/** EFI component name protocol */
+static EFI_COMPONENT_NAME2_PROTOCOL efi_wtf = {
+ .GetDriverName = efi_driver_name,
+ .GetControllerName = efi_driver_controller_name,
+ .SupportedLanguages = "en",
+};
+
+/**
+ * Install EFI driver
+ *
+ * @ret rc Return status code
+ */
+int efi_driver_install ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Calculate driver version number. We use the build
+ * timestamp (in seconds since the Epoch) shifted right by six
+ * bits: this gives us an approximately one-minute resolution
+ * and a scheme which will last until the year 10680.
+ */
+ efi_driver_binding.Version = ( build_timestamp >> 6 );
+
+ /* Install protocols on image handle */
+ efi_driver_binding.ImageHandle = efi_image_handle;
+ efi_driver_binding.DriverBindingHandle = efi_image_handle;
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &efi_image_handle,
+ &efi_driver_binding_protocol_guid, &efi_driver_binding,
+ &efi_component_name2_protocol_guid, &efi_wtf,
+ NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &efi_driver_binding, "EFIDRV could not install "
+ "protocols: %s\n", strerror ( rc ) );
+ return rc;
+ }
+
+ return 0;
+}
+
+/**
+ * Uninstall EFI driver
+ *
+ */
+void efi_driver_uninstall ( void ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ /* Uninstall protocols */
+ bs->UninstallMultipleProtocolInterfaces (
+ efi_image_handle,
+ &efi_driver_binding_protocol_guid, &efi_driver_binding,
+ &efi_component_name2_protocol_guid, &efi_wtf, NULL );
+}
+
+/**
+ * Try to connect EFI driver
+ *
+ * @v device EFI device
+ * @ret rc Return status code
+ */
+static int efi_driver_connect ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE drivers[2] =
+ { efi_driver_binding.DriverBindingHandle, NULL };
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Check if we want to drive this device */
+ if ( ( efirc = efi_driver_supported ( &efi_driver_binding, device,
+ NULL ) ) != 0 ) {
+ /* Not supported; not an error */
+ return 0;
+ }
+
+ /* Disconnect any existing drivers */
+ DBGC2 ( device, "EFIDRV %p %s before disconnecting:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_EFI_PROTOCOLS ( device, device );
+ DBGC ( device, "EFIDRV %p %s disconnecting existing drivers\n",
+ device, efi_handle_name ( device ) );
+ if ( ( efirc = bs->DisconnectController ( device, NULL,
+ NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIDRV %p %s could not disconnect existing "
+ "drivers: %s\n", device, efi_handle_name ( device ),
+ strerror ( rc ) );
+ /* Ignore the error and attempt to connect our drivers */
+ }
+ DBGC2 ( device, "EFIDRV %p %s after disconnecting:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_EFI_PROTOCOLS ( device, device );
+
+ /* Connect our driver */
+ DBGC ( device, "EFIDRV %p %s connecting new drivers\n",
+ device, efi_handle_name ( device ) );
+ if ( ( efirc = bs->ConnectController ( device, drivers, NULL,
+ FALSE ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIDRV %p %s could not connect new drivers: "
+ "%s\n", device, efi_handle_name ( device ),
+ strerror ( rc ) );
+ return rc;
+ }
+ DBGC2 ( device, "EFIDRV %p %s after connecting:\n",
+ device, efi_handle_name ( device ) );
+ DBGC2_EFI_PROTOCOLS ( device, device );
+
+ return 0;
+}
+
+/**
+ * Try to disconnect EFI driver
+ *
+ * @v device EFI device
+ * @ret rc Return status code
+ */
+static int efi_driver_disconnect ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ /* Disconnect our driver */
+ bs->DisconnectController ( device,
+ efi_driver_binding.DriverBindingHandle,
+ NULL );
+ return 0;
+}
+
+/**
+ * Reconnect original EFI driver
+ *
+ * @v device EFI device
+ * @ret rc Return status code
+ */
+static int efi_driver_reconnect ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ /* Reconnect any available driver */
+ bs->ConnectController ( device, NULL, NULL, FALSE );
+
+ return 0;
+}
+
+/**
+ * Connect/disconnect EFI driver from all handles
+ *
+ * @v method Connect/disconnect method
+ * @ret rc Return status code
+ */
+static int efi_driver_handles ( int ( * method ) ( EFI_HANDLE handle ) ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_HANDLE *handles;
+ UINTN num_handles;
+ EFI_STATUS efirc;
+ UINTN i;
+ int rc;
+
+ /* Enumerate all handles */
+ if ( ( efirc = bs->LocateHandleBuffer ( AllHandles, NULL, NULL,
+ &num_handles,
+ &handles ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &efi_driver_binding, "EFIDRV could not list handles: "
+ "%s\n", strerror ( rc ) );
+ goto err_locate;
+ }
+
+ /* Connect/disconnect driver from all handles */
+ for ( i = 0 ; i < num_handles ; i++ ) {
+ if ( ( rc = method ( handles[i] ) ) != 0 )
+ goto err_method;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_method:
+ bs->FreePool ( handles );
+ err_locate:
+ return rc;
+}
+
+/**
+ * Connect EFI driver to all possible devices
+ *
+ * @ret rc Return status code
+ */
+int efi_driver_connect_all ( void ) {
+
+ DBGC ( &efi_driver_binding, "EFIDRV connecting our drivers\n" );
+ return efi_driver_handles ( efi_driver_connect );
+}
+
+/**
+ * Disconnect EFI driver from all possible devices
+ *
+ * @ret rc Return status code
+ */
+void efi_driver_disconnect_all ( void ) {
+
+ DBGC ( &efi_driver_binding, "EFIDRV disconnecting our drivers\n" );
+ efi_driver_handles ( efi_driver_disconnect );
+}
+
+/**
+ * Reconnect original EFI drivers to all possible devices
+ *
+ * @ret rc Return status code
+ */
+void efi_driver_reconnect_all ( void ) {
+
+ DBGC ( &efi_driver_binding, "EFIDRV reconnecting old drivers\n" );
+ efi_driver_handles ( efi_driver_reconnect );
+}
diff --git a/roms/ipxe/src/interface/efi/efi_file.c b/roms/ipxe/src/interface/efi/efi_file.c
new file mode 100644
index 00000000..2ef3c573
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_file.c
@@ -0,0 +1,714 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * EFI file protocols
+ *
+ */
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <wchar.h>
+#include <ipxe/image.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/SimpleFileSystem.h>
+#include <ipxe/efi/Protocol/BlockIo.h>
+#include <ipxe/efi/Protocol/DiskIo.h>
+#include <ipxe/efi/Guid/FileInfo.h>
+#include <ipxe/efi/Guid/FileSystemInfo.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_file.h>
+
+/** EFI file information GUID */
+static EFI_GUID efi_file_info_id = EFI_FILE_INFO_ID;
+
+/** EFI file system information GUID */
+static EFI_GUID efi_file_system_info_id = EFI_FILE_SYSTEM_INFO_ID;
+
+/** EFI media ID */
+#define EFI_MEDIA_ID_MAGIC 0x69505845
+
+/** An image exposed as an EFI file */
+struct efi_file {
+ /** EFI file protocol */
+ EFI_FILE_PROTOCOL file;
+ /** Image */
+ struct image *image;
+ /** Current file position */
+ size_t pos;
+};
+
+static struct efi_file efi_file_root;
+
+/**
+ * Get EFI file name (for debugging)
+ *
+ * @v file EFI file
+ * @ret name Name
+ */
+static const char * efi_file_name ( struct efi_file *file ) {
+
+ return ( file->image ? file->image->name : "<root>" );
+}
+
+/**
+ * Find EFI file image
+ *
+ * @v wname Filename
+ * @ret image Image, or NULL
+ */
+static struct image * efi_file_find ( const CHAR16 *wname ) {
+ char name[ wcslen ( wname ) + 1 /* NUL */ ];
+ struct image *image;
+
+ /* Find image */
+ snprintf ( name, sizeof ( name ), "%ls", wname );
+ list_for_each_entry ( image, &images, list ) {
+ if ( strcasecmp ( image->name, name ) == 0 )
+ return image;
+ }
+
+ return NULL;
+
+}
+
+/**
+ * Open file
+ *
+ * @v this EFI file
+ * @ret new New EFI file
+ * @v wname Filename
+ * @v mode File mode
+ * @v attributes File attributes (for newly-created files)
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_file_open ( EFI_FILE_PROTOCOL *this, EFI_FILE_PROTOCOL **new,
+ CHAR16 *wname, UINT64 mode __unused,
+ UINT64 attributes __unused ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+ struct efi_file *new_file;
+ struct image *image;
+
+ /* Initial '\' indicates opening from the root directory */
+ while ( *wname == L'\\' ) {
+ file = &efi_file_root;
+ wname++;
+ }
+
+ /* Allow root directory itself to be opened */
+ if ( ( wname[0] == L'\0' ) || ( wname[0] == L'.' ) ) {
+ *new = &efi_file_root.file;
+ return 0;
+ }
+
+ /* Fail unless opening from the root */
+ if ( file->image ) {
+ DBGC ( file, "EFIFILE %s is not a directory\n",
+ efi_file_name ( file ) );
+ return EFI_NOT_FOUND;
+ }
+
+ /* Identify image */
+ image = efi_file_find ( wname );
+ if ( ! image ) {
+ DBGC ( file, "EFIFILE \"%ls\" does not exist\n", wname );
+ return EFI_NOT_FOUND;
+ }
+
+ /* Fail unless opening read-only */
+ if ( mode != EFI_FILE_MODE_READ ) {
+ DBGC ( file, "EFIFILE %s cannot be opened in mode %#08llx\n",
+ image->name, mode );
+ return EFI_WRITE_PROTECTED;
+ }
+
+ /* Allocate and initialise file */
+ new_file = zalloc ( sizeof ( *new_file ) );
+ memcpy ( &new_file->file, &efi_file_root.file,
+ sizeof ( new_file->file ) );
+ new_file->image = image_get ( image );
+ *new = &new_file->file;
+ DBGC ( new_file, "EFIFILE %s opened\n", efi_file_name ( new_file ) );
+
+ return 0;
+}
+
+/**
+ * Close file
+ *
+ * @v this EFI file
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_close ( EFI_FILE_PROTOCOL *this ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ /* Do nothing if this is the root */
+ if ( ! file->image )
+ return 0;
+
+ /* Close file */
+ DBGC ( file, "EFIFILE %s closed\n", efi_file_name ( file ) );
+ image_put ( file->image );
+ free ( file );
+
+ return 0;
+}
+
+/**
+ * Close and delete file
+ *
+ * @v this EFI file
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_delete ( EFI_FILE_PROTOCOL *this ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ DBGC ( file, "EFIFILE %s cannot be deleted\n", efi_file_name ( file ) );
+
+ /* Close file */
+ efi_file_close ( this );
+
+ /* Warn of failure to delete */
+ return EFI_WARN_DELETE_FAILURE;
+}
+
+/**
+ * Return variable-length data structure
+ *
+ * @v base Base data structure (starting with UINT64)
+ * @v base_len Length of base data structure
+ * @v name Name to append to base data structure
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS efi_file_varlen ( UINT64 *base, size_t base_len,
+ const char *name, UINTN *len, VOID *data ) {
+ size_t name_len;
+
+ /* Calculate structure length */
+ name_len = strlen ( name );
+ *base = ( base_len + ( name_len + 1 /* NUL */ ) * sizeof ( wchar_t ) );
+ if ( *len < *base ) {
+ *len = *base;
+ return EFI_BUFFER_TOO_SMALL;
+ }
+
+ /* Copy data to buffer */
+ *len = *base;
+ memcpy ( data, base, base_len );
+ efi_snprintf ( ( data + base_len ), ( name_len + 1 /* NUL */ ),
+ "%s", name );
+
+ return 0;
+}
+
+/**
+ * Return file information structure
+ *
+ * @v image Image, or NULL for the root directory
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS efi_file_info ( struct image *image, UINTN *len,
+ VOID *data ) {
+ EFI_FILE_INFO info;
+ const char *name;
+
+ /* Populate file information */
+ memset ( &info, 0, sizeof ( info ) );
+ if ( image ) {
+ info.FileSize = image->len;
+ info.PhysicalSize = image->len;
+ info.Attribute = EFI_FILE_READ_ONLY;
+ name = image->name;
+ } else {
+ info.Attribute = ( EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY );
+ name = "";
+ }
+
+ return efi_file_varlen ( &info.Size, SIZE_OF_EFI_FILE_INFO, name,
+ len, data );
+}
+
+/**
+ * Read directory entry
+ *
+ * @v file EFI file
+ * @v len Length to read
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS efi_file_read_dir ( struct efi_file *file, UINTN *len,
+ VOID *data ) {
+ EFI_STATUS efirc;
+ struct image *image;
+ unsigned int index;
+
+ /* Construct directory entry at current position */
+ index = file->pos;
+ for_each_image ( image ) {
+ if ( index-- == 0 ) {
+ efirc = efi_file_info ( image, len, data );
+ if ( efirc == 0 )
+ file->pos++;
+ return efirc;
+ }
+ }
+
+ /* No more entries */
+ *len = 0;
+ return 0;
+}
+
+/**
+ * Read from file
+ *
+ * @v this EFI file
+ * @v len Length to read
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_read ( EFI_FILE_PROTOCOL *this,
+ UINTN *len, VOID *data ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+ size_t remaining;
+
+ /* If this is the root directory, then construct a directory entry */
+ if ( ! file->image )
+ return efi_file_read_dir ( file, len, data );
+
+ /* Read from the file */
+ remaining = ( file->image->len - file->pos );
+ if ( *len > remaining )
+ *len = remaining;
+ DBGC ( file, "EFIFILE %s read [%#08zx,%#08zx)\n",
+ efi_file_name ( file ), file->pos,
+ ( ( size_t ) ( file->pos + *len ) ) );
+ copy_from_user ( data, file->image->data, file->pos, *len );
+ file->pos += *len;
+ return 0;
+}
+
+/**
+ * Write to file
+ *
+ * @v this EFI file
+ * @v len Length to write
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_write ( EFI_FILE_PROTOCOL *this,
+ UINTN *len, VOID *data __unused ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ DBGC ( file, "EFIFILE %s cannot write [%#08zx, %#08zx)\n",
+ efi_file_name ( file ), file->pos,
+ ( ( size_t ) ( file->pos + *len ) ) );
+ return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * Set file position
+ *
+ * @v this EFI file
+ * @v position New file position
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_set_position ( EFI_FILE_PROTOCOL *this,
+ UINT64 position ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ /* If this is the root directory, reset to the start */
+ if ( ! file->image ) {
+ DBGC ( file, "EFIFILE root directory rewound\n" );
+ file->pos = 0;
+ return 0;
+ }
+
+ /* Check for the magic end-of-file value */
+ if ( position == 0xffffffffffffffffULL )
+ position = file->image->len;
+
+ /* Fail if we attempt to seek past the end of the file (since
+ * we do not support writes).
+ */
+ if ( position > file->image->len ) {
+ DBGC ( file, "EFIFILE %s cannot seek to %#08llx of %#08zx\n",
+ efi_file_name ( file ), position, file->image->len );
+ return EFI_UNSUPPORTED;
+ }
+
+ /* Set position */
+ file->pos = position;
+ DBGC ( file, "EFIFILE %s position set to %#08zx\n",
+ efi_file_name ( file ), file->pos );
+
+ return 0;
+}
+
+/**
+ * Get file position
+ *
+ * @v this EFI file
+ * @ret position New file position
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_get_position ( EFI_FILE_PROTOCOL *this,
+ UINT64 *position ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ *position = file->pos;
+ return 0;
+}
+
+/**
+ * Get file information
+ *
+ * @v this EFI file
+ * @v type Type of information
+ * @v len Buffer size
+ * @v data Buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_get_info ( EFI_FILE_PROTOCOL *this,
+ EFI_GUID *type,
+ UINTN *len, VOID *data ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+ EFI_FILE_SYSTEM_INFO fsinfo;
+ struct image *image;
+
+ /* Determine information to return */
+ if ( memcmp ( type, &efi_file_info_id, sizeof ( *type ) ) == 0 ) {
+
+ /* Get file information */
+ DBGC ( file, "EFIFILE %s get file information\n",
+ efi_file_name ( file ) );
+ return efi_file_info ( file->image, len, data );
+
+ } else if ( memcmp ( type, &efi_file_system_info_id,
+ sizeof ( *type ) ) == 0 ) {
+
+ /* Get file system information */
+ DBGC ( file, "EFIFILE %s get file system information\n",
+ efi_file_name ( file ) );
+ memset ( &fsinfo, 0, sizeof ( fsinfo ) );
+ fsinfo.ReadOnly = 1;
+ for_each_image ( image )
+ fsinfo.VolumeSize += image->len;
+ return efi_file_varlen ( &fsinfo.Size,
+ SIZE_OF_EFI_FILE_SYSTEM_INFO, "iPXE",
+ len, data );
+ } else {
+
+ DBGC ( file, "EFIFILE %s cannot get information of type %s\n",
+ efi_file_name ( file ), efi_guid_ntoa ( type ) );
+ return EFI_UNSUPPORTED;
+ }
+}
+
+/**
+ * Set file information
+ *
+ * @v this EFI file
+ * @v type Type of information
+ * @v len Buffer size
+ * @v data Buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_file_set_info ( EFI_FILE_PROTOCOL *this, EFI_GUID *type,
+ UINTN len __unused, VOID *data __unused ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ DBGC ( file, "EFIFILE %s cannot set information of type %s\n",
+ efi_file_name ( file ), efi_guid_ntoa ( type ) );
+ return EFI_WRITE_PROTECTED;
+}
+
+/**
+ * Flush file modified data
+ *
+ * @v this EFI file
+ * @v type Type of information
+ * @v len Buffer size
+ * @v data Buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI efi_file_flush ( EFI_FILE_PROTOCOL *this ) {
+ struct efi_file *file = container_of ( this, struct efi_file, file );
+
+ DBGC ( file, "EFIFILE %s flushed\n", efi_file_name ( file ) );
+ return 0;
+}
+
+/** Root directory */
+static struct efi_file efi_file_root = {
+ .file = {
+ .Revision = EFI_FILE_PROTOCOL_REVISION,
+ .Open = efi_file_open,
+ .Close = efi_file_close,
+ .Delete = efi_file_delete,
+ .Read = efi_file_read,
+ .Write = efi_file_write,
+ .GetPosition = efi_file_get_position,
+ .SetPosition = efi_file_set_position,
+ .GetInfo = efi_file_get_info,
+ .SetInfo = efi_file_set_info,
+ .Flush = efi_file_flush,
+ },
+ .image = NULL,
+};
+
+/**
+ * Open root directory
+ *
+ * @v filesystem EFI simple file system
+ * @ret file EFI file handle
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_file_open_volume ( EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *filesystem __unused,
+ EFI_FILE_PROTOCOL **file ) {
+
+ DBGC ( &efi_file_root, "EFIFILE open volume\n" );
+ *file = &efi_file_root.file;
+ return 0;
+}
+
+/** EFI simple file system protocol */
+static EFI_SIMPLE_FILE_SYSTEM_PROTOCOL efi_simple_file_system_protocol = {
+ .Revision = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION,
+ .OpenVolume = efi_file_open_volume,
+};
+
+/** Dummy block I/O reset */
+static EFI_STATUS EFIAPI
+efi_block_io_reset ( EFI_BLOCK_IO_PROTOCOL *this __unused, BOOLEAN extended ) {
+
+ DBGC ( &efi_file_root, "EFIFILE block %sreset\n",
+ ( extended ? "extended " : "" ) );
+ return 0;
+}
+
+/** Dummy block I/O read */
+static EFI_STATUS EFIAPI
+efi_block_io_read_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused, UINT32 MediaId,
+ EFI_LBA lba, UINTN len, VOID *data ) {
+
+ DBGC ( &efi_file_root, "EFIFILE block read ID %#08x LBA %#08llx -> "
+ "%p+%zx\n", MediaId, ( ( unsigned long long ) lba ),
+ data, ( ( size_t ) len ) );
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy block I/O write */
+static EFI_STATUS EFIAPI
+efi_block_io_write_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused,
+ UINT32 MediaId, EFI_LBA lba, UINTN len,
+ VOID *data ) {
+
+ DBGC ( &efi_file_root, "EFIFILE block write ID %#08x LBA %#08llx <- "
+ "%p+%zx\n", MediaId, ( ( unsigned long long ) lba ),
+ data, ( ( size_t ) len ) );
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy block I/O flush */
+static EFI_STATUS EFIAPI
+efi_block_io_flush_blocks ( EFI_BLOCK_IO_PROTOCOL *this __unused ) {
+
+ DBGC ( &efi_file_root, "EFIFILE block flush\n" );
+ return 0;
+}
+
+/** Dummy block I/O media */
+static EFI_BLOCK_IO_MEDIA efi_block_io_media = {
+ .MediaId = EFI_MEDIA_ID_MAGIC,
+ .MediaPresent = TRUE,
+ .ReadOnly = TRUE,
+ .BlockSize = 1,
+};
+
+/** Dummy EFI block I/O protocol */
+static EFI_BLOCK_IO_PROTOCOL efi_block_io_protocol = {
+ .Revision = EFI_BLOCK_IO_PROTOCOL_REVISION,
+ .Media = &efi_block_io_media,
+ .Reset = efi_block_io_reset,
+ .ReadBlocks = efi_block_io_read_blocks,
+ .WriteBlocks = efi_block_io_write_blocks,
+ .FlushBlocks = efi_block_io_flush_blocks,
+};
+
+/** Dummy disk I/O read */
+static EFI_STATUS EFIAPI
+efi_disk_io_read_disk ( EFI_DISK_IO_PROTOCOL *this __unused, UINT32 MediaId,
+ UINT64 offset, UINTN len, VOID *data ) {
+
+ DBGC ( &efi_file_root, "EFIFILE disk read ID %#08x offset %#08llx -> "
+ "%p+%zx\n", MediaId, ( ( unsigned long long ) offset ),
+ data, ( ( size_t ) len ) );
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy disk I/O write */
+static EFI_STATUS EFIAPI
+efi_disk_io_write_disk ( EFI_DISK_IO_PROTOCOL *this __unused, UINT32 MediaId,
+ UINT64 offset, UINTN len, VOID *data ) {
+
+ DBGC ( &efi_file_root, "EFIFILE disk write ID %#08x offset %#08llx <- "
+ "%p+%zx\n", MediaId, ( ( unsigned long long ) offset ),
+ data, ( ( size_t ) len ) );
+ return EFI_NO_MEDIA;
+}
+
+/** Dummy EFI disk I/O protocol */
+static EFI_DISK_IO_PROTOCOL efi_disk_io_protocol = {
+ .Revision = EFI_DISK_IO_PROTOCOL_REVISION,
+ .ReadDisk = efi_disk_io_read_disk,
+ .WriteDisk = efi_disk_io_write_disk,
+};
+
+/**
+ * Install EFI simple file system protocol
+ *
+ * @v handle EFI handle
+ * @ret rc Return status code
+ */
+int efi_file_install ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_DISK_IO_PROTOCOL *diskio;
+ void *interface;
+ } diskio;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Install the simple file system protocol, block I/O
+ * protocol, and disk I/O protocol. We don't have a block
+ * device, but large parts of the EDK2 codebase make the
+ * assumption that file systems are normally attached to block
+ * devices, and so we create a dummy block device on the same
+ * handle just to keep things looking normal.
+ */
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &handle,
+ &efi_block_io_protocol_guid,
+ &efi_block_io_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
+ &efi_simple_file_system_protocol_guid,
+ &efi_simple_file_system_protocol, NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( handle, "Could not install simple file system "
+ "protocols: %s\n", strerror ( rc ) );
+ goto err_install;
+ }
+
+ /* The FAT filesystem driver has a bug: if a block device
+ * contains no FAT filesystem but does have an
+ * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL instance, the FAT driver
+ * will assume that it must have previously installed the
+ * EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. This causes the FAT
+ * driver to claim control of our device, and to refuse to
+ * stop driving it, which prevents us from later uninstalling
+ * correctly.
+ *
+ * Work around this bug by opening the disk I/O protocol
+ * ourselves, thereby preventing the FAT driver from opening
+ * it.
+ *
+ * Note that the alternative approach of opening the block I/O
+ * protocol (and thereby in theory preventing DiskIo from
+ * attaching to the block I/O protocol) causes an endless loop
+ * of calls to our DRIVER_STOP method when starting the EFI
+ * shell. I have no idea why this is.
+ */
+ if ( ( efirc = bs->OpenProtocol ( handle, &efi_disk_io_protocol_guid,
+ &diskio.interface, efi_image_handle,
+ handle,
+ EFI_OPEN_PROTOCOL_BY_DRIVER ) ) != 0){
+ rc = -EEFI ( efirc );
+ DBGC ( handle, "Could not open disk I/O protocol: %s\n",
+ strerror ( rc ) );
+ DBGC_EFI_OPENERS ( handle, handle, &efi_disk_io_protocol_guid );
+ goto err_open;
+ }
+ assert ( diskio.diskio == &efi_disk_io_protocol );
+
+ return 0;
+
+ bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
+ efi_image_handle, handle );
+ err_open:
+ bs->UninstallMultipleProtocolInterfaces (
+ handle,
+ &efi_simple_file_system_protocol_guid,
+ &efi_simple_file_system_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
+ &efi_block_io_protocol_guid,
+ &efi_block_io_protocol, NULL );
+ err_install:
+ return rc;
+}
+
+/**
+ * Uninstall EFI simple file system protocol
+ *
+ * @v handle EFI handle
+ */
+void efi_file_uninstall ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Close our own disk I/O protocol */
+ bs->CloseProtocol ( handle, &efi_disk_io_protocol_guid,
+ efi_image_handle, handle );
+
+ /* We must install the file system protocol first, since
+ * otherwise the EDK2 code will attempt to helpfully uninstall
+ * it when the block I/O protocol is uninstalled, leading to a
+ * system lock-up.
+ */
+ if ( ( efirc = bs->UninstallMultipleProtocolInterfaces (
+ handle,
+ &efi_simple_file_system_protocol_guid,
+ &efi_simple_file_system_protocol,
+ &efi_disk_io_protocol_guid,
+ &efi_disk_io_protocol,
+ &efi_block_io_protocol_guid,
+ &efi_block_io_protocol, NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( handle, "Could not uninstall simple file system "
+ "protocols: %s\n", strerror ( rc ) );
+ /* Oh dear */
+ }
+}
diff --git a/roms/ipxe/src/interface/efi/efi_guid.c b/roms/ipxe/src/interface/efi/efi_guid.c
new file mode 100644
index 00000000..52ba58ae
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_guid.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/Arp.h>
+#include <ipxe/efi/Protocol/BlockIo.h>
+#include <ipxe/efi/Protocol/BusSpecificDriverOverride.h>
+#include <ipxe/efi/Protocol/ComponentName.h>
+#include <ipxe/efi/Protocol/ComponentName2.h>
+#include <ipxe/efi/Protocol/DevicePath.h>
+#include <ipxe/efi/Protocol/DevicePathToText.h>
+#include <ipxe/efi/Protocol/Dhcp4.h>
+#include <ipxe/efi/Protocol/DiskIo.h>
+#include <ipxe/efi/Protocol/DriverBinding.h>
+#include <ipxe/efi/Protocol/GraphicsOutput.h>
+#include <ipxe/efi/Protocol/HiiConfigAccess.h>
+#include <ipxe/efi/Protocol/Ip4.h>
+#include <ipxe/efi/Protocol/Ip4Config.h>
+#include <ipxe/efi/Protocol/LoadFile.h>
+#include <ipxe/efi/Protocol/LoadFile2.h>
+#include <ipxe/efi/Protocol/LoadedImage.h>
+#include <ipxe/efi/Protocol/ManagedNetwork.h>
+#include <ipxe/efi/Protocol/Mtftp4.h>
+#include <ipxe/efi/Protocol/NetworkInterfaceIdentifier.h>
+#include <ipxe/efi/Protocol/PciIo.h>
+#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
+#include <ipxe/efi/Protocol/PxeBaseCode.h>
+#include <ipxe/efi/Protocol/SimpleFileSystem.h>
+#include <ipxe/efi/Protocol/SimpleNetwork.h>
+#include <ipxe/efi/Protocol/TcgService.h>
+#include <ipxe/efi/Protocol/Tcp4.h>
+#include <ipxe/efi/Protocol/Udp4.h>
+#include <ipxe/efi/Protocol/VlanConfig.h>
+
+/** @file
+ *
+ * EFI GUIDs
+ *
+ */
+
+/** ARP protocol GUID */
+EFI_GUID efi_arp_protocol_guid
+ = EFI_ARP_PROTOCOL_GUID;
+
+/** ARP service binding protocol GUID */
+EFI_GUID efi_arp_service_binding_protocol_guid
+ = EFI_ARP_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** Block I/O protocol GUID */
+EFI_GUID efi_block_io_protocol_guid
+ = EFI_BLOCK_IO_PROTOCOL_GUID;
+
+/** Bus specific driver override protocol GUID */
+EFI_GUID efi_bus_specific_driver_override_protocol_guid
+ = EFI_BUS_SPECIFIC_DRIVER_OVERRIDE_PROTOCOL_GUID;
+
+/** Component name protocol GUID */
+EFI_GUID efi_component_name_protocol_guid
+ = EFI_COMPONENT_NAME_PROTOCOL_GUID;
+
+/** Component name 2 protocol GUID */
+EFI_GUID efi_component_name2_protocol_guid
+ = EFI_COMPONENT_NAME2_PROTOCOL_GUID;
+
+/** Device path protocol GUID */
+EFI_GUID efi_device_path_protocol_guid
+ = EFI_DEVICE_PATH_PROTOCOL_GUID;
+
+/** DHCPv4 protocol GUID */
+EFI_GUID efi_dhcp4_protocol_guid
+ = EFI_DHCP4_PROTOCOL_GUID;
+
+/** DHCPv4 service binding protocol GUID */
+EFI_GUID efi_dhcp4_service_binding_protocol_guid
+ = EFI_DHCP4_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** Disk I/O protocol GUID */
+EFI_GUID efi_disk_io_protocol_guid
+ = EFI_DISK_IO_PROTOCOL_GUID;
+
+/** Driver binding protocol GUID */
+EFI_GUID efi_driver_binding_protocol_guid
+ = EFI_DRIVER_BINDING_PROTOCOL_GUID;
+
+/** Graphics output protocol GUID */
+EFI_GUID efi_graphics_output_protocol_guid
+ = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
+
+/** HII configuration access protocol GUID */
+EFI_GUID efi_hii_config_access_protocol_guid
+ = EFI_HII_CONFIG_ACCESS_PROTOCOL_GUID;
+
+/** IPv4 protocol GUID */
+EFI_GUID efi_ip4_protocol_guid
+ = EFI_IP4_PROTOCOL_GUID;
+
+/** IPv4 configuration protocol GUID */
+EFI_GUID efi_ip4_config_protocol_guid
+ = EFI_IP4_CONFIG_PROTOCOL_GUID;
+
+/** IPv4 service binding protocol GUID */
+EFI_GUID efi_ip4_service_binding_protocol_guid
+ = EFI_IP4_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** Load file protocol GUID */
+EFI_GUID efi_load_file_protocol_guid
+ = EFI_LOAD_FILE_PROTOCOL_GUID;
+
+/** Load file 2 protocol GUID */
+EFI_GUID efi_load_file2_protocol_guid
+ = EFI_LOAD_FILE2_PROTOCOL_GUID;
+
+/** Loaded image protocol GUID */
+EFI_GUID efi_loaded_image_protocol_guid
+ = EFI_LOADED_IMAGE_PROTOCOL_GUID;
+
+/** Loaded image device path protocol GUID */
+EFI_GUID efi_loaded_image_device_path_protocol_guid
+ = EFI_LOADED_IMAGE_DEVICE_PATH_PROTOCOL_GUID;
+
+/** Managed network protocol GUID */
+EFI_GUID efi_managed_network_protocol_guid
+ = EFI_MANAGED_NETWORK_PROTOCOL_GUID;
+
+/** Managed network service binding protocol GUID */
+EFI_GUID efi_managed_network_service_binding_protocol_guid
+ = EFI_MANAGED_NETWORK_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** MTFTPv4 protocol GUID */
+EFI_GUID efi_mtftp4_protocol_guid
+ = EFI_MTFTP4_PROTOCOL_GUID;
+
+/** MTFTPv4 service binding protocol GUID */
+EFI_GUID efi_mtftp4_service_binding_protocol_guid
+ = EFI_MTFTP4_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** Network interface identifier protocol GUID (old version) */
+EFI_GUID efi_nii_protocol_guid
+ = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID;
+
+/** Network interface identifier protocol GUID (new version) */
+EFI_GUID efi_nii31_protocol_guid
+ = EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_GUID_31;
+
+/** PCI I/O protocol GUID */
+EFI_GUID efi_pci_io_protocol_guid
+ = EFI_PCI_IO_PROTOCOL_GUID;
+
+/** PCI root bridge I/O protocol GUID */
+EFI_GUID efi_pci_root_bridge_io_protocol_guid
+ = EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_GUID;
+
+/** PXE base code protocol GUID */
+EFI_GUID efi_pxe_base_code_protocol_guid
+ = EFI_PXE_BASE_CODE_PROTOCOL_GUID;
+
+/** Simple file system protocol GUID */
+EFI_GUID efi_simple_file_system_protocol_guid
+ = EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID;
+
+/** Simple network protocol GUID */
+EFI_GUID efi_simple_network_protocol_guid
+ = EFI_SIMPLE_NETWORK_PROTOCOL_GUID;
+
+/** TCG protocol GUID */
+EFI_GUID efi_tcg_protocol_guid
+ = EFI_TCG_PROTOCOL_GUID;
+
+/** TCPv4 protocol GUID */
+EFI_GUID efi_tcp4_protocol_guid
+ = EFI_TCP4_PROTOCOL_GUID;
+
+/** TCPv4 service binding protocol GUID */
+EFI_GUID efi_tcp4_service_binding_protocol_guid
+ = EFI_TCP4_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** UDPv4 protocol GUID */
+EFI_GUID efi_udp4_protocol_guid
+ = EFI_UDP4_PROTOCOL_GUID;
+
+/** UDPv4 service binding protocol GUID */
+EFI_GUID efi_udp4_service_binding_protocol_guid
+ = EFI_UDP4_SERVICE_BINDING_PROTOCOL_GUID;
+
+/** VLAN configuration protocol GUID */
+EFI_GUID efi_vlan_config_protocol_guid
+ = EFI_VLAN_CONFIG_PROTOCOL_GUID;
diff --git a/roms/ipxe/src/interface/efi/efi_hii.c b/roms/ipxe/src/interface/efi/efi_hii.c
new file mode 100644
index 00000000..834060b5
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_hii.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_hii.h>
+
+/** Tiano GUID */
+static const EFI_GUID tiano_guid = EFI_IFR_TIANO_GUID;
+
+/**
+ * Add string to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v fmt Format string
+ * @v ... Arguments
+ * @ret string_id String identifier, or zero on failure
+ */
+unsigned int efi_ifr_string ( struct efi_ifr_builder *ifr, const char *fmt,
+ ... ) {
+ EFI_HII_STRING_BLOCK *new_strings;
+ EFI_HII_SIBT_STRING_UCS2_BLOCK *ucs2;
+ size_t new_strings_len;
+ va_list args;
+ size_t len;
+ unsigned int string_id;
+
+ /* Do nothing if a previous allocation has failed */
+ if ( ifr->failed )
+ return 0;
+
+ /* Calculate string length */
+ va_start ( args, fmt );
+ len = ( efi_vsnprintf ( NULL, 0, fmt, args ) + 1 /* wNUL */ );
+ va_end ( args );
+
+ /* Reallocate strings */
+ new_strings_len = ( ifr->strings_len +
+ offsetof ( typeof ( *ucs2 ), StringText ) +
+ ( len * sizeof ( ucs2->StringText[0] ) ) );
+ new_strings = realloc ( ifr->strings, new_strings_len );
+ if ( ! new_strings ) {
+ ifr->failed = 1;
+ return 0;
+ }
+ ucs2 = ( ( ( void * ) new_strings ) + ifr->strings_len );
+ ifr->strings = new_strings;
+ ifr->strings_len = new_strings_len;
+
+ /* Fill in string */
+ ucs2->Header.BlockType = EFI_HII_SIBT_STRING_UCS2;
+ va_start ( args, fmt );
+ efi_vsnprintf ( ucs2->StringText, len, fmt, args );
+ va_end ( args );
+
+ /* Allocate string ID */
+ string_id = ++(ifr->string_id);
+
+ DBGC ( ifr, "IFR %p string %#04x is \"%ls\"\n",
+ ifr, string_id, ucs2->StringText );
+ return string_id;
+}
+
+/**
+ * Add IFR opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v opcode Opcode
+ * @v len Opcode length
+ * @ret op Opcode, or NULL
+ */
+static void * efi_ifr_op ( struct efi_ifr_builder *ifr, unsigned int opcode,
+ size_t len ) {
+ EFI_IFR_OP_HEADER *new_ops;
+ EFI_IFR_OP_HEADER *op;
+ size_t new_ops_len;
+
+ /* Do nothing if a previous allocation has failed */
+ if ( ifr->failed )
+ return NULL;
+
+ /* Reallocate opcodes */
+ new_ops_len = ( ifr->ops_len + len );
+ new_ops = realloc ( ifr->ops, new_ops_len );
+ if ( ! new_ops ) {
+ ifr->failed = 1;
+ return NULL;
+ }
+ op = ( ( ( void * ) new_ops ) + ifr->ops_len );
+ ifr->ops = new_ops;
+ ifr->ops_len = new_ops_len;
+
+ /* Fill in opcode header */
+ op->OpCode = opcode;
+ op->Length = len;
+
+ return op;
+}
+
+/**
+ * Add end opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ */
+void efi_ifr_end_op ( struct efi_ifr_builder *ifr ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_END *end;
+
+ /* Add opcode */
+ end = efi_ifr_op ( ifr, EFI_IFR_END_OP, sizeof ( *end ) );
+
+ DBGC ( ifr, "IFR %p end\n", ifr );
+ DBGC2_HDA ( ifr, dispaddr, end, sizeof ( *end ) );
+}
+
+/**
+ * Add false opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ */
+void efi_ifr_false_op ( struct efi_ifr_builder *ifr ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_FALSE *false;
+
+ /* Add opcode */
+ false = efi_ifr_op ( ifr, EFI_IFR_FALSE_OP, sizeof ( *false ) );
+
+ DBGC ( ifr, "IFR %p false\n", ifr );
+ DBGC2_HDA ( ifr, dispaddr, false, sizeof ( *false ) );
+}
+
+/**
+ * Add form opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v title_id Title string identifier
+ * @ret form_id Form identifier
+ */
+unsigned int efi_ifr_form_op ( struct efi_ifr_builder *ifr,
+ unsigned int title_id ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_FORM *form;
+
+ /* Add opcode */
+ form = efi_ifr_op ( ifr, EFI_IFR_FORM_OP, sizeof ( *form ) );
+ if ( ! form )
+ return 0;
+ form->Header.Scope = 1;
+ form->FormId = ++(ifr->form_id);
+ form->FormTitle = title_id;
+
+ DBGC ( ifr, "IFR %p name/value store %#04x title %#04x\n",
+ ifr, form->FormId, title_id );
+ DBGC2_HDA ( ifr, dispaddr, form, sizeof ( *form ) );
+ return form->FormId;
+}
+
+/**
+ * Add formset opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v guid GUID
+ * @v title_id Title string identifier
+ * @v help_id Help string identifier
+ * @v ... Class GUIDs (terminated by NULL)
+ */
+void efi_ifr_form_set_op ( struct efi_ifr_builder *ifr, const EFI_GUID *guid,
+ unsigned int title_id, unsigned int help_id, ... ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_FORM_SET *formset;
+ EFI_GUID *class_guid;
+ unsigned int num_class_guids = 0;
+ size_t len;
+ va_list args;
+
+ /* Count number of class GUIDs */
+ va_start ( args, help_id );
+ while ( va_arg ( args, const EFI_GUID * ) != NULL )
+ num_class_guids++;
+ va_end ( args );
+
+ /* Add opcode */
+ len = ( sizeof ( *formset ) +
+ ( num_class_guids * sizeof ( *class_guid ) ) );
+ formset = efi_ifr_op ( ifr, EFI_IFR_FORM_SET_OP, len );
+ if ( ! formset )
+ return;
+ formset->Header.Scope = 1;
+ memcpy ( &formset->Guid, guid, sizeof ( formset->Guid ) );
+ formset->FormSetTitle = title_id;
+ formset->Help = help_id;
+ formset->Flags = num_class_guids;
+
+ /* Add class GUIDs */
+ class_guid = ( ( ( void * ) formset ) + sizeof ( *formset ) );
+ va_start ( args, help_id );
+ while ( num_class_guids-- ) {
+ memcpy ( class_guid++, va_arg ( args, const EFI_GUID * ),
+ sizeof ( *class_guid ) );
+ }
+ va_end ( args );
+
+ DBGC ( ifr, "IFR %p formset title %#04x help %#04x\n",
+ ifr, title_id, help_id );
+ DBGC2_HDA ( ifr, dispaddr, formset, len );
+}
+
+/**
+ * Add get opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v varstore_id Variable store identifier
+ * @v varstore_info Variable string identifier or offset
+ * @v varstore_type Variable type
+ */
+void efi_ifr_get_op ( struct efi_ifr_builder *ifr, unsigned int varstore_id,
+ unsigned int varstore_info, unsigned int varstore_type ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_GET *get;
+
+ /* Add opcode */
+ get = efi_ifr_op ( ifr, EFI_IFR_GET_OP, sizeof ( *get ) );
+ get->VarStoreId = varstore_id;
+ get->VarStoreInfo.VarName = varstore_info;
+ get->VarStoreType = varstore_type;
+
+ DBGC ( ifr, "IFR %p get varstore %#04x:%#04x type %#02x\n",
+ ifr, varstore_id, varstore_info, varstore_type );
+ DBGC2_HDA ( ifr, dispaddr, get, sizeof ( *get ) );
+}
+
+/**
+ * Add GUID class opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v class Class
+ */
+void efi_ifr_guid_class_op ( struct efi_ifr_builder *ifr, unsigned int class ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_GUID_CLASS *guid_class;
+
+ /* Add opcode */
+ guid_class = efi_ifr_op ( ifr, EFI_IFR_GUID_OP,
+ sizeof ( *guid_class ) );
+ if ( ! guid_class )
+ return;
+ memcpy ( &guid_class->Guid, &tiano_guid, sizeof ( guid_class->Guid ) );
+ guid_class->ExtendOpCode = EFI_IFR_EXTEND_OP_CLASS;
+ guid_class->Class = class;
+
+ DBGC ( ifr, "IFR %p GUID class %#02x\n", ifr, class );
+ DBGC2_HDA ( ifr, dispaddr, guid_class, sizeof ( *guid_class ) );
+}
+
+/**
+ * Add GUID subclass opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v subclass Subclass
+ */
+void efi_ifr_guid_subclass_op ( struct efi_ifr_builder *ifr,
+ unsigned int subclass ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_GUID_SUBCLASS *guid_subclass;
+
+ /* Add opcode */
+ guid_subclass = efi_ifr_op ( ifr, EFI_IFR_GUID_OP,
+ sizeof ( *guid_subclass ) );
+ if ( ! guid_subclass )
+ return;
+ memcpy ( &guid_subclass->Guid, &tiano_guid,
+ sizeof ( guid_subclass->Guid ) );
+ guid_subclass->ExtendOpCode = EFI_IFR_EXTEND_OP_SUBCLASS;
+ guid_subclass->SubClass = subclass;
+
+ DBGC ( ifr, "IFR %p GUID subclass %#02x\n", ifr, subclass );
+ DBGC2_HDA ( ifr, dispaddr, guid_subclass, sizeof ( *guid_subclass ) );
+}
+
+/**
+ * Add numeric opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v prompt_id Prompt string identifier
+ * @v help_id Help string identifier
+ * @v question_id Question identifier
+ * @v varstore_id Variable store identifier
+ * @v varstore_info Variable string identifier or offset
+ * @v vflags Variable flags
+ * @v min_value Minimum value
+ * @v max_value Maximum value
+ * @v step Step
+ * @v flags Flags
+ */
+void efi_ifr_numeric_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
+ unsigned int help_id, unsigned int question_id,
+ unsigned int varstore_id, unsigned int varstore_info,
+ unsigned int vflags, unsigned long min_value,
+ unsigned long max_value, unsigned int step,
+ unsigned int flags ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_NUMERIC *numeric;
+ unsigned int size;
+
+ /* Add opcode */
+ numeric = efi_ifr_op ( ifr, EFI_IFR_NUMERIC_OP, sizeof ( *numeric ) );
+ if ( ! numeric )
+ return;
+ numeric->Question.Header.Prompt = prompt_id;
+ numeric->Question.Header.Help = help_id;
+ numeric->Question.QuestionId = question_id;
+ numeric->Question.VarStoreId = varstore_id;
+ numeric->Question.VarStoreInfo.VarName = varstore_info;
+ numeric->Question.Flags = vflags;
+ size = ( flags & EFI_IFR_NUMERIC_SIZE );
+ switch ( size ) {
+ case EFI_IFR_NUMERIC_SIZE_1 :
+ numeric->data.u8.MinValue = min_value;
+ numeric->data.u8.MaxValue = max_value;
+ numeric->data.u8.Step = step;
+ break;
+ case EFI_IFR_NUMERIC_SIZE_2 :
+ numeric->data.u16.MinValue = min_value;
+ numeric->data.u16.MaxValue = max_value;
+ numeric->data.u16.Step = step;
+ break;
+ case EFI_IFR_NUMERIC_SIZE_4 :
+ numeric->data.u32.MinValue = min_value;
+ numeric->data.u32.MaxValue = max_value;
+ numeric->data.u32.Step = step;
+ break;
+ case EFI_IFR_NUMERIC_SIZE_8 :
+ numeric->data.u64.MinValue = min_value;
+ numeric->data.u64.MaxValue = max_value;
+ numeric->data.u64.Step = step;
+ break;
+ }
+
+ DBGC ( ifr, "IFR %p numeric prompt %#04x help %#04x question %#04x "
+ "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id,
+ varstore_id, varstore_info );
+ DBGC2_HDA ( ifr, dispaddr, numeric, sizeof ( *numeric ) );
+}
+
+/**
+ * Add string opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v prompt_id Prompt string identifier
+ * @v help_id Help string identifier
+ * @v question_id Question identifier
+ * @v varstore_id Variable store identifier
+ * @v varstore_info Variable string identifier or offset
+ * @v vflags Variable flags
+ * @v min_size Minimum size
+ * @v max_size Maximum size
+ * @v flags Flags
+ */
+void efi_ifr_string_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
+ unsigned int help_id, unsigned int question_id,
+ unsigned int varstore_id, unsigned int varstore_info,
+ unsigned int vflags, unsigned int min_size,
+ unsigned int max_size, unsigned int flags ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_STRING *string;
+
+ /* Add opcode */
+ string = efi_ifr_op ( ifr, EFI_IFR_STRING_OP, sizeof ( *string ) );
+ if ( ! string )
+ return;
+ string->Question.Header.Prompt = prompt_id;
+ string->Question.Header.Help = help_id;
+ string->Question.QuestionId = question_id;
+ string->Question.VarStoreId = varstore_id;
+ string->Question.VarStoreInfo.VarName = varstore_info;
+ string->Question.Flags = vflags;
+ string->MinSize = min_size;
+ string->MaxSize = max_size;
+ string->Flags = flags;
+
+ DBGC ( ifr, "IFR %p string prompt %#04x help %#04x question %#04x "
+ "varstore %#04x:%#04x\n", ifr, prompt_id, help_id, question_id,
+ varstore_id, varstore_info );
+ DBGC2_HDA ( ifr, dispaddr, string, sizeof ( *string ) );
+}
+
+/**
+ * Add suppress-if opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ */
+void efi_ifr_suppress_if_op ( struct efi_ifr_builder *ifr ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_SUPPRESS_IF *suppress_if;
+
+ /* Add opcode */
+ suppress_if = efi_ifr_op ( ifr, EFI_IFR_SUPPRESS_IF_OP,
+ sizeof ( *suppress_if ) );
+ suppress_if->Header.Scope = 1;
+
+ DBGC ( ifr, "IFR %p suppress-if\n", ifr );
+ DBGC2_HDA ( ifr, dispaddr, suppress_if, sizeof ( *suppress_if ) );
+}
+
+/**
+ * Add text opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v prompt_id Prompt string identifier
+ * @v help_id Help string identifier
+ * @v text_id Text string identifier
+ */
+void efi_ifr_text_op ( struct efi_ifr_builder *ifr, unsigned int prompt_id,
+ unsigned int help_id, unsigned int text_id ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_TEXT *text;
+
+ /* Add opcode */
+ text = efi_ifr_op ( ifr, EFI_IFR_TEXT_OP, sizeof ( *text ) );
+ if ( ! text )
+ return;
+ text->Statement.Prompt = prompt_id;
+ text->Statement.Help = help_id;
+ text->TextTwo = text_id;
+
+ DBGC ( ifr, "IFR %p text prompt %#04x help %#04x text %#04x\n",
+ ifr, prompt_id, help_id, text_id );
+ DBGC2_HDA ( ifr, dispaddr, text, sizeof ( *text ) );
+}
+
+/**
+ * Add true opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ */
+void efi_ifr_true_op ( struct efi_ifr_builder *ifr ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_TRUE *true;
+
+ /* Add opcode */
+ true = efi_ifr_op ( ifr, EFI_IFR_TRUE_OP, sizeof ( *true ) );
+
+ DBGC ( ifr, "IFR %p true\n", ifr );
+ DBGC2_HDA ( ifr, dispaddr, true, sizeof ( *true ) );
+}
+
+/**
+ * Add name/value store opcode to IFR builder
+ *
+ * @v ifr IFR builder
+ * @v guid GUID
+ * @ret varstore_id Variable store identifier, or 0 on failure
+ */
+unsigned int efi_ifr_varstore_name_value_op ( struct efi_ifr_builder *ifr,
+ const EFI_GUID *guid ) {
+ size_t dispaddr = ifr->ops_len;
+ EFI_IFR_VARSTORE_NAME_VALUE *varstore;
+
+ /* Add opcode */
+ varstore = efi_ifr_op ( ifr, EFI_IFR_VARSTORE_NAME_VALUE_OP,
+ sizeof ( *varstore ) );
+ if ( ! varstore )
+ return 0;
+ varstore->VarStoreId = ++(ifr->varstore_id);
+ memcpy ( &varstore->Guid, guid, sizeof ( varstore->Guid ) );
+
+ DBGC ( ifr, "IFR %p name/value store %#04x\n",
+ ifr, varstore->VarStoreId );
+ DBGC2_HDA ( ifr, dispaddr, varstore, sizeof ( *varstore ) );
+ return varstore->VarStoreId;
+}
+
+/**
+ * Free memory used by IFR builder
+ *
+ * @v ifr IFR builder
+ */
+void efi_ifr_free ( struct efi_ifr_builder *ifr ) {
+
+ free ( ifr->ops );
+ free ( ifr->strings );
+ memset ( ifr, 0, sizeof ( *ifr ) );
+}
+
+/**
+ * Construct package list from IFR builder
+ *
+ * @v ifr IFR builder
+ * @v guid Package GUID
+ * @v language Language
+ * @v language_id Language string ID
+ * @ret package Package list, or NULL
+ *
+ * The package list is allocated using malloc(), and must eventually
+ * be freed by the caller. (The caller must also call efi_ifr_free()
+ * to free the temporary storage used during construction.)
+ */
+EFI_HII_PACKAGE_LIST_HEADER * efi_ifr_package ( struct efi_ifr_builder *ifr,
+ const EFI_GUID *guid,
+ const char *language,
+ unsigned int language_id ) {
+ struct {
+ EFI_HII_PACKAGE_LIST_HEADER header;
+ struct {
+ EFI_HII_PACKAGE_HEADER header;
+ uint8_t data[ifr->ops_len];
+ } __attribute__ (( packed )) ops;
+ struct {
+ union {
+ EFI_HII_STRING_PACKAGE_HDR header;
+ uint8_t pad[offsetof(EFI_HII_STRING_PACKAGE_HDR,
+ Language) +
+ strlen ( language ) + 1 /* NUL */ ];
+ } __attribute__ (( packed )) header;
+ uint8_t data[ifr->strings_len];
+ EFI_HII_STRING_BLOCK end;
+ } __attribute__ (( packed )) strings;
+ EFI_HII_PACKAGE_HEADER end;
+ } __attribute__ (( packed )) *package;
+
+ /* Fail if any previous allocation failed */
+ if ( ifr->failed )
+ return NULL;
+
+ /* Allocate package list */
+ package = zalloc ( sizeof ( *package ) );
+ if ( ! package )
+ return NULL;
+
+ /* Populate package list */
+ package->header.PackageLength = sizeof ( *package );
+ memcpy ( &package->header.PackageListGuid, guid,
+ sizeof ( package->header.PackageListGuid ) );
+ package->ops.header.Length = sizeof ( package->ops );
+ package->ops.header.Type = EFI_HII_PACKAGE_FORMS;
+ memcpy ( package->ops.data, ifr->ops, sizeof ( package->ops.data ) );
+ package->strings.header.header.Header.Length =
+ sizeof ( package->strings );
+ package->strings.header.header.Header.Type =
+ EFI_HII_PACKAGE_STRINGS;
+ package->strings.header.header.HdrSize =
+ sizeof ( package->strings.header );
+ package->strings.header.header.StringInfoOffset =
+ sizeof ( package->strings.header );
+ package->strings.header.header.LanguageName = language_id;
+ strcpy ( package->strings.header.header.Language, language );
+ memcpy ( package->strings.data, ifr->strings,
+ sizeof ( package->strings.data ) );
+ package->strings.end.BlockType = EFI_HII_SIBT_END;
+ package->end.Type = EFI_HII_PACKAGE_END;
+ package->end.Length = sizeof ( package->end );
+
+ return &package->header;
+}
+
diff --git a/roms/ipxe/src/interface/efi/efi_init.c b/roms/ipxe/src/interface/efi/efi_init.c
new file mode 100644
index 00000000..93ada21d
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_init.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <errno.h>
+#include <ipxe/init.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/Protocol/LoadedImage.h>
+
+/** Image handle passed to entry point */
+EFI_HANDLE efi_image_handle;
+
+/** Loaded image protocol for this image */
+EFI_LOADED_IMAGE_PROTOCOL *efi_loaded_image;
+
+/** System table passed to entry point */
+EFI_SYSTEM_TABLE *efi_systab;
+
+/** Event used to signal shutdown */
+static EFI_EVENT efi_shutdown_event;
+
+/* Forward declarations */
+static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
+
+/**
+ * Shut down in preparation for booting an OS.
+ *
+ * This hook gets called at ExitBootServices time in order to make
+ * sure that everything is properly shut down before the OS takes
+ * over.
+ */
+static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
+ void *context __unused ) {
+ shutdown_boot();
+}
+
+/**
+ * Look up EFI configuration table
+ *
+ * @v guid Configuration table GUID
+ * @ret table Configuration table, or NULL
+ */
+static void * efi_find_table ( EFI_GUID *guid ) {
+ unsigned int i;
+
+ for ( i = 0 ; i < efi_systab->NumberOfTableEntries ; i++ ) {
+ if ( memcmp ( &efi_systab->ConfigurationTable[i].VendorGuid,
+ guid, sizeof ( *guid ) ) == 0 )
+ return efi_systab->ConfigurationTable[i].VendorTable;
+ }
+
+ return NULL;
+}
+
+/**
+ * Initialise EFI environment
+ *
+ * @v image_handle Image handle
+ * @v systab System table
+ * @ret efirc EFI return status code
+ */
+EFI_STATUS efi_init ( EFI_HANDLE image_handle,
+ EFI_SYSTEM_TABLE *systab ) {
+ EFI_BOOT_SERVICES *bs;
+ struct efi_protocol *prot;
+ struct efi_config_table *tab;
+ void *loaded_image;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Store image handle and system table pointer for future use */
+ efi_image_handle = image_handle;
+ efi_systab = systab;
+
+ /* Sanity checks */
+ if ( ! systab ) {
+ efirc = EFI_NOT_AVAILABLE_YET;
+ goto err_sanity;
+ }
+ if ( ! systab->ConOut ) {
+ efirc = EFI_NOT_AVAILABLE_YET;
+ goto err_sanity;
+ }
+ if ( ! systab->BootServices ) {
+ DBGC ( systab, "EFI provided no BootServices entry point\n" );
+ efirc = EFI_NOT_AVAILABLE_YET;
+ goto err_sanity;
+ }
+ if ( ! systab->RuntimeServices ) {
+ DBGC ( systab, "EFI provided no RuntimeServices entry "
+ "point\n" );
+ efirc = EFI_NOT_AVAILABLE_YET;
+ goto err_sanity;
+ }
+ DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
+ bs = systab->BootServices;
+
+ /* Look up used protocols */
+ for_each_table_entry ( prot, EFI_PROTOCOLS ) {
+ if ( ( efirc = bs->LocateProtocol ( &prot->guid, NULL,
+ prot->protocol ) ) == 0 ) {
+ DBGC ( systab, "EFI protocol %s is at %p\n",
+ efi_guid_ntoa ( &prot->guid ),
+ *(prot->protocol) );
+ } else {
+ DBGC ( systab, "EFI does not provide protocol %s\n",
+ efi_guid_ntoa ( &prot->guid ) );
+ /* Fail if protocol is required */
+ if ( prot->required )
+ goto err_missing_protocol;
+ }
+ }
+
+ /* Look up used configuration tables */
+ for_each_table_entry ( tab, EFI_CONFIG_TABLES ) {
+ if ( ( *(tab->table) = efi_find_table ( &tab->guid ) ) ) {
+ DBGC ( systab, "EFI configuration table %s is at %p\n",
+ efi_guid_ntoa ( &tab->guid ), *(tab->table) );
+ } else {
+ DBGC ( systab, "EFI does not provide configuration "
+ "table %s\n", efi_guid_ntoa ( &tab->guid ) );
+ if ( tab->required ) {
+ efirc = EFI_NOT_AVAILABLE_YET;
+ goto err_missing_table;
+ }
+ }
+ }
+
+ /* Get loaded image protocol */
+ if ( ( efirc = bs->OpenProtocol ( image_handle,
+ &efi_loaded_image_protocol_guid,
+ &loaded_image, image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( systab, "EFI could not get loaded image protocol: %s",
+ strerror ( rc ) );
+ goto err_no_loaded_image;
+ }
+ efi_loaded_image = loaded_image;
+ DBGC ( systab, "EFI image base address %p\n",
+ efi_loaded_image->ImageBase );
+
+ /* EFI is perfectly capable of gracefully shutting down any
+ * loaded devices if it decides to fall back to a legacy boot.
+ * For no particularly comprehensible reason, it doesn't
+ * bother doing so when ExitBootServices() is called.
+ */
+ if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES,
+ TPL_CALLBACK, efi_shutdown_hook,
+ NULL, &efi_shutdown_event ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( systab, "EFI could not create ExitBootServices event: "
+ "%s\n", strerror ( rc ) );
+ goto err_create_event;
+ }
+
+ /* Install driver binding protocol */
+ if ( ( rc = efi_driver_install() ) != 0 ) {
+ DBGC ( systab, "EFI could not install driver: %s\n",
+ strerror ( rc ) );
+ efirc = EFIRC ( rc );
+ goto err_driver_install;
+ }
+
+ /* Install image unload method */
+ efi_loaded_image->Unload = efi_unload;
+
+ return 0;
+
+ efi_driver_uninstall();
+ err_driver_install:
+ bs->CloseEvent ( efi_shutdown_event );
+ err_create_event:
+ err_no_loaded_image:
+ err_missing_table:
+ err_missing_protocol:
+ err_sanity:
+ return efirc;
+}
+
+/**
+ * Shut down EFI environment
+ *
+ * @v image_handle Image handle
+ */
+static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_SYSTEM_TABLE *systab = efi_systab;
+
+ DBGC ( systab, "EFI image unloading\n" );
+
+ /* Shut down */
+ shutdown_exit();
+
+ /* Disconnect any remaining devices */
+ efi_driver_disconnect_all();
+
+ /* Uninstall driver binding protocol */
+ efi_driver_uninstall();
+
+ /* Uninstall exit boot services event */
+ bs->CloseEvent ( efi_shutdown_event );
+
+ DBGC ( systab, "EFI image unloaded\n" );
+
+ return 0;
+}
diff --git a/roms/ipxe/src/interface/efi/efi_pci.c b/roms/ipxe/src/interface/efi/efi_pci.c
new file mode 100644
index 00000000..86c781d8
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_pci.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ipxe/pci.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_pci.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/Protocol/PciIo.h>
+#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
+
+/** @file
+ *
+ * iPXE PCI I/O API for EFI
+ *
+ */
+
+/* Disambiguate the various error causes */
+#define EINFO_EEFI_PCI \
+ __einfo_uniqify ( EINFO_EPLATFORM, 0x01, \
+ "Could not open PCI I/O protocol" )
+#define EINFO_EEFI_PCI_NOT_PCI \
+ __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED, \
+ "Not a PCI device" )
+#define EEFI_PCI_NOT_PCI __einfo_error ( EINFO_EEFI_PCI_NOT_PCI )
+#define EINFO_EEFI_PCI_IN_USE \
+ __einfo_platformify ( EINFO_EEFI_PCI, EFI_ACCESS_DENIED, \
+ "PCI device already has a driver" )
+#define EEFI_PCI_IN_USE __einfo_error ( EINFO_EEFI_PCI_IN_USE )
+#define EEFI_PCI( efirc ) \
+ EPLATFORM ( EINFO_EEFI_PCI, efirc, \
+ EEFI_PCI_NOT_PCI, EEFI_PCI_IN_USE )
+
+/******************************************************************************
+ *
+ * iPXE PCI API
+ *
+ ******************************************************************************
+ */
+
+/** PCI root bridge I/O protocol */
+static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci;
+EFI_REQUEST_PROTOCOL ( EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL, &efipci );
+
+static unsigned long efipci_address ( struct pci_device *pci,
+ unsigned long location ) {
+ return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ),
+ PCI_SLOT ( pci->busdevfn ),
+ PCI_FUNC ( pci->busdevfn ),
+ EFIPCI_OFFSET ( location ) );
+}
+
+int efipci_read ( struct pci_device *pci, unsigned long location,
+ void *value ) {
+ EFI_STATUS efirc;
+ int rc;
+
+ if ( ! efipci )
+ return -ENOTSUP;
+
+ if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ value ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFIPCI config read from " PCI_FMT " offset %02lx "
+ "failed: %s\n", PCI_ARGS ( pci ),
+ EFIPCI_OFFSET ( location ), strerror ( rc ) );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int efipci_write ( struct pci_device *pci, unsigned long location,
+ unsigned long value ) {
+ EFI_STATUS efirc;
+ int rc;
+
+ if ( ! efipci )
+ return -ENOTSUP;
+
+ if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ &value ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFIPCI config write to " PCI_FMT " offset %02lx "
+ "failed: %s\n", PCI_ARGS ( pci ),
+ EFIPCI_OFFSET ( location ), strerror ( rc ) );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
+
+/******************************************************************************
+ *
+ * EFI PCI device instantiation
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open EFI PCI device
+ *
+ * @v device EFI device handle
+ * @v attributes Protocol opening attributes
+ * @v pci PCI device to fill in
+ * @ret rc Return status code
+ */
+int efipci_open ( EFI_HANDLE device, UINT32 attributes,
+ struct pci_device *pci ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_PCI_IO_PROTOCOL *pci_io;
+ void *interface;
+ } pci_io;
+ UINTN pci_segment, pci_bus, pci_dev, pci_fn;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* See if device is a PCI device */
+ if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid,
+ &pci_io.interface, efi_image_handle,
+ device, attributes ) ) != 0 ) {
+ rc = -EEFI_PCI ( efirc );
+ DBGCP ( device, "EFIPCI %p %s cannot open PCI protocols: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ goto err_open_protocol;
+ }
+
+ /* Get PCI bus:dev.fn address */
+ if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment,
+ &pci_bus, &pci_dev,
+ &pci_fn ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIPCI %p %s could not get PCI location: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ goto err_get_location;
+ }
+ DBGC2 ( device, "EFIPCI %p %s is PCI %04lx:%02lx:%02lx.%lx\n", device,
+ efi_handle_name ( device ), ( ( unsigned long ) pci_segment ),
+ ( ( unsigned long ) pci_bus ), ( ( unsigned long ) pci_dev ),
+ ( ( unsigned long ) pci_fn ) );
+
+ /* Try to enable I/O cycles, memory cycles, and bus mastering.
+ * Some platforms will 'helpfully' report errors if these bits
+ * can't be enabled (for example, if the card doesn't actually
+ * support I/O cycles). Work around any such platforms by
+ * enabling bits individually and simply ignoring any errors.
+ */
+ pci_io.pci_io->Attributes ( pci_io.pci_io,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_IO, NULL );
+ pci_io.pci_io->Attributes ( pci_io.pci_io,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL );
+ pci_io.pci_io->Attributes ( pci_io.pci_io,
+ EfiPciIoAttributeOperationEnable,
+ EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL );
+
+ /* Populate PCI device */
+ pci_init ( pci, PCI_BUSDEVFN ( pci_bus, pci_dev, pci_fn ) );
+ if ( ( rc = pci_read_config ( pci ) ) != 0 ) {
+ DBGC ( device, "EFIPCI %p %s cannot read PCI configuration: "
+ "%s\n", device, efi_handle_name ( device ),
+ strerror ( rc ) );
+ goto err_pci_read_config;
+ }
+
+ return 0;
+
+ err_pci_read_config:
+ err_get_location:
+ bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
+ efi_image_handle, device );
+ err_open_protocol:
+ return rc;
+}
+
+/**
+ * Close EFI PCI device
+ *
+ * @v device EFI device handle
+ */
+void efipci_close ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
+ efi_image_handle, device );
+}
+
+/**
+ * Get EFI PCI device information
+ *
+ * @v device EFI device handle
+ * @v pci PCI device to fill in
+ * @ret rc Return status code
+ */
+int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) {
+ int rc;
+
+ /* Open PCI device, if possible */
+ if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL,
+ pci ) ) != 0 )
+ return rc;
+
+ /* Close PCI device */
+ efipci_close ( device );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * EFI PCI driver
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device EFI device handle
+ * @ret rc Return status code
+ */
+static int efipci_supported ( EFI_HANDLE device ) {
+ struct pci_device pci;
+ int rc;
+
+ /* Get PCI device information */
+ if ( ( rc = efipci_info ( device, &pci ) ) != 0 )
+ return rc;
+
+ /* Look for a driver */
+ if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) {
+ DBGCP ( device, "EFIPCI %p %s has no driver\n",
+ device, efi_handle_name ( device ) );
+ return rc;
+ }
+ DBGC ( device, "EFIPCI %p %s has driver \"%s\"\n",
+ device, efi_handle_name ( device ), pci.id->name );
+
+ return 0;
+}
+
+/**
+ * Attach driver to device
+ *
+ * @v efidev EFI device
+ * @ret rc Return status code
+ */
+static int efipci_start ( struct efi_device *efidev ) {
+ EFI_HANDLE device = efidev->device;
+ struct pci_device *pci;
+ int rc;
+
+ /* Allocate PCI device */
+ pci = zalloc ( sizeof ( *pci ) );
+ if ( ! pci ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Open PCI device */
+ if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER |
+ EFI_OPEN_PROTOCOL_EXCLUSIVE ),
+ pci ) ) != 0 ) {
+ DBGC ( device, "EFIPCI %p %s could not open PCI device: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid );
+ goto err_open;
+ }
+
+ /* Find driver */
+ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) {
+ DBGC ( device, "EFIPCI %p %s has no driver\n",
+ device, efi_handle_name ( device ) );
+ goto err_find_driver;
+ }
+
+ /* Mark PCI device as a child of the EFI device */
+ pci->dev.parent = &efidev->dev;
+ list_add ( &pci->dev.siblings, &efidev->dev.children );
+
+ /* Probe driver */
+ if ( ( rc = pci_probe ( pci ) ) != 0 ) {
+ DBGC ( device, "EFIPCI %p %s could not probe driver \"%s\": "
+ "%s\n", device, efi_handle_name ( device ),
+ pci->id->name, strerror ( rc ) );
+ goto err_probe;
+ }
+ DBGC ( device, "EFIPCI %p %s using driver \"%s\"\n",
+ device, efi_handle_name ( device ), pci->id->name );
+
+ efidev_set_drvdata ( efidev, pci );
+ return 0;
+
+ pci_remove ( pci );
+ err_probe:
+ list_del ( &pci->dev.siblings );
+ err_find_driver:
+ efipci_close ( device );
+ err_open:
+ free ( pci );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Detach driver from device
+ *
+ * @v efidev EFI device
+ */
+static void efipci_stop ( struct efi_device *efidev ) {
+ struct pci_device *pci = efidev_get_drvdata ( efidev );
+ EFI_HANDLE device = efidev->device;
+
+ pci_remove ( pci );
+ list_del ( &pci->dev.siblings );
+ efipci_close ( device );
+ free ( pci );
+}
+
+/** EFI PCI driver */
+struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+ .name = "PCI",
+ .supported = efipci_supported,
+ .start = efipci_start,
+ .stop = efipci_stop,
+};
diff --git a/roms/ipxe/src/interface/efi/efi_reboot.c b/roms/ipxe/src/interface/efi/efi_reboot.c
new file mode 100644
index 00000000..96638c48
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_reboot.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * EFI reboot mechanism
+ *
+ */
+
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/reboot.h>
+
+/**
+ * Reboot system
+ *
+ * @v warm Perform a warm reboot
+ */
+static void efi_reboot ( int warm ) {
+ EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
+
+ /* Use runtime services to reset system */
+ rs->ResetSystem ( ( warm ? EfiResetWarm : EfiResetCold ), 0, 0, NULL );
+}
+
+/**
+ * Power off system
+ *
+ * @ret rc Return status code
+ */
+static int efi_poweroff ( void ) {
+ EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices;
+
+ /* Use runtime services to power off system */
+ rs->ResetSystem ( EfiResetShutdown, 0, 0, NULL );
+
+ /* Should never happen */
+ return -ECANCELED;
+}
+
+PROVIDE_REBOOT ( efi, reboot, efi_reboot );
+PROVIDE_REBOOT ( efi, poweroff, efi_poweroff );
diff --git a/roms/ipxe/src/interface/efi/efi_smbios.c b/roms/ipxe/src/interface/efi/efi_smbios.c
new file mode 100644
index 00000000..304f95a5
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_smbios.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <errno.h>
+#include <ipxe/smbios.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Guid/SmBios.h>
+
+/** @file
+ *
+ * iPXE SMBIOS API for EFI
+ *
+ */
+
+/** SMBIOS configuration table */
+static struct smbios_entry *smbios_entry;
+EFI_USE_TABLE ( SMBIOS_TABLE, &smbios_entry, 0 );
+
+/**
+ * Find SMBIOS
+ *
+ * @v smbios SMBIOS entry point descriptor structure to fill in
+ * @ret rc Return status code
+ */
+static int efi_find_smbios ( struct smbios *smbios ) {
+
+ if ( ! smbios_entry ) {
+ DBG ( "No SMBIOS table provided\n" );
+ return -ENODEV;
+ }
+
+ if ( smbios_entry->signature != SMBIOS_SIGNATURE ) {
+ DBG ( "Invalid SMBIOS signature\n" );
+ return -ENODEV;
+ }
+
+ smbios->address = phys_to_user ( smbios_entry->smbios_address );
+ smbios->len = smbios_entry->smbios_len;
+ smbios->count = smbios_entry->smbios_count;
+ smbios->version =
+ SMBIOS_VERSION ( smbios_entry->major, smbios_entry->minor );
+ DBG ( "Found SMBIOS v%d.%d entry point at %p (%x+%zx)\n",
+ smbios_entry->major, smbios_entry->minor, smbios_entry,
+ smbios_entry->smbios_address, smbios->len );
+
+ return 0;
+}
+
+PROVIDE_SMBIOS ( efi, find_smbios, efi_find_smbios );
diff --git a/roms/ipxe/src/interface/efi/efi_snp.c b/roms/ipxe/src/interface/efi/efi_snp.c
new file mode 100644
index 00000000..67fba342
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_snp.c
@@ -0,0 +1,1209 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/in.h>
+#include <ipxe/version.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_strings.h>
+#include <ipxe/efi/efi_utils.h>
+#include <ipxe/efi/efi_snp.h>
+#include <usr/autoboot.h>
+
+/** List of SNP devices */
+static LIST_HEAD ( efi_snp_devices );
+
+/** Network devices are currently claimed for use by iPXE */
+static int efi_snp_claimed;
+
+/**
+ * Set EFI SNP mode state
+ *
+ * @v snp SNP interface
+ */
+static void efi_snp_set_state ( struct efi_snp_device *snpdev ) {
+ struct net_device *netdev = snpdev->netdev;
+ EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
+
+ /* Calculate state */
+ if ( ! snpdev->started ) {
+ /* Start() method not called; report as Stopped */
+ mode->State = EfiSimpleNetworkStopped;
+ } else if ( ! netdev_is_open ( netdev ) ) {
+ /* Network device not opened; report as Started */
+ mode->State = EfiSimpleNetworkStarted;
+ } else if ( efi_snp_claimed ) {
+ /* Network device opened but claimed for use by iPXE; report
+ * as Started to inhibit receive polling.
+ */
+ mode->State = EfiSimpleNetworkStarted;
+ } else {
+ /* Network device opened and available for use via SNP; report
+ * as Initialized.
+ */
+ mode->State = EfiSimpleNetworkInitialized;
+ }
+}
+
+/**
+ * Set EFI SNP mode based on iPXE net device parameters
+ *
+ * @v snp SNP interface
+ */
+static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) {
+ struct net_device *netdev = snpdev->netdev;
+ EFI_SIMPLE_NETWORK_MODE *mode = &snpdev->mode;
+ struct ll_protocol *ll_protocol = netdev->ll_protocol;
+ unsigned int ll_addr_len = ll_protocol->ll_addr_len;
+
+ mode->HwAddressSize = ll_addr_len;
+ mode->MediaHeaderSize = ll_protocol->ll_header_len;
+ mode->MaxPacketSize = netdev->max_pkt_len;
+ mode->ReceiveFilterMask = ( EFI_SIMPLE_NETWORK_RECEIVE_UNICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_MULTICAST |
+ EFI_SIMPLE_NETWORK_RECEIVE_BROADCAST );
+ assert ( ll_addr_len <= sizeof ( mode->CurrentAddress ) );
+ memcpy ( &mode->CurrentAddress, netdev->ll_addr, ll_addr_len );
+ memcpy ( &mode->BroadcastAddress, netdev->ll_broadcast, ll_addr_len );
+ ll_protocol->init_addr ( netdev->hw_addr, &mode->PermanentAddress );
+ mode->IfType = ntohs ( ll_protocol->ll_proto );
+ mode->MacAddressChangeable = TRUE;
+ mode->MediaPresentSupported = TRUE;
+ mode->MediaPresent = ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
+}
+
+/**
+ * Poll net device and count received packets
+ *
+ * @v snpdev SNP device
+ */
+static void efi_snp_poll ( struct efi_snp_device *snpdev ) {
+ struct io_buffer *iobuf;
+ unsigned int before = 0;
+ unsigned int after = 0;
+ unsigned int arrived;
+
+ /* We have to report packet arrivals, and this is the easiest
+ * way to fake it.
+ */
+ list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
+ before++;
+ netdev_poll ( snpdev->netdev );
+ list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list )
+ after++;
+ arrived = ( after - before );
+
+ snpdev->rx_count_interrupts += arrived;
+ snpdev->rx_count_events += arrived;
+}
+
+/**
+ * Change SNP state from "stopped" to "started"
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_start ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p START\n", snpdev );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ snpdev->started = 1;
+ efi_snp_set_state ( snpdev );
+ return 0;
+}
+
+/**
+ * Change SNP state from "started" to "stopped"
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_stop ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p STOP\n", snpdev );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ snpdev->started = 0;
+ efi_snp_set_state ( snpdev );
+ return 0;
+}
+
+/**
+ * Open the network device
+ *
+ * @v snp SNP interface
+ * @v extra_rx_bufsize Extra RX buffer size, in bytes
+ * @v extra_tx_bufsize Extra TX buffer size, in bytes
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_initialize ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN extra_rx_bufsize, UINTN extra_tx_bufsize ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p INITIALIZE (%ld extra RX, %ld extra TX)\n",
+ snpdev, ( ( unsigned long ) extra_rx_bufsize ),
+ ( ( unsigned long ) extra_tx_bufsize ) );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not open %s: %s\n",
+ snpdev, snpdev->netdev->name, strerror ( rc ) );
+ return EFIRC ( rc );
+ }
+ efi_snp_set_state ( snpdev );
+
+ return 0;
+}
+
+/**
+ * Reset the network device
+ *
+ * @v snp SNP interface
+ * @v ext_verify Extended verification required
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p RESET (%s extended verification)\n",
+ snpdev, ( ext_verify ? "with" : "without" ) );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ netdev_close ( snpdev->netdev );
+ efi_snp_set_state ( snpdev );
+
+ if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n",
+ snpdev, snpdev->netdev->name, strerror ( rc ) );
+ return EFIRC ( rc );
+ }
+ efi_snp_set_state ( snpdev );
+
+ return 0;
+}
+
+/**
+ * Shut down the network device
+ *
+ * @v snp SNP interface
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p SHUTDOWN\n", snpdev );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ netdev_close ( snpdev->netdev );
+ efi_snp_set_state ( snpdev );
+
+ return 0;
+}
+
+/**
+ * Manage receive filters
+ *
+ * @v snp SNP interface
+ * @v enable Receive filters to enable
+ * @v disable Receive filters to disable
+ * @v mcast_reset Reset multicast filters
+ * @v mcast_count Number of multicast filters
+ * @v mcast Multicast filters
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_receive_filters ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, UINT32 enable,
+ UINT32 disable, BOOLEAN mcast_reset,
+ UINTN mcast_count, EFI_MAC_ADDRESS *mcast ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ unsigned int i;
+
+ DBGC2 ( snpdev, "SNPDEV %p RECEIVE_FILTERS %08x&~%08x%s %ld mcast\n",
+ snpdev, enable, disable, ( mcast_reset ? " reset" : "" ),
+ ( ( unsigned long ) mcast_count ) );
+ for ( i = 0 ; i < mcast_count ; i++ ) {
+ DBGC2_HDA ( snpdev, i, &mcast[i],
+ snpdev->netdev->ll_protocol->ll_addr_len );
+ }
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Lie through our teeth, otherwise MNP refuses to accept us */
+ return 0;
+}
+
+/**
+ * Set station address
+ *
+ * @v snp SNP interface
+ * @v reset Reset to permanent address
+ * @v new New station address
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_station_address ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
+ EFI_MAC_ADDRESS *new ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+
+ DBGC2 ( snpdev, "SNPDEV %p STATION_ADDRESS %s\n", snpdev,
+ ( reset ? "reset" : ll_protocol->ntoa ( new ) ) );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Set the MAC address */
+ if ( reset )
+ new = &snpdev->mode.PermanentAddress;
+ memcpy ( snpdev->netdev->ll_addr, new, ll_protocol->ll_addr_len );
+
+ /* MAC address changes take effect only on netdev_open() */
+ if ( netdev_is_open ( snpdev->netdev ) ) {
+ DBGC ( snpdev, "SNPDEV %p MAC address changed while net "
+ "device open\n", snpdev );
+ }
+
+ return 0;
+}
+
+/**
+ * Get (or reset) statistics
+ *
+ * @v snp SNP interface
+ * @v reset Reset statistics
+ * @v stats_len Size of statistics table
+ * @v stats Statistics table
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_statistics ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN reset,
+ UINTN *stats_len, EFI_NETWORK_STATISTICS *stats ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ EFI_NETWORK_STATISTICS stats_buf;
+
+ DBGC2 ( snpdev, "SNPDEV %p STATISTICS%s", snpdev,
+ ( reset ? " reset" : "" ) );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Gather statistics */
+ memset ( &stats_buf, 0, sizeof ( stats_buf ) );
+ stats_buf.TxGoodFrames = snpdev->netdev->tx_stats.good;
+ stats_buf.TxDroppedFrames = snpdev->netdev->tx_stats.bad;
+ stats_buf.TxTotalFrames = ( snpdev->netdev->tx_stats.good +
+ snpdev->netdev->tx_stats.bad );
+ stats_buf.RxGoodFrames = snpdev->netdev->rx_stats.good;
+ stats_buf.RxDroppedFrames = snpdev->netdev->rx_stats.bad;
+ stats_buf.RxTotalFrames = ( snpdev->netdev->rx_stats.good +
+ snpdev->netdev->rx_stats.bad );
+ if ( *stats_len > sizeof ( stats_buf ) )
+ *stats_len = sizeof ( stats_buf );
+ if ( stats )
+ memcpy ( stats, &stats_buf, *stats_len );
+
+ /* Reset statistics if requested to do so */
+ if ( reset ) {
+ memset ( &snpdev->netdev->tx_stats, 0,
+ sizeof ( snpdev->netdev->tx_stats ) );
+ memset ( &snpdev->netdev->rx_stats, 0,
+ sizeof ( snpdev->netdev->rx_stats ) );
+ }
+
+ return 0;
+}
+
+/**
+ * Convert multicast IP address to MAC address
+ *
+ * @v snp SNP interface
+ * @v ipv6 Address is IPv6
+ * @v ip IP address
+ * @v mac MAC address
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_mcast_ip_to_mac ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ipv6,
+ EFI_IP_ADDRESS *ip, EFI_MAC_ADDRESS *mac ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ const char *ip_str;
+ int rc;
+
+ ip_str = ( ipv6 ? "(IPv6)" /* FIXME when we have inet6_ntoa() */ :
+ inet_ntoa ( *( ( struct in_addr * ) ip ) ) );
+ DBGC2 ( snpdev, "SNPDEV %p MCAST_IP_TO_MAC %s\n", snpdev, ip_str );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Try to hash the address */
+ if ( ( rc = ll_protocol->mc_hash ( ( ipv6 ? AF_INET6 : AF_INET ),
+ ip, mac ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not hash %s: %s\n",
+ snpdev, ip_str, strerror ( rc ) );
+ return EFIRC ( rc );
+ }
+
+ return 0;
+}
+
+/**
+ * Read or write non-volatile storage
+ *
+ * @v snp SNP interface
+ * @v read Operation is a read
+ * @v offset Starting offset within NVRAM
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read,
+ UINTN offset, UINTN len, VOID *data ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p NVDATA %s %lx+%lx\n", snpdev,
+ ( read ? "read" : "write" ), ( ( unsigned long ) offset ),
+ ( ( unsigned long ) len ) );
+ if ( ! read )
+ DBGC2_HDA ( snpdev, offset, data, len );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ return EFI_UNSUPPORTED;
+}
+
+/**
+ * Read interrupt status and TX recycled buffer status
+ *
+ * @v snp SNP interface
+ * @v interrupts Interrupt status, or NULL
+ * @v txbufs Recycled transmit buffer address, or NULL
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINT32 *interrupts, VOID **txbufs ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+
+ DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Interrupt status. In practice, this seems to be used only
+ * to detect TX completions.
+ */
+ if ( interrupts ) {
+ *interrupts = 0;
+ /* Report TX completions once queue is empty; this
+ * avoids having to add hooks in the net device layer.
+ */
+ if ( snpdev->tx_count_interrupts &&
+ list_empty ( &snpdev->netdev->tx_queue ) ) {
+ *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT;
+ snpdev->tx_count_interrupts--;
+ }
+ /* Report RX */
+ if ( snpdev->rx_count_interrupts ) {
+ *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT;
+ snpdev->rx_count_interrupts--;
+ }
+ DBGC2 ( snpdev, " INTS:%02x", *interrupts );
+ }
+
+ /* TX completions. It would be possible to design a more
+ * idiotic scheme for this, but it would be a challenge.
+ * According to the UEFI header file, txbufs will be filled in
+ * with a list of "recycled transmit buffers" (i.e. completed
+ * TX buffers). Observant readers may care to note that
+ * *txbufs is a void pointer. Precisely how a list of
+ * completed transmit buffers is meant to be represented as an
+ * array of voids is left as an exercise for the reader.
+ *
+ * The only users of this interface (MnpDxe/MnpIo.c and
+ * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until
+ * seeing a non-NULL result return in txbufs. This is valid
+ * provided that they do not ever attempt to transmit more
+ * than one packet concurrently (and that TX never times out).
+ */
+ if ( txbufs ) {
+ if ( snpdev->tx_count_txbufs &&
+ list_empty ( &snpdev->netdev->tx_queue ) ) {
+ *txbufs = "Which idiot designed this API?";
+ snpdev->tx_count_txbufs--;
+ } else {
+ *txbufs = NULL;
+ }
+ DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) );
+ }
+
+ DBGC2 ( snpdev, "\n" );
+ return 0;
+}
+
+/**
+ * Start packet transmission
+ *
+ * @v snp SNP interface
+ * @v ll_header_len Link-layer header length, if to be filled in
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @v ll_src Link-layer source address, if specified
+ * @v ll_dest Link-layer destination address, if specified
+ * @v net_proto Network-layer protocol (in host order)
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN ll_header_len, UINTN len, VOID *data,
+ EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
+ UINT16 *net_proto ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct io_buffer *iobuf;
+ size_t payload_len;
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data,
+ ( ( unsigned long ) len ) );
+ if ( ll_header_len ) {
+ if ( ll_src ) {
+ DBGC2 ( snpdev, " src %s",
+ ll_protocol->ntoa ( ll_src ) );
+ }
+ if ( ll_dest ) {
+ DBGC2 ( snpdev, " dest %s",
+ ll_protocol->ntoa ( ll_dest ) );
+ }
+ if ( net_proto ) {
+ DBGC2 ( snpdev, " proto %04x", *net_proto );
+ }
+ }
+ DBGC2 ( snpdev, "\n" );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Sanity checks */
+ if ( ll_header_len ) {
+ if ( ll_header_len != ll_protocol->ll_header_len ) {
+ DBGC ( snpdev, "SNPDEV %p TX invalid header length "
+ "%ld\n", snpdev,
+ ( ( unsigned long ) ll_header_len ) );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+ if ( len < ll_header_len ) {
+ DBGC ( snpdev, "SNPDEV %p invalid packet length %ld\n",
+ snpdev, ( ( unsigned long ) len ) );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+ if ( ! ll_dest ) {
+ DBGC ( snpdev, "SNPDEV %p TX missing destination "
+ "address\n", snpdev );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+ if ( ! net_proto ) {
+ DBGC ( snpdev, "SNPDEV %p TX missing network "
+ "protocol\n", snpdev );
+ rc = -EINVAL;
+ goto err_sanity;
+ }
+ if ( ! ll_src )
+ ll_src = &snpdev->mode.CurrentAddress;
+ }
+
+ /* Allocate buffer */
+ payload_len = ( len - ll_protocol->ll_header_len );
+ iobuf = alloc_iob ( MAX_LL_HEADER_LEN + ( ( payload_len > IOB_ZLEN ) ?
+ payload_len : IOB_ZLEN ) );
+ if ( ! iobuf ) {
+ DBGC ( snpdev, "SNPDEV %p TX could not allocate %ld-byte "
+ "buffer\n", snpdev, ( ( unsigned long ) len ) );
+ rc = -ENOMEM;
+ goto err_alloc_iob;
+ }
+ iob_reserve ( iobuf, ( MAX_LL_HEADER_LEN -
+ ll_protocol->ll_header_len ) );
+ memcpy ( iob_put ( iobuf, len ), data, len );
+
+ /* Create link-layer header, if specified */
+ if ( ll_header_len ) {
+ iob_pull ( iobuf, ll_protocol->ll_header_len );
+ if ( ( rc = ll_protocol->push ( snpdev->netdev,
+ iobuf, ll_dest, ll_src,
+ htons ( *net_proto ) )) != 0 ){
+ DBGC ( snpdev, "SNPDEV %p TX could not construct "
+ "header: %s\n", snpdev, strerror ( rc ) );
+ goto err_ll_push;
+ }
+ }
+
+ /* Transmit packet */
+ if ( ( rc = netdev_tx ( snpdev->netdev, iob_disown ( iobuf ) ) ) != 0){
+ DBGC ( snpdev, "SNPDEV %p TX could not transmit: %s\n",
+ snpdev, strerror ( rc ) );
+ goto err_tx;
+ }
+
+ /* Record transmission as outstanding */
+ snpdev->tx_count_interrupts++;
+ snpdev->tx_count_txbufs++;
+
+ return 0;
+
+ err_tx:
+ err_ll_push:
+ free_iob ( iobuf );
+ err_alloc_iob:
+ err_sanity:
+ return EFIRC ( rc );
+}
+
+/**
+ * Receive packet
+ *
+ * @v snp SNP interface
+ * @v ll_header_len Link-layer header length, if to be filled in
+ * @v len Length of data buffer
+ * @v data Data buffer
+ * @v ll_src Link-layer source address, if specified
+ * @v ll_dest Link-layer destination address, if specified
+ * @v net_proto Network-layer protocol (in host order)
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp,
+ UINTN *ll_header_len, UINTN *len, VOID *data,
+ EFI_MAC_ADDRESS *ll_src, EFI_MAC_ADDRESS *ll_dest,
+ UINT16 *net_proto ) {
+ struct efi_snp_device *snpdev =
+ container_of ( snp, struct efi_snp_device, snp );
+ struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol;
+ struct io_buffer *iobuf;
+ const void *iob_ll_dest;
+ const void *iob_ll_src;
+ uint16_t iob_net_proto;
+ unsigned int iob_flags;
+ int rc;
+
+ DBGC2 ( snpdev, "SNPDEV %p RECEIVE %p(+%lx)", snpdev, data,
+ ( ( unsigned long ) *len ) );
+
+ /* Fail if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return EFI_NOT_READY;
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Dequeue a packet, if one is available */
+ iobuf = netdev_rx_dequeue ( snpdev->netdev );
+ if ( ! iobuf ) {
+ DBGC2 ( snpdev, "\n" );
+ rc = -EAGAIN;
+ goto out_no_packet;
+ }
+ DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) );
+
+ /* Return packet to caller */
+ memcpy ( data, iobuf->data, iob_len ( iobuf ) );
+ *len = iob_len ( iobuf );
+
+ /* Attempt to decode link-layer header */
+ if ( ( rc = ll_protocol->pull ( snpdev->netdev, iobuf, &iob_ll_dest,
+ &iob_ll_src, &iob_net_proto,
+ &iob_flags ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not parse header: %s\n",
+ snpdev, strerror ( rc ) );
+ goto out_bad_ll_header;
+ }
+
+ /* Return link-layer header parameters to caller, if required */
+ if ( ll_header_len )
+ *ll_header_len = ll_protocol->ll_header_len;
+ if ( ll_src )
+ memcpy ( ll_src, iob_ll_src, ll_protocol->ll_addr_len );
+ if ( ll_dest )
+ memcpy ( ll_dest, iob_ll_dest, ll_protocol->ll_addr_len );
+ if ( net_proto )
+ *net_proto = ntohs ( iob_net_proto );
+
+ rc = 0;
+
+ out_bad_ll_header:
+ free_iob ( iobuf );
+ out_no_packet:
+ return EFIRC ( rc );
+}
+
+/**
+ * Poll event
+ *
+ * @v event Event
+ * @v context Event context
+ */
+static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event,
+ VOID *context ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_snp_device *snpdev = context;
+
+ DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev );
+
+ /* Do nothing unless the net device is open */
+ if ( ! netdev_is_open ( snpdev->netdev ) )
+ return;
+
+ /* Do nothing if net device is currently claimed for use by iPXE */
+ if ( efi_snp_claimed )
+ return;
+
+ /* Poll the network device */
+ efi_snp_poll ( snpdev );
+
+ /* Fire event if packets have been received */
+ if ( snpdev->rx_count_events != 0 ) {
+ DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n",
+ snpdev );
+ bs->SignalEvent ( event );
+ snpdev->rx_count_events--;
+ }
+}
+
+/** SNP interface */
+static EFI_SIMPLE_NETWORK_PROTOCOL efi_snp_device_snp = {
+ .Revision = EFI_SIMPLE_NETWORK_PROTOCOL_REVISION,
+ .Start = efi_snp_start,
+ .Stop = efi_snp_stop,
+ .Initialize = efi_snp_initialize,
+ .Reset = efi_snp_reset,
+ .Shutdown = efi_snp_shutdown,
+ .ReceiveFilters = efi_snp_receive_filters,
+ .StationAddress = efi_snp_station_address,
+ .Statistics = efi_snp_statistics,
+ .MCastIpToMac = efi_snp_mcast_ip_to_mac,
+ .NvData = efi_snp_nvdata,
+ .GetStatus = efi_snp_get_status,
+ .Transmit = efi_snp_transmit,
+ .Receive = efi_snp_receive,
+};
+
+/******************************************************************************
+ *
+ * Component name protocol
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Look up driver name
+ *
+ * @v name2 Component name protocol
+ * @v language Language to use
+ * @v driver_name Driver name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_driver_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2,
+ CHAR8 *language __unused, CHAR16 **driver_name ) {
+ struct efi_snp_device *snpdev =
+ container_of ( name2, struct efi_snp_device, name2 );
+
+ *driver_name = snpdev->driver_name;
+ return 0;
+}
+
+/**
+ * Look up controller name
+ *
+ * @v name2 Component name protocol
+ * @v device Device
+ * @v child Child device, or NULL
+ * @v language Language to use
+ * @v driver_name Device name to fill in
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_get_controller_name ( EFI_COMPONENT_NAME2_PROTOCOL *name2,
+ EFI_HANDLE device __unused,
+ EFI_HANDLE child __unused,
+ CHAR8 *language __unused,
+ CHAR16 **controller_name ) {
+ struct efi_snp_device *snpdev =
+ container_of ( name2, struct efi_snp_device, name2 );
+
+ *controller_name = snpdev->controller_name;
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Load file protocol
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Load file
+ *
+ * @v loadfile Load file protocol
+ * @v path File path
+ * @v booting Loading as part of a boot attempt
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file,
+ EFI_DEVICE_PATH_PROTOCOL *path __unused,
+ BOOLEAN booting, UINTN *len __unused,
+ VOID *data __unused ) {
+ struct efi_snp_device *snpdev =
+ container_of ( load_file, struct efi_snp_device, load_file );
+ struct net_device *netdev = snpdev->netdev;
+
+ /* Fail unless this is a boot attempt */
+ if ( ! booting ) {
+ DBGC ( snpdev, "SNPDEV %p cannot load non-boot file\n",
+ snpdev );
+ return EFI_UNSUPPORTED;
+ }
+
+ /* Claim network devices for use by iPXE */
+ efi_snp_claim();
+
+ /* Boot from network device */
+ ipxe ( netdev );
+
+ /* Release network devices for use via SNP */
+ efi_snp_release();
+
+ /* Assume boot process was aborted */
+ return EFI_ABORTED;
+}
+
+/** Load file protocol */
+static EFI_LOAD_FILE_PROTOCOL efi_snp_load_file_protocol = {
+ .LoadFile = efi_snp_load_file,
+};
+
+/******************************************************************************
+ *
+ * iPXE network driver
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Locate SNP device corresponding to network device
+ *
+ * @v netdev Network device
+ * @ret snp SNP device, or NULL if not found
+ */
+static struct efi_snp_device * efi_snp_demux ( struct net_device *netdev ) {
+ struct efi_snp_device *snpdev;
+
+ list_for_each_entry ( snpdev, &efi_snp_devices, list ) {
+ if ( snpdev->netdev == netdev )
+ return snpdev;
+ }
+ return NULL;
+}
+
+/**
+ * Create SNP device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int efi_snp_probe ( struct net_device *netdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_device *efidev;
+ struct efi_snp_device *snpdev;
+ union {
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ void *interface;
+ } path;
+ EFI_DEVICE_PATH_PROTOCOL *path_end;
+ MAC_ADDR_DEVICE_PATH *macpath;
+ size_t path_prefix_len = 0;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Find parent EFI device */
+ efidev = efidev_parent ( netdev->dev );
+ if ( ! efidev ) {
+ DBG ( "SNP skipping non-EFI device %s\n", netdev->name );
+ rc = 0;
+ goto err_no_efidev;
+ }
+
+ /* Allocate the SNP device */
+ snpdev = zalloc ( sizeof ( *snpdev ) );
+ if ( ! snpdev ) {
+ rc = -ENOMEM;
+ goto err_alloc_snp;
+ }
+ snpdev->netdev = netdev_get ( netdev );
+ snpdev->efidev = efidev;
+
+ /* Sanity check */
+ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) {
+ DBGC ( snpdev, "SNPDEV %p cannot support link-layer address "
+ "length %d for %s\n", snpdev,
+ netdev->ll_protocol->ll_addr_len, netdev->name );
+ rc = -ENOTSUP;
+ goto err_ll_addr_len;
+ }
+
+ /* Populate the SNP structure */
+ memcpy ( &snpdev->snp, &efi_snp_device_snp, sizeof ( snpdev->snp ) );
+ snpdev->snp.Mode = &snpdev->mode;
+ if ( ( efirc = bs->CreateEvent ( EVT_NOTIFY_WAIT, TPL_NOTIFY,
+ efi_snp_wait_for_packet, snpdev,
+ &snpdev->snp.WaitForPacket ) ) != 0 ){
+ rc = -EEFI ( efirc );
+ DBGC ( snpdev, "SNPDEV %p could not create event: %s\n",
+ snpdev, strerror ( rc ) );
+ goto err_create_event;
+ }
+
+ /* Populate the SNP mode structure */
+ snpdev->mode.State = EfiSimpleNetworkStopped;
+ efi_snp_set_mode ( snpdev );
+
+ /* Populate the NII structure */
+ snpdev->nii.Revision =
+ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL_REVISION;
+ strncpy ( snpdev->nii.StringId, "iPXE",
+ sizeof ( snpdev->nii.StringId ) );
+
+ /* Populate the component name structure */
+ efi_snprintf ( snpdev->driver_name,
+ ( sizeof ( snpdev->driver_name ) /
+ sizeof ( snpdev->driver_name[0] ) ),
+ "%s %s", product_short_name, netdev->dev->driver_name );
+ efi_snprintf ( snpdev->controller_name,
+ ( sizeof ( snpdev->controller_name ) /
+ sizeof ( snpdev->controller_name[0] ) ),
+ "%s %s (%s, %s)", product_short_name,
+ netdev->dev->driver_name, netdev->dev->name,
+ netdev_addr ( netdev ) );
+ snpdev->name2.GetDriverName = efi_snp_get_driver_name;
+ snpdev->name2.GetControllerName = efi_snp_get_controller_name;
+ snpdev->name2.SupportedLanguages = "en";
+
+ /* Populate the load file protocol structure */
+ memcpy ( &snpdev->load_file, &efi_snp_load_file_protocol,
+ sizeof ( snpdev->load_file ) );
+
+ /* Populate the device name */
+ efi_snprintf ( snpdev->name, ( sizeof ( snpdev->name ) /
+ sizeof ( snpdev->name[0] ) ),
+ "%s", netdev->name );
+
+ /* Get the parent device path */
+ if ( ( efirc = bs->OpenProtocol ( efidev->device,
+ &efi_device_path_protocol_guid,
+ &path.interface, efi_image_handle,
+ efidev->device,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( snpdev, "SNPDEV %p cannot get %p %s device path: %s\n",
+ snpdev, efidev->device,
+ efi_handle_name ( efidev->device ), strerror ( rc ) );
+ goto err_open_device_path;
+ }
+
+ /* Allocate the new device path */
+ path_end = efi_devpath_end ( path.path );
+ path_prefix_len = ( ( ( void * ) path_end ) - ( ( void * ) path.path ));
+ snpdev->path = zalloc ( path_prefix_len + sizeof ( *macpath ) +
+ sizeof ( *path_end ) );
+ if ( ! snpdev->path ) {
+ rc = -ENOMEM;
+ goto err_alloc_device_path;
+ }
+
+ /* Populate the device path */
+ memcpy ( snpdev->path, path.path, path_prefix_len );
+ macpath = ( ( ( void * ) snpdev->path ) + path_prefix_len );
+ path_end = ( ( void * ) ( macpath + 1 ) );
+ memset ( macpath, 0, sizeof ( *macpath ) );
+ macpath->Header.Type = MESSAGING_DEVICE_PATH;
+ macpath->Header.SubType = MSG_MAC_ADDR_DP;
+ macpath->Header.Length[0] = sizeof ( *macpath );
+ memcpy ( &macpath->MacAddress, netdev->ll_addr,
+ sizeof ( macpath->MacAddress ) );
+ macpath->IfType = ntohs ( netdev->ll_protocol->ll_proto );
+ memset ( path_end, 0, sizeof ( *path_end ) );
+ path_end->Type = END_DEVICE_PATH_TYPE;
+ path_end->SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE;
+ path_end->Length[0] = sizeof ( *path_end );
+
+ /* Install the SNP */
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ &efi_component_name2_protocol_guid, &snpdev->name2,
+ &efi_load_file_protocol_guid, &snpdev->load_file,
+ NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( snpdev, "SNPDEV %p could not install protocols: "
+ "%s\n", snpdev, strerror ( rc ) );
+ goto err_install_protocol_interface;
+ }
+
+ /* Add as child of EFI parent device */
+ if ( ( rc = efi_child_add ( efidev->device, snpdev->handle ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not become child of %p %s: "
+ "%s\n", snpdev, efidev->device,
+ efi_handle_name ( efidev->device ), strerror ( rc ) );
+ goto err_efi_child_add;
+ }
+
+ /* Install HII */
+ if ( ( rc = efi_snp_hii_install ( snpdev ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not install HII: %s\n",
+ snpdev, strerror ( rc ) );
+ /* HII fails on several platforms. It's
+ * non-essential, so treat this as a non-fatal
+ * error.
+ */
+ }
+
+ /* Add to list of SNP devices */
+ list_add ( &snpdev->list, &efi_snp_devices );
+
+ /* Close device path */
+ bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid,
+ efi_image_handle, efidev->device );
+
+ DBGC ( snpdev, "SNPDEV %p installed for %s as device %p %s\n",
+ snpdev, netdev->name, snpdev->handle,
+ efi_handle_name ( snpdev->handle ) );
+ return 0;
+
+ if ( snpdev->package_list )
+ efi_snp_hii_uninstall ( snpdev );
+ efi_child_del ( efidev->device, snpdev->handle );
+ err_efi_child_add:
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ &efi_component_name2_protocol_guid, &snpdev->name2,
+ &efi_load_file_protocol_guid, &snpdev->load_file,
+ NULL );
+ err_install_protocol_interface:
+ free ( snpdev->path );
+ err_alloc_device_path:
+ bs->CloseProtocol ( efidev->device, &efi_device_path_protocol_guid,
+ efi_image_handle, efidev->device );
+ err_open_device_path:
+ bs->CloseEvent ( snpdev->snp.WaitForPacket );
+ err_create_event:
+ err_ll_addr_len:
+ netdev_put ( netdev );
+ free ( snpdev );
+ err_alloc_snp:
+ err_no_efidev:
+ return rc;
+}
+
+/**
+ * Handle SNP device or link state change
+ *
+ * @v netdev Network device
+ */
+static void efi_snp_notify ( struct net_device *netdev ) {
+ struct efi_snp_device *snpdev;
+
+ /* Locate SNP device */
+ snpdev = efi_snp_demux ( netdev );
+ if ( ! snpdev ) {
+ DBG ( "SNP skipping non-SNP device %s\n", netdev->name );
+ return;
+ }
+
+ /* Update link state */
+ snpdev->mode.MediaPresent =
+ ( netdev_link_ok ( netdev ) ? TRUE : FALSE );
+ DBGC ( snpdev, "SNPDEV %p link is %s\n", snpdev,
+ ( snpdev->mode.MediaPresent ? "up" : "down" ) );
+
+ /* Update mode state */
+ efi_snp_set_state ( snpdev );
+}
+
+/**
+ * Destroy SNP device
+ *
+ * @v netdev Network device
+ */
+static void efi_snp_remove ( struct net_device *netdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ struct efi_snp_device *snpdev;
+
+ /* Locate SNP device */
+ snpdev = efi_snp_demux ( netdev );
+ if ( ! snpdev ) {
+ DBG ( "SNP skipping non-SNP device %s\n", netdev->name );
+ return;
+ }
+
+ /* Uninstall the SNP */
+ if ( snpdev->package_list )
+ efi_snp_hii_uninstall ( snpdev );
+ efi_child_del ( snpdev->efidev->device, snpdev->handle );
+ list_del ( &snpdev->list );
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_simple_network_protocol_guid, &snpdev->snp,
+ &efi_device_path_protocol_guid, snpdev->path,
+ &efi_nii_protocol_guid, &snpdev->nii,
+ &efi_nii31_protocol_guid, &snpdev->nii,
+ &efi_component_name2_protocol_guid, &snpdev->name2,
+ &efi_load_file_protocol_guid, &snpdev->load_file,
+ NULL );
+ free ( snpdev->path );
+ bs->CloseEvent ( snpdev->snp.WaitForPacket );
+ netdev_put ( snpdev->netdev );
+ free ( snpdev );
+}
+
+/** SNP driver */
+struct net_driver efi_snp_driver __net_driver = {
+ .name = "SNP",
+ .probe = efi_snp_probe,
+ .notify = efi_snp_notify,
+ .remove = efi_snp_remove,
+};
+
+/**
+ * Find SNP device by EFI device handle
+ *
+ * @v handle EFI device handle
+ * @ret snpdev SNP device, or NULL
+ */
+struct efi_snp_device * find_snpdev ( EFI_HANDLE handle ) {
+ struct efi_snp_device *snpdev;
+
+ list_for_each_entry ( snpdev, &efi_snp_devices, list ) {
+ if ( snpdev->handle == handle )
+ return snpdev;
+ }
+ return NULL;
+}
+
+/**
+ * Get most recently opened SNP device
+ *
+ * @ret snpdev Most recently opened SNP device, or NULL
+ */
+struct efi_snp_device * last_opened_snpdev ( void ) {
+ struct net_device *netdev;
+
+ netdev = last_opened_netdev();
+ if ( ! netdev )
+ return NULL;
+
+ return efi_snp_demux ( netdev );
+}
+
+/**
+ * Set SNP claimed/released state
+ *
+ * @v claimed Network devices are claimed for use by iPXE
+ */
+void efi_snp_set_claimed ( int claimed ) {
+ struct efi_snp_device *snpdev;
+
+ /* Claim SNP devices */
+ efi_snp_claimed = claimed;
+
+ /* Update SNP mode state for each interface */
+ list_for_each_entry ( snpdev, &efi_snp_devices, list )
+ efi_snp_set_state ( snpdev );
+}
diff --git a/roms/ipxe/src/interface/efi/efi_snp_hii.c b/roms/ipxe/src/interface/efi/efi_snp_hii.c
new file mode 100644
index 00000000..c49c76a3
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_snp_hii.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * EFI SNP HII protocol
+ *
+ * The HII protocols are some of the less-well designed parts of the
+ * entire EFI specification. This is a significant accomplishment.
+ *
+ * The face-slappingly ludicrous query string syntax seems to be
+ * motivated by the desire to allow a caller to query multiple drivers
+ * simultaneously via the single-instance HII_CONFIG_ROUTING_PROTOCOL,
+ * which is supposed to pass relevant subsets of the query string to
+ * the relevant drivers.
+ *
+ * Nobody uses the HII_CONFIG_ROUTING_PROTOCOL. Not even the EFI
+ * setup browser uses the HII_CONFIG_ROUTING_PROTOCOL. To the best of
+ * my knowledge, there has only ever been one implementation of the
+ * HII_CONFIG_ROUTING_PROTOCOL (as part of EDK2), and it just doesn't
+ * work. It's so badly broken that I can't even figure out what the
+ * code is _trying_ to do.
+ *
+ * Fundamentally, the problem seems to be that Javascript programmers
+ * should not be allowed to design APIs for C code.
+ */
+
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <errno.h>
+#include <ipxe/settings.h>
+#include <ipxe/nvo.h>
+#include <ipxe/device.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/version.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_hii.h>
+#include <ipxe/efi/efi_snp.h>
+#include <ipxe/efi/efi_strings.h>
+
+/** EFI platform setup formset GUID */
+static EFI_GUID efi_hii_platform_setup_formset_guid
+ = EFI_HII_PLATFORM_SETUP_FORMSET_GUID;
+
+/** EFI IBM UCM compliant formset GUID */
+static EFI_GUID efi_hii_ibm_ucm_compliant_formset_guid
+ = EFI_HII_IBM_UCM_COMPLIANT_FORMSET_GUID;
+
+/** EFI HII database protocol */
+static EFI_HII_DATABASE_PROTOCOL *efihii;
+EFI_REQUEST_PROTOCOL ( EFI_HII_DATABASE_PROTOCOL, &efihii );
+
+/**
+ * Identify settings to be exposed via HII
+ *
+ * @v snpdev SNP device
+ * @ret settings Settings, or NULL
+ */
+static struct settings * efi_snp_hii_settings ( struct efi_snp_device *snpdev ){
+
+ return find_child_settings ( netdev_settings ( snpdev->netdev ),
+ NVO_SETTINGS_NAME );
+}
+
+/**
+ * Check whether or not setting is applicable
+ *
+ * @v snpdev SNP device
+ * @v setting Setting
+ * @ret applies Setting applies
+ */
+static int efi_snp_hii_setting_applies ( struct efi_snp_device *snpdev,
+ struct setting *setting ) {
+
+ return nvo_applies ( efi_snp_hii_settings ( snpdev ), setting );
+}
+
+/**
+ * Generate a random GUID
+ *
+ * @v guid GUID to fill in
+ */
+static void efi_snp_hii_random_guid ( EFI_GUID *guid ) {
+ uint8_t *byte = ( ( uint8_t * ) guid );
+ unsigned int i;
+
+ for ( i = 0 ; i < sizeof ( *guid ) ; i++ )
+ *(byte++) = random();
+}
+
+/**
+ * Generate EFI SNP questions
+ *
+ * @v snpdev SNP device
+ * @v ifr IFR builder
+ * @v varstore_id Variable store identifier
+ */
+static void efi_snp_hii_questions ( struct efi_snp_device *snpdev,
+ struct efi_ifr_builder *ifr,
+ unsigned int varstore_id ) {
+ struct setting *setting;
+ struct setting *previous = NULL;
+ unsigned int name_id;
+ unsigned int prompt_id;
+ unsigned int help_id;
+ unsigned int question_id;
+
+ /* Add all applicable settings */
+ for_each_table_entry ( setting, SETTINGS ) {
+ if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
+ continue;
+ if ( previous && ( setting_cmp ( setting, previous ) == 0 ) )
+ continue;
+ previous = setting;
+ name_id = efi_ifr_string ( ifr, "%s", setting->name );
+ prompt_id = efi_ifr_string ( ifr, "%s", setting->description );
+ help_id = efi_ifr_string ( ifr, "http://ipxe.org/cfg/%s",
+ setting->name );
+ question_id = setting->tag;
+ efi_ifr_string_op ( ifr, prompt_id, help_id,
+ question_id, varstore_id, name_id,
+ 0, 0x00, 0xff, 0 );
+ }
+}
+
+/**
+ * Build HII package list for SNP device
+ *
+ * @v snpdev SNP device
+ * @ret package Package list, or NULL on error
+ */
+static EFI_HII_PACKAGE_LIST_HEADER *
+efi_snp_hii_package_list ( struct efi_snp_device *snpdev ) {
+ struct net_device *netdev = snpdev->netdev;
+ struct device *dev = netdev->dev;
+ struct efi_ifr_builder ifr;
+ EFI_HII_PACKAGE_LIST_HEADER *package;
+ const char *name;
+ EFI_GUID package_guid;
+ EFI_GUID formset_guid;
+ EFI_GUID varstore_guid;
+ unsigned int title_id;
+ unsigned int varstore_id;
+
+ /* Initialise IFR builder */
+ efi_ifr_init ( &ifr );
+
+ /* Determine product name */
+ name = ( product_name[0] ? product_name : product_short_name );
+
+ /* Generate GUIDs */
+ efi_snp_hii_random_guid ( &package_guid );
+ efi_snp_hii_random_guid ( &formset_guid );
+ efi_snp_hii_random_guid ( &varstore_guid );
+
+ /* Generate title string (used more than once) */
+ title_id = efi_ifr_string ( &ifr, "%s (%s)", name,
+ netdev_addr ( netdev ) );
+
+ /* Generate opcodes */
+ efi_ifr_form_set_op ( &ifr, &formset_guid, title_id,
+ efi_ifr_string ( &ifr, "Configure %s",
+ product_short_name ),
+ &efi_hii_platform_setup_formset_guid,
+ &efi_hii_ibm_ucm_compliant_formset_guid, NULL );
+ efi_ifr_guid_class_op ( &ifr, EFI_NETWORK_DEVICE_CLASS );
+ efi_ifr_guid_subclass_op ( &ifr, 0x03 );
+ varstore_id = efi_ifr_varstore_name_value_op ( &ifr, &varstore_guid );
+ efi_ifr_form_op ( &ifr, title_id );
+ efi_ifr_text_op ( &ifr,
+ efi_ifr_string ( &ifr, "Name" ),
+ efi_ifr_string ( &ifr, "Firmware product name" ),
+ efi_ifr_string ( &ifr, "%s", name ) );
+ efi_ifr_text_op ( &ifr,
+ efi_ifr_string ( &ifr, "Version" ),
+ efi_ifr_string ( &ifr, "Firmware version" ),
+ efi_ifr_string ( &ifr, "%s", product_version ) );
+ efi_ifr_text_op ( &ifr,
+ efi_ifr_string ( &ifr, "Driver" ),
+ efi_ifr_string ( &ifr, "Firmware driver" ),
+ efi_ifr_string ( &ifr, "%s", dev->driver_name ) );
+ efi_ifr_text_op ( &ifr,
+ efi_ifr_string ( &ifr, "Device" ),
+ efi_ifr_string ( &ifr, "Hardware device" ),
+ efi_ifr_string ( &ifr, "%s", dev->name ) );
+ efi_snp_hii_questions ( snpdev, &ifr, varstore_id );
+ efi_ifr_end_op ( &ifr );
+ efi_ifr_end_op ( &ifr );
+
+ /* Build package */
+ package = efi_ifr_package ( &ifr, &package_guid, "en-us",
+ efi_ifr_string ( &ifr, "English" ) );
+ if ( ! package ) {
+ DBGC ( snpdev, "SNPDEV %p could not build IFR package\n",
+ snpdev );
+ efi_ifr_free ( &ifr );
+ return NULL;
+ }
+
+ /* Free temporary storage */
+ efi_ifr_free ( &ifr );
+ return package;
+}
+
+/**
+ * Append response to result string
+ *
+ * @v snpdev SNP device
+ * @v key Key
+ * @v value Value
+ * @v results Result string
+ * @ret rc Return status code
+ *
+ * The result string is allocated dynamically using
+ * BootServices::AllocatePool(), and the caller is responsible for
+ * eventually calling BootServices::FreePool().
+ */
+static int efi_snp_hii_append ( struct efi_snp_device *snpdev __unused,
+ const char *key, const char *value,
+ wchar_t **results ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ size_t len;
+ void *new;
+
+ /* Allocate new string */
+ len = ( ( *results ? ( wcslen ( *results ) + 1 /* "&" */ ) : 0 ) +
+ strlen ( key ) + 1 /* "=" */ + strlen ( value ) + 1 /* NUL */ );
+ bs->AllocatePool ( EfiBootServicesData, ( len * sizeof ( wchar_t ) ),
+ &new );
+ if ( ! new )
+ return -ENOMEM;
+
+ /* Populate string */
+ efi_snprintf ( new, len, "%ls%s%s=%s", ( *results ? *results : L"" ),
+ ( *results ? L"&" : L"" ), key, value );
+ bs->FreePool ( *results );
+ *results = new;
+
+ return 0;
+}
+
+/**
+ * Fetch HII setting
+ *
+ * @v snpdev SNP device
+ * @v key Key
+ * @v value Value
+ * @v results Result string
+ * @v have_setting Flag indicating detection of a setting
+ * @ret rc Return status code
+ */
+static int efi_snp_hii_fetch ( struct efi_snp_device *snpdev,
+ const char *key, const char *value,
+ wchar_t **results, int *have_setting ) {
+ struct settings *settings = efi_snp_hii_settings ( snpdev );
+ struct settings *origin;
+ struct setting *setting;
+ struct setting fetched;
+ int len;
+ char *buf;
+ char *encoded;
+ int i;
+ int rc;
+
+ /* Handle ConfigHdr components */
+ if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
+ ( strcasecmp ( key, "NAME" ) == 0 ) ||
+ ( strcasecmp ( key, "PATH" ) == 0 ) ) {
+ return efi_snp_hii_append ( snpdev, key, value, results );
+ }
+ if ( have_setting )
+ *have_setting = 1;
+
+ /* Do nothing more unless we have a settings block */
+ if ( ! settings ) {
+ rc = -ENOTSUP;
+ goto err_no_settings;
+ }
+
+ /* Identify setting */
+ setting = find_setting ( key );
+ if ( ! setting ) {
+ DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
+ snpdev, key );
+ rc = -ENODEV;
+ goto err_find_setting;
+ }
+
+ /* Encode value */
+ if ( setting_exists ( settings, setting ) ) {
+
+ /* Calculate formatted length */
+ len = fetchf_setting ( settings, setting, &origin, &fetched,
+ NULL, 0 );
+ if ( len < 0 ) {
+ rc = len;
+ DBGC ( snpdev, "SNPDEV %p could not fetch %s: %s\n",
+ snpdev, setting->name, strerror ( rc ) );
+ goto err_fetchf_len;
+ }
+
+ /* Allocate buffer for formatted value and HII-encoded value */
+ buf = zalloc ( len + 1 /* NUL */ + ( len * 4 ) + 1 /* NUL */ );
+ if ( ! buf ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ encoded = ( buf + len + 1 /* NUL */ );
+
+ /* Format value */
+ fetchf_setting ( origin, &fetched, NULL, NULL, buf,
+ ( len + 1 /* NUL */ ) );
+ for ( i = 0 ; i < len ; i++ ) {
+ sprintf ( ( encoded + ( 4 * i ) ), "%04x",
+ *( ( uint8_t * ) buf + i ) );
+ }
+
+ } else {
+
+ /* Non-existent or inapplicable setting */
+ buf = NULL;
+ encoded = "";
+ }
+
+ /* Append results */
+ if ( ( rc = efi_snp_hii_append ( snpdev, key, encoded,
+ results ) ) != 0 ) {
+ goto err_append;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_append:
+ free ( buf );
+ err_alloc:
+ err_fetchf_len:
+ err_find_setting:
+ err_no_settings:
+ return rc;
+}
+
+/**
+ * Fetch HII setting
+ *
+ * @v snpdev SNP device
+ * @v key Key
+ * @v value Value
+ * @v results Result string (unused)
+ * @v have_setting Flag indicating detection of a setting (unused)
+ * @ret rc Return status code
+ */
+static int efi_snp_hii_store ( struct efi_snp_device *snpdev,
+ const char *key, const char *value,
+ wchar_t **results __unused,
+ int *have_setting __unused ) {
+ struct settings *settings = efi_snp_hii_settings ( snpdev );
+ struct setting *setting;
+ char *buf;
+ char tmp[5];
+ char *endp;
+ int len;
+ int i;
+ int rc;
+
+ /* Handle ConfigHdr components */
+ if ( ( strcasecmp ( key, "GUID" ) == 0 ) ||
+ ( strcasecmp ( key, "NAME" ) == 0 ) ||
+ ( strcasecmp ( key, "PATH" ) == 0 ) ) {
+ /* Nothing to do */
+ return 0;
+ }
+
+ /* Do nothing more unless we have a settings block */
+ if ( ! settings ) {
+ rc = -ENOTSUP;
+ goto err_no_settings;
+ }
+
+ /* Identify setting */
+ setting = find_setting ( key );
+ if ( ! setting ) {
+ DBGC ( snpdev, "SNPDEV %p no such setting \"%s\"\n",
+ snpdev, key );
+ rc = -ENODEV;
+ goto err_find_setting;
+ }
+
+ /* Allocate buffer */
+ len = ( strlen ( value ) / 4 );
+ buf = zalloc ( len + 1 /* NUL */ );
+ if ( ! buf ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ /* Decode value */
+ tmp[4] = '\0';
+ for ( i = 0 ; i < len ; i++ ) {
+ memcpy ( tmp, ( value + ( i * 4 ) ), 4 );
+ buf[i] = strtoul ( tmp, &endp, 16 );
+ if ( endp != &tmp[4] ) {
+ DBGC ( snpdev, "SNPDEV %p invalid character %s\n",
+ snpdev, tmp );
+ rc = -EINVAL;
+ goto err_inval;
+ }
+ }
+
+ /* Store value */
+ if ( ( rc = storef_setting ( settings, setting, buf ) ) != 0 ) {
+ DBGC ( snpdev, "SNPDEV %p could not store \"%s\" into %s: %s\n",
+ snpdev, buf, setting->name, strerror ( rc ) );
+ goto err_storef;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_storef:
+ err_inval:
+ free ( buf );
+ err_alloc:
+ err_find_setting:
+ err_no_settings:
+ return rc;
+}
+
+/**
+ * Process portion of HII configuration string
+ *
+ * @v snpdev SNP device
+ * @v string HII configuration string
+ * @v progress Progress through HII configuration string
+ * @v results Results string
+ * @v have_setting Flag indicating detection of a setting (unused)
+ * @v process Function used to process key=value pairs
+ * @ret rc Return status code
+ */
+static int efi_snp_hii_process ( struct efi_snp_device *snpdev,
+ wchar_t *string, wchar_t **progress,
+ wchar_t **results, int *have_setting,
+ int ( * process ) ( struct efi_snp_device *,
+ const char *key,
+ const char *value,
+ wchar_t **results,
+ int *have_setting ) ) {
+ wchar_t *wkey = string;
+ wchar_t *wend = string;
+ wchar_t *wvalue = NULL;
+ size_t key_len;
+ size_t value_len;
+ void *temp;
+ char *key;
+ char *value;
+ int rc;
+
+ /* Locate key, value (if any), and end */
+ while ( *wend ) {
+ if ( *wend == L'&' )
+ break;
+ if ( *(wend++) == L'=' )
+ wvalue = wend;
+ }
+
+ /* Allocate memory for key and value */
+ key_len = ( ( wvalue ? ( wvalue - 1 ) : wend ) - wkey );
+ value_len = ( wvalue ? ( wend - wvalue ) : 0 );
+ temp = zalloc ( key_len + 1 /* NUL */ + value_len + 1 /* NUL */ );
+ if ( ! temp )
+ return -ENOMEM;
+ key = temp;
+ value = ( temp + key_len + 1 /* NUL */ );
+
+ /* Copy key and value */
+ while ( key_len-- )
+ key[key_len] = wkey[key_len];
+ while ( value_len-- )
+ value[value_len] = wvalue[value_len];
+
+ /* Process key and value */
+ if ( ( rc = process ( snpdev, key, value, results,
+ have_setting ) ) != 0 ) {
+ goto err;
+ }
+
+ /* Update progress marker */
+ *progress = wend;
+
+ err:
+ /* Free temporary storage */
+ free ( temp );
+
+ return rc;
+}
+
+/**
+ * Fetch configuration
+ *
+ * @v hii HII configuration access protocol
+ * @v request Configuration to fetch
+ * @ret progress Progress made through configuration to fetch
+ * @ret results Query results
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_hii_extract_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
+ EFI_STRING request, EFI_STRING *progress,
+ EFI_STRING *results ) {
+ struct efi_snp_device *snpdev =
+ container_of ( hii, struct efi_snp_device, hii );
+ int have_setting = 0;
+ wchar_t *pos;
+ int rc;
+
+ DBGC ( snpdev, "SNPDEV %p ExtractConfig request \"%ls\"\n",
+ snpdev, request );
+
+ /* Initialise results */
+ *results = NULL;
+
+ /* Process all request fragments */
+ for ( pos = *progress = request ; *progress && **progress ;
+ pos = *progress + 1 ) {
+ if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
+ results, &have_setting,
+ efi_snp_hii_fetch ) ) != 0 ) {
+ return EFIRC ( rc );
+ }
+ }
+
+ /* If we have no explicit request, return all settings */
+ if ( ! have_setting ) {
+ struct setting *setting;
+
+ for_each_table_entry ( setting, SETTINGS ) {
+ if ( ! efi_snp_hii_setting_applies ( snpdev, setting ) )
+ continue;
+ if ( ( rc = efi_snp_hii_fetch ( snpdev, setting->name,
+ NULL, results,
+ NULL ) ) != 0 ) {
+ return EFIRC ( rc );
+ }
+ }
+ }
+
+ DBGC ( snpdev, "SNPDEV %p ExtractConfig results \"%ls\"\n",
+ snpdev, *results );
+ return 0;
+}
+
+/**
+ * Store configuration
+ *
+ * @v hii HII configuration access protocol
+ * @v config Configuration to store
+ * @ret progress Progress made through configuration to store
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_hii_route_config ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
+ EFI_STRING config, EFI_STRING *progress ) {
+ struct efi_snp_device *snpdev =
+ container_of ( hii, struct efi_snp_device, hii );
+ wchar_t *pos;
+ int rc;
+
+ DBGC ( snpdev, "SNPDEV %p RouteConfig \"%ls\"\n", snpdev, config );
+
+ /* Process all request fragments */
+ for ( pos = *progress = config ; *progress && **progress ;
+ pos = *progress + 1 ) {
+ if ( ( rc = efi_snp_hii_process ( snpdev, pos, progress,
+ NULL, NULL,
+ efi_snp_hii_store ) ) != 0 ) {
+ return EFIRC ( rc );
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Handle form actions
+ *
+ * @v hii HII configuration access protocol
+ * @v action Form browser action
+ * @v question_id Question ID
+ * @v type Type of value
+ * @v value Value
+ * @ret action_request Action requested by driver
+ * @ret efirc EFI status code
+ */
+static EFI_STATUS EFIAPI
+efi_snp_hii_callback ( const EFI_HII_CONFIG_ACCESS_PROTOCOL *hii,
+ EFI_BROWSER_ACTION action __unused,
+ EFI_QUESTION_ID question_id __unused,
+ UINT8 type __unused, EFI_IFR_TYPE_VALUE *value __unused,
+ EFI_BROWSER_ACTION_REQUEST *action_request __unused ) {
+ struct efi_snp_device *snpdev =
+ container_of ( hii, struct efi_snp_device, hii );
+
+ DBGC ( snpdev, "SNPDEV %p Callback\n", snpdev );
+ return EFI_UNSUPPORTED;
+}
+
+/** HII configuration access protocol */
+static EFI_HII_CONFIG_ACCESS_PROTOCOL efi_snp_device_hii = {
+ .ExtractConfig = efi_snp_hii_extract_config,
+ .RouteConfig = efi_snp_hii_route_config,
+ .Callback = efi_snp_hii_callback,
+};
+
+/**
+ * Install HII protocol and packages for SNP device
+ *
+ * @v snpdev SNP device
+ * @ret rc Return status code
+ */
+int efi_snp_hii_install ( struct efi_snp_device *snpdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ int efirc;
+ int rc;
+
+ /* Do nothing if HII database protocol is not supported */
+ if ( ! efihii ) {
+ rc = -ENOTSUP;
+ goto err_no_hii;
+ }
+
+ /* Initialise HII protocol */
+ memcpy ( &snpdev->hii, &efi_snp_device_hii, sizeof ( snpdev->hii ) );
+
+ /* Create HII package list */
+ snpdev->package_list = efi_snp_hii_package_list ( snpdev );
+ if ( ! snpdev->package_list ) {
+ DBGC ( snpdev, "SNPDEV %p could not create HII package list\n",
+ snpdev );
+ rc = -ENOMEM;
+ goto err_build_package_list;
+ }
+
+ /* Add HII packages */
+ if ( ( efirc = efihii->NewPackageList ( efihii, snpdev->package_list,
+ snpdev->handle,
+ &snpdev->hii_handle ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( snpdev, "SNPDEV %p could not add HII packages: %s\n",
+ snpdev, strerror ( rc ) );
+ goto err_new_package_list;
+ }
+
+ /* Install HII protocol */
+ if ( ( efirc = bs->InstallMultipleProtocolInterfaces (
+ &snpdev->handle,
+ &efi_hii_config_access_protocol_guid, &snpdev->hii,
+ NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( snpdev, "SNPDEV %p could not install HII protocol: %s\n",
+ snpdev, strerror ( rc ) );
+ goto err_install_protocol;
+ }
+
+ return 0;
+
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_hii_config_access_protocol_guid, &snpdev->hii,
+ NULL );
+ err_install_protocol:
+ efihii->RemovePackageList ( efihii, snpdev->hii_handle );
+ err_new_package_list:
+ free ( snpdev->package_list );
+ snpdev->package_list = NULL;
+ err_build_package_list:
+ err_no_hii:
+ return rc;
+}
+
+/**
+ * Uninstall HII protocol and package for SNP device
+ *
+ * @v snpdev SNP device
+ */
+void efi_snp_hii_uninstall ( struct efi_snp_device *snpdev ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ /* Do nothing if HII database protocol is not supported */
+ if ( ! efihii )
+ return;
+
+ /* Uninstall protocols and remove package list */
+ bs->UninstallMultipleProtocolInterfaces (
+ snpdev->handle,
+ &efi_hii_config_access_protocol_guid, &snpdev->hii,
+ NULL );
+ efihii->RemovePackageList ( efihii, snpdev->hii_handle );
+ free ( snpdev->package_list );
+ snpdev->package_list = NULL;
+}
diff --git a/roms/ipxe/src/interface/efi/efi_strings.c b/roms/ipxe/src/interface/efi/efi_strings.c
new file mode 100644
index 00000000..751589b4
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_strings.c
@@ -0,0 +1,148 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <ipxe/vsprintf.h>
+#include <ipxe/efi/efi_strings.h>
+
+/** Context used by efi_vsnprintf() and friends */
+struct efi_sputc_context {
+ /** printf context */
+ struct printf_context ctx;
+ /** Buffer for formatted string (used by efi_printf_sputc()) */
+ wchar_t *buf;
+ /** Buffer length (used by efi_printf_sputc())
+ *
+ * Note that this is a number of wide characters, not a number
+ * of bytes.
+ */
+ size_t max_wlen;
+};
+
+/**
+ * Write wide character to buffer
+ *
+ * @v ctx Context
+ * @v c Character
+ */
+static void efi_printf_sputc ( struct printf_context *ctx, unsigned int c ) {
+ struct efi_sputc_context * sctx =
+ container_of ( ctx, struct efi_sputc_context, ctx );
+
+ if ( ctx->len < sctx->max_wlen )
+ sctx->buf[ctx->len] = c;
+}
+
+/**
+ * Write a formatted string to a wide-character buffer
+ *
+ * @v wbuf Buffer into which to write the string
+ * @v wsize Size of buffer (in wide characters)
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret wlen Length of formatted string (in wide characters)
+ *
+ * If the buffer is too small to contain the string, the returned
+ * length is the length that would have been written had enough space
+ * been available.
+ */
+int efi_vsnprintf ( wchar_t *wbuf, size_t wsize, const char *fmt,
+ va_list args ) {
+ struct efi_sputc_context sctx;
+ size_t wlen;
+ size_t wend;
+
+ /* Hand off to vcprintf */
+ sctx.ctx.handler = efi_printf_sputc;
+ sctx.buf = wbuf;
+ sctx.max_wlen = wsize;
+ wlen = vcprintf ( &sctx.ctx, fmt, args );
+
+ /* Add trailing NUL */
+ if ( wsize ) {
+ wend = wsize - 1;
+ if ( wlen < wend )
+ wend = wlen;
+ wbuf[wend] = '\0';
+ }
+
+ return wlen;
+}
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v wbuf Buffer into which to write the string
+ * @v wsize Size of buffer (in wide characters)
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret wlen Length of formatted string (in wide characters)
+ */
+int efi_snprintf ( wchar_t *wbuf, size_t wsize, const char *fmt, ... ) {
+ va_list args;
+ int i;
+
+ va_start ( args, fmt );
+ i = efi_vsnprintf ( wbuf, wsize, fmt, args );
+ va_end ( args );
+ return i;
+}
+
+/**
+ * Version of efi_vsnprintf() that accepts a signed buffer size
+ *
+ * @v wbuf Buffer into which to write the string
+ * @v swsize Size of buffer (in wide characters)
+ * @v fmt Format string
+ * @v args Arguments corresponding to the format string
+ * @ret wlen Length of formatted string (in wide characters)
+ */
+int efi_vssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt,
+ va_list args ) {
+
+ /* Treat negative buffer size as zero buffer size */
+ if ( swsize < 0 )
+ swsize = 0;
+
+ /* Hand off to vsnprintf */
+ return efi_vsnprintf ( wbuf, swsize, fmt, args );
+}
+
+/**
+ * Version of efi_vsnprintf() that accepts a signed buffer size
+ *
+ * @v wbuf Buffer into which to write the string
+ * @v swsize Size of buffer (in wide characters)
+ * @v fmt Format string
+ * @v ... Arguments corresponding to the format string
+ * @ret wlen Length of formatted string (in wide characters)
+ */
+int efi_ssnprintf ( wchar_t *wbuf, ssize_t swsize, const char *fmt, ... ) {
+ va_list args;
+ int len;
+
+ /* Hand off to vssnprintf */
+ va_start ( args, fmt );
+ len = efi_vssnprintf ( wbuf, swsize, fmt, args );
+ va_end ( args );
+ return len;
+}
diff --git a/roms/ipxe/src/interface/efi/efi_timer.c b/roms/ipxe/src/interface/efi/efi_timer.c
new file mode 100644
index 00000000..7a1ff786
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_timer.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+#include <unistd.h>
+#include <ipxe/timer.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/Cpu.h>
+
+/** @file
+ *
+ * iPXE timer API for EFI
+ *
+ */
+
+/** Scale factor to apply to CPU timer 0
+ *
+ * The timer is scaled down in order to ensure that reasonable values
+ * for "number of ticks" don't exceed the size of an unsigned long.
+ */
+#define EFI_TIMER0_SHIFT 12
+
+/** Calibration time */
+#define EFI_CALIBRATE_DELAY_MS 1
+
+/** CPU protocol */
+static EFI_CPU_ARCH_PROTOCOL *cpu_arch;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_ARCH_PROTOCOL, &cpu_arch );
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs Number of microseconds for which to delay
+ */
+static void efi_udelay ( unsigned long usecs ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not delay for %ldus: %s\n",
+ usecs, strerror ( rc ) );
+ /* Probably screwed */
+ }
+}
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks Current time, in ticks
+ */
+static unsigned long efi_currticks ( void ) {
+ UINT64 time;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Read CPU timer 0 (TSC) */
+ if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
+ NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not read CPU timer: %s\n", strerror ( rc ) );
+ /* Probably screwed */
+ return -1UL;
+ }
+
+ return ( time >> EFI_TIMER0_SHIFT );
+}
+
+/**
+ * Get number of ticks per second
+ *
+ * @ret ticks_per_sec Number of ticks per second
+ */
+static unsigned long efi_ticks_per_sec ( void ) {
+ static unsigned long ticks_per_sec = 0;
+
+ /* Calibrate timer, if necessary. EFI does nominally provide
+ * the timer speed via the (optional) TimerPeriod parameter to
+ * the GetTimerValue() call, but it gets the speed slightly
+ * wrong. By up to three orders of magnitude. Not helpful.
+ */
+ if ( ! ticks_per_sec ) {
+ unsigned long start;
+ unsigned long elapsed;
+
+ DBG ( "Calibrating EFI timer with a %d ms delay\n",
+ EFI_CALIBRATE_DELAY_MS );
+ start = currticks();
+ mdelay ( EFI_CALIBRATE_DELAY_MS );
+ elapsed = ( currticks() - start );
+ ticks_per_sec = ( elapsed * ( 1000 / EFI_CALIBRATE_DELAY_MS ));
+ DBG ( "EFI CPU timer calibrated at %ld ticks in %d ms (%ld "
+ "ticks/sec)\n", elapsed, EFI_CALIBRATE_DELAY_MS,
+ ticks_per_sec );
+ }
+
+ return ticks_per_sec;
+}
+
+PROVIDE_TIMER ( efi, udelay, efi_udelay );
+PROVIDE_TIMER ( efi, currticks, efi_currticks );
+PROVIDE_TIMER ( efi, ticks_per_sec, efi_ticks_per_sec );
diff --git a/roms/ipxe/src/interface/efi/efi_uaccess.c b/roms/ipxe/src/interface/efi/efi_uaccess.c
new file mode 100644
index 00000000..8b429b9e
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_uaccess.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <ipxe/uaccess.h>
+#include <ipxe/efi/efi.h>
+
+/** @file
+ *
+ * iPXE user access API for EFI
+ *
+ */
+
+PROVIDE_UACCESS_INLINE ( efi, phys_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_phys );
+PROVIDE_UACCESS_INLINE ( efi, virt_to_user );
+PROVIDE_UACCESS_INLINE ( efi, user_to_virt );
+PROVIDE_UACCESS_INLINE ( efi, userptr_add );
+PROVIDE_UACCESS_INLINE ( efi, memcpy_user );
+PROVIDE_UACCESS_INLINE ( efi, memmove_user );
+PROVIDE_UACCESS_INLINE ( efi, memset_user );
+PROVIDE_UACCESS_INLINE ( efi, strlen_user );
+PROVIDE_UACCESS_INLINE ( efi, memchr_user );
diff --git a/roms/ipxe/src/interface/efi/efi_umalloc.c b/roms/ipxe/src/interface/efi/efi_umalloc.c
new file mode 100644
index 00000000..356efaa6
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_umalloc.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/umalloc.h>
+#include <ipxe/efi/efi.h>
+
+/** @file
+ *
+ * iPXE user memory allocation API for EFI
+ *
+ */
+
+/** Equivalent of NOWHERE for user pointers */
+#define UNOWHERE ( ~UNULL )
+
+/**
+ * Reallocate external memory
+ *
+ * @v old_ptr Memory previously allocated by umalloc(), or UNULL
+ * @v new_size Requested size
+ * @ret new_ptr Allocated memory, or UNULL
+ *
+ * Calling realloc() with a new size of zero is a valid way to free a
+ * memory block.
+ */
+static userptr_t efi_urealloc ( userptr_t old_ptr, size_t new_size ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_PHYSICAL_ADDRESS phys_addr;
+ unsigned int new_pages, old_pages;
+ userptr_t new_ptr = UNOWHERE;
+ size_t old_size;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Allocate new memory if necessary. If allocation fails,
+ * return without touching the old block.
+ */
+ if ( new_size ) {
+ new_pages = ( EFI_SIZE_TO_PAGES ( new_size ) + 1 );
+ if ( ( efirc = bs->AllocatePages ( AllocateAnyPages,
+ EfiBootServicesData,
+ new_pages,
+ &phys_addr ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not allocate %d pages: %s\n",
+ new_pages, strerror ( rc ) );
+ return UNULL;
+ }
+ assert ( phys_addr != 0 );
+ new_ptr = phys_to_user ( phys_addr + EFI_PAGE_SIZE );
+ copy_to_user ( new_ptr, -EFI_PAGE_SIZE,
+ &new_size, sizeof ( new_size ) );
+ DBG ( "EFI allocated %d pages at %llx\n",
+ new_pages, phys_addr );
+ }
+
+ /* Copy across relevant part of the old data region (if any),
+ * then free it. Note that at this point either (a) new_ptr
+ * is valid, or (b) new_size is 0; either way, the memcpy() is
+ * valid.
+ */
+ if ( old_ptr && ( old_ptr != UNOWHERE ) ) {
+ copy_from_user ( &old_size, old_ptr, -EFI_PAGE_SIZE,
+ sizeof ( old_size ) );
+ memcpy_user ( new_ptr, 0, old_ptr, 0,
+ ( (old_size < new_size) ? old_size : new_size ));
+ old_pages = ( EFI_SIZE_TO_PAGES ( old_size ) + 1 );
+ phys_addr = user_to_phys ( old_ptr, -EFI_PAGE_SIZE );
+ if ( ( efirc = bs->FreePages ( phys_addr, old_pages ) ) != 0 ){
+ rc = -EEFI ( efirc );
+ DBG ( "EFI could not free %d pages at %llx: %s\n",
+ old_pages, phys_addr, strerror ( rc ) );
+ /* Not fatal; we have leaked memory but successfully
+ * allocated (if asked to do so).
+ */
+ }
+ DBG ( "EFI freed %d pages at %llx\n", old_pages, phys_addr );
+ }
+
+ return new_ptr;
+}
+
+PROVIDE_UMALLOC ( efi, urealloc, efi_urealloc );
diff --git a/roms/ipxe/src/interface/efi/efi_utils.c b/roms/ipxe/src/interface/efi/efi_utils.c
new file mode 100644
index 00000000..936ad48e
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_utils.c
@@ -0,0 +1,218 @@
+/*
+ * 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_pci.h>
+#include <ipxe/efi/efi_utils.h>
+
+/** @file
+ *
+ * EFI utilities
+ *
+ */
+
+/**
+ * Find end of device path
+ *
+ * @v path Path to device
+ * @ret path_end End of device path
+ */
+EFI_DEVICE_PATH_PROTOCOL * efi_devpath_end ( EFI_DEVICE_PATH_PROTOCOL *path ) {
+
+ while ( path->Type != END_DEVICE_PATH_TYPE ) {
+ path = ( ( ( void * ) path ) +
+ /* There's this amazing new-fangled thing known as
+ * a UINT16, but who wants to use one of those? */
+ ( ( path->Length[1] << 8 ) | path->Length[0] ) );
+ }
+
+ return path;
+}
+
+/**
+ * Locate parent device supporting a given protocol
+ *
+ * @v device EFI device handle
+ * @v protocol Protocol GUID
+ * @v parent Parent EFI device handle to fill in
+ * @ret rc Return status code
+ */
+int efi_locate_device ( EFI_HANDLE device, EFI_GUID *protocol,
+ EFI_HANDLE *parent ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_DEVICE_PATH_PROTOCOL *path;
+ void *interface;
+ } path;
+ EFI_DEVICE_PATH_PROTOCOL *devpath;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Get device path */
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_device_path_protocol_guid,
+ &path.interface,
+ efi_image_handle, device,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIDEV %p %s cannot open device path: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ goto err_open_device_path;
+ }
+ devpath = path.path;
+
+ /* Check for presence of specified protocol */
+ if ( ( efirc = bs->LocateDevicePath ( protocol, &devpath,
+ parent ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFIDEV %p %s has no parent supporting %s: %s\n",
+ device, efi_handle_name ( device ),
+ efi_guid_ntoa ( protocol ), strerror ( rc ) );
+ goto err_locate_protocol;
+ }
+
+ /* Success */
+ rc = 0;
+
+ err_locate_protocol:
+ bs->CloseProtocol ( device, &efi_device_path_protocol_guid,
+ efi_image_handle, device );
+ err_open_device_path:
+ return rc;
+}
+
+/**
+ * Add EFI device as child of another EFI device
+ *
+ * @v parent EFI parent device handle
+ * @v child EFI child device handle
+ * @ret rc Return status code
+ */
+int efi_child_add ( EFI_HANDLE parent, EFI_HANDLE child ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *devpath;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Re-open the device path protocol */
+ if ( ( efirc = bs->OpenProtocol ( parent,
+ &efi_device_path_protocol_guid,
+ &devpath,
+ efi_image_handle, child,
+ EFI_OPEN_PROTOCOL_BY_CHILD_CONTROLLER
+ ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( parent, "EFIDEV %p %s could not add child",
+ parent, efi_handle_name ( parent ) );
+ DBGC ( parent, " %p %s: %s\n", child,
+ efi_handle_name ( child ), strerror ( rc ) );
+ DBGC_EFI_OPENERS ( parent, parent,
+ &efi_device_path_protocol_guid );
+ return rc;
+ }
+
+ DBGC2 ( parent, "EFIDEV %p %s added child",
+ parent, efi_handle_name ( parent ) );
+ DBGC2 ( parent, " %p %s\n", child, efi_handle_name ( child ) );
+ return 0;
+}
+
+/**
+ * Remove EFI device as child of another EFI device
+ *
+ * @v parent EFI parent device handle
+ * @v child EFI child device handle
+ */
+void efi_child_del ( EFI_HANDLE parent, EFI_HANDLE child ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+ bs->CloseProtocol ( parent, &efi_device_path_protocol_guid,
+ efi_image_handle, child );
+ DBGC2 ( parent, "EFIDEV %p %s removed child",
+ parent, efi_handle_name ( parent ) );
+ DBGC2 ( parent, " %p %s\n",
+ child, efi_handle_name ( child ) );
+}
+
+/**
+ * Get underlying PCI device information
+ *
+ * @v device EFI device handle
+ * @v prefix Device name prefix
+ * @v dev Generic device to fill in
+ * @ret rc Return status code
+ */
+static int efi_pci_info ( EFI_HANDLE device, const char *prefix,
+ struct device *dev ) {
+ EFI_HANDLE pci_device;
+ struct pci_device pci;
+ int rc;
+
+ /* Find parent PCI device */
+ if ( ( rc = efi_locate_device ( device, &efi_pci_io_protocol_guid,
+ &pci_device ) ) != 0 ) {
+ DBGC ( device, "EFIDEV %p %s is not a PCI device: %s\n",
+ device, efi_handle_name ( device ), strerror ( rc ) );
+ return rc;
+ }
+
+ /* Get PCI device information */
+ if ( ( rc = efipci_info ( pci_device, &pci ) ) != 0 ) {
+ DBGC ( device, "EFIDEV %p %s could not get PCI information: "
+ "%s\n", device, efi_handle_name ( device ),
+ strerror ( rc ) );
+ return rc;
+ }
+
+ /* Populate device information */
+ memcpy ( &dev->desc, &pci.dev.desc, sizeof ( dev->desc ) );
+ snprintf ( dev->name, sizeof ( dev->name ), "%s-%s",
+ prefix, pci.dev.name );
+
+ return 0;
+}
+
+/**
+ * Get underlying device information
+ *
+ * @v device EFI device handle
+ * @v prefix Device name prefix
+ * @v dev Generic device to fill in
+ */
+void efi_device_info ( EFI_HANDLE device, const char *prefix,
+ struct device *dev ) {
+ int rc;
+
+ /* Try getting underlying PCI device information */
+ if ( ( rc = efi_pci_info ( device, prefix, dev ) ) == 0 )
+ return;
+
+ /* If we cannot get any underlying device information, fall
+ * back to providing information about the EFI handle.
+ */
+ DBGC ( device, "EFIDEV %p %s could not get underlying device "
+ "information\n", device, efi_handle_name ( device ) );
+ dev->desc.bus_type = BUS_TYPE_EFI;
+ snprintf ( dev->name, sizeof ( dev->name ), "%s-%p", prefix, device );
+}
diff --git a/roms/ipxe/src/interface/efi/efi_wrap.c b/roms/ipxe/src/interface/efi/efi_wrap.c
new file mode 100644
index 00000000..ff46b76e
--- /dev/null
+++ b/roms/ipxe/src/interface/efi/efi_wrap.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+/**
+ * @file
+ *
+ * EFI image wrapping
+ *
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/Protocol/LoadedImage.h>
+#include <ipxe/efi/efi_wrap.h>
+
+/** EFI system table wrapper */
+static EFI_SYSTEM_TABLE efi_systab_wrapper;
+
+/** EFI boot services table wrapper */
+static EFI_BOOT_SERVICES efi_bs_wrapper;
+
+/** Colour for debug messages */
+#define colour &efi_systab_wrapper
+
+/**
+ * Convert EFI status code to text
+ *
+ * @v efirc EFI status code
+ * @ret text EFI status code text
+ */
+static const char * efi_status ( EFI_STATUS efirc ) {
+ static char buf[ 19 /* "0xXXXXXXXXXXXXXXXX" + NUL */ ];
+
+ switch ( efirc ) {
+ case EFI_SUCCESS : return "0";
+ case EFI_LOAD_ERROR : return "LOAD_ERROR";
+ case EFI_INVALID_PARAMETER : return "INVALID_PARAMETER";
+ case EFI_UNSUPPORTED : return "UNSUPPORTED";
+ case EFI_BAD_BUFFER_SIZE : return "BAD_BUFFER_SIZE";
+ case EFI_BUFFER_TOO_SMALL : return "BUFFER_TOO_SMALL";
+ case EFI_NOT_READY : return "NOT_READY";
+ case EFI_DEVICE_ERROR : return "DEVICE_ERROR";
+ case EFI_WRITE_PROTECTED : return "WRITE_PROTECTED";
+ case EFI_OUT_OF_RESOURCES : return "OUT_OF_RESOURCES";
+ case EFI_VOLUME_CORRUPTED : return "VOLUME_CORRUPTED";
+ case EFI_VOLUME_FULL : return "VOLUME_FULL";
+ case EFI_NO_MEDIA : return "NO_MEDIA";
+ case EFI_MEDIA_CHANGED : return "MEDIA_CHANGED";
+ case EFI_NOT_FOUND : return "NOT_FOUND";
+ case EFI_ACCESS_DENIED : return "ACCESS_DENIED";
+ case EFI_NO_RESPONSE : return "NO_RESPONSE";
+ case EFI_NO_MAPPING : return "NO_MAPPING";
+ case EFI_TIMEOUT : return "TIMEOUT";
+ case EFI_NOT_STARTED : return "NOT_STARTED";
+ case EFI_ALREADY_STARTED : return "ALREADY_STARTED";
+ case EFI_ABORTED : return "ABORTED";
+ case EFI_ICMP_ERROR : return "ICMP_ERROR";
+ case EFI_TFTP_ERROR : return "TFTP_ERROR";
+ case EFI_PROTOCOL_ERROR : return "PROTOCOL_ERROR";
+ case EFI_INCOMPATIBLE_VERSION : return "INCOMPATIBLE_VERSION";
+ case EFI_SECURITY_VIOLATION : return "SECURITY_VIOLATION";
+ case EFI_CRC_ERROR : return "CRC_ERROR";
+ case EFI_END_OF_MEDIA : return "END_OF_MEDIA";
+ case EFI_END_OF_FILE : return "END_OF_FILE";
+ case EFI_INVALID_LANGUAGE : return "INVALID_LANGUAGE";
+ case EFI_COMPROMISED_DATA : return "COMPROMISED_DATA";
+ case EFI_WARN_UNKNOWN_GLYPH : return "WARN_UNKNOWN_GLYPH";
+ case EFI_WARN_DELETE_FAILURE : return "WARN_DELETE_FAILURE";
+ case EFI_WARN_WRITE_FAILURE : return "WARN_WRITE_FAILURE";
+ case EFI_WARN_BUFFER_TOO_SMALL : return "WARN_BUFFER_TOO_SMALL";
+ case EFI_WARN_STALE_DATA : return "WARN_STALE_DATA";
+ default:
+ snprintf ( buf, sizeof ( buf ), "%#lx",
+ ( unsigned long ) efirc );
+ return buf;
+ }
+}
+
+/**
+ * Wrap HandleProtocol()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_handle_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol,
+ VOID **interface ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "HandleProtocol ( %p %s, %s, ... ) ", handle,
+ efi_handle_name ( handle ), efi_guid_ntoa ( protocol ) );
+ efirc = bs->HandleProtocol ( handle, protocol, interface );
+ DBGC ( colour, "= %s ( %p ) -> %p\n",
+ efi_status ( efirc ), *interface, retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap LocateHandle()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_locate_handle_wrapper ( EFI_LOCATE_SEARCH_TYPE search_type,
+ EFI_GUID *protocol, VOID *search_key,
+ UINTN *buffer_size, EFI_HANDLE *buffer ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "LocateHandle ( %d, %s, ..., %zd, ... ) ", search_type,
+ efi_guid_ntoa ( protocol ), ( ( size_t ) *buffer_size ) );
+ efirc = bs->LocateHandle ( search_type, protocol, search_key,
+ buffer_size, buffer );
+ DBGC ( colour, "= %s ( %zd ) -> %p\n",
+ efi_status ( efirc ), ( ( size_t ) *buffer_size ), retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap LocateDevicePath()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_locate_device_path_wrapper ( EFI_GUID *protocol,
+ EFI_DEVICE_PATH_PROTOCOL **device_path,
+ EFI_HANDLE *device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "LocateDevicePath ( %s, %s, ... ) ",
+ efi_guid_ntoa ( protocol ), efi_devpath_text ( *device_path ) );
+ efirc = bs->LocateDevicePath ( protocol, device_path, device );
+ DBGC ( colour, "= %s ( %p, ",
+ efi_status ( efirc ), efi_devpath_text ( *device_path ) );
+ DBGC ( colour, "%p %s ) -> %p\n",
+ *device, efi_handle_name ( *device ), retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap LoadImage()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_load_image_wrapper ( BOOLEAN boot_policy, EFI_HANDLE parent_image_handle,
+ EFI_DEVICE_PATH_PROTOCOL *device_path,
+ VOID *source_buffer, UINTN source_size,
+ EFI_HANDLE *image_handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "LoadImage ( %d, %p %s, ", boot_policy,
+ parent_image_handle, efi_handle_name ( parent_image_handle ) );
+ DBGC ( colour, "%s, %p, %#llx, ... ) ",
+ efi_devpath_text ( device_path ), source_buffer,
+ ( ( unsigned long long ) source_size ) );
+ efirc = bs->LoadImage ( boot_policy, parent_image_handle, device_path,
+ source_buffer, source_size, image_handle );
+ DBGC ( colour, "= %s ( ", efi_status ( efirc ) );
+ if ( efirc == 0 ) {
+ DBGC ( colour, "%p %s ", *image_handle,
+ efi_handle_name ( *image_handle ) );
+ }
+ DBGC ( colour, ") -> %p\n", retaddr );
+
+ /* Wrap the new image */
+ if ( efirc == 0 )
+ efi_wrap ( *image_handle );
+
+ return efirc;
+}
+
+/**
+ * Wrap ExitBootServices()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "ExitBootServices ( %p %s, %#llx ) ",
+ image_handle, efi_handle_name ( image_handle ),
+ ( ( unsigned long long ) map_key ) );
+ efirc = bs->ExitBootServices ( image_handle, map_key );
+ DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap OpenProtocol()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_open_protocol_wrapper ( EFI_HANDLE handle, EFI_GUID *protocol,
+ VOID **interface, EFI_HANDLE agent_handle,
+ EFI_HANDLE controller_handle, UINT32 attributes ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "OpenProtocol ( %p %s, %s, ..., ", handle,
+ efi_handle_name ( handle ), efi_guid_ntoa ( protocol ) );
+ DBGC ( colour, "%p %s, ", agent_handle,
+ efi_handle_name ( agent_handle ) );
+ DBGC ( colour, "%p %s, %#x ) ", controller_handle,
+ efi_handle_name ( controller_handle ), attributes );
+ efirc = bs->OpenProtocol ( handle, protocol, interface, agent_handle,
+ controller_handle, attributes );
+ DBGC ( colour, "= %s ( %p ) -> %p\n",
+ efi_status ( efirc ), *interface, retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap LocateProtocol()
+ *
+ */
+static EFI_STATUS EFIAPI
+efi_locate_protocol_wrapper ( EFI_GUID *protocol, VOID *registration,
+ VOID **interface ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ void *retaddr = __builtin_return_address ( 0 );
+ EFI_STATUS efirc;
+
+ DBGC ( colour, "LocateProtocol ( %s, %p, ... ) ",
+ efi_guid_ntoa ( protocol ), registration );
+ efirc = bs->LocateProtocol ( protocol, registration, interface );
+ DBGC ( colour, "= %s ( %p ) -> %p\n",
+ efi_status ( efirc ), *interface, retaddr );
+ return efirc;
+}
+
+/**
+ * Wrap the calls made by a loaded image
+ *
+ * @v handle Image handle
+ */
+ void efi_wrap ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_LOADED_IMAGE_PROTOCOL *image;
+ void *intf;
+ } loaded;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Do nothing unless debugging is enabled */
+ if ( ! DBG_LOG )
+ return;
+
+ /* Populate table wrappers */
+ memcpy ( &efi_systab_wrapper, efi_systab,
+ sizeof ( efi_systab_wrapper ) );
+ memcpy ( &efi_bs_wrapper, bs, sizeof ( efi_bs_wrapper ) );
+ efi_systab_wrapper.BootServices = &efi_bs_wrapper;
+ efi_bs_wrapper.HandleProtocol = efi_handle_protocol_wrapper;
+ efi_bs_wrapper.LocateHandle = efi_locate_handle_wrapper;
+ efi_bs_wrapper.LocateDevicePath = efi_locate_device_path_wrapper;
+ efi_bs_wrapper.LoadImage = efi_load_image_wrapper;
+ efi_bs_wrapper.ExitBootServices = efi_exit_boot_services_wrapper;
+ efi_bs_wrapper.OpenProtocol = efi_open_protocol_wrapper;
+ efi_bs_wrapper.LocateProtocol = efi_locate_protocol_wrapper;
+
+ /* Open loaded image protocol */
+ if ( ( efirc = bs->OpenProtocol ( handle,
+ &efi_loaded_image_protocol_guid,
+ &loaded.intf, efi_image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( colour, "Could not get loaded image protocol for %p %s: "
+ "%s\n", handle, efi_handle_name ( handle ),
+ strerror ( rc ) );
+ return;
+ }
+
+ /* Provide system table wrapper to image */
+ loaded.image->SystemTable = &efi_systab_wrapper;
+ DBGC ( colour, "Wrapped image %p %s at base %p has protocols:\n",
+ handle, efi_handle_name ( handle ), loaded.image->ImageBase );
+ DBGC_EFI_PROTOCOLS ( colour, handle );
+ DBGC ( colour, "Parent image %p %s\n", loaded.image->ParentHandle,
+ efi_handle_name ( loaded.image->ParentHandle ) );
+ DBGC ( colour, "Device %p %s ", loaded.image->DeviceHandle,
+ efi_handle_name ( loaded.image->DeviceHandle ) );
+ DBGC ( colour, "file %p %s\n", loaded.image->FilePath,
+ efi_devpath_text ( loaded.image->FilePath ) );
+
+ /* Close loaded image protocol */
+ bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid,
+ efi_image_handle, NULL );
+}