]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[efi] Prescroll the display after a failed wrapped ExitBootServices() call
authorMichael Brown <mcb30@ipxe.org>
Tue, 18 Mar 2025 13:50:11 +0000 (13:50 +0000)
committerMichael Brown <mcb30@ipxe.org>
Tue, 18 Mar 2025 14:13:56 +0000 (14:13 +0000)
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>
src/interface/efi/efi_wrap.c

index c2fab66472a9ed45c36ee83bcf3a51e7705ffbb2..cbec48a0016ff952f1f27c6bf681e591d74ff541 100644 (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;
 }