[PATCH] hack: efi/libstub: fix t14s exit_boot_services() failure

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The UEFI firmware on the Lenovo ThinkPad T14s is broken and
ExitBootServices() often fails and prevents the kernel from starting:

	EFI stub: Exiting boot services...
	EFI stub: Exit boot services failed.

One bootloader entry may fail to start almost consistently (once in a
while it may start), while a second entry may always work even when the
kernel, dtb and initramfs images are copies of the failing entry on the
same ESP.

This can be worked around by starting and exiting a UEFI shell from the
bootloader or by starting the bootloader manually via the Boot Menu
(F12) before starting the kernel.

Notably starting the kernel automatically from the shell startup.nsh
does not work, while calling the same script manually works.

Experiments have revealed that allocating an event before calling
ExitBootServices() can make the call succeed. There is often no need to
actually signal the event group, but the event must remain allocated
(i.e. CloseEvent() must not be called).

Also with the event signalling, ExitBootServices() sometimes fails when
starting the kernel automatically from a shell startup.nsh, while
systemd-boot seems to always work. This was only observed after removing
some efi_printk() used during the experiments from the stub...

Signed-off-by: Johan Hovold <johan+linaro@xxxxxxxxxx>
---
 .../firmware/efi/libstub/efi-stub-helper.c    | 20 +++++++++++++++++++
 drivers/firmware/efi/libstub/efistub.h        |  4 ++--
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index de659f6a815f..f228895bf090 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -409,6 +409,9 @@ char *efi_convert_cmdline(efi_loaded_image_t *image, int *cmd_line_len)
 	return (char *)cmdline_addr;
 }
 
+#define EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES \
+	EFI_GUID(0x8be0e274, 0x3970, 0x4b44,  0x80, 0xc5, 0x1a, 0xb9, 0x50, 0x2f, 0x3b, 0xfc)
+
 /**
  * efi_exit_boot_services() - Exit boot services
  * @handle:	handle of the exiting image
@@ -429,10 +432,26 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv,
 {
 	struct efi_boot_memmap *map;
 	efi_status_t status;
+	efi_guid_t guid = EFI_EVENT_GROUP_BEFORE_EXIT_BOOT_SERVICES;
+	efi_event_t event;
 
 	if (efi_disable_pci_dma)
 		efi_pci_disable_bridge_busmaster();
 
+	status = efi_bs_call(create_event_ex, 0, 0, NULL, NULL, &guid, &event);
+	if (status == EFI_SUCCESS) {
+		status = efi_bs_call(signal_event, event);
+		if (status != EFI_SUCCESS)
+			efi_err("%s - signal event failed: %02lx\n", __func__, status);
+#if 0
+		status = efi_bs_call(close_event, event);
+		if (status != EFI_SUCCESS)
+			efi_err("%s - close event failed: %02lx\n", __func__, status);
+#endif
+	} else {
+		efi_err("%s - create event ex failed: %02lx\n", __func__, status);
+	}
+
 	status = efi_get_memory_map(&map, true);
 	if (status != EFI_SUCCESS)
 		return status;
@@ -446,6 +465,7 @@ efi_status_t efi_exit_boot_services(void *handle, void *priv,
 	status = efi_bs_call(exit_boot_services, handle, map->map_key);
 
 	if (status == EFI_INVALID_PARAMETER) {
+		//efi_err("Exit boot services failed: %lx\n", status);
 		/*
 		 * The memory map changed between efi_get_memory_map() and
 		 * exit_boot_services().  Per the UEFI Spec v2.6, Section 6.4:
diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h
index 685098f9626f..e3f710823a29 100644
--- a/drivers/firmware/efi/libstub/efistub.h
+++ b/drivers/firmware/efi/libstub/efistub.h
@@ -272,7 +272,7 @@ union efi_boot_services {
 		efi_status_t (__efiapi *wait_for_event)(unsigned long,
 							efi_event_t *,
 							unsigned long *);
-		void *signal_event;
+		efi_status_t (__efiapi *signal_event)(efi_event_t);
 		efi_status_t (__efiapi *close_event)(efi_event_t);
 		void *check_event;
 		void *install_protocol_interface;
@@ -322,7 +322,7 @@ union efi_boot_services {
 		void *calculate_crc32;
 		void (__efiapi *copy_mem)(void *, const void *, unsigned long);
 		void (__efiapi *set_mem)(void *, unsigned long, unsigned char);
-		void *create_event_ex;
+		efi_status_t (__efiapi *create_event_ex)(u32, int, void *, void *, void *, efi_event_t *);
 	};
 	struct {
 		efi_table_hdr_t hdr;
-- 
2.45.2





[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [Linux for Sparc]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux