mirror of
https://github.com/ipxe/ipxe
synced 2026-04-16 03:00:10 +03:00
[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:
@@ -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,
|
||||
};
|
||||
@@ -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 () );
|
||||
}
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
@@ -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 );
|
||||
Reference in New Issue
Block a user