[efi] Prescroll the display after a failed wrapped ExitBootServices() call

On some systems (observed with an HP Elitebook 840 G10), writing
console output that happens to cause the display to scroll will modify
the system memory map.  This causes builds with DEBUG=efi_wrap to
typically fail to boot, since the debug output from the wrapped
ExitBootServices() call itself is sufficient to change the memory map
and therefore cause ExitBootServices() to fail due to an invalid
memory map key.

Work around these UEFI firmware bugs by prescrolling the display after
a failed ExitBootServices() attempt, in order to minimise the chance
that further scrolling will happen during the subsequent attempt.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
pull/1165/merge
Michael Brown 2025-03-18 13:50:11 +00:00
parent 8ea8411f0d
commit 6e4196baff
1 changed files with 44 additions and 0 deletions

View File

@ -40,6 +40,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Colour for debug messages */
#define colour &efi_systab
/** Number of lines to prescroll when needed */
#define EFI_WRAP_PRESCROLL 16
/**
* Convert EFI status code to text
*
@ -195,6 +198,38 @@ static const char * efi_timer_delay ( EFI_TIMER_DELAY type ) {
}
}
/**
* Pre-scroll display to create space for output lines
*
* @v lines Number of lines required
* @ret efirc EFI status code
*/
static int efi_prescroll ( unsigned int lines ) {
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *conout = efi_systab->ConOut;
UINTN columns;
UINTN rows;
UINTN space;
EFI_STATUS efirc;
/* Get number of rows and columns */
if ( ( efirc = conout->QueryMode ( conout, conout->Mode->Mode,
&columns, &rows ) ) != 0 )
return efirc;
/* Calculate available space */
space = ( rows - conout->Mode->CursorRow - 1 );
/* Scroll to create space */
while ( space++ < lines )
conout->OutputString ( conout, L"\n" );
/* Move cursor to start of space */
conout->SetCursorPosition ( conout, 0,
( conout->Mode->CursorRow - lines ) );
return 0;
}
/**
* Dump information about a loaded image
*
@ -783,6 +818,15 @@ efi_exit_boot_services_wrapper ( EFI_HANDLE image_handle, UINTN map_key ) {
if ( efirc != 0 ) {
DBGC ( colour, "ExitBootServices ( ... ) = %s -> %p\n",
efi_status ( efirc ), retaddr );
/* On some systems, scrolling the output will cause
* the system memory map to change (and so cause
* ExitBootServices() to fail).
*
* After the first failed attempt, prescroll the
* screen to maximise the chance of the subsequent
* attempt succeeding.
*/
efi_prescroll ( EFI_WRAP_PRESCROLL );
}
return efirc;
}