summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
authorMichael Brown2008-10-12 02:55:55 +0200
committerMichael Brown2008-10-13 11:24:14 +0200
commit81d92c6d34f9ce68f7c2bbd5b92352b3a631bcd0 (patch)
tree7bb6912503c83076ef9cad54a0503abc7aa19907 /src/interface/efi
parent[sanboot] Quick and dirty hack to make SAN boot protocols selectable (diff)
downloadipxe-81d92c6d34f9ce68f7c2bbd5b92352b3a631bcd0.tar.gz
ipxe-81d92c6d34f9ce68f7c2bbd5b92352b3a631bcd0.tar.xz
ipxe-81d92c6d34f9ce68f7c2bbd5b92352b3a631bcd0.zip
[efi] Add EFI image format and basic runtime environment
We have EFI APIs for CPU I/O, PCI I/O, timers, console I/O, user access and user memory allocation. EFI executables are created using the vanilla GNU toolchain, with the EXE header handcrafted in assembly and relocations generated by a custom efilink utility.
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_console.c273
-rw-r--r--src/interface/efi/efi_entry.c83
-rw-r--r--src/interface/efi/efi_io.c201
-rw-r--r--src/interface/efi/efi_pci.c81
-rw-r--r--src/interface/efi/efi_timer.c115
-rw-r--r--src/interface/efi/efi_uaccess.c37
-rw-r--r--src/interface/efi/efi_umalloc.c96
7 files changed, 886 insertions, 0 deletions
diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c
new file mode 100644
index 00000000..b6e0dafd
--- /dev/null
+++ b/src/interface/efi/efi_console.c
@@ -0,0 +1,273 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stddef.h>
+#include <assert.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/ansiesc.h>
+#include <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
+
+/** Current character attribute */
+static unsigned int efi_attr = ATTR_DEFAULT;
+
+/**
+ * Handle ANSI CUP (cursor position)
+ *
+ * @v count Parameter count
+ * @v params[0] Row (1 is top)
+ * @v params[1] Column (1 is left)
+ */
+static void efi_handle_cup ( 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 count Parameter count
+ * @v params[0] Region to erase
+ */
+static void efi_handle_ed ( 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 count Parameter count
+ * @v params List of graphic rendition aspects
+ */
+static void efi_handle_sgr ( 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 );
+}
+
+/** 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 },
+ { 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;
+
+ /* 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 ) {
+ DBG ( "EFI could not read keystroke: %lx\n", efirc );
+ 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;
+}
+
+struct console_driver efi_console __console_driver = {
+ .putchar = efi_putchar,
+ .getchar = efi_getchar,
+ .iskey = efi_iskey,
+};
diff --git a/src/interface/efi/efi_entry.c b/src/interface/efi/efi_entry.c
new file mode 100644
index 00000000..b00828ad
--- /dev/null
+++ b/src/interface/efi/efi_entry.c
@@ -0,0 +1,83 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdlib.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/uuid.h>
+
+/** Image handle passed to entry point */
+EFI_HANDLE efi_image_handle;
+
+/** System table passed to entry point */
+EFI_SYSTEM_TABLE *efi_systab;
+
+/** Declared used EFI protocols */
+static struct efi_protocol efi_protocols[0] \
+ __table_start ( struct efi_protocol, efi_protocols );
+static struct efi_protocol efi_protocols_end[0] \
+ __table_end ( struct efi_protocol, efi_protocols );
+
+/**
+ * EFI entry point
+ *
+ * @v image_handle Image handle
+ * @v systab System table
+ * @ret efirc EFI return status code
+ */
+EFI_STATUS EFIAPI efi_entry ( EFI_HANDLE image_handle,
+ EFI_SYSTEM_TABLE *systab ) {
+ EFI_BOOT_SERVICES *bs;
+ struct efi_protocol *prot;
+ EFI_STATUS efirc;
+
+ /* Store image handle and system table pointer for future use */
+ efi_image_handle = image_handle;
+ efi_systab = systab;
+
+ /* Sanity checks */
+ if ( ! systab )
+ return EFI_NOT_AVAILABLE_YET;
+ if ( ! systab->ConOut )
+ return EFI_NOT_AVAILABLE_YET;
+ if ( ! systab->BootServices ) {
+ DBGC ( systab, "EFI provided no BootServices entry point\n" );
+ return EFI_NOT_AVAILABLE_YET;
+ }
+ if ( ! systab->RuntimeServices ) {
+ DBGC ( systab, "EFI provided no RuntimeServices entry "
+ "point\n" );
+ return EFI_NOT_AVAILABLE_YET;
+ }
+ DBGC ( systab, "EFI handle %p systab %p\n", image_handle, systab );
+
+ /* Look up required protocols */
+ bs = systab->BootServices;
+ for ( prot = efi_protocols ; prot < efi_protocols_end ; prot++ ) {
+ if ( ( efirc = bs->LocateProtocol ( &prot->u.guid, NULL,
+ prot->protocol ) ) != 0 ) {
+ DBGC ( systab, "EFI does not provide protocol %s\n",
+ uuid_ntoa ( &prot->u.uuid ) );
+ return efirc;
+ }
+ DBGC ( systab, "EFI protocol %s is at %p\n",
+ uuid_ntoa ( &prot->u.uuid ), *(prot->protocol) );
+ }
+
+ /* Call to main() */
+ return RC_TO_EFIRC ( main () );
+}
diff --git a/src/interface/efi/efi_io.c b/src/interface/efi/efi_io.c
new file mode 100644
index 00000000..1d4537ab
--- /dev/null
+++ b/src/interface/efi/efi_io.c
@@ -0,0 +1,201 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/io.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/CpuIo.h>
+#include <gpxe/efi/efi_io.h>
+
+/** @file
+ *
+ * gPXE I/O API for EFI
+ *
+ */
+
+/** CPU I/O protocol */
+static EFI_CPU_IO_PROTOCOL *cpu_io;
+EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io );
+
+/** Maximum address that can be used for port I/O */
+#define MAX_PORT_ADDRESS 0xffff
+
+/**
+ * Determine whether or not address is a port I/O address
+ *
+ * @v io_addr I/O address
+ * @v is_port I/O address is a port I/O address
+ */
+#define IS_PORT_ADDRESS(io_addr) \
+ ( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS )
+
+/**
+ * Determine EFI CPU I/O width code
+ *
+ * @v size Size of value
+ * @ret width EFI width code
+ *
+ * Someone at Intel clearly gets paid by the number of lines of code
+ * they write. No-one should ever be able to make I/O this
+ * convoluted. The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite
+ * idiocy.
+ */
+static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) {
+ switch ( size ) {
+ case 1 : return EfiCpuIoWidthFifoUint8;
+ case 2 : return EfiCpuIoWidthFifoUint16;
+ case 4 : return EfiCpuIoWidthFifoUint32;
+ case 8 : return EfiCpuIoWidthFifoUint64;
+ default :
+ assert ( 0 );
+ /* I wonder what this will actually do... */
+ return EfiCpuIoWidthMaximum;
+ }
+}
+
+/**
+ * Read from device
+ *
+ * @v io_addr I/O address
+ * @v size Size of value
+ * @ret data Value read
+ */
+unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM read;
+ unsigned long long data = 0;
+ EFI_STATUS efirc;
+
+ read = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Read : cpu_io->Mem.Read );
+
+ if ( ( efirc = read ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, 1,
+ ( void * ) &data ) ) != 0 ) {
+ DBG ( "EFI I/O read at %p failed: %lx\n", io_addr, efirc );
+ return -1ULL;
+ }
+
+ return data;
+}
+
+/**
+ * Write to device
+ *
+ * @v data Value to write
+ * @v io_addr I/O address
+ * @v size Size of value
+ */
+void efi_iowrite ( unsigned long long data, volatile void *io_addr,
+ size_t size ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM write;
+ EFI_STATUS efirc;
+
+ write = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Write : cpu_io->Mem.Write );
+
+ if ( ( efirc = write ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, 1,
+ ( void * ) &data ) ) != 0 ) {
+ DBG ( "EFI I/O write at %p failed: %lx\n", io_addr, efirc );
+ }
+}
+
+/**
+ * String read from device
+ *
+ * @v io_addr I/O address
+ * @v data Data buffer
+ * @v size Size of values
+ * @v count Number of values to read
+ */
+void efi_ioreads ( volatile void *io_addr, void *data,
+ size_t size, unsigned int count ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM read;
+ EFI_STATUS efirc;
+
+ read = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Read : cpu_io->Mem.Read );
+
+ if ( ( efirc = read ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, count,
+ ( void * ) data ) ) != 0 ) {
+ DBG ( "EFI I/O string read at %p failed: %lx\n",
+ io_addr, efirc );
+ }
+}
+
+/**
+ * String write to device
+ *
+ * @v io_addr I/O address
+ * @v data Data buffer
+ * @v size Size of values
+ * @v count Number of values to write
+ */
+void efi_iowrites ( volatile void *io_addr, const void *data,
+ size_t size, unsigned int count ) {
+ EFI_CPU_IO_PROTOCOL_IO_MEM write;
+ EFI_STATUS efirc;
+
+ write = ( IS_PORT_ADDRESS ( io_addr ) ?
+ cpu_io->Io.Write : cpu_io->Mem.Write );
+
+ if ( ( efirc = write ( cpu_io, efi_width ( size ),
+ ( intptr_t ) io_addr, count,
+ ( void * ) data ) ) != 0 ) {
+ DBG ( "EFI I/O write at %p failed: %lx\n",
+ io_addr, efirc );
+ }
+}
+
+/**
+ * Wait for I/O-mapped operation to complete
+ *
+ */
+static void efi_iodelay ( void ) {
+ /* Write to non-existent port. Probably x86-only. */
+ outb ( 0, 0x80 );
+}
+
+PROVIDE_IOAPI_INLINE ( efi, phys_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, bus_to_phys );
+PROVIDE_IOAPI_INLINE ( efi, ioremap );
+PROVIDE_IOAPI_INLINE ( efi, iounmap );
+PROVIDE_IOAPI_INLINE ( efi, io_to_bus );
+PROVIDE_IOAPI_INLINE ( efi, readb );
+PROVIDE_IOAPI_INLINE ( efi, readw );
+PROVIDE_IOAPI_INLINE ( efi, readl );
+PROVIDE_IOAPI_INLINE ( efi, readq );
+PROVIDE_IOAPI_INLINE ( efi, writeb );
+PROVIDE_IOAPI_INLINE ( efi, writew );
+PROVIDE_IOAPI_INLINE ( efi, writel );
+PROVIDE_IOAPI_INLINE ( efi, writeq );
+PROVIDE_IOAPI_INLINE ( efi, inb );
+PROVIDE_IOAPI_INLINE ( efi, inw );
+PROVIDE_IOAPI_INLINE ( efi, inl );
+PROVIDE_IOAPI_INLINE ( efi, outb );
+PROVIDE_IOAPI_INLINE ( efi, outw );
+PROVIDE_IOAPI_INLINE ( efi, outl );
+PROVIDE_IOAPI_INLINE ( efi, insb );
+PROVIDE_IOAPI_INLINE ( efi, insw );
+PROVIDE_IOAPI_INLINE ( efi, insl );
+PROVIDE_IOAPI_INLINE ( efi, outsb );
+PROVIDE_IOAPI_INLINE ( efi, outsw );
+PROVIDE_IOAPI_INLINE ( efi, outsl );
+PROVIDE_IOAPI ( efi, iodelay, efi_iodelay );
+PROVIDE_IOAPI_INLINE ( efi, mb );
diff --git a/src/interface/efi/efi_pci.c b/src/interface/efi/efi_pci.c
new file mode 100644
index 00000000..93408165
--- /dev/null
+++ b/src/interface/efi/efi_pci.c
@@ -0,0 +1,81 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <errno.h>
+#include <gpxe/pci.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/PciRootBridgeIo.h>
+
+/** @file
+ *
+ * gPXE PCI I/O API for EFI
+ *
+ */
+
+/** PCI root bridge I/O protocol */
+static EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *efipci;
+EFI_REQUIRE_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_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ),
+ EFIPCI_OFFSET ( location ) );
+}
+
+int efipci_read ( struct pci_device *pci, unsigned long location,
+ void *value ) {
+ EFI_STATUS efirc;
+
+ if ( ( efirc = efipci->Pci.Read ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ value ) ) != 0 ) {
+ DBG ( "EFIPCI config read from %02x:%02x.%x offset %02lx "
+ "failed: %lx\n", pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+ efirc );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int efipci_write ( struct pci_device *pci, unsigned long location,
+ unsigned long value ) {
+ EFI_STATUS efirc;
+
+ if ( ( efirc = efipci->Pci.Write ( efipci, EFIPCI_WIDTH ( location ),
+ efipci_address ( pci, location ), 1,
+ &value ) ) != 0 ) {
+ DBG ( "EFIPCI config write to %02x:%02x.%x offset %02lx "
+ "failed: %lx\n", pci->bus, PCI_SLOT ( pci->devfn ),
+ PCI_FUNC ( pci->devfn ), EFIPCI_OFFSET ( location ),
+ efirc );
+ return -EIO;
+ }
+
+ return 0;
+}
+
+PROVIDE_PCIAPI_INLINE ( efi, pci_max_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 );
diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c
new file mode 100644
index 00000000..b4e54a65
--- /dev/null
+++ b/src/interface/efi/efi_timer.c
@@ -0,0 +1,115 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <limits.h>
+#include <assert.h>
+#include <unistd.h>
+#include <gpxe/timer.h>
+#include <gpxe/efi/efi.h>
+#include <gpxe/efi/Protocol/Cpu.h>
+
+/** @file
+ *
+ * gPXE 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;
+
+ if ( ( efirc = bs->Stall ( usecs ) ) != 0 ) {
+ DBG ( "EFI could not delay for %ldus: %lx\n",
+ usecs, efirc );
+ /* 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;
+
+ /* Read CPU timer 0 (TSC) */
+ if ( ( efirc = cpu_arch->GetTimerValue ( cpu_arch, 0, &time,
+ NULL ) ) != 0 ) {
+ DBG ( "EFI could not read CPU timer: %lx\n", efirc );
+ /* 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/src/interface/efi/efi_uaccess.c b/src/interface/efi/efi_uaccess.c
new file mode 100644
index 00000000..1c54c031
--- /dev/null
+++ b/src/interface/efi/efi_uaccess.c
@@ -0,0 +1,37 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gpxe/uaccess.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE 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/src/interface/efi/efi_umalloc.c b/src/interface/efi/efi_umalloc.c
new file mode 100644
index 00000000..d7357a1d
--- /dev/null
+++ b/src/interface/efi/efi_umalloc.c
@@ -0,0 +1,96 @@
+/*
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <assert.h>
+#include <gpxe/umalloc.h>
+#include <gpxe/efi/efi.h>
+
+/** @file
+ *
+ * gPXE 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;
+
+ /* 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 ) {
+ DBG ( "EFI could not allocate %d pages: %lx\n",
+ new_pages, efirc );
+ 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 ){
+ DBG ( "EFI could not free %d pages at %llx: %lx\n",
+ old_pages, phys_addr, efirc );
+ /* 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 );