[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.
This commit is contained in:
Michael Brown
2008-10-12 01:55:55 +01:00
parent 54c024e0af
commit 81d92c6d34
40 changed files with 4781 additions and 1 deletions
+273
View File
@@ -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,
};
+83
View File
@@ -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 () );
}
+201
View File
@@ -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 );
+81
View File
@@ -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 );
+115
View File
@@ -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 );
+37
View File
@@ -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 );
+96
View File
@@ -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 );