summaryrefslogtreecommitdiffstats
path: root/src/interface/efi
diff options
context:
space:
mode:
Diffstat (limited to 'src/interface/efi')
-rw-r--r--src/interface/efi/efi_autoexec.c238
-rw-r--r--src/interface/efi/efi_console.c63
-rw-r--r--src/interface/efi/efi_entropy.c4
-rw-r--r--src/interface/efi/efi_fbcon.c243
-rw-r--r--src/interface/efi/efi_init.c27
-rw-r--r--src/interface/efi/efi_timer.c2
-rw-r--r--src/interface/efi/efi_watchdog.c34
-rw-r--r--src/interface/efi/efi_wrap.c108
8 files changed, 564 insertions, 155 deletions
diff --git a/src/interface/efi/efi_autoexec.c b/src/interface/efi/efi_autoexec.c
index 88eb379bb..79d4a4caf 100644
--- a/src/interface/efi/efi_autoexec.c
+++ b/src/interface/efi/efi_autoexec.c
@@ -24,11 +24,14 @@
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
+#include <stdlib.h>
#include <errno.h>
#include <ipxe/image.h>
#include <ipxe/init.h>
+#include <ipxe/in.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_autoexec.h>
+#include <ipxe/efi/Protocol/PxeBaseCode.h>
#include <ipxe/efi/Protocol/SimpleFileSystem.h>
#include <ipxe/efi/Guid/FileInfo.h>
@@ -39,10 +42,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
*/
/** Autoexec script filename */
-#define AUTOEXEC_FILENAME L"autoexec.ipxe"
+static wchar_t efi_autoexec_wname[] = L"autoexec.ipxe";
/** Autoexec script image name */
-#define AUTOEXEC_NAME "autoexec.ipxe"
+static char efi_autoexec_name[] = "autoexec.ipxe";
/** Autoexec script (if any) */
static void *efi_autoexec;
@@ -51,21 +54,21 @@ static void *efi_autoexec;
static size_t efi_autoexec_len;
/**
- * Load autoexec script
+ * Load autoexec script from filesystem
*
* @v device Device handle
* @ret rc Return status code
*/
-int efi_autoexec_load ( EFI_HANDLE device ) {
+static int efi_autoexec_filesystem ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- static wchar_t name[] = AUTOEXEC_FILENAME;
union {
void *interface;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *fs;
} u;
struct {
EFI_FILE_INFO info;
- CHAR16 name[ sizeof ( name ) / sizeof ( name[0] ) ];
+ CHAR16 name[ sizeof ( efi_autoexec_wname ) /
+ sizeof ( efi_autoexec_wname[0] ) ];
} info;
EFI_FILE_PROTOCOL *root;
EFI_FILE_PROTOCOL *file;
@@ -74,10 +77,6 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
EFI_STATUS efirc;
int rc;
- /* Sanity check */
- assert ( efi_autoexec == NULL );
- assert ( efi_autoexec_len == 0 );
-
/* Open simple file system protocol */
if ( ( efirc = bs->OpenProtocol ( device,
&efi_simple_file_system_protocol_guid,
@@ -99,11 +98,12 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
}
/* Open autoexec script */
- if ( ( efirc = root->Open ( root, &file, name,
+ if ( ( efirc = root->Open ( root, &file, efi_autoexec_wname,
EFI_FILE_MODE_READ, 0 ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s has no %ls: %s\n",
- efi_handle_name ( device ), name, strerror ( rc ) );
+ efi_handle_name ( device ), efi_autoexec_wname,
+ strerror ( rc ) );
goto err_open;
}
@@ -113,7 +113,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
&info ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not get %ls info: %s\n",
- efi_handle_name ( device ), name, strerror ( rc ) );
+ efi_handle_name ( device ), efi_autoexec_wname,
+ strerror ( rc ) );
goto err_getinfo;
}
size = info.info.FileSize;
@@ -122,7 +123,7 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
if ( ! size ) {
rc = -EINVAL;
DBGC ( device, "EFI %s has zero-length %ls\n",
- efi_handle_name ( device ), name );
+ efi_handle_name ( device ), efi_autoexec_wname );
goto err_empty;
}
@@ -131,7 +132,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
&data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not allocate %ls: %s\n",
- efi_handle_name ( device ), name, strerror ( rc ) );
+ efi_handle_name ( device ), efi_autoexec_wname,
+ strerror ( rc ) );
goto err_alloc;
}
@@ -139,7 +141,8 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
if ( ( efirc = file->Read ( file, &size, data ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( device, "EFI %s could not read %ls: %s\n",
- efi_handle_name ( device ), name, strerror ( rc ) );
+ efi_handle_name ( device ), efi_autoexec_wname,
+ strerror ( rc ) );
goto err_read;
}
@@ -148,7 +151,7 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
efi_autoexec_len = size;
data = NULL;
DBGC ( device, "EFI %s found %ls\n",
- efi_handle_name ( device ), name );
+ efi_handle_name ( device ), efi_autoexec_wname );
/* Success */
rc = 0;
@@ -170,13 +173,205 @@ int efi_autoexec_load ( EFI_HANDLE device ) {
}
/**
+ * Load autoexec script from TFTP server
+ *
+ * @v device Device handle
+ * @ret rc Return status code
+ */
+static int efi_autoexec_tftp ( EFI_HANDLE device ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ void *interface;
+ EFI_PXE_BASE_CODE_PROTOCOL *pxe;
+ } u;
+ EFI_PXE_BASE_CODE_MODE *mode;
+ EFI_PXE_BASE_CODE_PACKET *packet;
+ union {
+ struct in_addr in;
+ EFI_IP_ADDRESS ip;
+ } server;
+ size_t filename_max;
+ char *filename;
+ char *sep;
+ UINT64 size;
+ VOID *data;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Open PXE base code protocol */
+ if ( ( efirc = bs->OpenProtocol ( device,
+ &efi_pxe_base_code_protocol_guid,
+ &u.interface, efi_image_handle,
+ device,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL ))!=0){
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFI %s has no PXE base code instance: %s\n",
+ efi_handle_name ( device ), strerror ( rc ) );
+ goto err_pxe;
+ }
+
+ /* Do not attempt to parse DHCPv6 packets */
+ mode = u.pxe->Mode;
+ if ( mode->UsingIpv6 ) {
+ rc = -ENOTSUP;
+ DBGC ( device, "EFI %s has IPv6 PXE base code\n",
+ efi_handle_name ( device ) );
+ goto err_ipv6;
+ }
+
+ /* Identify relevant reply packet */
+ if ( mode->PxeReplyReceived &&
+ mode->PxeReply.Dhcpv4.BootpBootFile[0] ) {
+ /* Use boot filename if present in PXE reply */
+ DBGC ( device, "EFI %s using PXE reply filename\n",
+ efi_handle_name ( device ) );
+ packet = &mode->PxeReply;
+ } else if ( mode->DhcpAckReceived &&
+ mode->DhcpAck.Dhcpv4.BootpBootFile[0] ) {
+ /* Otherwise, use boot filename if present in DHCPACK */
+ DBGC ( device, "EFI %s using DHCPACK filename\n",
+ efi_handle_name ( device ) );
+ packet = &mode->DhcpAck;
+ } else if ( mode->ProxyOfferReceived &&
+ mode->ProxyOffer.Dhcpv4.BootpBootFile[0] ) {
+ /* Otherwise, use boot filename if present in ProxyDHCPOFFER */
+ DBGC ( device, "EFI %s using ProxyDHCPOFFER filename\n",
+ efi_handle_name ( device ) );
+ packet = &mode->ProxyOffer;
+ } else {
+ /* No boot filename available */
+ rc = -ENOENT;
+ DBGC ( device, "EFI %s has no PXE boot filename\n",
+ efi_handle_name ( device ) );
+ goto err_packet;
+ }
+
+ /* Allocate filename */
+ filename_max = ( sizeof ( packet->Dhcpv4.BootpBootFile )
+ + ( sizeof ( efi_autoexec_name ) - 1 /* NUL */ )
+ + 1 /* NUL */ );
+ filename = zalloc ( filename_max );
+ if ( ! filename ) {
+ rc = -ENOMEM;
+ goto err_filename;
+ }
+
+ /* Extract next-server address and boot filename */
+ memset ( &server, 0, sizeof ( server ) );
+ memcpy ( &server.in, packet->Dhcpv4.BootpSiAddr,
+ sizeof ( server.in ) );
+ memcpy ( filename, packet->Dhcpv4.BootpBootFile,
+ sizeof ( packet->Dhcpv4.BootpBootFile ) );
+
+ /* Update filename to autoexec script name */
+ sep = strrchr ( filename, '/' );
+ if ( ! sep )
+ sep = strrchr ( filename, '\\' );
+ if ( ! sep )
+ sep = ( filename - 1 );
+ strcpy ( ( sep + 1 ), efi_autoexec_name );
+
+ /* Get file size */
+ if ( ( efirc = u.pxe->Mtftp ( u.pxe,
+ EFI_PXE_BASE_CODE_TFTP_GET_FILE_SIZE,
+ NULL, FALSE, &size, NULL, &server.ip,
+ ( ( UINT8 * ) filename ), NULL,
+ FALSE ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFI %s could not get size of %s:%s: %s\n",
+ efi_handle_name ( device ), inet_ntoa ( server.in ),
+ filename, strerror ( rc ) );
+ goto err_size;
+ }
+
+ /* Ignore zero-length files */
+ if ( ! size ) {
+ rc = -EINVAL;
+ DBGC ( device, "EFI %s has zero-length %s:%s\n",
+ efi_handle_name ( device ), inet_ntoa ( server.in ),
+ filename );
+ goto err_empty;
+ }
+
+ /* Allocate temporary copy */
+ if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, size,
+ &data ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFI %s could not allocate %s:%s: %s\n",
+ efi_handle_name ( device ), inet_ntoa ( server.in ),
+ filename, strerror ( rc ) );
+ goto err_alloc;
+ }
+
+ /* Download file */
+ if ( ( efirc = u.pxe->Mtftp ( u.pxe, EFI_PXE_BASE_CODE_TFTP_READ_FILE,
+ data, FALSE, &size, NULL, &server.ip,
+ ( ( UINT8 * ) filename ), NULL,
+ FALSE ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( device, "EFI %s could not download %s:%s: %s\n",
+ efi_handle_name ( device ), inet_ntoa ( server.in ),
+ filename, strerror ( rc ) );
+ goto err_download;
+ }
+
+ /* Record autoexec script */
+ efi_autoexec = data;
+ efi_autoexec_len = size;
+ data = NULL;
+ DBGC ( device, "EFI %s found %s:%s\n", efi_handle_name ( device ),
+ inet_ntoa ( server.in ), filename );
+
+ /* Success */
+ rc = 0;
+
+ err_download:
+ if ( data )
+ bs->FreePool ( data );
+ err_alloc:
+ err_empty:
+ err_size:
+ free ( filename );
+ err_filename:
+ err_packet:
+ err_ipv6:
+ bs->CloseProtocol ( device, &efi_pxe_base_code_protocol_guid,
+ efi_image_handle, device );
+ err_pxe:
+ return rc;
+}
+
+/**
+ * Load autoexec script
+ *
+ * @v device Device handle
+ * @ret rc Return status code
+ */
+int efi_autoexec_load ( EFI_HANDLE device ) {
+ int rc;
+
+ /* Sanity check */
+ assert ( efi_autoexec == NULL );
+ assert ( efi_autoexec_len == 0 );
+
+ /* Try loading from file system, if supported */
+ if ( ( rc = efi_autoexec_filesystem ( device ) ) == 0 )
+ return 0;
+
+ /* Try loading via TFTP, if supported */
+ if ( ( rc = efi_autoexec_tftp ( device ) ) == 0 )
+ return 0;
+
+ return -ENOENT;
+}
+
+/**
* Register autoexec script
*
*/
static void efi_autoexec_startup ( void ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
- const char *name = AUTOEXEC_NAME;
struct image *image;
/* Do nothing if we have no autoexec script */
@@ -184,15 +379,16 @@ static void efi_autoexec_startup ( void ) {
return;
/* Create autoexec image */
- image = image_memory ( name, virt_to_user ( efi_autoexec ),
+ image = image_memory ( efi_autoexec_name,
+ virt_to_user ( efi_autoexec ),
efi_autoexec_len );
if ( ! image ) {
DBGC ( device, "EFI %s could not create %s\n",
- efi_handle_name ( device ), name );
+ efi_handle_name ( device ), efi_autoexec_name );
return;
}
DBGC ( device, "EFI %s registered %s\n",
- efi_handle_name ( device ), name );
+ efi_handle_name ( device ), efi_autoexec_name );
/* Free temporary copy */
bs->FreePool ( efi_autoexec );
diff --git a/src/interface/efi/efi_console.c b/src/interface/efi/efi_console.c
index 98ebbf3ac..04bbd9e0c 100644
--- a/src/interface/efi/efi_console.c
+++ b/src/interface/efi/efi_console.c
@@ -26,7 +26,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/efi.h>
#include <ipxe/efi/Protocol/ConsoleControl/ConsoleControl.h>
#include <ipxe/ansiesc.h>
+#include <ipxe/utf8.h>
#include <ipxe/console.h>
+#include <ipxe/keymap.h>
#include <ipxe/init.h>
#include <config/console.h>
@@ -54,8 +56,6 @@ FILE_LICENCE ( GPL2_OR_LATER );
#define ATTR_DEFAULT ATTR_FCOL_WHITE
-#define CTRL_MASK 0x1f
-
/* Set default console usage if applicable */
#if ! ( defined ( CONSOLE_EFI ) && CONSOLE_EXPLICIT ( CONSOLE_EFI ) )
#undef CONSOLE_EFI
@@ -202,6 +202,9 @@ static struct ansiesc_context efi_ansiesc_ctx = {
.handlers = efi_ansiesc_handlers,
};
+/** EFI console UTF-8 accumulator */
+static struct utf8_accumulator efi_utf8_acc;
+
/**
* Print a character to EFI console
*
@@ -209,13 +212,25 @@ static struct ansiesc_context efi_ansiesc_ctx = {
*/
static void efi_putchar ( int character ) {
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
- wchar_t wstr[] = { character, 0 };
+ wchar_t wstr[2];
/* Intercept ANSI escape sequences */
character = ansiesc_process ( &efi_ansiesc_ctx, character );
if ( character < 0 )
return;
+ /* Accumulate Unicode characters */
+ character = utf8_accumulate ( &efi_utf8_acc, character );
+ if ( character == 0 )
+ return;
+
+ /* Treat unrepresentable (non-UCS2) characters as invalid */
+ if ( character & ~( ( wchar_t ) -1UL ) )
+ character = UTF8_INVALID;
+
+ /* Output character */
+ wstr[0] = character;
+ wstr[1] = L'\0';
conout->OutputString ( conout, wstr );
}
@@ -285,6 +300,9 @@ static int efi_getchar ( void ) {
EFI_SIMPLE_TEXT_INPUT_PROTOCOL *conin = efi_systab->ConIn;
EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *conin_ex = efi_conin_ex;
const char *ansi_seq;
+ unsigned int character;
+ unsigned int shift;
+ unsigned int toggle;
EFI_KEY_DATA key;
EFI_STATUS efirc;
int rc;
@@ -317,16 +335,37 @@ static int efi_getchar ( void ) {
key.KeyState.KeyToggleState, key.Key.UnicodeChar,
key.Key.ScanCode );
- /* Translate Ctrl-<key> */
- if ( ( key.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID ) &&
- ( key.KeyState.KeyShiftState & ( EFI_LEFT_CONTROL_PRESSED |
- EFI_RIGHT_CONTROL_PRESSED ) ) ) {
- key.Key.UnicodeChar &= CTRL_MASK;
- }
+ /* If key has a Unicode representation, remap and return it.
+ * There is unfortunately no way to avoid remapping the
+ * numeric keypad, since EFI destroys the scan code
+ * information that would allow us to differentiate between
+ * main keyboard and numeric keypad.
+ */
+ if ( ( character = key.Key.UnicodeChar ) != 0 ) {
+
+ /* Apply shift state */
+ shift = key.KeyState.KeyShiftState;
+ if ( shift & EFI_SHIFT_STATE_VALID ) {
+ if ( shift & ( EFI_LEFT_CONTROL_PRESSED |
+ EFI_RIGHT_CONTROL_PRESSED ) ) {
+ character |= KEYMAP_CTRL;
+ }
+ if ( shift & EFI_RIGHT_ALT_PRESSED ) {
+ character |= KEYMAP_ALTGR;
+ }
+ }
- /* If key has a Unicode representation, return it */
- if ( key.Key.UnicodeChar )
- return key.Key.UnicodeChar;
+ /* Apply toggle state */
+ toggle = key.KeyState.KeyToggleState;
+ if ( toggle & EFI_TOGGLE_STATE_VALID ) {
+ if ( toggle & EFI_CAPS_LOCK_ACTIVE ) {
+ character |= KEYMAP_CAPSLOCK_REDO;
+ }
+ }
+
+ /* Remap and return key */
+ return key_remap ( character );
+ }
/* Otherwise, check for a special key that we know about */
if ( ( ansi_seq = scancode_to_ansi_seq ( key.Key.ScanCode ) ) ) {
diff --git a/src/interface/efi/efi_entropy.c b/src/interface/efi/efi_entropy.c
index b7cb6a40c..71341d9d4 100644
--- a/src/interface/efi/efi_entropy.c
+++ b/src/interface/efi/efi_entropy.c
@@ -109,8 +109,8 @@ static void efi_entropy_disable ( void ) {
/* Close timer tick event */
bs->CloseEvent ( tick );
- /* Return to TPL_CALLBACK */
- bs->RaiseTPL ( TPL_CALLBACK );
+ /* Return to internal TPL */
+ bs->RaiseTPL ( efi_internal_tpl );
}
/**
diff --git a/src/interface/efi/efi_fbcon.c b/src/interface/efi/efi_fbcon.c
index 1ea25df04..e3c4d001d 100644
--- a/src/interface/efi/efi_fbcon.c
+++ b/src/interface/efi/efi_fbcon.c
@@ -62,6 +62,12 @@ struct console_driver efi_console __attribute__ (( weak ));
#define CONSOLE_EFIFB ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG )
#endif
+/** Number of ASCII glyphs in cache */
+#define EFIFB_ASCII 128
+
+/** Number of dynamic non-ASCII glyphs in cache */
+#define EFIFB_DYNAMIC 32
+
/* Forward declaration */
struct console_driver efifb_console __console_driver;
@@ -84,22 +90,159 @@ struct efifb {
struct fbcon_colour_map map;
/** Font definition */
struct fbcon_font font;
- /** Character glyphs */
+ /** Character glyph cache */
userptr_t glyphs;
+ /** Dynamic characters in cache */
+ unsigned int dynamic[EFIFB_DYNAMIC];
+ /** Next dynamic character cache entry to evict */
+ unsigned int next;
};
/** The EFI frame buffer */
static struct efifb efifb;
/**
- * Get character glyph
+ * Draw character glyph
*
* @v character Character
+ * @v index Index within glyph cache
+ * @v toggle Bits to toggle in each bitmask
+ * @ret height Character height, or negative error
+ */
+static int efifb_draw ( unsigned int character, unsigned int index,
+ unsigned int toggle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_IMAGE_OUTPUT *blt;
+ EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
+ unsigned int height;
+ unsigned int x;
+ unsigned int y;
+ uint8_t bitmask;
+ size_t offset;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* Clear existing glyph */
+ offset = ( index * efifb.font.height );
+ memset_user ( efifb.glyphs, offset, 0, efifb.font.height );
+
+ /* Get glyph */
+ blt = NULL;
+ if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont, character,
+ NULL, &blt, NULL ) ) != 0 ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &efifb, "EFIFB could not get glyph %#02x: %s\n",
+ character, strerror ( rc ) );
+ goto err_get;
+ }
+ assert ( blt != NULL );
+
+ /* Sanity check */
+ if ( blt->Width > 8 ) {
+ DBGC ( &efifb, "EFIFB glyph %#02x invalid width %d\n",
+ character, blt->Width );
+ rc = -EINVAL;
+ goto err_width;
+ }
+
+ /* Convert glyph to bitmap */
+ pixel = blt->Image.Bitmap;
+ height = blt->Height;
+ for ( y = 0 ; ( ( y < height ) && ( y < efifb.font.height ) ) ; y++ ) {
+ bitmask = 0;
+ for ( x = 0 ; x < blt->Width ; x++ ) {
+ bitmask = rol8 ( bitmask, 1 );
+ if ( pixel->Blue || pixel->Green || pixel->Red )
+ bitmask |= 0x01;
+ pixel++;
+ }
+ bitmask ^= toggle;
+ copy_to_user ( efifb.glyphs, offset++, &bitmask,
+ sizeof ( bitmask ) );
+ }
+
+ /* Free glyph */
+ bs->FreePool ( blt );
+
+ return height;
+
+ err_width:
+ bs->FreePool ( blt );
+ err_get:
+ return rc;
+}
+
+/**
+ * Draw "unknown character" glyph
+ *
+ * @v index Index within glyph cache
+ * @ret rc Return status code
+ */
+static int efifb_draw_unknown ( unsigned int index ) {
+
+ /* Draw an inverted '?' glyph */
+ return efifb_draw ( '?', index, -1U );
+}
+
+/**
+ * Get dynamic glyph index
+ *
+ * @v character Unicode character
+ * @ret index Glyph cache index
+ */
+static unsigned int efifb_dynamic ( unsigned int character ) {
+ unsigned int dynamic;
+ unsigned int index;
+ unsigned int i;
+ int height;
+
+ /* Search existing cached entries */
+ for ( i = 0 ; i < EFIFB_DYNAMIC ; i++ ) {
+ if ( character == efifb.dynamic[i] )
+ return ( EFIFB_ASCII + i );
+ }
+
+ /* Overwrite the oldest cache entry */
+ dynamic = ( efifb.next++ % EFIFB_DYNAMIC );
+ index = ( EFIFB_ASCII + dynamic );
+ DBGC2 ( &efifb, "EFIFB dynamic %#02x is glyph %#02x\n",
+ dynamic, character );
+
+ /* Draw glyph */
+ height = efifb_draw ( character, index, 0 );
+ if ( height < 0 )
+ efifb_draw_unknown ( index );
+
+ /* Record cached character */
+ efifb.dynamic[dynamic] = character;
+
+ return index;
+}
+
+/**
+ * Get character glyph
+ *
+ * @v character Unicode character
* @v glyph Character glyph to fill in
*/
static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
- size_t offset = ( character * efifb.font.height );
+ unsigned int index;
+ size_t offset;
+
+ /* Identify glyph */
+ if ( character < EFIFB_ASCII ) {
+
+ /* ASCII character: use fixed cache entry */
+ index = character;
+
+ } else {
+
+ /* Non-ASCII character: use dynamic glyph cache */
+ index = efifb_dynamic ( character );
+ }
+ /* Copy cached glyph */
+ offset = ( index * efifb.font.height );
copy_from_user ( glyph, efifb.glyphs, offset, efifb.font.height );
}
@@ -109,16 +252,10 @@ static void efifb_glyph ( unsigned int character, uint8_t *glyph ) {
* @ret rc Return status code
*/
static int efifb_glyphs ( void ) {
- EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
- EFI_IMAGE_OUTPUT *blt;
- EFI_GRAPHICS_OUTPUT_BLT_PIXEL *pixel;
- size_t offset;
- size_t len;
- uint8_t bitmask;
unsigned int character;
- unsigned int x;
- unsigned int y;
- EFI_STATUS efirc;
+ int height;
+ int max;
+ size_t len;
int rc;
/* Get font height. The GetFontInfo() call nominally returns
@@ -128,38 +265,32 @@ static int efifb_glyphs ( void ) {
* height.
*/
efifb.font.height = 0;
- for ( character = 0 ; character < 256 ; character++ ) {
+ max = 0;
+ for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
/* Skip non-printable characters */
if ( ! isprint ( character ) )
continue;
/* Get glyph */
- blt = NULL;
- if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
- character, NULL, &blt,
- NULL ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
- character, strerror ( rc ) );
- continue;
+ height = efifb_draw ( character, 0, 0 );
+ if ( height < 0 ) {
+ rc = height;
+ goto err_height;
}
- assert ( blt != NULL );
/* Calculate maximum height */
- if ( efifb.font.height < blt->Height )
- efifb.font.height = blt->Height;
-
- /* Free glyph */
- bs->FreePool ( blt );
+ if ( max < height )
+ max = height;
}
- if ( ! efifb.font.height ) {
+ if ( ! max ) {
DBGC ( &efifb, "EFIFB could not get font height\n" );
return -ENOENT;
}
+ efifb.font.height = max;
/* Allocate glyph data */
- len = ( 256 * efifb.font.height * sizeof ( bitmask ) );
+ len = ( ( EFIFB_ASCII + EFIFB_DYNAMIC ) * efifb.font.height );
efifb.glyphs = umalloc ( len );
if ( ! efifb.glyphs ) {
rc = -ENOMEM;
@@ -168,60 +299,32 @@ static int efifb_glyphs ( void ) {
memset_user ( efifb.glyphs, 0, 0, len );
/* Get font data */
- for ( character = 0 ; character < 256 ; character++ ) {
+ for ( character = 0 ; character < EFIFB_ASCII ; character++ ) {
/* Skip non-printable characters */
- if ( ! isprint ( character ) )
- continue;
-
- /* Get glyph */
- blt = NULL;
- if ( ( efirc = efifb.hiifont->GetGlyph ( efifb.hiifont,
- character, NULL, &blt,
- NULL ) ) != 0 ) {
- rc = -EEFI ( efirc );
- DBGC ( &efifb, "EFIFB could not get glyph %d: %s\n",
- character, strerror ( rc ) );
- continue;
- }
- assert ( blt != NULL );
-
- /* Sanity check */
- if ( blt->Width > 8 ) {
- DBGC ( &efifb, "EFIFB glyph %d invalid width %d\n",
- character, blt->Width );
- continue;
- }
- if ( blt->Height > efifb.font.height ) {
- DBGC ( &efifb, "EFIFB glyph %d invalid height %d\n",
- character, blt->Height );
+ if ( ! isprint ( character ) ) {
+ efifb_draw_unknown ( character );
continue;
}
- /* Convert glyph to bitmap */
- pixel = blt->Image.Bitmap;
- offset = ( character * efifb.font.height );
- for ( y = 0 ; y < blt->Height ; y++ ) {
- bitmask = 0;
- for ( x = 0 ; x < blt->Width ; x++ ) {
- bitmask = rol8 ( bitmask, 1 );
- if ( pixel->Blue || pixel->Green || pixel->Red )
- bitmask |= 0x01;
- pixel++;
- }
- copy_to_user ( efifb.glyphs, offset++, &bitmask,
- sizeof ( bitmask ) );
+ /* Get glyph */
+ height = efifb_draw ( character, character, 0 );
+ if ( height < 0 ) {
+ rc = height;
+ goto err_draw;
}
-
- /* Free glyph */
- bs->FreePool ( blt );
}
+ /* Clear dynamic glyph character cache */
+ memset ( efifb.dynamic, 0, sizeof ( efifb.dynamic ) );
+
efifb.font.glyph = efifb_glyph;
return 0;
+ err_draw:
ufree ( efifb.glyphs );
err_alloc:
+ err_height:
return rc;
}
diff --git a/src/interface/efi/efi_init.c b/src/interface/efi/efi_init.c
index b7cac16e5..5d98f9ff7 100644
--- a/src/interface/efi/efi_init.c
+++ b/src/interface/efi/efi_init.c
@@ -47,6 +47,9 @@ EFI_DEVICE_PATH_PROTOCOL *efi_loaded_image_path;
*/
EFI_SYSTEM_TABLE * _C2 ( PLATFORM, _systab );
+/** Internal task priority level */
+EFI_TPL efi_internal_tpl = TPL_CALLBACK;
+
/** External task priority level */
EFI_TPL efi_external_tpl = TPL_APPLICATION;
@@ -79,6 +82,17 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle );
static EFIAPI void efi_shutdown_hook ( EFI_EVENT event __unused,
void *context __unused ) {
+ /* This callback is invoked at TPL_NOTIFY in order to ensure
+ * that we have an opportunity to shut down cleanly before
+ * other shutdown hooks perform destructive operations such as
+ * disabling the IOMMU.
+ *
+ * Modify the internal task priority level so that no code
+ * attempts to raise from TPL_NOTIFY to TPL_CALLBACK (which
+ * would trigger a fatal exception).
+ */
+ efi_internal_tpl = TPL_NOTIFY;
+
/* Mark shutdown as being in progress, to indicate that large
* parts of the system (e.g. timers) are no longer functional.
*/
@@ -273,7 +287,7 @@ EFI_STATUS efi_init ( EFI_HANDLE image_handle,
* bother doing so when ExitBootServices() is called.
*/
if ( ( efirc = bs->CreateEvent ( EVT_SIGNAL_EXIT_BOOT_SERVICES,
- TPL_CALLBACK, efi_shutdown_hook,
+ TPL_NOTIFY, efi_shutdown_hook,
NULL, &efi_shutdown_event ) ) != 0 ) {
rc = -EEFI ( efirc );
DBGC ( systab, "EFI could not create ExitBootServices event: "
@@ -316,9 +330,13 @@ EFI_STATUS efi_init ( EFI_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;
+ struct efi_saved_tpl tpl;
DBGC ( systab, "EFI image unloading\n" );
+ /* Raise TPL */
+ efi_raise_tpl ( &tpl );
+
/* Shut down */
shutdown_exit();
@@ -336,6 +354,9 @@ static EFI_STATUS EFIAPI efi_unload ( EFI_HANDLE image_handle __unused ) {
DBGC ( systab, "EFI image unloaded\n" );
+ /* Restore TPL */
+ efi_restore_tpl ( &tpl );
+
return 0;
}
@@ -366,7 +387,7 @@ __attribute__ (( noreturn )) void __stack_chk_fail ( void ) {
}
/**
- * Raise task priority level to TPL_CALLBACK
+ * Raise task priority level to internal level
*
* @v tpl Saved TPL
*/
@@ -377,7 +398,7 @@ void efi_raise_tpl ( struct efi_saved_tpl *tpl ) {
tpl->previous = efi_external_tpl;
/* Raise TPL and record previous TPL as new external TPL */
- tpl->current = bs->RaiseTPL ( TPL_CALLBACK );
+ tpl->current = bs->RaiseTPL ( efi_internal_tpl );
efi_external_tpl = tpl->current;
}
diff --git a/src/interface/efi/efi_timer.c b/src/interface/efi/efi_timer.c
index 405cd3454..6427eb1d8 100644
--- a/src/interface/efi/efi_timer.c
+++ b/src/interface/efi/efi_timer.c
@@ -137,7 +137,7 @@ static unsigned long efi_currticks ( void ) {
efi_jiffies++;
} else {
bs->RestoreTPL ( efi_external_tpl );
- bs->RaiseTPL ( TPL_CALLBACK );
+ bs->RaiseTPL ( efi_internal_tpl );
}
return ( efi_jiffies * ( TICKS_PER_SEC / EFI_JIFFIES_PER_SEC ) );
diff --git a/src/interface/efi/efi_watchdog.c b/src/interface/efi/efi_watchdog.c
index 7061f81d8..dcc9a5668 100644
--- a/src/interface/efi/efi_watchdog.c
+++ b/src/interface/efi/efi_watchdog.c
@@ -34,6 +34,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <ipxe/retry.h>
#include <ipxe/timer.h>
+#include <ipxe/init.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_watchdog.h>
@@ -80,3 +81,36 @@ static void efi_watchdog_expired ( struct retry_timer *timer,
/** Watchdog holdoff timer */
struct retry_timer efi_watchdog = TIMER_INIT ( efi_watchdog_expired );
+
+/**
+ * Disable watching when shutting down to boot an operating system
+ *
+ * @v booting System is shutting down for OS boot
+ */
+static void efi_watchdog_shutdown ( int booting ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* If we are shutting down to boot an operating system, then
+ * disable the boot services watchdog timer. The UEFI
+ * specification mandates that the platform firmware does this
+ * as part of the ExitBootServices() call, but some platforms
+ * (e.g. Hyper-V) are observed to occasionally forget to do
+ * so, resulting in a reboot approximately five minutes after
+ * starting the operating system.
+ */
+ if ( booting &&
+ ( ( efirc = bs->SetWatchdogTimer ( 0, 0, 0, NULL ) ) != 0 ) ) {
+ rc = -EEFI ( efirc );
+ DBGC ( &efi_watchdog, "EFI could not disable watchdog timer: "
+ "%s\n", strerror ( rc ) );
+ /* Nothing we can do */
+ }
+}
+
+/** Watchdog startup/shutdown function */
+struct startup_fn efi_watchdog_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+ .name = "efi_watchdog",
+ .shutdown = efi_watchdog_shutdown,
+};
diff --git a/src/interface/efi/efi_wrap.c b/src/interface/efi/efi_wrap.c
index 5c02a7ee1..5d5d2caee 100644
--- a/src/interface/efi/efi_wrap.c
+++ b/src/interface/efi/efi_wrap.c
@@ -196,6 +196,47 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) {
}
/**
+ * Dump information about a loaded image
+ *
+ * @v handle Image handle
+ */
+static void efi_dump_image ( EFI_HANDLE handle ) {
+ EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+ union {
+ EFI_LOADED_IMAGE_PROTOCOL *image;
+ void *intf;
+ } loaded;
+ EFI_STATUS efirc;
+ int rc;
+
+ /* 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, "WRAP %s could not get loaded image protocol: "
+ "%s\n", efi_handle_name ( handle ), strerror ( rc ) );
+ return;
+ }
+
+ /* Dump image information */
+ DBGC ( colour, "WRAP %s at base %p has protocols:\n",
+ efi_handle_name ( handle ), loaded.image->ImageBase );
+ DBGC_EFI_PROTOCOLS ( colour, handle );
+ DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) );
+ DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle ));
+ DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) );
+ DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle ));
+ DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) );
+ DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) );
+
+ /* Close loaded image protocol */
+ bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid,
+ efi_image_handle, NULL );
+}
+
+/**
* Wrap RaiseTPL()
*
*/
@@ -655,9 +696,9 @@ efi_load_image_wrapper ( BOOLEAN boot_policy, EFI_HANDLE parent_image_handle,
DBGC ( colour, "%s ", efi_handle_name ( *image_handle ) );
DBGC ( colour, ") -> %p\n", retaddr );
- /* Wrap the new image */
+ /* Dump information about loaded image */
if ( efirc == 0 )
- efi_wrap ( *image_handle );
+ efi_dump_image ( *image_handle );
return efirc;
}
@@ -735,11 +776,14 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) {
void *retaddr = __builtin_return_address ( 0 );
EFI_STATUS efirc;
- DBGC ( colour, "ExitBootServices ( %s, %#llx ) ",
+ DBGC ( colour, "ExitBootServices ( %s, %#llx ) -> %p\n",
efi_handle_name ( image_handle ),
- ( ( unsigned long long ) map_key ) );
+ ( ( unsigned long long ) map_key ), retaddr );
efirc = bs->ExitBootServices ( image_handle, map_key );
- DBGC ( colour, "= %s -> %p\n", efi_status ( efirc ), retaddr );
+ if ( efirc != 0 ) {
+ DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n",
+ efi_status ( efirc ), retaddr );
+ }
return efirc;
}
@@ -1129,12 +1173,11 @@ efi_create_event_ex_wrapper ( UINT32 type, EFI_TPL notify_tpl,
}
/**
- * Build table wrappers
+ * Build boot services table wrapper
*
- * @ret systab Wrapped system table
+ * @ret bs Wrapped boot services table
*/
-EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
- static EFI_SYSTEM_TABLE efi_systab_wrapper;
+EFI_BOOT_SERVICES * efi_wrap_bs ( void ) {
static EFI_BOOT_SERVICES efi_bs_wrapper;
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
@@ -1194,12 +1237,7 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
= efi_uninstall_multiple_protocol_interfaces_wrapper;
efi_bs_wrapper.CreateEventEx = efi_create_event_ex_wrapper;
- /* Build system table wrapper */
- memcpy ( &efi_systab_wrapper, efi_systab,
- sizeof ( efi_systab_wrapper ) );
- efi_systab_wrapper.BootServices = &efi_bs_wrapper;
-
- return &efi_systab_wrapper;
+ return &efi_bs_wrapper;
}
/**
@@ -1208,42 +1246,20 @@ EFI_SYSTEM_TABLE * efi_wrap_systab ( void ) {
* @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;
+ static EFI_SYSTEM_TABLE efi_systab_copy;
/* Do nothing unless debugging is enabled */
if ( ! DBG_LOG )
return;
- /* 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, "WRAP %s could not get loaded image protocol: "
- "%s\n", efi_handle_name ( handle ), strerror ( rc ) );
- return;
+ /* Construct modified system table */
+ if ( efi_systab != &efi_systab_copy ) {
+ memcpy ( &efi_systab_copy, efi_systab,
+ sizeof ( efi_systab_copy ) );
+ efi_systab->BootServices = efi_wrap_bs();
+ efi_systab = &efi_systab_copy;
}
- /* Provide system table wrapper to image */
- loaded.image->SystemTable = efi_wrap_systab();
- DBGC ( colour, "WRAP %s at base %p has protocols:\n",
- efi_handle_name ( handle ), loaded.image->ImageBase );
- DBGC_EFI_PROTOCOLS ( colour, handle );
- DBGC ( colour, "WRAP %s parent", efi_handle_name ( handle ) );
- DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->ParentHandle ));
- DBGC ( colour, "WRAP %s device", efi_handle_name ( handle ) );
- DBGC ( colour, " %s\n", efi_handle_name ( loaded.image->DeviceHandle ));
- DBGC ( colour, "WRAP %s file", efi_handle_name ( handle ) );
- DBGC ( colour, " %s\n", efi_devpath_text ( loaded.image->FilePath ) );
-
- /* Close loaded image protocol */
- bs->CloseProtocol ( handle, &efi_loaded_image_protocol_guid,
- efi_image_handle, NULL );
+ /* Dump image information */
+ efi_dump_image ( handle );
}