diff options
| author | Simon Rettberg | 2022-05-11 10:41:01 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2022-05-11 10:41:01 +0200 |
| commit | a12e3c379cf2e5946c7316259ef46736cdd5f222 (patch) | |
| tree | 49638dad528a4490e293ea4a0f87e39ce862a75b /src/interface | |
| parent | Local UEFI disk boot support (diff) | |
| parent | [cloud] Allow aws-import script to run on Python 3.6 (diff) | |
| download | ipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.tar.gz ipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.tar.xz ipxe-a12e3c379cf2e5946c7316259ef46736cdd5f222.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src/interface')
| -rw-r--r-- | src/interface/efi/efi_autoexec.c | 238 | ||||
| -rw-r--r-- | src/interface/efi/efi_console.c | 63 | ||||
| -rw-r--r-- | src/interface/efi/efi_entropy.c | 4 | ||||
| -rw-r--r-- | src/interface/efi/efi_fbcon.c | 243 | ||||
| -rw-r--r-- | src/interface/efi/efi_init.c | 27 | ||||
| -rw-r--r-- | src/interface/efi/efi_timer.c | 2 | ||||
| -rw-r--r-- | src/interface/efi/efi_watchdog.c | 34 | ||||
| -rw-r--r-- | src/interface/efi/efi_wrap.c | 108 | ||||
| -rw-r--r-- | src/interface/smbios/smbios_settings.c | 3 |
9 files changed, 566 insertions, 156 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 ); } diff --git a/src/interface/smbios/smbios_settings.c b/src/interface/smbios/smbios_settings.c index 2d571f2e4..ec31b43f2 100644 --- a/src/interface/smbios/smbios_settings.c +++ b/src/interface/smbios/smbios_settings.c @@ -140,7 +140,8 @@ static int smbios_fetch ( struct settings *settings __unused, * is 2.6 or higher; we match this behaviour. */ raw = &buf[tag_offset]; - if ( ( setting->type == &setting_type_uuid ) && + if ( ( ( setting->type == &setting_type_uuid ) || + ( setting->type == &setting_type_guid ) ) && ( tag_len == sizeof ( uuid ) ) && ( smbios_version() >= SMBIOS_VERSION ( 2, 6 ) ) ) { DBG ( "SMBIOS detected mangled UUID\n" ); |
