Skip to contentSkip to conversation

efi/x86: confine type unsafe casting to mixed mode

PATCH In review Ard Biesheuvel 8 participants 87 messages Dec 18, 2019 – Mar 4, 2020

Subsystems (5)
Documentation Arm (32-bit) x86 arm64 +1 more subsystems
lore
Share & tools
lore

Ard Biesheuvel

author maintainer
#0 lore raw

v2 cover efi/x86: confine type unsafe casting to mixed mode

Currently, we support mixed mode (64-bit Linux running on 32-bit firmware) by explicitly reasoning about pointer sizes for every call into the
firmware: on x86, there are 32-bit and 64-bit versions of each protocol interface, and each call gets routed via one of the two, depending on the native size of the firmware.

There is a lot of casting and pointer mangling involved in this, and as a result, we end up with much less coverage in terms of type checking by the compiler, due to the indirection via an anonymous, variadic thunking routine.

This peculiarity of x86 is also leaking into generic EFI code, which is shared with ia64, arm64, ARM and likely RiscV in the future. So let's try to clean this up a bit.

The approach taken by this series is to replace the 32/64 bit distinction with a distinction between native calls and mixed mode calls, where the former can be either 32 or 64 bit [depending on the platform] and use the ordinary native protocol definitions, while mixed mode calls retain the existing casting/thunking approach based on the 32-bit protocol definitions.

Given that GCC now supports emitting function calls using the MS calling convention, we can get rid of all the wrapping and casting, and emit the indirect calls directly.

Changes since v1:
- Substantially more cleanup work to simplify and hide the mixed mode handling in generic EFI code.
- Get rid of all the pointless passing around of sys_table_arg
- Incorporate an updated version of Matthew's PCI DMA disable patch using EFI events to defer poking the busmaster bits until after all the ordinary ExitBootServices() callbacks have executed.

Code can be found here
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efistub-x86-cleanup-v2

Cc Hans de Goede <[email protected]>
Cc Matthew Garrett <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Andy Lutomirski <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Arvind Sankar <[email protected]>

Ard Biesheuvel (20):
efi/libstub: remove unused __efi_call_early() macro efi/x86: rename efi_is_native() to efi_is_mixed() efi/libstub: use a helper to iterate over a EFI handle array efi/libstub: extend native protocol definitions with mixed_mode
aliases
efi/libstub: distinguish between native/mixed not 32/64 bit efi/libstub/x86: use mixed mode helpers to populate efi_config efi/libstub: drop explicit 32/64-bit protocol definitions efi/libstub: use stricter typing for firmware function pointers efi/libstub: annotate firmware routines as __efiapi efi/libstub/x86: avoid thunking for native firmware calls efi/libstub: get rid of 'sys_table_arg' macro parameter efi/libstub: unify the efi_char16_printk implementations efi/libstub/x86: drop __efi_early() export of efi_config struct efi/libstub: drop sys_table_arg from printk routines efi/libstub: remove 'sys_table_arg' from all function prototypes efi/libstub: drop protocol argument from efi_call_proto() macro efi/libstub: drop 'table' argument from efi_table_attr() macro efi/libstub: use 'func' not 'f' as macro parameter efi/libstub: tidy up types and names of global cmdline variables efi/libstub: import type definitions for creating and signalling
events

Matthew Garrett (1):
efi: Allow disabling PCI busmastering on bridges during boot

 .../admin-guide/kernel-parameters.txt         |   4 +
 arch/arm/include/asm/efi.h                    |  17 +-
 arch/arm64/include/asm/efi.h                  |  16 +-
 arch/x86/Kconfig                              |   1 +
 arch/x86/boot/compressed/Makefile             |   2 +-
 arch/x86/boot/compressed/eboot.c              | 180 ++--
 arch/x86/boot/compressed/eboot.h              |  30 +-
 arch/x86/boot/compressed/efi_stub_32.S        |  87 --
 arch/x86/boot/compressed/efi_stub_64.S        |   5 -
 arch/x86/boot/compressed/head_32.S            |   8 +-
 arch/x86/boot/compressed/head_64.S            |  16 +-
 arch/x86/include/asm/efi.h                    |  76 +-
 arch/x86/platform/efi/efi.c                   |  12 +-
 arch/x86/platform/efi/efi_64.c                |   6 +-
 arch/x86/platform/efi/quirks.c                |   2 +-
 drivers/firmware/efi/Kconfig                  |  22 +
 drivers/firmware/efi/libstub/Makefile         |   2 +-
 drivers/firmware/efi/libstub/arm-stub.c       |  98 ++-
 drivers/firmware/efi/libstub/arm32-stub.c     |  40 +-
 drivers/firmware/efi/libstub/arm64-stub.c     |  24 +-
 .../firmware/efi/libstub/efi-stub-helper.c    | 226 +++--
 drivers/firmware/efi/libstub/efistub.h        |  35 +-
 drivers/firmware/efi/libstub/fdt.c            |  53 +-
 drivers/firmware/efi/libstub/gop.c            |  30 +-
 drivers/firmware/efi/libstub/pci.c            | 116 +++
 drivers/firmware/efi/libstub/random.c         |  54 +-
 drivers/firmware/efi/libstub/secureboot.c     |   6 +-
 drivers/firmware/efi/libstub/tpm.c            |  23 +-
 include/linux/efi.h                           | 816 +++++++++---------
 29 files changed, 980 insertions(+), 1027 deletions(-)
 delete mode 100644 arch/x86/boot/compressed/efi_stub_32.S
 delete mode 100644 arch/x86/boot/compressed/efi_stub_64.S
 create mode 100644 drivers/firmware/efi/libstub/pci.c
Signature · 1 line
2.17.1
author maintainer
#1 lore raw

PATCH v2 1/21 efi/libstub: remove unused __efi_call_early() macro

The macro __efi_call_early() is defined by various architectures but never used. Let's get rid of it.

---
 arch/arm/include/asm/efi.h   | 1 -
 arch/arm64/include/asm/efi.h | 1 -
 arch/x86/include/asm/efi.h   | 3 ---
 3 files changed, 5 deletions(-)
arch/arm/include/asm/efi.h +0 -1
index 7667826b93f1..2306ed783ceb 100644
@@ -51,7 +51,6 @@ void efi_virtmap_unload(void);
 /* arch specific definitions used by the stub code */
 
 #define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
-#define __efi_call_early(f, ...)	f(__VA_ARGS__)
 #define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
 #define efi_is_64bit()			(false)
 
arch/arm64/include/asm/efi.h +0 -1
index b54d3a86c444..7cfac5e0e310 100644
@@ -94,7 +94,6 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 }
 
 #define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
-#define __efi_call_early(f, ...)	f(__VA_ARGS__)
 #define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
 #define efi_is_64bit()			(true)
 
arch/x86/include/asm/efi.h +0 -3
index d028e9acdf1c..59c19e0b6027 100644
@@ -233,9 +233,6 @@ static inline bool efi_is_64bit(void)
 	__efi_early()->call(efi_table_attr(efi_boot_services, f,	\
 		__efi_early()->boot_services), __VA_ARGS__)
 
-#define __efi_call_early(f, ...)					\
-	__efi_early()->call((unsigned long)f, __VA_ARGS__);
-
 #define efi_call_runtime(f, ...)					\
 	__efi_early()->call(efi_table_attr(efi_runtime_services, f,	\
 		__efi_early()->runtime_services), __VA_ARGS__)
Signature · 1 line
2.17.1
author maintainer
#2 lore raw

PATCH v2 2/21 efi/x86: rename efi_is_native() to efi_is_mixed()

In reply to Ard Biesheuvel

The ARM architecture does not permit combining 32-bit and 64-bit code at the same privilege level, and so EFI mixed mode is strictly a x86 concept.

In preparation of turning the 32/64 bit distinction in shared stub code to a native vs mixed one, refactor x86's current use of the helper function efi_is_native() into efi_is_mixed().

---
 arch/x86/include/asm/efi.h     | 10 ++++++----
 arch/x86/platform/efi/efi.c    |  8 ++++----
 arch/x86/platform/efi/efi_64.c |  4 ++--
 arch/x86/platform/efi/quirks.c |  2 +-
 4 files changed, 13 insertions(+), 11 deletions(-)
arch/x86/include/asm/efi.h +6 -4
index 59c19e0b6027..6094e7f49a99 100644
@@ -153,17 +153,19 @@ extern u64 efi_setup;
 
 #ifdef CONFIG_EFI
 
-static inline bool efi_is_native(void)
+static inline bool efi_is_mixed(void)
 {
-	return IS_ENABLED(CONFIG_X86_64) == efi_enabled(EFI_64BIT);
+	if (!IS_ENABLED(CONFIG_EFI_MIXED))
+		return false;
+	return IS_ENABLED(CONFIG_X86_64) && !efi_enabled(EFI_64BIT);
 }
 
 static inline bool efi_runtime_supported(void)
 {
-	if (efi_is_native())
+	if (!efi_is_mixed())
 		return true;
 
-	if (IS_ENABLED(CONFIG_EFI_MIXED) && !efi_enabled(EFI_OLD_MEMMAP))
+	if (!efi_enabled(EFI_OLD_MEMMAP))
 		return true;
 
 	return false;
arch/x86/platform/efi/efi.c +4 -4
index bb8e37a723d6..1493e964c267 100644
@@ -848,7 +848,7 @@ static bool should_map_region(efi_memory_desc_t *md)
 	 * Map all of RAM so that we can access arguments in the 1:1
 	 * mapping when making EFI runtime calls.
 	 */
-	if (IS_ENABLED(CONFIG_EFI_MIXED) && !efi_is_native()) {
+	if (efi_is_mixed()) {
 		if (md->type == EFI_CONVENTIONAL_MEMORY ||
 		    md->type == EFI_LOADER_DATA ||
 		    md->type == EFI_LOADER_CODE)
@@ -923,7 +923,7 @@ static void __init kexec_enter_virtual_mode(void)
 	 * kexec kernel because in the initial boot something else might
 	 * have been mapped at these virtual addresses.
 	 */
-	if (!efi_is_native() || efi_enabled(EFI_OLD_MEMMAP)) {
+	if (efi_is_mixed() || efi_enabled(EFI_OLD_MEMMAP)) {
 		efi_memmap_unmap();
 		clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
 		return;
@@ -1060,7 +1060,7 @@ static void __init __efi_enter_virtual_mode(void)
 
 	efi_sync_low_kernel_mappings();
 
-	if (efi_is_native()) {
+	if (!efi_is_mixed()) {
 		status = phys_efi_set_virtual_address_map(
 				efi.memmap.desc_size * count,
 				efi.memmap.desc_size,
@@ -1091,7 +1091,7 @@ static void __init __efi_enter_virtual_mode(void)
 	 */
 	efi.runtime_version = efi_systab.hdr.revision;
 
-	if (efi_is_native())
+	if (!efi_is_mixed())
 		efi_native_runtime_setup();
 	else
 		efi_thunk_runtime_setup();
arch/x86/platform/efi/efi_64.c +2 -2
index 08ce8177c3af..885e50a707a6 100644
@@ -388,7 +388,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
 	 * text and allocate a new stack because we can't rely on the
 	 * stack pointer being < 4GB.
 	 */
-	if (!IS_ENABLED(CONFIG_EFI_MIXED) || efi_is_native())
+	if (!efi_is_mixed())
 		return 0;
 
 	page = alloc_page(GFP_KERNEL|__GFP_DMA32);
@@ -449,7 +449,7 @@ void __init efi_map_region(efi_memory_desc_t *md)
 	 * booting in EFI mixed mode, because even though we may be
 	 * running a 64-bit kernel, the firmware may only be 32-bit.
 	 */
-	if (!efi_is_native () && IS_ENABLED(CONFIG_EFI_MIXED)) {
+	if (efi_is_mixed()) {
 		md->virt_addr = md->phys_addr;
 		return;
 	}
arch/x86/platform/efi/quirks.c +1 -1
index 7675cf754d90..84d7176983d2 100644
@@ -397,7 +397,7 @@ static void __init efi_unmap_pages(efi_memory_desc_t *md)
 	 * EFI runtime calls, hence don't unmap EFI boot services code/data
 	 * regions.
 	 */
-	if (!efi_is_native())
+	if (efi_is_mixed())
 		return;
 
 	if (kernel_unmap_pages_in_pgd(pgd, pa, md->num_pages))
Signature · 1 line
2.17.1
author maintainer
#3 lore raw

PATCH v2 3/21 efi/libstub: use a helper to iterate over a EFI handle array

In reply to Ard Biesheuvel

Iterating over a EFI handle array is a bit finicky, since we have to take mixed mode into account, where handles are only 32-bit while the native efi_handle_t type is 64-bit.

So introduce a helper, and replace the various occurrences of this pattern.

---
 arch/x86/boot/compressed/eboot.c   | 14 +++++---------
 drivers/firmware/efi/libstub/gop.c |  9 ++-------
 include/linux/efi.h                | 13 +++++++++++++
 3 files changed, 20 insertions(+), 16 deletions(-)
arch/x86/boot/compressed/eboot.c +5 -9
index 72b08fde6de6..959bcdd8c1fe 100644
@@ -135,6 +135,7 @@ static void setup_efi_pci(struct boot_params *params)
 	unsigned long size = 0;
 	unsigned long nr_pci;
 	struct setup_data *data;
+	efi_handle_t h;
 	int i;
 
 	status = efi_call_early(locate_handle,
@@ -164,14 +165,11 @@ static void setup_efi_pci(struct boot_params *params)
 	while (data && data->next)
 		data = (struct setup_data *)(unsigned long)data->next;
 
-	nr_pci = size / (efi_is_64bit() ? sizeof(u64) : sizeof(u32));
-	for (i = 0; i < nr_pci; i++) {
+	for_each_efi_handle(h, pci_handle, size, i) {
 		efi_pci_io_protocol_t *pci = NULL;
 		struct pci_setup_rom *rom;
 
-		status = efi_call_early(handle_protocol,
-					efi_is_64bit() ? ((u64 *)pci_handle)[i]
-						       : ((u32 *)pci_handle)[i],
+		status = efi_call_early(handle_protocol, h,
 					&pci_proto, (void **)&pci);
 		if (status != EFI_SUCCESS || !pci)
 			continue;
@@ -266,6 +264,7 @@ setup_uga(struct screen_info *si, efi_guid_t *uga_proto, unsigned long size)
 	void **uga_handle = NULL;
 	efi_uga_draw_protocol_t *uga = NULL, *first_uga;
 	unsigned long nr_ugas;
+	efi_handle_t handle;
 	int i;
 
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
@@ -283,13 +282,10 @@ setup_uga(struct screen_info *si, efi_guid_t *uga_proto, unsigned long size)
 	width = 0;
 
 	first_uga = NULL;
-	nr_ugas = size / (efi_is_64bit() ? sizeof(u64) : sizeof(u32));
-	for (i = 0; i < nr_ugas; i++) {
+	for_each_efi_handle(handle, uga_handle, size, i) {
 		efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
 		u32 w, h, depth, refresh;
 		void *pciio;
-		unsigned long handle = efi_is_64bit() ? ((u64 *)uga_handle)[i]
-						      : ((u32 *)uga_handle)[i];
 
 		status = efi_call_early(handle_protocol, handle,
 					uga_proto, (void **)&uga);
drivers/firmware/efi/libstub/gop.c +2 -7
index 94045ab7dd3d..5f4fbc2ac687 100644
@@ -91,7 +91,6 @@ setup_gop(efi_system_table_t *sys_table_arg, struct screen_info *si,
 	  efi_guid_t *proto, unsigned long size, void **handles)
 {
 	efi_graphics_output_protocol_t *gop, *first_gop;
-	unsigned long nr_gops;
 	u16 width, height;
 	u32 pixels_per_scan_line;
 	u32 ext_lfb_base;
@@ -99,22 +98,18 @@ setup_gop(efi_system_table_t *sys_table_arg, struct screen_info *si,
 	efi_pixel_bitmask_t pixel_info;
 	int pixel_format;
 	efi_status_t status;
+	efi_handle_t h;
 	int i;
-	bool is64 = efi_is_64bit();
 
 	first_gop = NULL;
 	gop = NULL;
 
-	nr_gops = size / (is64 ? sizeof(u64) : sizeof(u32));
-	for (i = 0; i < nr_gops; i++) {
+	for_each_efi_handle(h, handles, size, i) {
 		efi_graphics_output_protocol_mode_t *mode;
 		efi_graphics_output_mode_info_t *info = NULL;
 		efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
 		bool conout_found = false;
 		void *dummy = NULL;
-		efi_handle_t h = (efi_handle_t)(unsigned long)
-				 (is64 ? ((u64 *)handles)[i]
-				       : ((u32 *)handles)[i]);
 		efi_physical_addr_t current_fb_base;
 
 		status = efi_call_early(handle_protocol, h,
include/linux/efi.h +13 -0
index ea0b43878c59..3e3d9602647a 100644
@@ -48,6 +48,19 @@ typedef u16 efi_char16_t;		/* UNICODE character */
 typedef u64 efi_physical_addr_t;
 typedef void *efi_handle_t;
 
+#define efi_get_handle_at(array, idx)					\
+	(efi_is_64bit() ? (efi_handle_t)(unsigned long)((u64 *)(array))[idx] \
+		: (efi_handle_t)(unsigned long)((u32 *)(array))[idx])
+
+#define efi_get_handle_num(size)					\
+	((size) / (efi_is_64bit() ? sizeof(u64) : sizeof(u32)))
+
+#define for_each_efi_handle(handle, array, size, i)			\
+	for (i = 0;							\
+	     i < efi_get_handle_num(size) &&				\
+		((handle = efi_get_handle_at((array), i)) || true);	\
+	     i++)
+
 /*
  * The UEFI spec and EDK2 reference implementation both define EFI_GUID as
  * struct { u32 a; u16; b; u16 c; u8 d[8]; }; and so the implied alignment
Signature · 1 line
2.17.1
author maintainer
#4 lore raw

PATCH v2 4/21 efi/libstub: extend native protocol definitions with mixed_mode aliases

In reply to Ard Biesheuvel

In preparation of moving to a native vs. mixed mode split rather than a 32 vs. 64 bit split when it comes to invoking EFI firmware services, update all the native protocol definitions and redefine them as unions containing an anonymous struct for the native view and a struct called 'mixed_mode' describing the 32-bit view of the protocol when called from 64-bit code.

While at it, flesh out some PCI I/O member definitions that we will be needing shortly.

---
 arch/x86/boot/compressed/eboot.h        |  15 +-
 drivers/firmware/efi/libstub/arm-stub.c |   4 +-
 drivers/firmware/efi/libstub/random.c   |  22 +-
 include/linux/efi.h                     | 506 +++++++++++++-------
 4 files changed, 365 insertions(+), 182 deletions(-)
arch/x86/boot/compressed/eboot.h +11 -4
index 8297387c4676..26f1f2635f64 100644
@@ -24,10 +24,17 @@ typedef struct {
 	u64 blt;
 } efi_uga_draw_protocol_64_t;
 
-typedef struct {
-	void *get_mode;
-	void *set_mode;
-	void *blt;
+typedef union {
+	struct {
+		void *get_mode;
+		void *set_mode;
+		void *blt;
+	};
+	struct {
+		u32 get_mode;
+		u32 set_mode;
+		u32 blt;
+	} mixed_mode;
 } efi_uga_draw_protocol_t;
 
 #endif /* BOOT_COMPRESSED_EBOOT_H */
drivers/firmware/efi/libstub/arm-stub.c +2 -2
index 817237ce2420..60a301e1c072 100644
@@ -40,9 +40,9 @@ static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
 void efi_char16_printk(efi_system_table_t *sys_table_arg,
 			      efi_char16_t *str)
 {
-	struct efi_simple_text_output_protocol *out;
+	efi_simple_text_output_protocol_t *out;
 
-	out = (struct efi_simple_text_output_protocol *)sys_table_arg->con_out;
+	out = (efi_simple_text_output_protocol_t *)sys_table_arg->con_out;
 	out->output_string(out, str);
 }
 
drivers/firmware/efi/libstub/random.c +14 -8
index 97378cf96a2e..d92cd640c73d 100644
@@ -9,7 +9,7 @@
 
 #include "efistub.h"
 
-typedef struct efi_rng_protocol efi_rng_protocol_t;
+typedef union efi_rng_protocol efi_rng_protocol_t;
 
 typedef struct {
 	u32 get_info;
@@ -21,11 +21,17 @@ typedef struct {
 	u64 get_rng;
 } efi_rng_protocol_64_t;
 
-struct efi_rng_protocol {
-	efi_status_t (*get_info)(struct efi_rng_protocol *,
-				 unsigned long *, efi_guid_t *);
-	efi_status_t (*get_rng)(struct efi_rng_protocol *,
-				efi_guid_t *, unsigned long, u8 *out);
+union efi_rng_protocol {
+	struct {
+		efi_status_t (*get_info)(efi_rng_protocol_t *,
+					 unsigned long *, efi_guid_t *);
+		efi_status_t (*get_rng)(efi_rng_protocol_t *,
+					efi_guid_t *, unsigned long, u8 *out);
+	};
+	struct {
+		u32 get_info;
+		u32 get_rng;
+	} mixed_mode;
 };
 
 efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
@@ -33,7 +39,7 @@ efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
 {
 	efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
 	efi_status_t status;
-	struct efi_rng_protocol *rng = NULL;
+	efi_rng_protocol_t *rng = NULL;
 
 	status = efi_call_early(locate_protocol, &rng_proto, NULL,
 				(void **)&rng);
@@ -162,7 +168,7 @@ efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg)
 	efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
 	efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW;
 	efi_guid_t rng_table_guid = LINUX_EFI_RANDOM_SEED_TABLE_GUID;
-	struct efi_rng_protocol *rng = NULL;
+	efi_rng_protocol_t *rng = NULL;
 	struct linux_efi_random_seed *seed = NULL;
 	efi_status_t status;
 
include/linux/efi.h +338 -168

594 lines not on this page · raw message

Signature · 1 line
2.17.1
author maintainer
#5 lore raw

PATCH v2 5/21 efi/libstub: distinguish between native/mixed not 32/64 bit

In reply to Ard Biesheuvel

Currently, we support mixed mode by casting all boot time firmware calls to 64-bit explicitly on native 64-bit systems, and to 32-bit on 32-bit systems or 64-bit systems running with 32-bit firmware.

Due to this explicit awareness of the bitness in the code, we do a lot of casting even on generic code that is shared with other architectures, where mixed mode does not even exist. This casting leads to loss of coverage of type checking by the compiler, which we should try to avoid.

So instead of distinguishing between 32-bit vs 64-bit, distinguish between native vs mixed, and limit all the nasty casting and pointer mangling to the code that actually deals with mixed mode.

---
 arch/arm/include/asm/efi.h                     |  2 +-
 arch/arm64/include/asm/efi.h                   |  2 +-
 arch/x86/boot/compressed/eboot.c               |  3 +-
 arch/x86/include/asm/efi.h                     | 35 +++++++++++---
 drivers/firmware/efi/libstub/efi-stub-helper.c | 49 +++++++-------------
 include/linux/efi.h                            |  6 +--
 6 files changed, 51 insertions(+), 46 deletions(-)
arch/arm/include/asm/efi.h +1 -1
index 2306ed783ceb..9b0c64c28bff 100644
@@ -52,7 +52,7 @@ void efi_virtmap_unload(void);
 
 #define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
 #define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
-#define efi_is_64bit()			(false)
+#define efi_is_native()			(true)
 
 #define efi_table_attr(table, attr, instance)				\
 	((table##_t *)instance)->attr
arch/arm64/include/asm/efi.h +1 -1
index 7cfac5e0e310..189082c44c28 100644
@@ -95,7 +95,7 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 
 #define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
 #define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
-#define efi_is_64bit()			(true)
+#define efi_is_native()			(true)
 
 #define efi_table_attr(table, attr, instance)				\
 	((table##_t *)instance)->attr
arch/x86/boot/compressed/eboot.c +1 -2
index 959bcdd8c1fe..990b93379965 100644
@@ -63,8 +63,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 	 * large romsize. The UEFI spec limits the size of option ROMs to 16
 	 * MiB so we reject any ROMs over 16 MiB in size to catch this.
 	 */
-	romimage = (void *)(unsigned long)efi_table_attr(efi_pci_io_protocol,
-							 romimage, pci);
+	romimage = efi_table_attr(efi_pci_io_protocol, romimage, pci);
 	romsize = efi_table_attr(efi_pci_io_protocol, romsize, pci);
 	if (!romimage || !romsize || romsize > SZ_16M)
 		return EFI_INVALID_PARAMETER;
arch/x86/include/asm/efi.h +28 -7
index 6094e7f49a99..c27323cb49e5 100644
@@ -222,21 +222,42 @@ static inline bool efi_is_64bit(void)
 	return __efi_early()->is64;
 }
 
-#define efi_table_attr(table, attr, instance)				\
-	(efi_is_64bit() ?						\
-		((table##_64_t *)(unsigned long)instance)->attr :	\
-		((table##_32_t *)(unsigned long)instance)->attr)
+static inline bool efi_is_native(void)
+{
+	if (!IS_ENABLED(CONFIG_X86_64))
+		return true;
+	return efi_is_64bit();
+}
+
+#define efi_mixed_mode_cast(attr)					\
+	__builtin_choose_expr(						\
+		__builtin_types_compatible_p(u32, __typeof__(attr)),	\
+			(unsigned long)(attr), (attr))
+
+#define efi_table_attr(table, attr, instance) ({			\
+	__typeof__(((table##_t *)0)->attr) __ret;			\
+	if (efi_is_native()) {						\
+		__ret = ((table##_t *)(unsigned long)instance)->attr;	\
+	} else {							\
+		__ret = (__typeof__(__ret))efi_mixed_mode_cast(		\
+		((table##_t *)(unsigned long)instance)->mixed_mode.attr);\
+	}								\
+	__ret;								\
+})
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	__efi_early()->call(efi_table_attr(protocol, f, instance),	\
+	__efi_early()->call((unsigned long)				\
+				efi_table_attr(protocol, f, instance),	\
 		instance, ##__VA_ARGS__)
 
 #define efi_call_early(f, ...)						\
-	__efi_early()->call(efi_table_attr(efi_boot_services, f,	\
+	__efi_early()->call((unsigned long)				\
+				efi_table_attr(efi_boot_services, f,	\
 		__efi_early()->boot_services), __VA_ARGS__)
 
 #define efi_call_runtime(f, ...)					\
-	__efi_early()->call(efi_table_attr(efi_runtime_services, f,	\
+	__efi_early()->call((unsigned long)				\
+				efi_table_attr(efi_runtime_services, f,	\
 		__efi_early()->runtime_services), __VA_ARGS__)
 
 extern bool efi_reboot_required(void);
drivers/firmware/efi/libstub/efi-stub-helper.c +17 -32

73 lines not on this page · raw message

include/linux/efi.h +3 -3
index 048cdc9bc96d..1dbda78d2823 100644
@@ -49,11 +49,11 @@ typedef u64 efi_physical_addr_t;
 typedef void *efi_handle_t;
 
 #define efi_get_handle_at(array, idx)					\
-	(efi_is_64bit() ? (efi_handle_t)(unsigned long)((u64 *)(array))[idx] \
+	(efi_is_native() ? (array)[idx] 				\
 		: (efi_handle_t)(unsigned long)((u32 *)(array))[idx])
 
 #define efi_get_handle_num(size)					\
-	((size) / (efi_is_64bit() ? sizeof(u64) : sizeof(u32)))
+	((size) / (efi_is_native() ? sizeof(efi_handle_t) : sizeof(u32)))
 
 #define for_each_efi_handle(handle, array, size, i)			\
 	for (i = 0;							\
@@ -811,7 +811,7 @@ typedef struct {
 typedef union {
 	struct {
 		efi_guid_t guid;
-		unsigned long table;
+		void *table;
 	};
 	efi_config_table_32_t mixed_mode;
 } efi_config_table_t;
Signature · 1 line
2.17.1
author maintainer
#6 lore raw

PATCH v2 6/21 efi/libstub/x86: use mixed mode helpers to populate efi_config

In reply to Ard Biesheuvel

The efi_config struct returned by __efi_early() contains a couple of pointers that are obtained from the EFI system table, which could be 32-bit on a 64-bit system. For this reason, there are two versions of the setup_boot_services() routine, one for 32-bit and one for 64-bit.

We have helpers now that hide all this nastiness, so let's use those instead.

---
 arch/x86/boot/compressed/eboot.c   | 27 +++++---------------
 arch/x86/boot/compressed/head_32.S |  6 ++---
 arch/x86/include/asm/efi.h         |  6 ++---
 arch/x86/platform/efi/efi.c        |  4 +--
 include/linux/efi.h                |  6 ++---
 5 files changed, 18 insertions(+), 31 deletions(-)
arch/x86/boot/compressed/eboot.c +7 -20
index 990b93379965..f9074f12e89f 100644
@@ -27,19 +27,12 @@ __pure const struct efi_config *__efi_early(void)
 	return efi_early;
 }
 
-#define BOOT_SERVICES(bits)						\
-static void setup_boot_services##bits(struct efi_config *c)		\
-{									\
-	efi_system_table_##bits##_t *table;				\
-									\
-	table = (typeof(table))sys_table;				\
-									\
-	c->runtime_services	= table->runtime;			\
-	c->boot_services	= table->boottime;			\
-	c->text_output		= table->con_out;			\
+static void setup_boot_services(struct efi_config *c)
+{
+	c->runtime_services	= efi_table_attr(efi_system_table, runtime, sys_table);
+	c->boot_services	= efi_table_attr(efi_system_table, boottime, sys_table);
+	c->text_output		= efi_table_attr(efi_system_table, con_out, sys_table);
 }
-BOOT_SERVICES(32);
-BOOT_SERVICES(64);
 
 void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
 {
@@ -396,10 +389,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
 		return NULL;
 
-	if (efi_is_64bit())
-		setup_boot_services64(efi_early);
-	else
-		setup_boot_services32(efi_early);
+	setup_boot_services(efi_early);
 
 	status = efi_call_early(handle_protocol, handle,
 				&proto, (void *)&image);
@@ -758,10 +748,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
 		goto fail;
 
-	if (efi_is_64bit())
-		setup_boot_services64(efi_early);
-	else
-		setup_boot_services32(efi_early);
+	setup_boot_services(efi_early);
 
 	/*
 	 * make_boot_params() may have been called before efi_main(), in which
arch/x86/boot/compressed/head_32.S +3 -3
index f2dfd6d083ef..40468ab49b9b 100644
@@ -163,7 +163,7 @@ SYM_FUNC_START(efi_pe_entry)
 
 	/* Relocate efi_config->call() */
 	leal	efi32_config(%esi), %eax
-	add	%esi, 40(%eax)
+	add	%esi, 28(%eax)
 	pushl	%eax
 
 	call	make_boot_params
@@ -190,7 +190,7 @@ SYM_FUNC_START(efi32_stub_entry)
 
 	/* Relocate efi_config->call() */
 	leal	efi32_config(%esi), %eax
-	add	%esi, 40(%eax)
+	add	%esi, 28(%eax)
 	pushl	%eax
 2:
 	call	efi_main
@@ -265,7 +265,7 @@ SYM_FUNC_END(.Lrelocated)
 #ifdef CONFIG_EFI_STUB
 	.data
 efi32_config:
-	.fill 5,8,0
+	.fill 7,4,0
 	.long efi_call_phys
 	.long 0
 	.byte 0
arch/x86/include/asm/efi.h +3 -3
index c27323cb49e5..183cd49e0495 100644
@@ -202,9 +202,9 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
 struct efi_config {
 	u64 image_handle;
 	u64 table;
-	u64 runtime_services;
-	u64 boot_services;
-	u64 text_output;
+	efi_runtime_services_t *runtime_services;
+	efi_boot_services_t *boot_services;
+	efi_simple_text_output_protocol_t *text_output;
 	efi_status_t (*call)(unsigned long, ...);
 	bool is64;
 } __packed;
arch/x86/platform/efi/efi.c +2 -2
index 1493e964c267..27700268ed4a 100644
@@ -388,7 +388,7 @@ static int __init efi_systab_init(void *phys)
 		tmp |= systab64->con_in;
 		efi_systab.con_out_handle = systab64->con_out_handle;
 		tmp |= systab64->con_out_handle;
-		efi_systab.con_out = systab64->con_out;
+		efi_systab.con_out = (void *)(unsigned long)systab64->con_out;
 		tmp |= systab64->con_out;
 		efi_systab.stderr_handle = systab64->stderr_handle;
 		tmp |= systab64->stderr_handle;
@@ -430,7 +430,7 @@ static int __init efi_systab_init(void *phys)
 		efi_systab.con_in_handle = systab32->con_in_handle;
 		efi_systab.con_in = systab32->con_in;
 		efi_systab.con_out_handle = systab32->con_out_handle;
-		efi_systab.con_out = systab32->con_out;
+		efi_systab.con_out = (void *)(unsigned long)systab32->con_out;
 		efi_systab.stderr_handle = systab32->stderr_handle;
 		efi_systab.stderr = systab32->stderr;
 		efi_systab.runtime = (void *)(unsigned long)systab32->runtime;
include/linux/efi.h +3 -3
index 1dbda78d2823..2c33841d0d4a 100644
@@ -822,6 +822,8 @@ typedef struct {
 	unsigned long *ptr;
 } efi_config_table_type_t;
 
+typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
+
 #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL)
 
 #define EFI_2_30_SYSTEM_TABLE_REVISION  ((2 << 16) | (30))
@@ -873,7 +875,7 @@ typedef union {
 		unsigned long con_in_handle;
 		unsigned long con_in;
 		unsigned long con_out_handle;
-		unsigned long con_out;
+		efi_simple_text_output_protocol_t *con_out;
 		unsigned long stderr_handle;
 		unsigned long stderr;
 		efi_runtime_services_t *runtime;
@@ -1553,8 +1555,6 @@ typedef struct {
 	u64 test_string;
 } efi_simple_text_output_protocol_64_t;
 
-typedef union efi_simple_text_output_protocol efi_simple_text_output_protocol_t;
-
 union efi_simple_text_output_protocol {
 	struct {
 		void *reset;
Signature · 1 line
2.17.1
author maintainer
#7 lore raw

PATCH v2 7/21 efi/libstub: drop explicit 32/64-bit protocol definitions

In reply to Ard Biesheuvel

Now that we have incorporated the mixed mode protocol definitions into the native ones using unions, we no longer need the separate 32/64 bit struct definitions, with the exception of the EFI system table definition and the boot services, runtime services and configuration table definitions. So drop the unused ones.

---
 arch/x86/boot/compressed/eboot.h      |  12 -
 drivers/firmware/efi/libstub/random.c |  10 -
 include/linux/efi.h                   | 233 +-------------------
 3 files changed, 1 insertion(+), 254 deletions(-)
arch/x86/boot/compressed/eboot.h +0 -12
index 26f1f2635f64..de13865dc7d2 100644
@@ -12,18 +12,6 @@
 
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
-typedef struct {
-	u32 get_mode;
-	u32 set_mode;
-	u32 blt;
-} efi_uga_draw_protocol_32_t;
-
-typedef struct {
-	u64 get_mode;
-	u64 set_mode;
-	u64 blt;
-} efi_uga_draw_protocol_64_t;
-
 typedef union {
 	struct {
 		void *get_mode;
drivers/firmware/efi/libstub/random.c +0 -10
index d92cd640c73d..1a5a4a9db2a7 100644
@@ -11,16 +11,6 @@
 
 typedef union efi_rng_protocol efi_rng_protocol_t;
 
-typedef struct {
-	u32 get_info;
-	u32 get_rng;
-} efi_rng_protocol_32_t;
-
-typedef struct {
-	u64 get_info;
-	u64 get_rng;
-} efi_rng_protocol_64_t;
-
 union efi_rng_protocol {
 	struct {
 		efi_status_t (*get_info)(efi_rng_protocol_t *,
include/linux/efi.h +1 -232

299 lines not on this page · raw message

Signature · 1 line
2.17.1
author maintainer
#8 lore raw

PATCH v2 8/21 efi/libstub: use stricter typing for firmware function pointers

In reply to Ard Biesheuvel

We will soon remove another level of pointer casting, so let's make sure all type handling involving firmware calls at boot time is correct.

---
 arch/x86/boot/compressed/eboot.c               | 10 ++++++----
 arch/x86/boot/compressed/eboot.h               |  9 ++++++---
 drivers/firmware/efi/libstub/efi-stub-helper.c |  5 +++--
 drivers/firmware/efi/libstub/tpm.c             |  4 ++--
 include/linux/efi.h                            |  6 +++++-
 5 files changed, 22 insertions(+), 12 deletions(-)
arch/x86/boot/compressed/eboot.c +6 -4
index f9074f12e89f..4a965212ee64 100644
@@ -63,7 +63,8 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 
 	size = romsize + sizeof(*rom);
 
-	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size, &rom);
+	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size,
+				(void **)&rom);
 	if (status != EFI_SUCCESS) {
 		efi_printk(sys_table, "Failed to allocate memory for 'rom'\n");
 		return status;
@@ -188,9 +189,9 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 	struct setup_data *data, *new;
 	efi_status_t status;
 	u32 size = 0;
-	void *p;
+	apple_properties_protocol_t *p;
 
-	status = efi_call_early(locate_protocol, &guid, NULL, &p);
+	status = efi_call_early(locate_protocol, &guid, NULL, (void **)&p);
 	if (status != EFI_SUCCESS)
 		return;
 
@@ -205,7 +206,8 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 
 	do {
 		status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
-					size + sizeof(struct setup_data), &new);
+					size + sizeof(struct setup_data),
+					(void **)&new);
 		if (status != EFI_SUCCESS) {
 			efi_printk(sys_table, "Failed to allocate memory for 'properties'\n");
 			return;
arch/x86/boot/compressed/eboot.h +6 -3
index de13865dc7d2..b8d11928f528 100644
@@ -12,9 +12,12 @@
 
 #define DESC_TYPE_CODE_DATA	(1 << 0)
 
-typedef union {
+typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t;
+
+union efi_uga_draw_protocol {
 	struct {
-		void *get_mode;
+		efi_status_t (*get_mode)(efi_uga_draw_protocol_t *,
+					 u32*, u32*, u32*, u32*);
 		void *set_mode;
 		void *blt;
 	};
@@ -23,6 +26,6 @@ typedef union {
 		u32 set_mode;
 		u32 blt;
 	} mixed_mode;
-} efi_uga_draw_protocol_t;
+};
 
 #endif /* BOOT_COMPRESSED_EBOOT_H */
drivers/firmware/efi/libstub/efi-stub-helper.c +3 -2
index 489c4febc185..5eaf6ada960f 100644
@@ -418,12 +418,13 @@ static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
 	return status;
 }
 
-static efi_status_t efi_file_read(void *handle, unsigned long *size, void *addr)
+static efi_status_t efi_file_read(efi_file_handle_t *handle,
+				  unsigned long *size, void *addr)
 {
 	return efi_call_proto(efi_file_handle, read, handle, size, addr);
 }
 
-static efi_status_t efi_file_close(void *handle)
+static efi_status_t efi_file_close(efi_file_handle_t *handle)
 {
 	return efi_call_proto(efi_file_handle, close, handle);
 }
drivers/firmware/efi/libstub/tpm.c +2 -2
index eb9af83e4d59..d270acd43de8 100644
@@ -69,11 +69,11 @@ void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
 	size_t log_size, last_entry_size;
 	efi_bool_t truncated;
 	int version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2;
-	void *tcg2_protocol = NULL;
+	efi_tcg2_protocol_t *tcg2_protocol = NULL;
 	int final_events_size = 0;
 
 	status = efi_call_early(locate_protocol, &tcg2_guid, NULL,
-				&tcg2_protocol);
+				(void **)&tcg2_protocol);
 	if (status != EFI_SUCCESS)
 		return;
 
include/linux/efi.h +5 -1
index d09ce1cea130..969eb06e780a 100644
@@ -388,7 +388,11 @@ union efi_pci_io_protocol {
 		void *allocate_buffer;
 		void *free_buffer;
 		void *flush;
-		void *get_location;
+		efi_status_t (*get_location)(efi_pci_io_protocol_t *,
+					     unsigned long *segment_nr,
+					     unsigned long *bus_nr,
+					     unsigned long *device_nr,
+					     unsigned long *function_nr);
 		void *attributes;
 		void *get_bar_attributes;
 		void *set_bar_attributes;
Signature · 1 line
2.17.1
author maintainer
#9 lore raw

PATCH v2 9/21 efi/libstub: annotate firmware routines as __efiapi

In reply to Ard Biesheuvel

Annotate all the firmware routines (boot services, runtime services and protocol methods) called in the boot context as __efiapi, and make it expand to __attribute__((ms_abi)) on 64-bit x86. This allows us to use the compiler to generate the calls into firmware that use the MS calling convention instead of the SysV one.

---
 arch/x86/Kconfig                      |   1 +
 arch/x86/boot/compressed/eboot.h      |   4 +-
 drivers/firmware/efi/libstub/random.c |  10 +-
 include/linux/efi.h                   | 156 +++++++++++---------
 4 files changed, 96 insertions(+), 75 deletions(-)
arch/x86/Kconfig +1 -0
index 5e8949953660..8ba81036a7ef 100644
@@ -1993,6 +1993,7 @@ config EFI
 config EFI_STUB
        bool "EFI stub support"
        depends on EFI && !X86_USE_3DNOW
+       depends on $(cc-option,-mabi=ms)
        select RELOCATABLE
        ---help---
           This kernel feature allows a bzImage to be loaded directly
arch/x86/boot/compressed/eboot.h +2 -2
index b8d11928f528..99f35343d443 100644
@@ -16,8 +16,8 @@ typedef union efi_uga_draw_protocol efi_uga_draw_protocol_t;
 
 union efi_uga_draw_protocol {
 	struct {
-		efi_status_t (*get_mode)(efi_uga_draw_protocol_t *,
-					 u32*, u32*, u32*, u32*);
+		efi_status_t (__efiapi *get_mode)(efi_uga_draw_protocol_t *,
+						  u32*, u32*, u32*, u32*);
 		void *set_mode;
 		void *blt;
 	};
drivers/firmware/efi/libstub/random.c +6 -4
index 1a5a4a9db2a7..4f5c249c62dc 100644
@@ -13,10 +13,12 @@ typedef union efi_rng_protocol efi_rng_protocol_t;
 
 union efi_rng_protocol {
 	struct {
-		efi_status_t (*get_info)(efi_rng_protocol_t *,
-					 unsigned long *, efi_guid_t *);
-		efi_status_t (*get_rng)(efi_rng_protocol_t *,
-					efi_guid_t *, unsigned long, u8 *out);
+		efi_status_t (__efiapi *get_info)(efi_rng_protocol_t *,
+						  unsigned long *,
+						  efi_guid_t *);
+		efi_status_t (__efiapi *get_rng)(efi_rng_protocol_t *,
+						 efi_guid_t *, unsigned long,
+						 u8 *out);
 	};
 	struct {
 		u32 get_info;
include/linux/efi.h +87 -69

247 lines not on this page · raw message

Signature · 1 line
2.17.1
author maintainer
#10 lore raw

PATCH v2 10/21 efi/libstub/x86: avoid thunking for native firmware calls

In reply to Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

---
 arch/x86/boot/compressed/Makefile      |  2 +-
 arch/x86/boot/compressed/efi_stub_32.S | 87 --------------------
 arch/x86/boot/compressed/efi_stub_64.S |  5 --
 arch/x86/boot/compressed/head_32.S     |  6 --
 arch/x86/boot/compressed/head_64.S     | 12 ---
 arch/x86/include/asm/efi.h             | 30 +++----
 arch/x86/platform/efi/efi_64.c         |  2 -
 7 files changed, 17 insertions(+), 127 deletions(-)
arch/x86/boot/compressed/Makefile +1 -1
index aa976adb7094..a20f55c59753 100644
@@ -89,7 +89,7 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
 
 $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
 
-vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \
+vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o \
 	$(objtree)/drivers/firmware/efi/libstub/lib.a
 vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
 
arch/x86/boot/compressed/efi_stub_32.S deleted +0 -87

87 lines not on this page · raw message

arch/x86/boot/compressed/efi_stub_64.S deleted +0 -5
deleted file mode 100644
index 99494dff2113..000000000000
@@ -1,5 +0,0 @@
-#include <asm/segment.h>
-#include <asm/msr.h>
-#include <asm/processor-flags.h>
-
-#include "../../platform/efi/efi_stub_64.S"
arch/x86/boot/compressed/head_32.S +0 -6
index 40468ab49b9b..7da4dfc53df6 100644
@@ -161,9 +161,7 @@ SYM_FUNC_START(efi_pe_entry)
 	popl	%ecx
 	movl	%ecx, efi32_config+8(%esi)	/* EFI System table pointer */
 
-	/* Relocate efi_config->call() */
 	leal	efi32_config(%esi), %eax
-	add	%esi, 28(%eax)
 	pushl	%eax
 
 	call	make_boot_params
@@ -188,9 +186,7 @@ SYM_FUNC_START(efi32_stub_entry)
 	movl	%ecx, efi32_config(%esi)	/* Handle */
 	movl	%edx, efi32_config+8(%esi)	/* EFI System table pointer */
 
-	/* Relocate efi_config->call() */
 	leal	efi32_config(%esi), %eax
-	add	%esi, 28(%eax)
 	pushl	%eax
 2:
 	call	efi_main
@@ -266,8 +262,6 @@ SYM_FUNC_END(.Lrelocated)
 	.data
 efi32_config:
 	.fill 7,4,0
-	.long efi_call_phys
-	.long 0
 	.byte 0
 #endif
 
arch/x86/boot/compressed/head_64.S +0 -12
index 58a512e33d8d..6dc6a7ebb9e1 100644
@@ -458,11 +458,6 @@ SYM_FUNC_START(efi_pe_entry)
 1:	popq	%rbp
 	subq	$1b, %rbp
 
-	/*
-	 * Relocate efi_config->call().
-	 */
-	addq	%rbp, efi64_config+40(%rip)
-
 	movq	%rax, %rdi
 	call	make_boot_params
 	cmpq	$0,%rax
@@ -477,11 +472,6 @@ handover_entry:
 1:	popq	%rbp
 	subq	$1b, %rbp
 
-	/*
-	 * Relocate efi_config->call().
-	 */
-	movq	efi_config(%rip), %rax
-	addq	%rbp, 40(%rax)
 2:
 	movq	efi_config(%rip), %rdi
 	call	efi_main
@@ -683,14 +673,12 @@ SYM_DATA_LOCAL(efi_config, .quad 0)
 #ifdef CONFIG_EFI_MIXED
 SYM_DATA_START(efi32_config)
 	.fill	5,8,0
-	.quad	efi64_thunk
 	.byte	0
 SYM_DATA_END(efi32_config)
 #endif
 
 SYM_DATA_START(efi64_config)
 	.fill	5,8,0
-	.quad	efi_call
 	.byte	1
 SYM_DATA_END(efi64_config)
 #endif /* CONFIG_EFI_STUB */
arch/x86/include/asm/efi.h +16 -14
index 183cd49e0495..e0789ec5c9f6 100644
@@ -152,6 +152,7 @@ struct efi_setup_data {
 extern u64 efi_setup;
 
 #ifdef CONFIG_EFI
+extern efi_status_t efi64_thunk(u32, ...);
 
 static inline bool efi_is_mixed(void)
 {
@@ -205,7 +206,6 @@ struct efi_config {
 	efi_runtime_services_t *runtime_services;
 	efi_boot_services_t *boot_services;
 	efi_simple_text_output_protocol_t *text_output;
-	efi_status_t (*call)(unsigned long, ...);
 	bool is64;
 } __packed;
 
@@ -235,30 +235,32 @@ static inline bool efi_is_native(void)
 			(unsigned long)(attr), (attr))
 
 #define efi_table_attr(table, attr, instance) ({			\
-	__typeof__(((table##_t *)0)->attr) __ret;			\
+	__typeof__(instance->attr) __ret;				\
 	if (efi_is_native()) {						\
-		__ret = ((table##_t *)(unsigned long)instance)->attr;	\
+		__ret = instance->attr;					\
 	} else {							\
-		__ret = (__typeof__(__ret))efi_mixed_mode_cast(		\
-		((table##_t *)(unsigned long)instance)->mixed_mode.attr);\
+		__ret = (__typeof__(__ret))				\
+			efi_mixed_mode_cast(instance->mixed_mode.attr);	\
 	}								\
 	__ret;								\
 })
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(protocol, f, instance),	\
-		instance, ##__VA_ARGS__)
+	(efi_is_native()						\
+		? instance->f(instance, ##__VA_ARGS__)			\
+		: efi64_thunk(instance->mixed_mode.f, instance,	##__VA_ARGS__))
 
 #define efi_call_early(f, ...)						\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(efi_boot_services, f,	\
-		__efi_early()->boot_services), __VA_ARGS__)
+	(efi_is_native()						\
+		? __efi_early()->boot_services->f(__VA_ARGS__)		\
+		: efi64_thunk(__efi_early()->boot_services->mixed_mode.f,\
+			__VA_ARGS__))
 
 #define efi_call_runtime(f, ...)					\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(efi_runtime_services, f,	\
-		__efi_early()->runtime_services), __VA_ARGS__)
+	(efi_is_native()						\
+		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
+		: efi64_thunk(__efi_early()->runtime_services->mixed_mode.f,\
+			__VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
arch/x86/platform/efi/efi_64.c +0 -2
index 885e50a707a6..03c2ed3c645c 100644
@@ -635,8 +635,6 @@ void efi_switch_mm(struct mm_struct *mm)
 }
 
 #ifdef CONFIG_EFI_MIXED
-extern efi_status_t efi64_thunk(u32, ...);
-
 static DEFINE_SPINLOCK(efi_runtime_lock);
 
 #define runtime_service32(func)						 \
Signature · 1 line
2.17.1
#11 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Hi Ard,

Quoted 9 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Regards,

Hans

Quoted 283 lines
---
  arch/x86/boot/compressed/Makefile      |  2 +-
  arch/x86/boot/compressed/efi_stub_32.S | 87 --------------------
  arch/x86/boot/compressed/efi_stub_64.S |  5 --
  arch/x86/boot/compressed/head_32.S     |  6 --
  arch/x86/boot/compressed/head_64.S     | 12 ---
  arch/x86/include/asm/efi.h             | 30 +++----
  arch/x86/platform/efi/efi_64.c         |  2 -
  7 files changed, 17 insertions(+), 127 deletions(-)
arch/x86/boot/compressed/Makefile +1 -1
index aa976adb7094..a20f55c59753 100644
@@ -89,7 +89,7 @@ vmlinux-objs-$(CONFIG_ACPI) += $(obj)/acpi.o
 
  $(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
 
-vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o \
+vmlinux-objs-$(CONFIG_EFI_STUB) += $(obj)/eboot.o \
  	$(objtree)/drivers/firmware/efi/libstub/lib.a
  vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_thunk_$(BITS).o
 
arch/x86/boot/compressed/efi_stub_32.S deleted +0 -87
deleted file mode 100644
index ed6c351d34ed..000000000000
@@ -1,87 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * EFI call stub for IA32.
- *
- * This stub allows us to make EFI calls in physical mode with interrupts
- * turned off. Note that this implementation is different from the one in
- * arch/x86/platform/efi/efi_stub_32.S because we're _already_ in physical
- * mode at this point.
- */
-
-#include <linux/linkage.h>
-#include <asm/page_types.h>
-
-/*
- * efi_call_phys(void *, ...) is a function with variable parameters.
- * All the callers of this function assure that all the parameters are 4-bytes.
- */
-
-/*
- * In gcc calling convention, EBX, ESP, EBP, ESI and EDI are all callee save.
- * So we'd better save all of them at the beginning of this function and restore
- * at the end no matter how many we use, because we can not assure EFI runtime
- * service functions will comply with gcc calling convention, too.
- */
-
-.text
-SYM_FUNC_START(efi_call_phys)
-	/*
-	 * 0. The function can only be called in Linux kernel. So CS has been
-	 * set to 0x0010, DS and SS have been set to 0x0018. In EFI, I found
-	 * the values of these registers are the same. And, the corresponding
-	 * GDT entries are identical. So I will do nothing about segment reg
-	 * and GDT, but change GDT base register in prelog and epilog.
-	 */
-
-	/*
-	 * 1. Because we haven't been relocated by this point we need to
-	 * use relative addressing.
-	 */
-	call	1f
-1:	popl	%edx
-	subl	$1b, %edx
-
-	/*
-	 * 2. Now on the top of stack is the return
-	 * address in the caller of efi_call_phys(), then parameter 1,
-	 * parameter 2, ..., param n. To make things easy, we save the return
-	 * address of efi_call_phys in a global variable.
-	 */
-	popl	%ecx
-	movl	%ecx, saved_return_addr(%edx)
-	/* get the function pointer into ECX*/
-	popl	%ecx
-	movl	%ecx, efi_rt_function_ptr(%edx)
-
-	/*
-	 * 3. Call the physical function.
-	 */
-	call	*%ecx
-
-	/*
-	 * 4. Balance the stack. And because EAX contain the return value,
-	 * we'd better not clobber it. We need to calculate our address
-	 * again because %ecx and %edx are not preserved across EFI function
-	 * calls.
-	 */
-	call	1f
-1:	popl	%edx
-	subl	$1b, %edx
-
-	movl	efi_rt_function_ptr(%edx), %ecx
-	pushl	%ecx
-
-	/*
-	 * 10. Push the saved return address onto the stack and return.
-	 */
-	movl	saved_return_addr(%edx), %ecx
-	pushl	%ecx
-	ret
-SYM_FUNC_END(efi_call_phys)
-.previous
-
-.data
-saved_return_addr:
-	.long 0
-efi_rt_function_ptr:
-	.long 0
arch/x86/boot/compressed/efi_stub_64.S deleted +0 -5
deleted file mode 100644
index 99494dff2113..000000000000
@@ -1,5 +0,0 @@
-#include <asm/segment.h>
-#include <asm/msr.h>
-#include <asm/processor-flags.h>
-
-#include "../../platform/efi/efi_stub_64.S"
arch/x86/boot/compressed/head_32.S +0 -6
index 40468ab49b9b..7da4dfc53df6 100644
@@ -161,9 +161,7 @@ SYM_FUNC_START(efi_pe_entry)
  	popl	%ecx
  	movl	%ecx, efi32_config+8(%esi)	/* EFI System table pointer */
 
-	/* Relocate efi_config->call() */
  	leal	efi32_config(%esi), %eax
-	add	%esi, 28(%eax)
  	pushl	%eax
 
  	call	make_boot_params
@@ -188,9 +186,7 @@ SYM_FUNC_START(efi32_stub_entry)
  	movl	%ecx, efi32_config(%esi)	/* Handle */
  	movl	%edx, efi32_config+8(%esi)	/* EFI System table pointer */
 
-	/* Relocate efi_config->call() */
  	leal	efi32_config(%esi), %eax
-	add	%esi, 28(%eax)
  	pushl	%eax
  2:
  	call	efi_main
@@ -266,8 +262,6 @@ SYM_FUNC_END(.Lrelocated)
  	.data
  efi32_config:
  	.fill 7,4,0
-	.long efi_call_phys
-	.long 0
  	.byte 0
  #endif
 
arch/x86/boot/compressed/head_64.S +0 -12
index 58a512e33d8d..6dc6a7ebb9e1 100644
@@ -458,11 +458,6 @@ SYM_FUNC_START(efi_pe_entry)
  1:	popq	%rbp
  	subq	$1b, %rbp
 
-	/*
-	 * Relocate efi_config->call().
-	 */
-	addq	%rbp, efi64_config+40(%rip)
-
  	movq	%rax, %rdi
  	call	make_boot_params
  	cmpq	$0,%rax
@@ -477,11 +472,6 @@ handover_entry:
  1:	popq	%rbp
  	subq	$1b, %rbp
 
-	/*
-	 * Relocate efi_config->call().
-	 */
-	movq	efi_config(%rip), %rax
-	addq	%rbp, 40(%rax)
  2:
  	movq	efi_config(%rip), %rdi
  	call	efi_main
@@ -683,14 +673,12 @@ SYM_DATA_LOCAL(efi_config, .quad 0)
  #ifdef CONFIG_EFI_MIXED
  SYM_DATA_START(efi32_config)
  	.fill	5,8,0
-	.quad	efi64_thunk
  	.byte	0
  SYM_DATA_END(efi32_config)
  #endif
 
  SYM_DATA_START(efi64_config)
  	.fill	5,8,0
-	.quad	efi_call
  	.byte	1
  SYM_DATA_END(efi64_config)
  #endif /* CONFIG_EFI_STUB */
arch/x86/include/asm/efi.h +16 -14
index 183cd49e0495..e0789ec5c9f6 100644
@@ -152,6 +152,7 @@ struct efi_setup_data {
  extern u64 efi_setup;
 
  #ifdef CONFIG_EFI
+extern efi_status_t efi64_thunk(u32, ...);
 
  static inline bool efi_is_mixed(void)
  {
@@ -205,7 +206,6 @@ struct efi_config {
  	efi_runtime_services_t *runtime_services;
  	efi_boot_services_t *boot_services;
  	efi_simple_text_output_protocol_t *text_output;
-	efi_status_t (*call)(unsigned long, ...);
  	bool is64;
  } __packed;
 
@@ -235,30 +235,32 @@ static inline bool efi_is_native(void)
  			(unsigned long)(attr), (attr))
 
  #define efi_table_attr(table, attr, instance) ({			\
-	__typeof__(((table##_t *)0)->attr) __ret;			\
+	__typeof__(instance->attr) __ret;				\
  	if (efi_is_native()) {						\
-		__ret = ((table##_t *)(unsigned long)instance)->attr;	\
+		__ret = instance->attr;					\
  	} else {							\
-		__ret = (__typeof__(__ret))efi_mixed_mode_cast(		\
-		((table##_t *)(unsigned long)instance)->mixed_mode.attr);\
+		__ret = (__typeof__(__ret))				\
+			efi_mixed_mode_cast(instance->mixed_mode.attr);	\
  	}								\
  	__ret;								\
  })
 
  #define efi_call_proto(protocol, f, instance, ...)			\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(protocol, f, instance),	\
-		instance, ##__VA_ARGS__)
+	(efi_is_native()						\
+		? instance->f(instance, ##__VA_ARGS__)			\
+		: efi64_thunk(instance->mixed_mode.f, instance,	##__VA_ARGS__))
 
  #define efi_call_early(f, ...)						\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(efi_boot_services, f,	\
-		__efi_early()->boot_services), __VA_ARGS__)
+	(efi_is_native()						\
+		? __efi_early()->boot_services->f(__VA_ARGS__)		\
+		: efi64_thunk(__efi_early()->boot_services->mixed_mode.f,\
+			__VA_ARGS__))
 
  #define efi_call_runtime(f, ...)					\
-	__efi_early()->call((unsigned long)				\
-				efi_table_attr(efi_runtime_services, f,	\
-		__efi_early()->runtime_services), __VA_ARGS__)
+	(efi_is_native()						\
+		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
+		: efi64_thunk(__efi_early()->runtime_services->mixed_mode.f,\
+			__VA_ARGS__))
 
  extern bool efi_reboot_required(void);
  extern bool efi_is_table_address(unsigned long phys_addr);
arch/x86/platform/efi/efi_64.c +0 -2
index 885e50a707a6..03c2ed3c645c 100644
@@ -635,8 +635,6 @@ void efi_switch_mm(struct mm_struct *mm)
  }
 
  #ifdef CONFIG_EFI_MIXED
-extern efi_status_t efi64_thunk(u32, ...);
-
  static DEFINE_SPINLOCK(efi_runtime_lock);
 
  #define runtime_service32(func)						 \
#12 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 9 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

#13 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 46 lines · Ard Biesheuvel
Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 9 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

Quoted 7 lines

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

... or in case that does not help, I have updated my -v3 branch to split the removal of the thunking sequence for x86_64 in several discrete steps.

#14 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

In reply to Ard Biesheuvel
Quoted 49 lines · Ard Biesheuvel
Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 10 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

Quoted 7 lines

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

Hmm. Most of the kernel is compiled with the stack alignment set to 8, and there a lot of asm that makes no effort to preserve alignment beyond 8 bytes. So if EFI calls need 16 byte alignment, you may need to do something special.

On new enough gcc (the versions that actually support the flags to set the alignment to 8), maybe you can use function attributes, or maybe you can stick a 16-byte-aligned local variable in functions that call EFI functions? The latter would be rather fragile.

#15 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 54 lines · Andy Lutomirski
Quoted 54 lines · Ard Biesheuvel
Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 10 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

Quoted 7 lines
Quoted 3 lines

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

Hmm. Most of the kernel is compiled with the stack alignment set to 8, and there a lot of asm that makes no effort to preserve alignment beyond 8 bytes. So if EFI calls need 16 byte alignment, you may need to do something special.

On new enough gcc (the versions that actually support the flags to set the alignment to 8), maybe you can use function attributes, or maybe you can stick a 16-byte-aligned local variable in functions that call EFI functions? The latter would be rather fragile.

This patch replaces open coded SysV to MS calling convention translation to GCC generated code (using __attribute__((ms_abi)) which we have been using for a long time in EDK2), because the former relies on a wrapper function

efi_call(fn, ...)

which is type unsafe and relies on a lot of nasty casting, especially combined with the mixed mode support. efi_call() is implemented as below, and as Hans reports, omitting this sequence causes a boot regression on one of the platforms he has tested this on.

So the question is which of the pieces below this UEFI implementation is actually relying on, and the stack pointer alignment is my first guess, but it could be any of the other things as well. Once we identify what it is we are missing, I can simply stick it back in, but without reverting to using the efi_call() thunk.

Note that the decompressor/stub are built with the default stack alignment of 16 afaict, but if GRUB enters the decompressor with a misaligned stack, we probably wouldn't notice until we hit something like a movaps, right?

Thanks,
Ard.

#define SAVE_XMM \
    mov %rsp, %rax; \
    subq $0x70, %rsp; \
    and $~0xf, %rsp; \
    mov %rax, (%rsp); \
    mov %cr0, %rax; \
    clts; \
    mov %rax, 0x8(%rsp); \
    movaps %xmm0, 0x60(%rsp); \
    movaps %xmm1, 0x50(%rsp); \
    movaps %xmm2, 0x40(%rsp); \
    movaps %xmm3, 0x30(%rsp); \
    movaps %xmm4, 0x20(%rsp); \
    movaps %xmm5, 0x10(%rsp)
#define RESTORE_XMM \
    movaps 0x60(%rsp), %xmm0; \
    movaps 0x50(%rsp), %xmm1; \
    movaps 0x40(%rsp), %xmm2; \
    movaps 0x30(%rsp), %xmm3; \
    movaps 0x20(%rsp), %xmm4; \
    movaps 0x10(%rsp), %xmm5; \
    mov 0x8(%rsp), %rsi; \
    mov %rsi, %cr0; \
    mov (%rsp), %rsp

SYM_FUNC_START(efi_call)

    pushq %rbp
    movq %rsp, %rbp
    SAVE_XMM
    mov 16(%rbp), %rax
    subq $48, %rsp
    mov %r9, 32(%rsp)
    mov %rax, 40(%rsp)
    mov %r8, %r9
    mov %rcx, %r8
    mov %rsi, %rcx
    call *%rdi
    addq $48, %rsp
    RESTORE_XMM
    popq %rbp
    ret

SYM_FUNC_END(efi_call)

#16 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 24 lines · Ard Biesheuvel
Quoted 4 lines · Andy Lutomirski

Hmm. Most of the kernel is compiled with the stack alignment set to 8, and there a lot of asm that makes no effort to preserve alignment beyond 8 bytes. So if EFI calls need 16 byte alignment, you may need to do something special.

On new enough gcc (the versions that actually support the flags to set the alignment to 8), maybe you can use function attributes, or maybe you can stick a 16-byte-aligned local variable in functions that call EFI functions? The latter would be rather fragile.

This patch replaces open coded SysV to MS calling convention translation to GCC generated code (using __attribute__((ms_abi)) which we have been using for a long time in EDK2), because the former relies on a wrapper function

efi_call(fn, ...)

which is type unsafe and relies on a lot of nasty casting, especially combined with the mixed mode support. efi_call() is implemented as below, and as Hans reports, omitting this sequence causes a boot regression on one of the platforms he has tested this on.

So the question is which of the pieces below this UEFI implementation is actually relying on, and the stack pointer alignment is my first guess, but it could be any of the other things as well. Once we identify what it is we are missing, I can simply stick it back in, but without reverting to using the efi_call() thunk.

Quoted 8 lines

Note that the decompressor/stub are built with the default stack alignment of 16 afaict, but if GRUB enters the decompressor with a misaligned stack, we probably wouldn't notice until we hit something like a movaps, right?

Thanks,
Ard.

Won't the entry code misalign the stack when efi_main is called, assuming the stack was properly aligned at efi_stub_entry? There needs to be a sub $8, %rsp in there, no?

arch/x86/boot/compressed/head_64.S:

#ifdef CONFIG_EFI_STUB
        .org 0x390

SYM_FUNC_START(efi64_stub_entry)
SYM_FUNC_START_ALIAS(efi_stub_entry)
movq $1, %rcx
handover_entry:

        call    efi_main	<--- this will enter efi_main with a misaligned stack?
        movq    %rax,%rsi
        movl    BP_code32_start(%esi), %eax
        leaq    startup_64(%rax), %rax
        jmp     *%rax

SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
#endif

#17 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 47 lines · Arvind Sankar
Quoted 24 lines · Ard Biesheuvel
Quoted 4 lines · Andy Lutomirski

Hmm. Most of the kernel is compiled with the stack alignment set to 8, and there a lot of asm that makes no effort to preserve alignment beyond 8 bytes. So if EFI calls need 16 byte alignment, you may need to do something special.

On new enough gcc (the versions that actually support the flags to set the alignment to 8), maybe you can use function attributes, or maybe you can stick a 16-byte-aligned local variable in functions that call EFI functions? The latter would be rather fragile.

This patch replaces open coded SysV to MS calling convention translation to GCC generated code (using __attribute__((ms_abi)) which we have been using for a long time in EDK2), because the former relies on a wrapper function

efi_call(fn, ...)

which is type unsafe and relies on a lot of nasty casting, especially combined with the mixed mode support. efi_call() is implemented as below, and as Hans reports, omitting this sequence causes a boot regression on one of the platforms he has tested this on.

So the question is which of the pieces below this UEFI implementation is actually relying on, and the stack pointer alignment is my first guess, but it could be any of the other things as well. Once we identify what it is we are missing, I can simply stick it back in, but without reverting to using the efi_call() thunk.

Quoted 8 lines

Note that the decompressor/stub are built with the default stack alignment of 16 afaict, but if GRUB enters the decompressor with a misaligned stack, we probably wouldn't notice until we hit something like a movaps, right?

Thanks,
Ard.

Won't the entry code misalign the stack when efi_main is called, assuming the stack was properly aligned at efi_stub_entry? There needs to be a sub $8, %rsp in there, no?

arch/x86/boot/compressed/head_64.S:

#ifdef CONFIG_EFI_STUB
        .org 0x390

SYM_FUNC_START(efi64_stub_entry)
SYM_FUNC_START_ALIAS(efi_stub_entry)
movq $1, %rcx
handover_entry:

Quoted 9 lines
        call    efi_main        <--- this will enter efi_main with a misaligned stack?
        movq    %rax,%rsi
        movl    BP_code32_start(%esi), %eax
        leaq    startup_64(%rax), %rax
        jmp     *%rax

SYM_FUNC_END(efi64_stub_entry)
SYM_FUNC_END_ALIAS(efi_stub_entry)
#endif

Indeed, well spotted. Note that the above is the version from the top of that branch, but the version that Hans tested isn't any different.

#18 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

In reply to Ard Biesheuvel

Hi Ard,

Quoted 45 lines · Ard Biesheuvel
Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 9 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

Quoted 7 lines

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

Ok, so I made this change on top of a37d90a2c570a25926fd1645482cb9f3c1d042a0 (the last "efi/libstub/x86: avoid thunking for native firmware calls" version I tried before reporting this problem) :

arch/x86/boot/compressed/head_64.S +1 -0
@@ -464,6 +464,7 @@ SYM_FUNC_START(efi_pe_entry)
 
 handover_entry:
        movq    efi_config(%rip), %rdi
+       and     $~0xf, %rsp
        call    efi_main
        movq    %rax,%rsi
        cmpq    $0,%rax

And that does the trick, the GPD pocket boots successfully with commit a37d90a2c570a25926fd1645482cb9f3c1d042a0 + the above change.

So it looks like your first hunch on how to fix this is correct :)

Regards,

Hans

#19 lore raw

Re: [PATCH v2 10/21] efi/libstub/x86: avoid thunking for native firmware calls

Quoted 72 lines · Hans de Goede

Hi Ard,

Quoted 45 lines · Ard Biesheuvel
Quoted 35 lines · Hans de Goede

Hi Ard,

Quoted 9 lines · Ard Biesheuvel

We use special wrapper routines to invoke firmware services in the native case as well as the mixed mode case. For mixed mode, the need is obvious, but for the native cases, we can simply rely on the compiler to generate the indirect call, given that GCC now has support for the MS calling convention (and has had it for quite some time now). Note that on i386, the decompressor and the EFI stub are not built with -mregparm=3 like the rest of the i386 kernel, so we can safely allow the compiler to emit the indirect calls here as well.

Quoted 4 lines

So drop all the wrappers and indirection, and switch to either native calls, or direct calls into the thunk routine for mixed mode.

I'm afraid that this patch breaks the boot on one of my machines.

Specifically this patch breaks my GDP pocket machine. This is a Cherry Trail device with a 64 UEFI running a 64 bit kernel build.

As soon as I cherry pick this patch into my personal 5.5.0-rc2 based tree, the GPD pocket stops booting and it stop so early on that I get 0 debug output. I guess I could try adding a few pr_efi_err calls and see if those still do something.

I noticed that you have made some changes to this patch, I've tried updating it to the version from your efistub-x86-cleanup-v3 branch, commit id a37d90a2c570a25926fd1645482cb9f3c1d042a0 and I have also cherry-picked the latest version of all preceding commits, unfortunately even with the new version, the GPD pocket still hangs at boot.

Quoted 6 lines

Unfortunately the nature of this patch makes it hard to figure out the root cause of this issue...

I've also tried another Cherry Trail device with 64 bit UEFI and that does not suffer from this problem.

Thanks Hans.

Quoted 7 lines

There are a number of things that change in the way the calls are made, but the most obvious thing to check is whether the stack needs to be aligned, since that is no longer being done.

If you have time to experiment a bit more, could you check whether doing 'and $~0xf %rsp' before 'call efi_main' in the .S stub code for x86_64 makes a difference?

Ok, so I made this change on top of a37d90a2c570a25926fd1645482cb9f3c1d042a0 (the last "efi/libstub/x86: avoid thunking for native firmware calls" version I tried before reporting this problem) :

arch/x86/boot/compressed/head_64.S +1 -0
@@ -464,6 +464,7 @@ SYM_FUNC_START(efi_pe_entry)
 
  handover_entry:
         movq    efi_config(%rip), %rdi
+       and     $~0xf, %rsp
         call    efi_main
         movq    %rax,%rsi
         cmpq    $0,%rax
Quoted 5 lines

And that does the trick, the GPD pocket boots successfully with commit a37d90a2c570a25926fd1645482cb9f3c1d042a0 + the above change.

So it looks like your first hunch on how to fix this is correct :)

Excellent! Thanks for testing.

I'll fold this in.

author maintainer
#20 lore raw

PATCH v2 11/21 efi/libstub: get rid of 'sys_table_arg' macro parameter

In reply to Ard Biesheuvel

The efi_call macros on ARM have a dependency on a variable 'sys_table_arg' existing in the scope of the macro instantiation. Since this variable always points to the same data structure, let's create a global getter for it and use that instead.

Note that the use of a global variable with external linkage is avoided, given the problems we had in the past with early processing of the GOT tables.

While at it, drop the redundant casts in the efi_table_attr and efi_call_proto macros.

---
 arch/arm/include/asm/efi.h              |  8 ++++----
 arch/arm64/include/asm/efi.h            |  8 ++++----
 arch/x86/boot/compressed/eboot.c        |  5 +++++
 drivers/firmware/efi/libstub/arm-stub.c | 11 ++++++++++-
 drivers/firmware/efi/libstub/efistub.h  |  2 ++
 drivers/firmware/efi/libstub/gop.c      |  2 ++
 6 files changed, 27 insertions(+), 9 deletions(-)
arch/arm/include/asm/efi.h +4 -4
index 9b0c64c28bff..555364b7bd2a 100644
@@ -50,15 +50,15 @@ void efi_virtmap_unload(void);
 
 /* arch specific definitions used by the stub code */
 
-#define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
-#define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
+#define efi_call_early(f, ...)		efi_system_table()->boottime->f(__VA_ARGS__)
+#define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
 #define efi_is_native()			(true)
 
 #define efi_table_attr(table, attr, instance)				\
-	((table##_t *)instance)->attr
+	instance->attr
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	((protocol##_t *)instance)->f(instance, ##__VA_ARGS__)
+	instance->f(instance, ##__VA_ARGS__)
 
 struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg);
 void free_screen_info(efi_system_table_t *sys_table, struct screen_info *si);
arch/arm64/include/asm/efi.h +4 -4
index 189082c44c28..9aa518d67588 100644
@@ -93,15 +93,15 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 	return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define efi_call_early(f, ...)		sys_table_arg->boottime->f(__VA_ARGS__)
-#define efi_call_runtime(f, ...)	sys_table_arg->runtime->f(__VA_ARGS__)
+#define efi_call_early(f, ...)		efi_system_table()->boottime->f(__VA_ARGS__)
+#define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
 #define efi_is_native()			(true)
 
 #define efi_table_attr(table, attr, instance)				\
-	((table##_t *)instance)->attr
+	instance->attr
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	((protocol##_t *)instance)->f(instance, ##__VA_ARGS__)
+	instance->f(instance, ##__VA_ARGS__)
 
 #define alloc_screen_info(x...)		&screen_info
 
arch/x86/boot/compressed/eboot.c +5 -0
index 4a965212ee64..bed78679e10b 100644
@@ -27,6 +27,11 @@ __pure const struct efi_config *__efi_early(void)
 	return efi_early;
 }
 
+__pure efi_system_table_t *efi_system_table(void)
+{
+	return sys_table;
+}
+
 static void setup_boot_services(struct efi_config *c)
 {
 	c->runtime_services	= efi_table_attr(efi_system_table, runtime, sys_table);
drivers/firmware/efi/libstub/arm-stub.c +10 -1
index 60a301e1c072..47f072ac3f30 100644
@@ -37,6 +37,13 @@
 
 static u64 virtmap_base = EFI_RT_VIRTUAL_BASE;
 
+static efi_system_table_t *__section(.data) sys_table;
+
+__pure efi_system_table_t *efi_system_table(void)
+{
+	return sys_table;
+}
+
 void efi_char16_printk(efi_system_table_t *sys_table_arg,
 			      efi_char16_t *str)
 {
@@ -110,7 +117,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
  * for both archictectures, with the arch-specific code provided in the
  * handle_kernel_image() function.
  */
-unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
+unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 			       unsigned long *image_addr)
 {
 	efi_loaded_image_t *image;
@@ -131,6 +138,8 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table,
 	enum efi_secureboot_mode secure_boot;
 	struct screen_info *si;
 
+	sys_table = sys_table_arg;
+
 	/* Check if we were booted by the EFI firmware */
 	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
 		goto fail;
drivers/firmware/efi/libstub/efistub.h +2 -0
index 05739ae013c8..e6775c16a97d 100644
@@ -29,6 +29,8 @@ extern int __pure nokaslr(void);
 extern int __pure is_quiet(void);
 extern int __pure novamap(void);
 
+extern __pure efi_system_table_t  *efi_system_table(void);
+
 #define pr_efi(sys_table, msg)		do {				\
 	if (!is_quiet()) efi_printk(sys_table, "EFI stub: "msg);	\
 } while (0)
drivers/firmware/efi/libstub/gop.c +2 -0
index 5f4fbc2ac687..6c49d0a9aa3f 100644
@@ -10,6 +10,8 @@
 #include <asm/efi.h>
 #include <asm/setup.h>
 
+#include "efistub.h"
+
 static void find_bits(unsigned long mask, u8 *pos, u8 *size)
 {
 	u8 first, len;
Signature · 1 line
2.17.1
author maintainer
#21 lore raw

PATCH v2 12/21 efi/libstub: unify the efi_char16_printk implementations

In reply to Ard Biesheuvel

Use a single implementation for efi_char16_printk() across all architectures.

---
 arch/x86/boot/compressed/eboot.c               | 6 ------
 drivers/firmware/efi/libstub/arm-stub.c        | 9 ---------
 drivers/firmware/efi/libstub/efi-stub-helper.c | 9 +++++++++
 3 files changed, 9 insertions(+), 15 deletions(-)
arch/x86/boot/compressed/eboot.c +0 -6
index bed78679e10b..2bcab1ef5a56 100644
@@ -39,12 +39,6 @@ static void setup_boot_services(struct efi_config *c)
 	c->text_output		= efi_table_attr(efi_system_table, con_out, sys_table);
 }
 
-void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
-{
-	efi_call_proto(efi_simple_text_output_protocol, output_string,
-		       efi_early->text_output, str);
-}
-
 static efi_status_t
 preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 {
drivers/firmware/efi/libstub/arm-stub.c +0 -9
index 47f072ac3f30..b44b1bd4d480 100644
@@ -44,15 +44,6 @@ __pure efi_system_table_t *efi_system_table(void)
 	return sys_table;
 }
 
-void efi_char16_printk(efi_system_table_t *sys_table_arg,
-			      efi_char16_t *str)
-{
-	efi_simple_text_output_protocol_t *out;
-
-	out = (efi_simple_text_output_protocol_t *)sys_table_arg->con_out;
-	out->output_string(out, str);
-}
-
 static struct screen_info *setup_graphics(efi_system_table_t *sys_table_arg)
 {
 	efi_guid_t gop_proto = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
drivers/firmware/efi/libstub/efi-stub-helper.c +9 -0
index 5eaf6ada960f..3b89317f4c80 100644
@@ -975,3 +975,12 @@ void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid)
 	}
 	return NULL;
 }
+
+void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
+{
+	efi_call_proto(efi_simple_text_output_protocol,
+		       output_string,
+		       efi_table_attr(efi_system_table, con_out,
+				      efi_system_table()),
+		       str);
+}
Signature · 1 line
2.17.1
author maintainer
#22 lore raw

PATCH v2 13/21 efi/libstub/x86: drop __efi_early() export of efi_config struct

In reply to Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

---
 arch/x86/boot/compressed/eboot.c   | 36 ++++++++------------
 arch/x86/boot/compressed/head_32.S |  2 +-
 arch/x86/boot/compressed/head_64.S |  4 +--
 arch/x86/include/asm/efi.h         | 23 +++++--------
 4 files changed, 26 insertions(+), 39 deletions(-)
arch/x86/boot/compressed/eboot.c +15 -21
index 2bcab1ef5a56..53e67334c4c3 100644
@@ -20,11 +20,17 @@
 
 static efi_system_table_t *sys_table;
 
-static struct efi_config *efi_early;
+struct efi_config {
+	u64 image_handle;
+	u64 table;
+	bool is64;
+} __packed;
 
-__pure const struct efi_config *__efi_early(void)
+static bool is64;
+
+__pure bool __efi_early_is64(void)
 {
-	return efi_early;
+	return is64;
 }
 
 __pure efi_system_table_t *efi_system_table(void)
@@ -32,13 +38,6 @@ __pure efi_system_table_t *efi_system_table(void)
 	return sys_table;
 }
 
-static void setup_boot_services(struct efi_config *c)
-{
-	c->runtime_services	= efi_table_attr(efi_system_table, runtime, sys_table);
-	c->boot_services	= efi_table_attr(efi_system_table, boottime, sys_table);
-	c->text_output		= efi_table_attr(efi_system_table, con_out, sys_table);
-}
-
 static efi_status_t
 preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 {
@@ -382,16 +381,14 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	unsigned long ramdisk_addr;
 	unsigned long ramdisk_size;
 
-	efi_early = c;
-	sys_table = (efi_system_table_t *)(unsigned long)efi_early->table;
-	handle = (void *)(unsigned long)efi_early->image_handle;
+	sys_table = (efi_system_table_t *)(unsigned long)c->table;
+	handle = (void *)(unsigned long)c->image_handle;
+	is64 = c->is64;
 
 	/* Check if we were booted by the EFI firmware */
 	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
 		return NULL;
 
-	setup_boot_services(efi_early);
-
 	status = efi_call_early(handle_protocol, handle,
 				&proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
@@ -738,10 +735,9 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	efi_system_table_t *_table;
 	unsigned long cmdline_paddr;
 
-	efi_early = c;
-
-	_table = (efi_system_table_t *)(unsigned long)efi_early->table;
-	handle = (void *)(unsigned long)efi_early->image_handle;
+	_table = (efi_system_table_t *)(unsigned long)c->table;
+	handle = (void *)(unsigned long)c->image_handle;
+	is64 = c->is64;
 
 	sys_table = _table;
 
@@ -749,8 +745,6 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
 		goto fail;
 
-	setup_boot_services(efi_early);
-
 	/*
 	 * make_boot_params() may have been called before efi_main(), in which
 	 * case this is the second time we parse the cmdline. This is ok,
arch/x86/boot/compressed/head_32.S +1 -1
index 7da4dfc53df6..c026ab881405 100644
@@ -261,7 +261,7 @@ SYM_FUNC_END(.Lrelocated)
 #ifdef CONFIG_EFI_STUB
 	.data
 efi32_config:
-	.fill 7,4,0
+	.fill 4,4,0
 	.byte 0
 #endif
 
arch/x86/boot/compressed/head_64.S +2 -2
index 6dc6a7ebb9e1..72065735abc1 100644
@@ -672,13 +672,13 @@ SYM_DATA_LOCAL(efi_config, .quad 0)
 
 #ifdef CONFIG_EFI_MIXED
 SYM_DATA_START(efi32_config)
-	.fill	5,8,0
+	.fill	2,8,0
 	.byte	0
 SYM_DATA_END(efi32_config)
 #endif
 
 SYM_DATA_START(efi64_config)
-	.fill	5,8,0
+	.fill	2,8,0
 	.byte	1
 SYM_DATA_END(efi64_config)
 #endif /* CONFIG_EFI_STUB */
arch/x86/include/asm/efi.h +8 -15
index e0789ec5c9f6..8137d4f5c104 100644
@@ -200,16 +200,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
 
 /* arch specific definitions used by the stub code */
 
-struct efi_config {
-	u64 image_handle;
-	u64 table;
-	efi_runtime_services_t *runtime_services;
-	efi_boot_services_t *boot_services;
-	efi_simple_text_output_protocol_t *text_output;
-	bool is64;
-} __packed;
-
-__pure const struct efi_config *__efi_early(void);
+__pure bool __efi_early_is64(void);
 
 static inline bool efi_is_64bit(void)
 {
@@ -219,7 +210,7 @@ static inline bool efi_is_64bit(void)
 	if (!IS_ENABLED(CONFIG_EFI_MIXED))
 		return true;
 
-	return __efi_early()->is64;
+	return __efi_early_is64();
 }
 
 static inline bool efi_is_native(void)
@@ -252,14 +243,16 @@ static inline bool efi_is_native(void)
 
 #define efi_call_early(f, ...)						\
 	(efi_is_native()						\
-		? __efi_early()->boot_services->f(__VA_ARGS__)		\
-		: efi64_thunk(__efi_early()->boot_services->mixed_mode.f,\
+		? efi_system_table()->boottime->f(__VA_ARGS__)		\
+		: efi64_thunk(efi_table_attr(efi_boot_services,		\
+			boottime, efi_system_table())->mixed_mode.f,	\
 			__VA_ARGS__))
 
 #define efi_call_runtime(f, ...)					\
 	(efi_is_native()						\
-		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
-		: efi64_thunk(__efi_early()->runtime_services->mixed_mode.f,\
+		? efi_system_table()->runtime->f(__VA_ARGS__)		\
+		: efi64_thunk(efi_table_attr(efi_runtime_services,	\
+			runtime, efi_system_table())->mixed_mode.f,	\
 			__VA_ARGS__))
 
 extern bool efi_reboot_required(void);
Signature · 1 line
2.17.1
#23 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Hi Ard,

Quoted 8 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

I synced my personal tree yesterday to 5.5-rc3 + the most recent version (as of yesterday afternoon) of your efistub-x86-cleanup-v3 branch on top.

This has been working fine on a bunch of devices, but it fails on a Teclast X89 Bay Trail (mixed mode) device. When reverting all the commits from your efistub-x86-cleanup-v3 branch one by one, things start working again after reverting this one.

Regards,

Hans

Quoted 175 lines
---
  arch/x86/boot/compressed/eboot.c   | 36 ++++++++------------
  arch/x86/boot/compressed/head_32.S |  2 +-
  arch/x86/boot/compressed/head_64.S |  4 +--
  arch/x86/include/asm/efi.h         | 23 +++++--------
  4 files changed, 26 insertions(+), 39 deletions(-)
arch/x86/boot/compressed/eboot.c +15 -21
index 2bcab1ef5a56..53e67334c4c3 100644
@@ -20,11 +20,17 @@
 
  static efi_system_table_t *sys_table;
 
-static struct efi_config *efi_early;
+struct efi_config {
+	u64 image_handle;
+	u64 table;
+	bool is64;
+} __packed;
 
-__pure const struct efi_config *__efi_early(void)
+static bool is64;
+
+__pure bool __efi_early_is64(void)
  {
-	return efi_early;
+	return is64;
  }
 
  __pure efi_system_table_t *efi_system_table(void)
@@ -32,13 +38,6 @@ __pure efi_system_table_t *efi_system_table(void)
  	return sys_table;
  }
 
-static void setup_boot_services(struct efi_config *c)
-{
-	c->runtime_services	= efi_table_attr(efi_system_table, runtime, sys_table);
-	c->boot_services	= efi_table_attr(efi_system_table, boottime, sys_table);
-	c->text_output		= efi_table_attr(efi_system_table, con_out, sys_table);
-}
-
  static efi_status_t
  preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
  {
@@ -382,16 +381,14 @@ struct boot_params *make_boot_params(struct efi_config *c)
  	unsigned long ramdisk_addr;
  	unsigned long ramdisk_size;
 
-	efi_early = c;
-	sys_table = (efi_system_table_t *)(unsigned long)efi_early->table;
-	handle = (void *)(unsigned long)efi_early->image_handle;
+	sys_table = (efi_system_table_t *)(unsigned long)c->table;
+	handle = (void *)(unsigned long)c->image_handle;
+	is64 = c->is64;
 
  	/* Check if we were booted by the EFI firmware */
  	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
  		return NULL;
 
-	setup_boot_services(efi_early);
-
  	status = efi_call_early(handle_protocol, handle,
  				&proto, (void *)&image);
  	if (status != EFI_SUCCESS) {
@@ -738,10 +735,9 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
  	efi_system_table_t *_table;
  	unsigned long cmdline_paddr;
 
-	efi_early = c;
-
-	_table = (efi_system_table_t *)(unsigned long)efi_early->table;
-	handle = (void *)(unsigned long)efi_early->image_handle;
+	_table = (efi_system_table_t *)(unsigned long)c->table;
+	handle = (void *)(unsigned long)c->image_handle;
+	is64 = c->is64;
 
  	sys_table = _table;
 
@@ -749,8 +745,6 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
  	if (sys_table->hdr.signature != EFI_SYSTEM_TABLE_SIGNATURE)
  		goto fail;
 
-	setup_boot_services(efi_early);
-
  	/*
  	 * make_boot_params() may have been called before efi_main(), in which
  	 * case this is the second time we parse the cmdline. This is ok,
arch/x86/boot/compressed/head_32.S +1 -1
index 7da4dfc53df6..c026ab881405 100644
@@ -261,7 +261,7 @@ SYM_FUNC_END(.Lrelocated)
  #ifdef CONFIG_EFI_STUB
  	.data
  efi32_config:
-	.fill 7,4,0
+	.fill 4,4,0
  	.byte 0
  #endif
 
arch/x86/boot/compressed/head_64.S +2 -2
index 6dc6a7ebb9e1..72065735abc1 100644
@@ -672,13 +672,13 @@ SYM_DATA_LOCAL(efi_config, .quad 0)
 
  #ifdef CONFIG_EFI_MIXED
  SYM_DATA_START(efi32_config)
-	.fill	5,8,0
+	.fill	2,8,0
  	.byte	0
  SYM_DATA_END(efi32_config)
  #endif
 
  SYM_DATA_START(efi64_config)
-	.fill	5,8,0
+	.fill	2,8,0
  	.byte	1
  SYM_DATA_END(efi64_config)
  #endif /* CONFIG_EFI_STUB */
arch/x86/include/asm/efi.h +8 -15
index e0789ec5c9f6..8137d4f5c104 100644
@@ -200,16 +200,7 @@ static inline efi_status_t efi_thunk_set_virtual_address_map(
 
  /* arch specific definitions used by the stub code */
 
-struct efi_config {
-	u64 image_handle;
-	u64 table;
-	efi_runtime_services_t *runtime_services;
-	efi_boot_services_t *boot_services;
-	efi_simple_text_output_protocol_t *text_output;
-	bool is64;
-} __packed;
-
-__pure const struct efi_config *__efi_early(void);
+__pure bool __efi_early_is64(void);
 
  static inline bool efi_is_64bit(void)
  {
@@ -219,7 +210,7 @@ static inline bool efi_is_64bit(void)
  	if (!IS_ENABLED(CONFIG_EFI_MIXED))
  		return true;
 
-	return __efi_early()->is64;
+	return __efi_early_is64();
  }
 
  static inline bool efi_is_native(void)
@@ -252,14 +243,16 @@ static inline bool efi_is_native(void)
 
  #define efi_call_early(f, ...)						\
  	(efi_is_native()						\
-		? __efi_early()->boot_services->f(__VA_ARGS__)		\
-		: efi64_thunk(__efi_early()->boot_services->mixed_mode.f,\
+		? efi_system_table()->boottime->f(__VA_ARGS__)		\
+		: efi64_thunk(efi_table_attr(efi_boot_services,		\
+			boottime, efi_system_table())->mixed_mode.f,	\
  			__VA_ARGS__))
 
  #define efi_call_runtime(f, ...)					\
  	(efi_is_native()						\
-		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
-		: efi64_thunk(__efi_early()->runtime_services->mixed_mode.f,\
+		? efi_system_table()->runtime->f(__VA_ARGS__)		\
+		: efi64_thunk(efi_table_attr(efi_runtime_services,	\
+			runtime, efi_system_table())->mixed_mode.f,	\
  			__VA_ARGS__))
 
  extern bool efi_reboot_required(void);
#24 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 13 lines · Hans de Goede

Hi Ard,

Quoted 8 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

Quoted 9 lines

I synced my personal tree yesterday to 5.5-rc3 + the most recent version (as of yesterday afternoon) of your efistub-x86-cleanup-v3 branch on top.

This has been working fine on a bunch of devices, but it fails on a Teclast X89 Bay Trail (mixed mode) device. When reverting all the commits from your efistub-x86-cleanup-v3 branch one by one, things start working again after reverting this one.

Oops again. And thanks again for taking the time to test this stuff.

Could you please try the branch below and check whether it fixes it?

https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-core-mm-fix

#25 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Hi,

Quoted 24 lines · Ard Biesheuvel
Quoted 13 lines · Hans de Goede

Hi Ard,

Quoted 8 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

Quoted 9 lines

I synced my personal tree yesterday to 5.5-rc3 + the most recent version (as of yesterday afternoon) of your efistub-x86-cleanup-v3 branch on top.

This has been working fine on a bunch of devices, but it fails on a Teclast X89 Bay Trail (mixed mode) device. When reverting all the commits from your efistub-x86-cleanup-v3 branch one by one, things start working again after reverting this one.

Quoted 5 lines

Oops again. And thanks again for taking the time to test this stuff.

Could you please try the branch below and check whether it fixes it?

https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-core-mm-fix

This seems to fix the issue, the Teclast X89 boots again and I've also tested this on 2 other devices (one mixed mode Bay Trail one 64 bit Cherry Trail device).

If I want to also test them, how do the 2 new series you've just posted come into the mix here? :

"[PATCH 0/3] efi/x86: clean up and simplify runtime call wrappers" "[PATCH 0/3] efi/x86: righten memory protections at runtime"

Regards,

Hans

#26 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 30 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel
Quoted 13 lines · Hans de Goede

Hi Ard,

Quoted 8 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

Quoted 9 lines

I synced my personal tree yesterday to 5.5-rc3 + the most recent version (as of yesterday afternoon) of your efistub-x86-cleanup-v3 branch on top.

This has been working fine on a bunch of devices, but it fails on a Teclast X89 Bay Trail (mixed mode) device. When reverting all the commits from your efistub-x86-cleanup-v3 branch one by one, things start working again after reverting this one.

Oops again. And thanks again for taking the time to test this stuff.

Quoted 8 lines
Quoted 3 lines

Could you please try the branch below and check whether it fixes it?

https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-core-mm-fix

This seems to fix the issue, the Teclast X89 boots again and I've also tested this on 2 other devices (one mixed mode Bay Trail one 64 bit Cherry Trail device).

Great, thanks.

Quoted 6 lines

If I want to also test them, how do the 2 new series you've just posted come into the mix here? :

"[PATCH 0/3] efi/x86: clean up and simplify runtime call wrappers" "[PATCH 0/3] efi/x86: righten memory protections at runtime"

I have accumulated a couple of more fixes in addition, and a patch that I haven't sent out yet.
Easiest to grab them from

https://git.kernel.org/pub/scm/linux/kernel/git/efi/efi.git/log/?h=next

Signature · 1 line
Ard.
#27 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

In reply to Ard Biesheuvel
Quoted 30 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

---
 arch/x86/boot/compressed/eboot.c   | 36 ++++++++------------
 arch/x86/boot/compressed/head_32.S |  2 +-
 arch/x86/boot/compressed/head_64.S |  4 +--
 arch/x86/include/asm/efi.h         | 23 +++++--------
 4 files changed, 26 insertions(+), 39 deletions(-)
arch/x86/boot/compressed/eboot.c +5 -1
index 2bcab1ef5a56..53e67334c4c3 100644
@@ -20,11 +20,17 @@
 
 static efi_system_table_t *sys_table;
 
-static struct efi_config *efi_early;
+struct efi_config {
+	u64 image_handle;
+	u64 table;
+	bool is64;
+} __packed;
 

arch/x86/boot/compressed/eboot.c line 36

Quoted 10 lines
arch/x86/boot/compressed/eboot.c +4 -2
-__pure const struct efi_config *__efi_early(void)
+static bool is64;
+
+__pure bool __efi_early_is64(void)
 {
-	return efi_early;
+	return is64;
 }
 
 __pure efi_system_table_t *efi_system_table(void)

This predates your series, but looking at the generated code I noticed that pure isn't enough to let gcc optimize out repeated calls to is64 and system_table. Declaring them as __attribute_const__ would allow optimization. They don't quite meet gcc's requirements for that as they access global non-const variables, but it seems like it ought to be safe, especially if we stop using the functions within eboot.c itself?

#28 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 41 lines · Arvind Sankar
Quoted 39 lines · Ard Biesheuvel

The various pointers we stash in the efi_config struct which we retrieve using __efi_early() are simply copies of the ones in the EFI system table, which we have started accessing directly in the previous patch. So drop all the __efi_early() related plumbing, except for the access to a boolean which tells us whether the firmware is 64-bit or not.

---
 arch/x86/boot/compressed/eboot.c   | 36 ++++++++------------
 arch/x86/boot/compressed/head_32.S |  2 +-
 arch/x86/boot/compressed/head_64.S |  4 +--
 arch/x86/include/asm/efi.h         | 23 +++++--------
 4 files changed, 26 insertions(+), 39 deletions(-)
arch/x86/boot/compressed/eboot.c +9 -3
index 2bcab1ef5a56..53e67334c4c3 100644
@@ -20,11 +20,17 @@
 
 static efi_system_table_t *sys_table;
 
-static struct efi_config *efi_early;
+struct efi_config {
+     u64 image_handle;
+     u64 table;
+     bool is64;
+} __packed;
 
-__pure const struct efi_config *__efi_early(void)
+static bool is64;
+
+__pure bool __efi_early_is64(void)
 {
-     return efi_early;
+     return is64;
 }
 
Quoted 8 lines

arch/x86/boot/compressed/eboot.c line 36

Quoted 1 line
arch/x86/boot/compressed/eboot.c +0 -0
 __pure efi_system_table_t *efi_system_table(void)

This predates your series, but looking at the generated code I noticed that pure isn't enough to let gcc optimize out repeated calls to is64 and system_table. Declaring them as __attribute_const__ would allow optimization. They don't quite meet gcc's requirements for that as they access global non-const variables, but it seems like it ought to be safe, especially if we stop using the functions within eboot.c itself?

The GCC documentation mentions that it does not make sense for a function annotated as const not to take any arguments, so I'd rather avoid it here.

#29 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 3 lines · Ard Biesheuvel

The GCC documentation mentions that it does not make sense for a function annotated as const not to take any arguments, so I'd rather avoid it here.

Where does it say that? I only see it saying it doesn't make sense for it to return void.

Currently if we call 5 EFI services in the same function, it has to re-evaluate systemtable and is64 for each call, which seems wasteful, though of course this is not exactly performance-critical code.

#30 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 9 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

The GCC documentation mentions that it does not make sense for a function annotated as const not to take any arguments, so I'd rather avoid it here.

Where does it say that? I only see it saying it doesn't make sense for it to return void.

You're right. I looked into this in the past, and I misremembered, and paraphrased it incorrectly.

The documentation does mention that const functions are not permitted to read global memory.

Quoted 3 lines

Currently if we call 5 EFI services in the same function, it has to re-evaluate systemtable and is64 for each call, which seems wasteful, though of course this is not exactly performance-critical code.

The alternative would be to use globals with external linkage in a way that is guaranteed not to rely on GOT entries, since we'll end up with absolute addresses that need to be fixed up first. This has caused breakage in the past, and is the reason we use this scheme with globals with static linkage and __pure getters.

However, hidden visibility should yield the same results so we should be able to make it work with that instead. However, given the breakage in the past, I don't think it's worth it since the performance gain will be negligible.

#31 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 11 lines · Ard Biesheuvel
Quoted 9 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

The GCC documentation mentions that it does not make sense for a function annotated as const not to take any arguments, so I'd rather avoid it here.

Where does it say that? I only see it saying it doesn't make sense for it to return void.

Quoted 6 lines

You're right. I looked into this in the past, and I misremembered, and paraphrased it incorrectly.

The documentation does mention that const functions are not permitted to read global memory.

What it says is that "[the const attribute] tells GCC that subsequent calls to function square with the same argument value can be replaced by the result of the first call regardless of the statements in between" and "The const attribute prohibits a function from reading objects that affect its return value between successive invocations. However, functions declared with the attribute can safely read objects that do not change their return value, such as non-volatile constants."

In this case since the globals in question never change after being set, it should be safe -- every call to these functions does return the same value.

Quoted 10 lines
Quoted 3 lines

Currently if we call 5 EFI services in the same function, it has to re-evaluate systemtable and is64 for each call, which seems wasteful, though of course this is not exactly performance-critical code.

The alternative would be to use globals with external linkage in a way that is guaranteed not to rely on GOT entries, since we'll end up with absolute addresses that need to be fixed up first. This has caused breakage in the past, and is the reason we use this scheme with globals with static linkage and __pure getters.

Quoted 4 lines

However, hidden visibility should yield the same results so we should be able to make it work with that instead. However, given the breakage in the past, I don't think it's worth it since the performance gain will be negligible.

This only saves the overhead of the function call, but the compiler would still have to re-read the variables and re-test them, since so far as it knows the firmware call could change them. With the const function attribute, it will only load them once. To make it work with just the variables, they'd have to be declared const and initialized in the assembly entry code.

#32 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 28 lines · Arvind Sankar
Quoted 11 lines · Ard Biesheuvel
Quoted 9 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

The GCC documentation mentions that it does not make sense for a function annotated as const not to take any arguments, so I'd rather avoid it here.

Where does it say that? I only see it saying it doesn't make sense for it to return void.

Quoted 6 lines

You're right. I looked into this in the past, and I misremembered, and paraphrased it incorrectly.

The documentation does mention that const functions are not permitted to read global memory.

What it says is that "[the const attribute] tells GCC that subsequent calls to function square with the same argument value can be replaced by the result of the first call regardless of the statements in between" and "The const attribute prohibits a function from reading objects that affect its return value between successive invocations. However, functions declared with the attribute can safely read objects that do not change their return value, such as non-volatile constants."

Quoted 4 lines

In this case since the globals in question never change after being set, it should be safe -- every call to these functions does return the same value.

I agree that it should be safe. However, the document I looked at literally says

"""
Many functions do not examine any values except their arguments, and have no effects except the return value. Basically this is just slightly more strict class than the pure attribute below, since function is not allowed to read global memory.
"""

However, this is actually a fairly old version of the docs [0], and the most recent version seems to permit it.

[0] https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html

Quoted 15 lines
Quoted 10 lines
Quoted 3 lines

Currently if we call 5 EFI services in the same function, it has to re-evaluate systemtable and is64 for each call, which seems wasteful, though of course this is not exactly performance-critical code.

The alternative would be to use globals with external linkage in a way that is guaranteed not to rely on GOT entries, since we'll end up with absolute addresses that need to be fixed up first. This has caused breakage in the past, and is the reason we use this scheme with globals with static linkage and __pure getters.

Quoted 4 lines

However, hidden visibility should yield the same results so we should be able to make it work with that instead. However, given the breakage in the past, I don't think it's worth it since the performance gain will be negligible.

Quoted 6 lines

This only saves the overhead of the function call, but the compiler would still have to re-read the variables and re-test them, since so far as it knows the firmware call could change them. With the const function attribute, it will only load them once. To make it work with just the variables, they'd have to be declared const and initialized in the assembly entry code.

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

#34 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 5 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Yeah, good point.

Quoted 7 lines
Quoted 4 lines

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

#35 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 10 lines · Ard Biesheuvel
Quoted 8 lines · Arvind Sankar
Quoted 6 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

#36 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 16 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

I wonder if commit 6d92bc9d483a ("x86/build: Build compressed x86 kernels as PIE") resolved this.

#37 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 22 lines · Arvind Sankar
Quoted 20 lines · Ard Biesheuvel
Quoted 18 lines · Ard Biesheuvel
Quoted 5 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Yeah, good point.

Quoted 7 lines
Quoted 4 lines

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Quoted 7 lines
Quoted 4 lines

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

I wonder if commit 6d92bc9d483a ("x86/build: Build compressed x86 kernels as PIE") resolved this.

Good question. But note that the stub is still built with -fPIC not -fPIE.

#38 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 25 lines · Ard Biesheuvel
Quoted 24 lines · Arvind Sankar
Quoted 22 lines · Ard Biesheuvel
Quoted 10 lines · Ard Biesheuvel
Quoted 8 lines · Arvind Sankar
Quoted 6 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

Quoted 7 lines
Quoted 5 lines
Quoted 2 lines

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

I wonder if commit 6d92bc9d483a ("x86/build: Build compressed x86 kernels as PIE") resolved this.

Good question. But note that the stub is still built with -fPIC not -fPIE.

Hm libstub is -fPIC but arch/x86/boot/compressed is -fPIE. With -fPIE gcc 5+ doesn't use GOTPCREL even with default visibility, but gcc 4.7 does. With -fPIC though you need hidden visibility to avoid GOTPCREL.

#39 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

In reply to Ard Biesheuvel
Quoted 17 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

OK, I have done a bit more digging, and it seems like recent toolchains can optimize away GOT indirections using R_386_GOT32X relocations, which can be converted into R_386_GOTOFF relocations by the linker if it is building a fully linked binary, making the actual contents of the GOT entries irrelevant.

Note that even if the GOT entries are not fixed up, assigning a global variable and then using it again may work by accident if the memory it points to is writable, which is why it is not easy to reproduce reliably.

efi_is64 only exists on 64-bit, so annotating that as 'hidden' should work. But efi_system_table() is also used on 32-bit, so I'll leave that one alone for now.

#40 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 33 lines · Ard Biesheuvel
Quoted 17 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

OK, I have done a bit more digging, and it seems like recent toolchains can optimize away GOT indirections using R_386_GOT32X relocations, which can be converted into R_386_GOTOFF relocations by the linker if it is building a fully linked binary, making the actual contents of the GOT entries irrelevant.

Quoted 8 lines

Note that even if the GOT entries are not fixed up, assigning a global variable and then using it again may work by accident if the memory it points to is writable, which is why it is not easy to reproduce reliably.

efi_is64 only exists on 64-bit, so annotating that as 'hidden' should work. But efi_system_table() is also used on 32-bit, so I'll leave that one alone for now.

With hidden visibility, 32-bit compiler seems to generate GOTOFF relocations directly so it shouldn't depend on recent toolchain?

https://godbolt.org/z/79iA_3

#41 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 40 lines · Arvind Sankar
Quoted 38 lines · Ard Biesheuvel
Quoted 17 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

OK, I have done a bit more digging, and it seems like recent toolchains can optimize away GOT indirections using R_386_GOT32X relocations, which can be converted into R_386_GOTOFF relocations by the linker if it is building a fully linked binary, making the actual contents of the GOT entries irrelevant.

Note that even if the GOT entries are not fixed up, assigning a global variable and then using it again may work by accident if the memory it points to is writable, which is why it is not easy to reproduce reliably.

Quoted 8 lines
Quoted 3 lines

efi_is64 only exists on 64-bit, so annotating that as 'hidden' should work. But efi_system_table() is also used on 32-bit, so I'll leave that one alone for now.

With hidden visibility, 32-bit compiler seems to generate GOTOFF relocations directly so it shouldn't depend on recent toolchain?

https://godbolt.org/z/79iA_3

Yes, that was my assumption at the time as well, but we still ended up with /some/ absolute GOT dereferences for some reason in the 32-bit kernel.

#42 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 45 lines · Ard Biesheuvel
Quoted 44 lines · Arvind Sankar
Quoted 41 lines · Ard Biesheuvel
Quoted 17 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

OK, I have done a bit more digging, and it seems like recent toolchains can optimize away GOT indirections using R_386_GOT32X relocations, which can be converted into R_386_GOTOFF relocations by the linker if it is building a fully linked binary, making the actual contents of the GOT entries irrelevant.

Note that even if the GOT entries are not fixed up, assigning a global variable and then using it again may work by accident if the memory it points to is writable, which is why it is not easy to reproduce reliably.

efi_is64 only exists on 64-bit, so annotating that as 'hidden' should work. But efi_system_table() is also used on 32-bit, so I'll leave that one alone for now.

Quoted 8 lines
Quoted 4 lines

With hidden visibility, 32-bit compiler seems to generate GOTOFF relocations directly so it shouldn't depend on recent toolchain?

https://godbolt.org/z/79iA_3

Yes, that was my assumption at the time as well, but we still ended up with /some/ absolute GOT dereferences for some reason in the 32-bit kernel.

Do you remember if we ever figured out what was wrong with Matt Fleming's patch to fixup the GOT for the EFI stub too?

For efi_system_table, if we don't want to risk creating GOT entries, one option would be to do what you did for is64, but define the wrapper function as an out-of-line function in the assembler code and declare it const.

#43 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 50 lines · Arvind Sankar
Quoted 48 lines · Ard Biesheuvel
Quoted 47 lines · Arvind Sankar
Quoted 33 lines · Ard Biesheuvel
Quoted 17 lines · Ard Biesheuvel
Quoted 15 lines · Ard Biesheuvel
Quoted 12 lines · Arvind Sankar
Quoted 7 lines · Ard Biesheuvel

Yeah, good point.

I pushed as branch here;
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efi-libstub-attr-const

Could you please check if that fixes the issue for efi_is_64bit() ?

Haven't built it yet -- but how does this handle the GOT issues you mentioned?

Quoted 8 lines
Quoted 3 lines

It doesn't. The trick is to add __attribute__((visibility("hidden"))) to the extern declaration of efi_is64, but I am having trouble to reproduce the original issue.

Some background:

https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/

OK, I have done a bit more digging, and it seems like recent toolchains can optimize away GOT indirections using R_386_GOT32X relocations, which can be converted into R_386_GOTOFF relocations by the linker if it is building a fully linked binary, making the actual contents of the GOT entries irrelevant.

Quoted 8 lines

Note that even if the GOT entries are not fixed up, assigning a global variable and then using it again may work by accident if the memory it points to is writable, which is why it is not easy to reproduce reliably.

efi_is64 only exists on 64-bit, so annotating that as 'hidden' should work. But efi_system_table() is also used on 32-bit, so I'll leave that one alone for now.

With hidden visibility, 32-bit compiler seems to generate GOTOFF relocations directly so it shouldn't depend on recent toolchain?

Quoted 9 lines
Quoted 5 lines
Quoted 1 line

https://godbolt.org/z/79iA_3

Yes, that was my assumption at the time as well, but we still ended up with /some/ absolute GOT dereferences for some reason in the 32-bit kernel.

Do you remember if we ever figured out what was wrong with Matt Fleming's patch to fixup the GOT for the EFI stub too?

No, I don't think we did. It is simply infeasible to diagnose this on someone else's laptop who is on the other side of the world.

Quoted 4 lines

For efi_system_table, if we don't want to risk creating GOT entries, one option would be to do what you did for is64, but define the wrapper function as an out-of-line function in the assembler code and declare it const.

Yeah, that would work. It would be for 32-bit only, so we could have a __weak implementation in C, and an asm one for i386.

Currently, I am not managing to get the 64-bit compiler to emit any GOT based references for efi_is64, though, even without the attribute, so I am not sure what's going on there.

#44 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 3 lines · Ard Biesheuvel

Currently, I am not managing to get the 64-bit compiler to emit any GOT based references for efi_is64, though, even without the attribute, so I am not sure what's going on there.

With gcc 9, I do get R_X86_64_REX_GOTPCRELX relocations in libstub, which are then eliminated by the linker -- if they weren't they would be using addresses from the GOT. They turn into R_X86_64_PC32 with the hidden visibility.

#45 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 10 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Currently, I am not managing to get the 64-bit compiler to emit any GOT based references for efi_is64, though, even without the attribute, so I am not sure what's going on there.

With gcc 9, I do get R_X86_64_REX_GOTPCRELX relocations in libstub, which are then eliminated by the linker -- if they weren't they would be using addresses from the GOT. They turn into R_X86_64_PC32 with the hidden visibility.

Indeed. I was looking at eboot.o but that gets built with -fPIE not -fPIC [if available].

libstub objects indeed have the R_X86_64_REX_GOTPCRELX relocations, so I'll add the hidden visibility to the extern declaration of efi_is64

#46 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 12 lines · Ard Biesheuvel
Quoted 10 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Currently, I am not managing to get the 64-bit compiler to emit any GOT based references for efi_is64, though, even without the attribute, so I am not sure what's going on there.

With gcc 9, I do get R_X86_64_REX_GOTPCRELX relocations in libstub, which are then eliminated by the linker -- if they weren't they would be using addresses from the GOT. They turn into R_X86_64_PC32 with the hidden visibility.

Quoted 5 lines

Indeed. I was looking at eboot.o but that gets built with -fPIE not -fPIC [if available].

libstub objects indeed have the R_X86_64_REX_GOTPCRELX relocations, so I'll add the hidden visibility to the extern declaration of efi_is64

Is there any reason not to compile the stub with -fPIE btw? Also fPIE seems to be there even in gcc 4.1.2, the check for availability can probably be dropped.

#47 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

Quoted 17 lines · Arvind Sankar
Quoted 15 lines · Ard Biesheuvel
Quoted 10 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Currently, I am not managing to get the 64-bit compiler to emit any GOT based references for efi_is64, though, even without the attribute, so I am not sure what's going on there.

With gcc 9, I do get R_X86_64_REX_GOTPCRELX relocations in libstub, which are then eliminated by the linker -- if they weren't they would be using addresses from the GOT. They turn into R_X86_64_PC32 with the hidden visibility.

Indeed. I was looking at eboot.o but that gets built with -fPIE not -fPIC [if available].

Quoted 6 lines
Quoted 2 lines

libstub objects indeed have the R_X86_64_REX_GOTPCRELX relocations, so I'll add the hidden visibility to the extern declaration of efi_is64

Is there any reason not to compile the stub with -fPIE btw? Also fPIE seems to be there even in gcc 4.1.2, the check for availability can probably be dropped.

No we should switch to -fPIE for the stub as well

#48 lore raw

Re: [PATCH v2 13/21] efi/libstub/x86: drop __efi_early() export of efi_config struct

In reply to Ard Biesheuvel
Quoted 8 lines · Ard Biesheuvel
Quoted 4 lines

Do you remember if we ever figured out what was wrong with Matt Fleming's patch to fixup the GOT for the EFI stub too?

No, I don't think we did. It is simply infeasible to diagnose this on someone else's laptop who is on the other side of the world.

Hm looking at that code, I think it wouldn't do the GOT relocation adjustment needed after copying the kernel for decompression. But then it shouldn't have worked at all presumably...

Maybe we should just try again and see if it breaks anyone.

author maintainer
#49 lore raw

PATCH v2 14/21 efi/libstub: drop sys_table_arg from printk routines

In reply to Ard Biesheuvel

As a first step towards getting rid of the need to pass around a function parameter 'sys_table_arg' pointing to the EFI system table, remove the references to it in the printing code, which is represents the majority of the use cases.

---
 arch/x86/boot/compressed/eboot.c               | 28 +++++++--------
 drivers/firmware/efi/libstub/arm-stub.c        | 28 +++++++--------
 drivers/firmware/efi/libstub/arm32-stub.c      | 14 ++++----
 drivers/firmware/efi/libstub/arm64-stub.c      | 12 +++----
 drivers/firmware/efi/libstub/efi-stub-helper.c | 37 ++++++++++----------
 drivers/firmware/efi/libstub/efistub.h         |  9 ++---
 drivers/firmware/efi/libstub/fdt.c             | 16 ++++-----
 drivers/firmware/efi/libstub/secureboot.c      |  4 +--
 drivers/firmware/efi/libstub/tpm.c             |  3 +-
 include/linux/efi.h                            |  2 +-
 10 files changed, 75 insertions(+), 78 deletions(-)
arch/x86/boot/compressed/eboot.c +14 -14
index 53e67334c4c3..92fbd455f496 100644
@@ -64,7 +64,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, size,
 				(void **)&rom);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to allocate memory for 'rom'\n");
+		efi_printk("Failed to allocate memory for 'rom'\n");
 		return status;
 	}
 
@@ -81,7 +81,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 				&rom->vendor);
 
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to read rom->vendor\n");
+		efi_printk("Failed to read rom->vendor\n");
 		goto free_struct;
 	}
 
@@ -90,7 +90,7 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 				&rom->devid);
 
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to read rom->devid\n");
+		efi_printk("Failed to read rom->devid\n");
 		goto free_struct;
 	}
 
@@ -139,7 +139,7 @@ static void setup_efi_pci(struct boot_params *params)
 					size, (void **)&pci_handle);
 
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table, "Failed to allocate memory for 'pci_handle'\n");
+			efi_printk("Failed to allocate memory for 'pci_handle'\n");
 			return;
 		}
 
@@ -194,7 +194,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 		return;
 
 	if (efi_table_attr(apple_properties_protocol, version, p) != 0x10000) {
-		efi_printk(sys_table, "Unsupported properties proto version\n");
+		efi_printk("Unsupported properties proto version\n");
 		return;
 	}
 
@@ -207,7 +207,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 					size + sizeof(struct setup_data),
 					(void **)&new);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table, "Failed to allocate memory for 'properties'\n");
+			efi_printk("Failed to allocate memory for 'properties'\n");
 			return;
 		}
 
@@ -392,14 +392,14 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	status = efi_call_early(handle_protocol, handle,
 				&proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
+		efi_printk("Failed to get handle for LOADED_IMAGE_PROTOCOL\n");
 		return NULL;
 	}
 
 	status = efi_low_alloc(sys_table, 0x4000, 1,
 			       (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to allocate lowmem for boot params\n");
+		efi_printk("Failed to allocate lowmem for boot params\n");
 		return NULL;
 	}
 
@@ -447,7 +447,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 
 	if (status != EFI_SUCCESS &&
 	    hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) {
-		efi_printk(sys_table, "Trying to load files to higher address\n");
+		efi_printk("Trying to load files to higher address\n");
 		status = handle_cmdline_files(sys_table, image,
 				      (char *)(unsigned long)hdr->cmd_line_ptr,
 				      "initrd=", -1UL,
@@ -777,7 +777,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
 				sizeof(*gdt), (void **)&gdt);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to allocate memory for 'gdt' structure\n");
+		efi_printk("Failed to allocate memory for 'gdt' structure\n");
 		goto fail;
 	}
 
@@ -785,7 +785,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	status = efi_low_alloc(sys_table, gdt->size, 8,
 			   (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "Failed to allocate memory for 'gdt'\n");
+		efi_printk("Failed to allocate memory for 'gdt'\n");
 		goto fail;
 	}
 
@@ -801,7 +801,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 					     hdr->kernel_alignment,
 					     LOAD_PHYSICAL_ADDR);
 		if (status != EFI_SUCCESS) {
-			efi_printk(sys_table, "efi_relocate_kernel() failed!\n");
+			efi_printk("efi_relocate_kernel() failed!\n");
 			goto fail;
 		}
 
@@ -811,7 +811,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 
 	status = exit_boot(boot_params, handle);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table, "exit_boot() failed!\n");
+		efi_printk("exit_boot() failed!\n");
 		goto fail;
 	}
 
@@ -904,7 +904,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 
 	return boot_params;
 fail:
-	efi_printk(sys_table, "efi_main() failed!\n");
+	efi_printk("efi_main() failed!\n");
 
 	return NULL;
 }
drivers/firmware/efi/libstub/arm-stub.c +14 -14
index b44b1bd4d480..2ed4bd457e66 100644
@@ -73,7 +73,7 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg)
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv),
 				(void **)&rsv);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table_arg, "Failed to allocate memreserve entry!\n");
+		pr_efi_err("Failed to allocate memreserve entry!\n");
 		return;
 	}
 
@@ -85,7 +85,7 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg)
 				&memreserve_table_guid,
 				rsv);
 	if (status != EFI_SUCCESS)
-		pr_efi_err(sys_table_arg, "Failed to install memreserve config table!\n");
+		pr_efi_err("Failed to install memreserve config table!\n");
 }
 
 
@@ -147,13 +147,13 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	status = sys_table->boottime->handle_protocol(handle,
 					&loaded_image_proto, (void *)&image);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Failed to get loaded image protocol\n");
+		pr_efi_err("Failed to get loaded image protocol\n");
 		goto fail;
 	}
 
 	dram_base = get_dram_base(sys_table);
 	if (dram_base == EFI_ERROR) {
-		pr_efi_err(sys_table, "Failed to find DRAM base\n");
+		pr_efi_err("Failed to find DRAM base\n");
 		goto fail;
 	}
 
@@ -164,7 +164,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	 */
 	cmdline_ptr = efi_convert_cmdline(sys_table, image, &cmdline_size);
 	if (!cmdline_ptr) {
-		pr_efi_err(sys_table, "getting command line via LOADED_IMAGE_PROTOCOL\n");
+		pr_efi_err("getting command line via LOADED_IMAGE_PROTOCOL\n");
 		goto fail;
 	}
 
@@ -176,7 +176,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	if (!IS_ENABLED(CONFIG_CMDLINE_FORCE) && cmdline_size > 0)
 		efi_parse_options(cmdline_ptr);
 
-	pr_efi(sys_table, "Booting Linux Kernel...\n");
+	pr_efi("Booting Linux Kernel...\n");
 
 	si = setup_graphics(sys_table);
 
@@ -185,7 +185,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 				     &reserve_size,
 				     dram_base, image);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Failed to relocate kernel\n");
+		pr_efi_err("Failed to relocate kernel\n");
 		goto fail_free_cmdline;
 	}
 
@@ -204,29 +204,29 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	if (!IS_ENABLED(CONFIG_EFI_ARMSTUB_DTB_LOADER) ||
 	     secure_boot != efi_secureboot_mode_disabled) {
 		if (strstr(cmdline_ptr, "dtb="))
-			pr_efi(sys_table, "Ignoring DTB from command line.\n");
+			pr_efi("Ignoring DTB from command line.\n");
 	} else {
 		status = handle_cmdline_files(sys_table, image, cmdline_ptr,
 					      "dtb=",
 					      ~0UL, &fdt_addr, &fdt_size);
 
 		if (status != EFI_SUCCESS) {
-			pr_efi_err(sys_table, "Failed to load device tree!\n");
+			pr_efi_err("Failed to load device tree!\n");
 			goto fail_free_image;
 		}
 	}
 
 	if (fdt_addr) {
-		pr_efi(sys_table, "Using DTB from command line\n");
+		pr_efi("Using DTB from command line\n");
 	} else {
 		/* Look for a device tree configuration table entry. */
 		fdt_addr = (uintptr_t)get_fdt(sys_table, &fdt_size);
 		if (fdt_addr)
-			pr_efi(sys_table, "Using DTB from configuration table\n");
+			pr_efi("Using DTB from configuration table\n");
 	}
 
 	if (!fdt_addr)
-		pr_efi(sys_table, "Generating empty DTB\n");
+		pr_efi("Generating empty DTB\n");
 
 	status = handle_cmdline_files(sys_table, image, cmdline_ptr, "initrd=",
 				      efi_get_max_initrd_addr(dram_base,
@@ -234,7 +234,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 				      (unsigned long *)&initrd_addr,
 				      (unsigned long *)&initrd_size);
 	if (status != EFI_SUCCESS)
-		pr_efi_err(sys_table, "Failed initrd from command line!\n");
+		pr_efi_err("Failed initrd from command line!\n");
 
 	efi_random_get_seed(sys_table);
 
@@ -275,7 +275,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	if (status == EFI_SUCCESS)
 		return new_fdt_addr;
 
-	pr_efi_err(sys_table, "Failed to update FDT and exit boot services\n");
+	pr_efi_err("Failed to update FDT and exit boot services\n");
 
 	efi_free(sys_table, initrd_size, initrd_addr);
 	efi_free(sys_table, fdt_size, fdt_addr);
drivers/firmware/efi/libstub/arm32-stub.c +6 -8
index 4566640de650..7b5c717ddfac 100644
@@ -18,7 +18,7 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
 	/* LPAE kernels need compatible hardware */
 	block = cpuid_feature_extract(CPUID_EXT_MMFR0, 0);
 	if (block < 5) {
-		pr_efi_err(sys_table_arg, "This LPAE kernel is not supported by your CPU\n");
+		pr_efi_err("This LPAE kernel is not supported by your CPU\n");
 		return EFI_UNSUPPORTED;
 	}
 	return EFI_SUCCESS;
@@ -121,8 +121,7 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
 	 */
 	status = efi_get_memory_map(sys_table_arg, &map);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table_arg,
-			   "reserve_kernel_base(): Unable to retrieve memory map.\n");
+		pr_efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
 		return status;
 	}
 
@@ -164,8 +163,7 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
 						(end - start) / EFI_PAGE_SIZE,
 						&start);
 			if (status != EFI_SUCCESS) {
-				pr_efi_err(sys_table_arg,
-					"reserve_kernel_base(): alloc failed.\n");
+				pr_efi_err("reserve_kernel_base(): alloc failed.\n");
 				goto out;
 			}
 			break;
@@ -224,7 +222,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 	status = reserve_kernel_base(sys_table, kernel_base, reserve_addr,
 				     reserve_size);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Unable to allocate memory for uncompressed kernel.\n");
+		pr_efi_err("Unable to allocate memory for uncompressed kernel.\n");
 		return status;
 	}
 
@@ -237,7 +235,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 				     *image_size,
 				     kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Failed to relocate kernel.\n");
+		pr_efi_err("Failed to relocate kernel.\n");
 		efi_free(sys_table, *reserve_size, *reserve_addr);
 		*reserve_size = 0;
 		return status;
@@ -249,7 +247,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 	 * address at which the zImage is loaded.
 	 */
 	if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
-		pr_efi_err(sys_table, "Failed to relocate kernel, no low memory available.\n");
+		pr_efi_err("Failed to relocate kernel, no low memory available.\n");
 		efi_free(sys_table, *reserve_size, *reserve_addr);
 		*reserve_size = 0;
 		efi_free(sys_table, *image_size, *image_addr);
drivers/firmware/efi/libstub/arm64-stub.c +6 -6
index 1550d244e996..b09dda600c78 100644
@@ -32,9 +32,9 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
 	tg = (read_cpuid(ID_AA64MMFR0_EL1) >> ID_AA64MMFR0_TGRAN_SHIFT) & 0xf;
 	if (tg != ID_AA64MMFR0_TGRAN_SUPPORTED) {
 		if (IS_ENABLED(CONFIG_ARM64_64K_PAGES))
-			pr_efi_err(sys_table_arg, "This 64 KB granular kernel is not supported by your CPU\n");
+			pr_efi_err("This 64 KB granular kernel is not supported by your CPU\n");
 		else
-			pr_efi_err(sys_table_arg, "This 16 KB granular kernel is not supported by your CPU\n");
+			pr_efi_err("This 16 KB granular kernel is not supported by your CPU\n");
 		return EFI_UNSUPPORTED;
 	}
 	return EFI_SUCCESS;
@@ -60,13 +60,13 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
 						      sizeof(phys_seed),
 						      (u8 *)&phys_seed);
 			if (status == EFI_NOT_FOUND) {
-				pr_efi(sys_table_arg, "EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
+				pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
 			} else if (status != EFI_SUCCESS) {
-				pr_efi_err(sys_table_arg, "efi_get_random_bytes() failed\n");
+				pr_efi_err("efi_get_random_bytes() failed\n");
 				return status;
 			}
 		} else {
-			pr_efi(sys_table_arg, "KASLR disabled on kernel command line\n");
+			pr_efi("KASLR disabled on kernel command line\n");
 		}
 	}
 
@@ -143,7 +143,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
 				       MIN_KIMG_ALIGN, reserve_addr);
 
 		if (status != EFI_SUCCESS) {
-			pr_efi_err(sys_table_arg, "Failed to relocate kernel\n");
+			pr_efi_err("Failed to relocate kernel\n");
 			*reserve_size = 0;
 			return status;
 		}
drivers/firmware/efi/libstub/efi-stub-helper.c +18 -19
index 3b89317f4c80..ebe792f2ed27 100644
@@ -58,7 +58,7 @@ struct file_info {
 	u64 size;
 };
 
-void efi_printk(efi_system_table_t *sys_table_arg, char *str)
+void efi_printk(char *str)
 {
 	char *s8;
 
@@ -68,10 +68,10 @@ void efi_printk(efi_system_table_t *sys_table_arg, char *str)
 		ch[0] = *s8;
 		if (*s8 == '\n') {
 			efi_char16_t nl[2] = { '\r', 0 };
-			efi_char16_printk(sys_table_arg, nl);
+			efi_char16_printk(nl);
 		}
 
-		efi_char16_printk(sys_table_arg, ch);
+		efi_char16_printk(ch);
 	}
 }
 
@@ -378,9 +378,9 @@ static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
 	status = efi_call_proto(efi_file_handle, open, fh, &h, filename_16,
 				EFI_FILE_MODE_READ, (u64)0);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to open file: ");
-		efi_char16_printk(sys_table_arg, filename_16);
-		efi_printk(sys_table_arg, "\n");
+		efi_printk("Failed to open file: ");
+		efi_char16_printk(filename_16);
+		efi_printk("\n");
 		return status;
 	}
 
@@ -390,7 +390,7 @@ static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
 	status = efi_call_proto(efi_file_handle, get_info, h, &info_guid,
 				&info_sz, NULL);
 	if (status != EFI_BUFFER_TOO_SMALL) {
-		efi_printk(sys_table_arg, "Failed to get file info size\n");
+		efi_printk("Failed to get file info size\n");
 		return status;
 	}
 
@@ -398,7 +398,7 @@ static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
 				info_sz, (void **)&info);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to alloc mem for file info\n");
+		efi_printk("Failed to alloc mem for file info\n");
 		return status;
 	}
 
@@ -413,7 +413,7 @@ static efi_status_t efi_file_size(efi_system_table_t *sys_table_arg, void *__fh,
 	efi_call_early(free_pool, info);
 
 	if (status != EFI_SUCCESS)
-		efi_printk(sys_table_arg, "Failed to get initrd info\n");
+		efi_printk("Failed to get initrd info\n");
 
 	return status;
 }
@@ -442,13 +442,13 @@ static efi_status_t efi_open_volume(efi_system_table_t *sys_table_arg,
 	status = efi_call_early(handle_protocol, handle,
 				&fs_proto, (void **)&io);
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg, "Failed to handle fs_proto\n");
+		efi_printk("Failed to handle fs_proto\n");
 		return status;
 	}
 
 	status = efi_call_proto(efi_file_io_interface, open_volume, io, &fh);
 	if (status != EFI_SUCCESS)
-		efi_printk(sys_table_arg, "Failed to open volume\n");
+		efi_printk("Failed to open volume\n");
 	else
 		*__fh = fh;
 
@@ -547,8 +547,7 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	 * by the thunking code.
 	 */
 	if (!efi_is_native()) {
-		pr_efi(sys_table_arg,
-		       "Ignoring file= arguments on mixed mode system\n");
+		pr_efi("Ignoring file= arguments on mixed mode system\n");
 		return EFI_SUCCESS;
 	}
 
@@ -589,7 +588,7 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
 				nr_files * sizeof(*files), (void **)&files);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table_arg, "Failed to alloc mem for file handle list\n");
+		pr_efi_err("Failed to alloc mem for file handle list\n");
 		goto fail;
 	}
 
@@ -652,13 +651,13 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 		status = efi_high_alloc(sys_table_arg, file_size_total, 0x1000,
 				    &file_addr, max_addr);
 		if (status != EFI_SUCCESS) {
-			pr_efi_err(sys_table_arg, "Failed to alloc highmem for files\n");
+			pr_efi_err("Failed to alloc highmem for files\n");
 			goto close_handles;
 		}
 
 		/* We've run out of free low memory. */
 		if (file_addr > max_addr) {
-			pr_efi_err(sys_table_arg, "We've run out of free low memory\n");
+			pr_efi_err("We've run out of free low memory\n");
 			status = EFI_INVALID_PARAMETER;
 			goto free_file_total;
 		}
@@ -680,7 +679,7 @@ efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg,
 						       &chunksize,
 						       (void *)addr);
 				if (status != EFI_SUCCESS) {
-					pr_efi_err(sys_table_arg, "Failed to read file\n");
+					pr_efi_err("Failed to read file\n");
 					goto free_file_total;
 				}
 				addr += chunksize;
@@ -766,7 +765,7 @@ efi_status_t efi_relocate_kernel(efi_system_table_t *sys_table_arg,
 					     alignment, &new_addr, min_addr);
 	}
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table_arg, "Failed to allocate usable memory for kernel.\n");
+		pr_efi_err("Failed to allocate usable memory for kernel.\n");
 		return status;
 	}
 
@@ -976,7 +975,7 @@ void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid)
 	return NULL;
 }
 
-void efi_char16_printk(efi_system_table_t *table, efi_char16_t *str)
+void efi_char16_printk(efi_char16_t *str)
 {
 	efi_call_proto(efi_simple_text_output_protocol,
 		       output_string,
drivers/firmware/efi/libstub/efistub.h +5 -4
index e6775c16a97d..4a6acd28ce65 100644
@@ -31,13 +31,14 @@ extern int __pure novamap(void);
 
 extern __pure efi_system_table_t  *efi_system_table(void);
 
-#define pr_efi(sys_table, msg)		do {				\
-	if (!is_quiet()) efi_printk(sys_table, "EFI stub: "msg);	\
+#define pr_efi(msg)		do {			\
+	if (!is_quiet()) efi_printk("EFI stub: "msg);	\
 } while (0)
 
-#define pr_efi_err(sys_table, msg) efi_printk(sys_table, "EFI stub: ERROR: "msg)
+#define pr_efi_err(msg) efi_printk("EFI stub: ERROR: "msg)
 
-void efi_char16_printk(efi_system_table_t *, efi_char16_t *);
+void efi_char16_printk(efi_char16_t *);
+void efi_char16_printk(efi_char16_t *);
 
 unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
 
drivers/firmware/efi/libstub/fdt.c +8 -8
index 0bf0190917e0..1bf7edfd81ec 100644
@@ -40,7 +40,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 	/* Do some checks on provided FDT, if it exists: */
 	if (orig_fdt) {
 		if (fdt_check_header(orig_fdt)) {
-			pr_efi_err(sys_table, "Device Tree header not valid!\n");
+			pr_efi_err("Device Tree header not valid!\n");
 			return EFI_LOAD_ERROR;
 		}
 		/*
@@ -48,7 +48,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 		 * configuration table:
 		 */
 		if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) {
-			pr_efi_err(sys_table, "Truncated device tree! foo!\n");
+			pr_efi_err("Truncated device tree! foo!\n");
 			return EFI_LOAD_ERROR;
 		}
 	}
@@ -277,17 +277,17 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	 */
 	status = efi_get_memory_map(sys_table, &map);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Unable to retrieve UEFI memory map.\n");
+		pr_efi_err("Unable to retrieve UEFI memory map.\n");
 		return status;
 	}
 
-	pr_efi(sys_table, "Exiting boot services and installing virtual address map...\n");
+	pr_efi("Exiting boot services and installing virtual address map...\n");
 
 	map.map = &memory_map;
 	status = efi_high_alloc(sys_table, MAX_FDT_SIZE, EFI_FDT_ALIGN,
 				new_fdt_addr, max_addr);
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n");
+		pr_efi_err("Unable to allocate memory for new device tree.\n");
 		goto fail;
 	}
 
@@ -304,7 +304,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 			    initrd_addr, initrd_size);
 
 	if (status != EFI_SUCCESS) {
-		pr_efi_err(sys_table, "Unable to construct new device tree.\n");
+		pr_efi_err("Unable to construct new device tree.\n");
 		goto fail_free_new_fdt;
 	}
 
@@ -350,7 +350,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 		return EFI_SUCCESS;
 	}
 
-	pr_efi_err(sys_table, "Exit boot services failed.\n");
+	pr_efi_err("Exit boot services failed.\n");
 
 fail_free_new_fdt:
 	efi_free(sys_table, MAX_FDT_SIZE, *new_fdt_addr);
@@ -371,7 +371,7 @@ void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
 		return NULL;
 
 	if (fdt_check_header(fdt) != 0) {
-		pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n");
+		pr_efi_err("Invalid header detected on UEFI supplied FDT, ignoring ...\n");
 		return NULL;
 	}
 	*fdt_size = fdt_totalsize(fdt);
drivers/firmware/efi/libstub/secureboot.c +2 -2
index edba5e7a3743..9071b57da8fe 100644
@@ -72,10 +72,10 @@ enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table_arg)
 		return efi_secureboot_mode_disabled;
 
 secure_boot_enabled:
-	pr_efi(sys_table_arg, "UEFI Secure Boot is enabled.\n");
+	pr_efi("UEFI Secure Boot is enabled.\n");
 	return efi_secureboot_mode_enabled;
 
 out_efi_err:
-	pr_efi_err(sys_table_arg, "Could not determine UEFI Secure Boot status.\n");
+	pr_efi_err("Could not determine UEFI Secure Boot status.\n");
 	return efi_secureboot_mode_unknown;
 }
drivers/firmware/efi/libstub/tpm.c +1 -2
index d270acd43de8..86658b005e85 100644
@@ -131,8 +131,7 @@ void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
 				(void **) &log_tbl);
 
 	if (status != EFI_SUCCESS) {
-		efi_printk(sys_table_arg,
-			   "Unable to allocate memory for event log\n");
+		efi_printk("Unable to allocate memory for event log\n");
 		return;
 	}
 
include/linux/efi.h +1 -1
index 6ae582a31738..1cddf3337471 100644
@@ -1551,7 +1551,7 @@ static inline int efi_runtime_map_copy(void *buf, size_t bufsz)
 
 /* prototypes shared between arch specific and generic stub code */
 
-void efi_printk(efi_system_table_t *sys_table_arg, char *str);
+void efi_printk(char *str);
 
 void efi_free(efi_system_table_t *sys_table_arg, unsigned long size,
 	      unsigned long addr);
Signature · 1 line
2.17.1
author maintainer
#50 lore raw

PATCH v2 15/21 efi/libstub: remove 'sys_table_arg' from all function prototypes

In reply to Ard Biesheuvel

We have a helper efi_system_table() that gives us the address of the EFI system table in memory, so there is no longer point in passing it around from each function to the next.

---
 arch/arm/include/asm/efi.h                     |  4 +-
 arch/arm64/include/asm/efi.h                   |  3 +-
 arch/x86/boot/compressed/eboot.c               | 40 +++++------
 drivers/firmware/efi/libstub/arm-stub.c        | 55 +++++++--------
 drivers/firmware/efi/libstub/arm32-stub.c      | 26 +++----
 drivers/firmware/efi/libstub/arm64-stub.c      | 12 ++--
 drivers/firmware/efi/libstub/efi-stub-helper.c | 74 +++++++++-----------
 drivers/firmware/efi/libstub/efistub.h         | 17 ++---
 drivers/firmware/efi/libstub/fdt.c             | 37 +++++-----
 drivers/firmware/efi/libstub/gop.c             | 10 ++-
 drivers/firmware/efi/libstub/random.c          | 10 ++-
 drivers/firmware/efi/libstub/secureboot.c      |  2 +-
 drivers/firmware/efi/libstub/tpm.c             |  5 +-
 include/linux/efi.h                            | 43 +++++-------
 14 files changed, 147 insertions(+), 191 deletions(-)
arch/arm/include/asm/efi.h +2 -2
index 555364b7bd2a..58e5acc424a0 100644
@@ -60,8 +60,8 @@ void efi_virtmap_unload(void);
 #define efi_call_proto(protocol, f, instance, ...)			\
 	instance->f(instance, ##__VA_ARGS__)
 
-struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg);
-void free_screen_info(efi_system_table_t *sys_table, struct screen_info *si);
+struct screen_info *alloc_screen_info(void);
+void free_screen_info(struct screen_info *si);
 
 static inline void efifb_setup_from_dmi(struct screen_info *si, const char *opt)
 {
arch/arm64/include/asm/efi.h +1 -2
index 9aa518d67588..d73693177f31 100644
@@ -105,8 +105,7 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 
 #define alloc_screen_info(x...)		&screen_info
 
-static inline void free_screen_info(efi_system_table_t *sys_table_arg,
-				    struct screen_info *si)
+static inline void free_screen_info(struct screen_info *si)
 {
 }
 
arch/x86/boot/compressed/eboot.c +18 -22
index 92fbd455f496..b4e859399451 100644
@@ -347,7 +347,7 @@ void setup_graphics(struct boot_params *boot_params)
 				EFI_LOCATE_BY_PROTOCOL,
 				&graphics_proto, NULL, &size, gop_handle);
 	if (status == EFI_BUFFER_TOO_SMALL)
-		status = efi_setup_gop(NULL, si, &graphics_proto, size);
+		status = efi_setup_gop(si, &graphics_proto, size);
 
 	if (status != EFI_SUCCESS) {
 		size = 0;
@@ -396,8 +396,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 		return NULL;
 	}
 
-	status = efi_low_alloc(sys_table, 0x4000, 1,
-			       (unsigned long *)&boot_params);
+	status = efi_low_alloc(0x4000, 1, (unsigned long *)&boot_params);
 	if (status != EFI_SUCCESS) {
 		efi_printk("Failed to allocate lowmem for boot params\n");
 		return NULL;
@@ -422,7 +421,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	hdr->type_of_loader = 0x21;
 
 	/* Convert unicode cmdline to ascii */
-	cmdline_ptr = efi_convert_cmdline(sys_table, image, &options_size);
+	cmdline_ptr = efi_convert_cmdline(image, &options_size);
 	if (!cmdline_ptr)
 		goto fail;
 
@@ -440,7 +439,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	if (status != EFI_SUCCESS)
 		goto fail2;
 
-	status = handle_cmdline_files(sys_table, image,
+	status = handle_cmdline_files(image,
 				      (char *)(unsigned long)hdr->cmd_line_ptr,
 				      "initrd=", hdr->initrd_addr_max,
 				      &ramdisk_addr, &ramdisk_size);
@@ -448,7 +447,7 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	if (status != EFI_SUCCESS &&
 	    hdr->xloadflags & XLF_CAN_BE_LOADED_ABOVE_4G) {
 		efi_printk("Trying to load files to higher address\n");
-		status = handle_cmdline_files(sys_table, image,
+		status = handle_cmdline_files(image,
 				      (char *)(unsigned long)hdr->cmd_line_ptr,
 				      "initrd=", -1UL,
 				      &ramdisk_addr, &ramdisk_size);
@@ -464,9 +463,9 @@ struct boot_params *make_boot_params(struct efi_config *c)
 	return boot_params;
 
 fail2:
-	efi_free(sys_table, options_size, hdr->cmd_line_ptr);
+	efi_free(options_size, hdr->cmd_line_ptr);
 fail:
-	efi_free(sys_table, 0x4000, (unsigned long)boot_params);
+	efi_free(0x4000, (unsigned long)boot_params);
 
 	return NULL;
 }
@@ -633,7 +632,7 @@ static efi_status_t allocate_e820(struct boot_params *params,
 	boot_map.key_ptr	= NULL;
 	boot_map.buff_size	= &buff_size;
 
-	status = efi_get_memory_map(sys_table, &boot_map);
+	status = efi_get_memory_map(&boot_map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -655,8 +654,7 @@ struct exit_boot_struct {
 	struct efi_info		*efi;
 };
 
-static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
-				   struct efi_boot_memmap *map,
+static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
 				   void *priv)
 {
 	const char *signature;
@@ -666,14 +664,14 @@ static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
 				   : EFI32_LOADER_SIGNATURE;
 	memcpy(&p->efi->efi_loader_signature, signature, sizeof(__u32));
 
-	p->efi->efi_systab		= (unsigned long)sys_table_arg;
+	p->efi->efi_systab		= (unsigned long)efi_system_table();
 	p->efi->efi_memdesc_size	= *map->desc_size;
 	p->efi->efi_memdesc_version	= *map->desc_ver;
 	p->efi->efi_memmap		= (unsigned long)*map->map;
 	p->efi->efi_memmap_size		= *map->map_size;
 
 #ifdef CONFIG_X86_64
-	p->efi->efi_systab_hi		= (unsigned long)sys_table_arg >> 32;
+	p->efi->efi_systab_hi		= (unsigned long)efi_system_table() >> 32;
 	p->efi->efi_memmap_hi		= (unsigned long)*map->map >> 32;
 #endif
 
@@ -705,8 +703,7 @@ static efi_status_t exit_boot(struct boot_params *boot_params, void *handle)
 		return status;
 
 	/* Might as well exit boot services now */
-	status = efi_exit_boot_services(sys_table, handle, &map, &priv,
-					exit_boot_func);
+	status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -759,14 +756,14 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	 * otherwise we ask the BIOS.
 	 */
 	if (boot_params->secure_boot == efi_secureboot_mode_unset)
-		boot_params->secure_boot = efi_get_secureboot(sys_table);
+		boot_params->secure_boot = efi_get_secureboot();
 
 	/* Ask the firmware to clear memory on unclean shutdown */
-	efi_enable_reset_attack_mitigation(sys_table);
+	efi_enable_reset_attack_mitigation();
 
-	efi_random_get_seed(sys_table);
+	efi_random_get_seed();
 
-	efi_retrieve_tpm2_eventlog(sys_table);
+	efi_retrieve_tpm2_eventlog();
 
 	setup_graphics(boot_params);
 
@@ -782,8 +779,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	}
 
 	gdt->size = 0x800;
-	status = efi_low_alloc(sys_table, gdt->size, 8,
-			   (unsigned long *)&gdt->address);
+	status = efi_low_alloc(gdt->size, 8, (unsigned long *)&gdt->address);
 	if (status != EFI_SUCCESS) {
 		efi_printk("Failed to allocate memory for 'gdt'\n");
 		goto fail;
@@ -795,7 +791,7 @@ efi_main(struct efi_config *c, struct boot_params *boot_params)
 	 */
 	if (hdr->pref_address != hdr->code32_start) {
 		unsigned long bzimage_addr = hdr->code32_start;
-		status = efi_relocate_kernel(sys_table, &bzimage_addr,
+		status = efi_relocate_kernel(&bzimage_addr,
 					     hdr->init_size, hdr->init_size,
 					     hdr->pref_address,
 					     hdr->kernel_alignment,
drivers/firmware/efi/libstub/arm-stub.c +26 -29

160 lines not on this page · raw message

drivers/firmware/efi/libstub/arm32-stub.c +11 -15
index 7b5c717ddfac..e7a38d912749 100644
@@ -7,7 +7,7 @@
 
 #include "efistub.h"
 
-efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
+efi_status_t check_platform_features(void)
 {
 	int block;
 
@@ -26,7 +26,7 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
 
 static efi_guid_t screen_info_guid = LINUX_EFI_ARM_SCREEN_INFO_TABLE_GUID;
 
-struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg)
+struct screen_info *alloc_screen_info(void)
 {
 	struct screen_info *si;
 	efi_status_t status;
@@ -52,7 +52,7 @@ struct screen_info *alloc_screen_info(efi_system_table_t *sys_table_arg)
 	return NULL;
 }
 
-void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
+void free_screen_info(struct screen_info *si)
 {
 	if (!si)
 		return;
@@ -61,8 +61,7 @@ void free_screen_info(efi_system_table_t *sys_table_arg, struct screen_info *si)
 	efi_call_early(free_pool, si);
 }
 
-static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
-					unsigned long dram_base,
+static efi_status_t reserve_kernel_base(unsigned long dram_base,
 					unsigned long *reserve_addr,
 					unsigned long *reserve_size)
 {
@@ -119,7 +118,7 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
 	 * released to the OS after ExitBootServices(), the decompressor can
 	 * safely overwrite them.
 	 */
-	status = efi_get_memory_map(sys_table_arg, &map);
+	status = efi_get_memory_map(&map);
 	if (status != EFI_SUCCESS) {
 		pr_efi_err("reserve_kernel_base(): Unable to retrieve memory map.\n");
 		return status;
@@ -190,8 +189,7 @@ static efi_status_t reserve_kernel_base(efi_system_table_t *sys_table_arg,
 	return status;
 }
 
-efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
-				 unsigned long *image_addr,
+efi_status_t handle_kernel_image(unsigned long *image_addr,
 				 unsigned long *image_size,
 				 unsigned long *reserve_addr,
 				 unsigned long *reserve_size,
@@ -219,8 +217,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 	 */
 	kernel_base += TEXT_OFFSET - 5 * PAGE_SIZE;
 
-	status = reserve_kernel_base(sys_table, kernel_base, reserve_addr,
-				     reserve_size);
+	status = reserve_kernel_base(kernel_base, reserve_addr, reserve_size);
 	if (status != EFI_SUCCESS) {
 		pr_efi_err("Unable to allocate memory for uncompressed kernel.\n");
 		return status;
@@ -231,12 +228,11 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 	 * memory window.
 	 */
 	*image_size = image->image_size;
-	status = efi_relocate_kernel(sys_table, image_addr, *image_size,
-				     *image_size,
+	status = efi_relocate_kernel(image_addr, *image_size, *image_size,
 				     kernel_base + MAX_UNCOMP_KERNEL_SIZE, 0, 0);
 	if (status != EFI_SUCCESS) {
 		pr_efi_err("Failed to relocate kernel.\n");
-		efi_free(sys_table, *reserve_size, *reserve_addr);
+		efi_free(*reserve_size, *reserve_addr);
 		*reserve_size = 0;
 		return status;
 	}
@@ -248,9 +244,9 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table,
 	 */
 	if (*image_addr + *image_size > dram_base + ZIMAGE_OFFSET_LIMIT) {
 		pr_efi_err("Failed to relocate kernel, no low memory available.\n");
-		efi_free(sys_table, *reserve_size, *reserve_addr);
+		efi_free(*reserve_size, *reserve_addr);
 		*reserve_size = 0;
-		efi_free(sys_table, *image_size, *image_addr);
+		efi_free(*image_size, *image_addr);
 		*image_size = 0;
 		return EFI_LOAD_ERROR;
 	}
drivers/firmware/efi/libstub/arm64-stub.c +5 -7
index b09dda600c78..beba45e478c7 100644
@@ -21,7 +21,7 @@
 
 #include "efistub.h"
 
-efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
+efi_status_t check_platform_features(void)
 {
 	u64 tg;
 
@@ -40,8 +40,7 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg)
 	return EFI_SUCCESS;
 }
 
-efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
-				 unsigned long *image_addr,
+efi_status_t handle_kernel_image(unsigned long *image_addr,
 				 unsigned long *image_size,
 				 unsigned long *reserve_addr,
 				 unsigned long *reserve_size,
@@ -56,8 +55,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
 
 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
 		if (!nokaslr()) {
-			status = efi_get_random_bytes(sys_table_arg,
-						      sizeof(phys_seed),
+			status = efi_get_random_bytes(sizeof(phys_seed),
 						      (u8 *)&phys_seed);
 			if (status == EFI_NOT_FOUND) {
 				pr_efi("EFI_RNG_PROTOCOL unavailable, no randomness supplied\n");
@@ -108,7 +106,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
 		 * locate the kernel at a randomized offset in physical memory.
 		 */
 		*reserve_size = kernel_memsize + offset;
-		status = efi_random_alloc(sys_table_arg, *reserve_size,
+		status = efi_random_alloc(*reserve_size,
 					  MIN_KIMG_ALIGN, reserve_addr,
 					  (u32)phys_seed);
 
@@ -139,7 +137,7 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table_arg,
 
 	if (status != EFI_SUCCESS) {
 		*reserve_size = kernel_memsize + TEXT_OFFSET;
-		status = efi_low_alloc(sys_table_arg, *reserve_size,
+		status = efi_low_alloc(*reserve_size,
 				       MIN_KIMG_ALIGN, reserve_addr);
 
 		if (status != EFI_SUCCESS) {
drivers/firmware/efi/libstub/efi-stub-helper.c +33 -41

215 lines not on this page · raw message

drivers/firmware/efi/libstub/efistub.h +7 -10
index 4a6acd28ce65..b5d9c9e65213 100644
@@ -40,10 +40,9 @@ extern __pure efi_system_table_t  *efi_system_table(void);
 void efi_char16_printk(efi_char16_t *);
 void efi_char16_printk(efi_char16_t *);
 
-unsigned long get_dram_base(efi_system_table_t *sys_table_arg);
+unsigned long get_dram_base(void);
 
-efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
-					    void *handle,
+efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
 					    unsigned long *new_fdt_addr,
 					    unsigned long max_addr,
 					    u64 initrd_addr, u64 initrd_size,
@@ -51,22 +50,20 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 					    unsigned long fdt_addr,
 					    unsigned long fdt_size);
 
-void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size);
+void *get_fdt(unsigned long *fdt_size);
 
 void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size,
 		     unsigned long desc_size, efi_memory_desc_t *runtime_map,
 		     int *count);
 
-efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table,
-				  unsigned long size, u8 *out);
+efi_status_t efi_get_random_bytes(unsigned long size, u8 *out);
 
-efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
-			      unsigned long size, unsigned long align,
+efi_status_t efi_random_alloc(unsigned long size, unsigned long align,
 			      unsigned long *addr, unsigned long random_seed);
 
-efi_status_t check_platform_features(efi_system_table_t *sys_table_arg);
+efi_status_t check_platform_features(void);
 
-void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid);
+void *get_efi_config_table(efi_guid_t guid);
 
 /* Helper macros for the usual case of using simple C variables: */
 #ifndef fdt_setprop_inplace_var
drivers/firmware/efi/libstub/fdt.c +17 -20
index 1bf7edfd81ec..0a91e5232127 100644
@@ -16,7 +16,7 @@
 #define EFI_DT_ADDR_CELLS_DEFAULT 2
 #define EFI_DT_SIZE_CELLS_DEFAULT 2
 
-static void fdt_update_cell_size(efi_system_table_t *sys_table, void *fdt)
+static void fdt_update_cell_size(void *fdt)
 {
 	int offset;
 
@@ -27,8 +27,7 @@ static void fdt_update_cell_size(efi_system_table_t *sys_table, void *fdt)
 	fdt_setprop_u32(fdt, offset, "#size-cells",    EFI_DT_SIZE_CELLS_DEFAULT);
 }
 
-static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
-			       unsigned long orig_fdt_size,
+static efi_status_t update_fdt(void *orig_fdt, unsigned long orig_fdt_size,
 			       void *fdt, int new_fdt_size, char *cmdline_ptr,
 			       u64 initrd_addr, u64 initrd_size)
 {
@@ -62,7 +61,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 			 * Any failure from the following function is
 			 * non-critical:
 			 */
-			fdt_update_cell_size(sys_table, fdt);
+			fdt_update_cell_size(fdt);
 		}
 	}
 
@@ -111,7 +110,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 
 	/* Add FDT entries for EFI runtime services in chosen node. */
 	node = fdt_subnode_offset(fdt, 0, "chosen");
-	fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table);
+	fdt_val64 = cpu_to_fdt64((u64)(unsigned long)efi_system_table());
 
 	status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64);
 	if (status)
@@ -140,7 +139,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt,
 	if (IS_ENABLED(CONFIG_RANDOMIZE_BASE)) {
 		efi_status_t efi_status;
 
-		efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64),
+		efi_status = efi_get_random_bytes(sizeof(fdt_val64),
 						  (u8 *)&fdt_val64);
 		if (efi_status == EFI_SUCCESS) {
 			status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64);
@@ -210,8 +209,7 @@ struct exit_boot_struct {
 	void			*new_fdt_addr;
 };
 
-static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
-				   struct efi_boot_memmap *map,
+static efi_status_t exit_boot_func(struct efi_boot_memmap *map,
 				   void *priv)
 {
 	struct exit_boot_struct *p = priv;
@@ -244,8 +242,7 @@ static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg,
  * with the final memory map in it.
  */
 
-efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
-					    void *handle,
+efi_status_t allocate_new_fdt_and_exit_boot(void *handle,
 					    unsigned long *new_fdt_addr,
 					    unsigned long max_addr,
 					    u64 initrd_addr, u64 initrd_size,
@@ -275,7 +272,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	 * subsequent allocations adding entries, since they could not affect
 	 * the number of EFI_MEMORY_RUNTIME regions.
 	 */
-	status = efi_get_memory_map(sys_table, &map);
+	status = efi_get_memory_map(&map);
 	if (status != EFI_SUCCESS) {
 		pr_efi_err("Unable to retrieve UEFI memory map.\n");
 		return status;
@@ -284,7 +281,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	pr_efi("Exiting boot services and installing virtual address map...\n");
 
 	map.map = &memory_map;
-	status = efi_high_alloc(sys_table, MAX_FDT_SIZE, EFI_FDT_ALIGN,
+	status = efi_high_alloc(MAX_FDT_SIZE, EFI_FDT_ALIGN,
 				new_fdt_addr, max_addr);
 	if (status != EFI_SUCCESS) {
 		pr_efi_err("Unable to allocate memory for new device tree.\n");
@@ -295,11 +292,11 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	 * Now that we have done our final memory allocation (and free)
 	 * we can get the memory map key needed for exit_boot_services().
 	 */
-	status = efi_get_memory_map(sys_table, &map);
+	status = efi_get_memory_map(&map);
 	if (status != EFI_SUCCESS)
 		goto fail_free_new_fdt;
 
-	status = update_fdt(sys_table, (void *)fdt_addr, fdt_size,
+	status = update_fdt((void *)fdt_addr, fdt_size,
 			    (void *)*new_fdt_addr, MAX_FDT_SIZE, cmdline_ptr,
 			    initrd_addr, initrd_size);
 
@@ -313,7 +310,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	priv.runtime_entry_count	= &runtime_entry_count;
 	priv.new_fdt_addr		= (void *)*new_fdt_addr;
 
-	status = efi_exit_boot_services(sys_table, handle, &map, &priv, exit_boot_func);
+	status = efi_exit_boot_services(handle, &map, &priv, exit_boot_func);
 
 	if (status == EFI_SUCCESS) {
 		efi_set_virtual_address_map_t *svam;
@@ -322,7 +319,7 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 			return EFI_SUCCESS;
 
 		/* Install the new virtual address map */
-		svam = sys_table->runtime->set_virtual_address_map;
+		svam = efi_system_table()->runtime->set_virtual_address_map;
 		status = svam(runtime_entry_count * desc_size, desc_size,
 			      desc_ver, runtime_map);
 
@@ -353,19 +350,19 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table,
 	pr_efi_err("Exit boot services failed.\n");
 
 fail_free_new_fdt:
-	efi_free(sys_table, MAX_FDT_SIZE, *new_fdt_addr);
+	efi_free(MAX_FDT_SIZE, *new_fdt_addr);
 
 fail:
-	sys_table->boottime->free_pool(runtime_map);
+	efi_system_table()->boottime->free_pool(runtime_map);
 
 	return EFI_LOAD_ERROR;
 }
 
-void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size)
+void *get_fdt(unsigned long *fdt_size)
 {
 	void *fdt;
 
-	fdt = get_efi_config_table(sys_table, DEVICE_TREE_GUID);
+	fdt = get_efi_config_table(DEVICE_TREE_GUID);
 
 	if (!fdt)
 		return NULL;
drivers/firmware/efi/libstub/gop.c +4 -6
index 6c49d0a9aa3f..c3afe8d4a688 100644
@@ -88,9 +88,8 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
 #define efi_gop_attr(table, attr, instance) \
 	(efi_table_attr(efi_graphics_output_protocol##table, attr, instance))
 
-static efi_status_t
-setup_gop(efi_system_table_t *sys_table_arg, struct screen_info *si,
-	  efi_guid_t *proto, unsigned long size, void **handles)
+static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
+			      unsigned long size, void **handles)
 {
 	efi_graphics_output_protocol_t *gop, *first_gop;
 	u16 width, height;
@@ -185,8 +184,7 @@ setup_gop(efi_system_table_t *sys_table_arg, struct screen_info *si,
 /*
  * See if we have Graphics Output Protocol
  */
-efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
-			   struct screen_info *si, efi_guid_t *proto,
+efi_status_t efi_setup_gop(struct screen_info *si, efi_guid_t *proto,
 			   unsigned long size)
 {
 	efi_status_t status;
@@ -203,7 +201,7 @@ efi_status_t efi_setup_gop(efi_system_table_t *sys_table_arg,
 	if (status != EFI_SUCCESS)
 		goto free_handle;
 
-	status = setup_gop(sys_table_arg, si, proto, size, gop_handle);
+	status = setup_gop(si, proto, size, gop_handle);
 
 free_handle:
 	efi_call_early(free_pool, gop_handle);
drivers/firmware/efi/libstub/random.c +4 -6
index 4f5c249c62dc..9b30d953d13b 100644
@@ -26,8 +26,7 @@ union efi_rng_protocol {
 	} mixed_mode;
 };
 
-efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
-				  unsigned long size, u8 *out)
+efi_status_t efi_get_random_bytes(unsigned long size, u8 *out)
 {
 	efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
 	efi_status_t status;
@@ -79,8 +78,7 @@ static unsigned long get_entry_num_slots(efi_memory_desc_t *md,
  */
 #define MD_NUM_SLOTS(md)	((md)->virt_addr)
 
-efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
-			      unsigned long size,
+efi_status_t efi_random_alloc(unsigned long size,
 			      unsigned long align,
 			      unsigned long *addr,
 			      unsigned long random_seed)
@@ -99,7 +97,7 @@ efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
 	map.key_ptr =	NULL;
 	map.buff_size =	&buff_size;
 
-	status = efi_get_memory_map(sys_table_arg, &map);
+	status = efi_get_memory_map(&map);
 	if (status != EFI_SUCCESS)
 		return status;
 
@@ -155,7 +153,7 @@ efi_status_t efi_random_alloc(efi_system_table_t *sys_table_arg,
 	return status;
 }
 
-efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg)
+efi_status_t efi_random_get_seed(void)
 {
 	efi_guid_t rng_proto = EFI_RNG_PROTOCOL_GUID;
 	efi_guid_t rng_algo_raw = EFI_RNG_ALGORITHM_RAW;
drivers/firmware/efi/libstub/secureboot.c +1 -1
index 9071b57da8fe..0935d824a6ab 100644
@@ -32,7 +32,7 @@ static const efi_char16_t shim_MokSBState_name[] = L"MokSBState";
  * Please keep the logic in sync with
  * arch/x86/xen/efi.c:xen_efi_get_secureboot().
  */
-enum efi_secureboot_mode efi_get_secureboot(efi_system_table_t *sys_table_arg)
+enum efi_secureboot_mode efi_get_secureboot(void)
 {
 	u32 attr;
 	u8 secboot, setupmode, moksbstate;
drivers/firmware/efi/libstub/tpm.c +2 -3
index 86658b005e85..9fae0850e2ac 100644
@@ -57,7 +57,7 @@ void efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg)
 
 #endif
 
-void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
+void efi_retrieve_tpm2_eventlog(void)
 {
 	efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID;
 	efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID;
@@ -139,8 +139,7 @@ void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg)
 	 * Figure out whether any events have already been logged to the
 	 * final events structure, and if so how much space they take up
 	 */
-	final_events_table = get_efi_config_table(sys_table_arg,
-						LINUX_EFI_TPM_FINAL_LOG_GUID);
+	final_events_table = get_efi_config_table(LINUX_EFI_TPM_FINAL_LOG_GUID);
 	if (final_events_table && final_events_table->nr_events) {
 		struct tcg_pcr_event2_head *header;
 		int offset;
include/linux/efi.h +16 -27

99 lines not on this page · raw message

Signature · 1 line
2.17.1
author maintainer
#51 lore raw

PATCH v2 16/21 efi/libstub: drop protocol argument from efi_call_proto() macro

In reply to Ard Biesheuvel

After refactoring the mixed mode support code, efi_call_proto() no longer uses its protocol argument in any of its implementation, so let's remove it altogether.

---
 arch/arm/include/asm/efi.h                     |  3 +--
 arch/arm64/include/asm/efi.h                   |  3 +--
 arch/x86/boot/compressed/eboot.c               | 23 ++++++++------------
 arch/x86/include/asm/efi.h                     |  6 ++---
 drivers/firmware/efi/libstub/efi-stub-helper.c | 22 ++++++++-----------
 drivers/firmware/efi/libstub/random.c          |  8 +++----
 drivers/firmware/efi/libstub/tpm.c             | 11 +++++-----
 7 files changed, 32 insertions(+), 44 deletions(-)
arch/arm/include/asm/efi.h +1 -2
index 58e5acc424a0..bdc5288cc643 100644
@@ -57,8 +57,7 @@ void efi_virtmap_unload(void);
 #define efi_table_attr(table, attr, instance)				\
 	instance->attr
 
-#define efi_call_proto(protocol, f, instance, ...)			\
-	instance->f(instance, ##__VA_ARGS__)
+#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
 
 struct screen_info *alloc_screen_info(void);
 void free_screen_info(struct screen_info *si);
arch/arm64/include/asm/efi.h +1 -2
index d73693177f31..4bc1e89671ab 100644
@@ -100,8 +100,7 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 #define efi_table_attr(table, attr, instance)				\
 	instance->attr
 
-#define efi_call_proto(protocol, f, instance, ...)			\
-	instance->f(instance, ##__VA_ARGS__)
+#define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
 
 #define alloc_screen_info(x...)		&screen_info
 
arch/x86/boot/compressed/eboot.c +9 -14
index b4e859399451..da3e7509bbe7 100644
@@ -76,27 +76,24 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 	rom->pcilen	= pci->romsize;
 	*__rom = rom;
 
-	status = efi_call_proto(efi_pci_io_protocol, pci.read, pci,
-				EfiPciIoWidthUint16, PCI_VENDOR_ID, 1,
-				&rom->vendor);
+	status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
+				PCI_VENDOR_ID, 1, &rom->vendor);
 
 	if (status != EFI_SUCCESS) {
 		efi_printk("Failed to read rom->vendor\n");
 		goto free_struct;
 	}
 
-	status = efi_call_proto(efi_pci_io_protocol, pci.read, pci,
-				EfiPciIoWidthUint16, PCI_DEVICE_ID, 1,
-				&rom->devid);
+	status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
+				PCI_DEVICE_ID, 1, &rom->devid);
 
 	if (status != EFI_SUCCESS) {
 		efi_printk("Failed to read rom->devid\n");
 		goto free_struct;
 	}
 
-	status = efi_call_proto(efi_pci_io_protocol, get_location, pci,
-				&rom->segment, &rom->bus, &rom->device,
-				&rom->function);
+	status = efi_call_proto(pci, get_location, &rom->segment, &rom->bus,
+				&rom->device, &rom->function);
 
 	if (status != EFI_SUCCESS)
 		goto free_struct;
@@ -198,7 +195,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 		return;
 	}
 
-	efi_call_proto(apple_properties_protocol, get_all, p, NULL, &size);
+	efi_call_proto(p, get_all, NULL, &size);
 	if (!size)
 		return;
 
@@ -211,8 +208,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 			return;
 		}
 
-		status = efi_call_proto(apple_properties_protocol, get_all, p,
-					new->data, &size);
+		status = efi_call_proto(p, get_all, new->data, &size);
 
 		if (status == EFI_BUFFER_TOO_SMALL)
 			efi_call_early(free_pool, new);
@@ -287,8 +283,7 @@ setup_uga(struct screen_info *si, efi_guid_t *uga_proto, unsigned long size)
 		pciio = NULL;
 		efi_call_early(handle_protocol, handle, &pciio_proto, &pciio);
 
-		status = efi_call_proto(efi_uga_draw_protocol, get_mode, uga,
-					&w, &h, &depth, &refresh);
+		status = efi_call_proto(uga, get_mode, &w, &h, &depth, &refresh);
 		if (status == EFI_SUCCESS && (!first_uga || pciio)) {
 			width = w;
 			height = h;
arch/x86/include/asm/efi.h +3 -3
index 8137d4f5c104..7bdd2fb11e7c 100644
@@ -236,10 +236,10 @@ static inline bool efi_is_native(void)
 	__ret;								\
 })
 
-#define efi_call_proto(protocol, f, instance, ...)			\
+#define efi_call_proto(inst, func, ...)					\
 	(efi_is_native()						\
-		? instance->f(instance, ##__VA_ARGS__)			\
-		: efi64_thunk(instance->mixed_mode.f, instance,	##__VA_ARGS__))
+		? inst->func(inst, ##__VA_ARGS__)			\
+		: efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__))
 
 #define efi_call_early(f, ...)						\
 	(efi_is_native()						\
drivers/firmware/efi/libstub/efi-stub-helper.c +9 -13
index 2ddeb312e8e5..4117813a1707 100644
@@ -370,8 +370,8 @@ static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
 	efi_guid_t info_guid = EFI_FILE_INFO_ID;
 	unsigned long info_sz;
 
-	status = efi_call_proto(efi_file_handle, open, fh, &h, filename_16,
-				EFI_FILE_MODE_READ, (u64)0);
+	status = efi_call_proto(fh, open, &h, filename_16, EFI_FILE_MODE_READ,
+				(u64)0);
 	if (status != EFI_SUCCESS) {
 		efi_printk("Failed to open file: ");
 		efi_char16_printk(filename_16);
@@ -382,8 +382,7 @@ static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
 	*handle = h;
 
 	info_sz = 0;
-	status = efi_call_proto(efi_file_handle, get_info, h, &info_guid,
-				&info_sz, NULL);
+	status = efi_call_proto(h, get_info, &info_guid, &info_sz, NULL);
 	if (status != EFI_BUFFER_TOO_SMALL) {
 		efi_printk("Failed to get file info size\n");
 		return status;
@@ -397,8 +396,7 @@ static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
 		return status;
 	}
 
-	status = efi_call_proto(efi_file_handle, get_info, h, &info_guid,
-				&info_sz, info);
+	status = efi_call_proto(h, get_info, &info_guid, &info_sz, info);
 	if (status == EFI_BUFFER_TOO_SMALL) {
 		efi_call_early(free_pool, info);
 		goto grow;
@@ -416,12 +414,12 @@ static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,
 static efi_status_t efi_file_read(efi_file_handle_t *handle,
 				  unsigned long *size, void *addr)
 {
-	return efi_call_proto(efi_file_handle, read, handle, size, addr);
+	return efi_call_proto(handle, read, size, addr);
 }
 
 static efi_status_t efi_file_close(efi_file_handle_t *handle)
 {
-	return efi_call_proto(efi_file_handle, close, handle);
+	return efi_call_proto(handle, close);
 }
 
 static efi_status_t efi_open_volume(efi_loaded_image_t *image,
@@ -440,7 +438,7 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image,
 		return status;
 	}
 
-	status = efi_call_proto(efi_file_io_interface, open_volume, io, &fh);
+	status = efi_call_proto(io, open_volume, &fh);
 	if (status != EFI_SUCCESS)
 		efi_printk("Failed to open volume\n");
 	else
@@ -969,9 +967,7 @@ void *get_efi_config_table(efi_guid_t guid)
 
 void efi_char16_printk(efi_char16_t *str)
 {
-	efi_call_proto(efi_simple_text_output_protocol,
-		       output_string,
-		       efi_table_attr(efi_system_table, con_out,
+	efi_call_proto(efi_table_attr(efi_system_table, con_out,
 				      efi_system_table()),
-		       str);
+		       output_string, str);
 }
drivers/firmware/efi/libstub/random.c +4 -4
index 9b30d953d13b..fbd5b5724b19 100644
@@ -37,7 +37,7 @@ efi_status_t efi_get_random_bytes(unsigned long size, u8 *out)
 	if (status != EFI_SUCCESS)
 		return status;
 
-	return efi_call_proto(efi_rng_protocol, get_rng, rng, NULL, size, out);
+	return efi_call_proto(rng, get_rng, NULL, size, out);
 }
 
 /*
@@ -173,7 +173,7 @@ efi_status_t efi_random_get_seed(void)
 	if (status != EFI_SUCCESS)
 		return status;
 
-	status = efi_call_proto(efi_rng_protocol, get_rng, rng, &rng_algo_raw,
+	status = efi_call_proto(rng, get_rng, &rng_algo_raw,
 				 EFI_RANDOM_SEED_SIZE, seed->bits);
 
 	if (status == EFI_UNSUPPORTED)
@@ -181,8 +181,8 @@ efi_status_t efi_random_get_seed(void)
 		 * Use whatever algorithm we have available if the raw algorithm
 		 * is not implemented.
 		 */
-		status = efi_call_proto(efi_rng_protocol, get_rng, rng, NULL,
-					 EFI_RANDOM_SEED_SIZE, seed->bits);
+		status = efi_call_proto(rng, get_rng, NULL,
+					EFI_RANDOM_SEED_SIZE, seed->bits);
 
 	if (status != EFI_SUCCESS)
 		goto err_freepool;
drivers/firmware/efi/libstub/tpm.c +5 -6
index 9fae0850e2ac..dbb4b3cfc4e8 100644
@@ -77,15 +77,14 @@ void efi_retrieve_tpm2_eventlog(void)
 	if (status != EFI_SUCCESS)
 		return;
 
-	status = efi_call_proto(efi_tcg2_protocol, get_event_log,
-				tcg2_protocol, version, &log_location,
-				&log_last_entry, &truncated);
+	status = efi_call_proto(tcg2_protocol, get_event_log, version,
+				&log_location, &log_last_entry, &truncated);
 
 	if (status != EFI_SUCCESS || !log_location) {
 		version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2;
-		status = efi_call_proto(efi_tcg2_protocol, get_event_log,
-					tcg2_protocol, version, &log_location,
-					&log_last_entry, &truncated);
+		status = efi_call_proto(tcg2_protocol, get_event_log, version,
+					&log_location, &log_last_entry,
+					&truncated);
 		if (status != EFI_SUCCESS || !log_location)
 			return;
 
Signature · 1 line
2.17.1
author maintainer
#52 lore raw

PATCH v2 17/21 efi/libstub: drop 'table' argument from efi_table_attr() macro

In reply to Ard Biesheuvel

None of the definitions of the efi_table_attr() still refer to their 'table' argument so let's get rid of it entirely.

---
 arch/arm/include/asm/efi.h                     |  3 +--
 arch/arm64/include/asm/efi.h                   |  3 +--
 arch/x86/boot/compressed/eboot.c               |  8 +++----
 arch/x86/include/asm/efi.h                     | 25 +++++++-------------
 drivers/firmware/efi/libstub/efi-stub-helper.c | 13 ++++------
 drivers/firmware/efi/libstub/gop.c             |  9 +++----
 6 files changed, 23 insertions(+), 38 deletions(-)
arch/arm/include/asm/efi.h +1 -2
index bdc5288cc643..bc720024a260 100644
@@ -54,8 +54,7 @@ void efi_virtmap_unload(void);
 #define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
 #define efi_is_native()			(true)
 
-#define efi_table_attr(table, attr, instance)				\
-	instance->attr
+#define efi_table_attr(inst, attr)	(inst->attr)
 
 #define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
 
arch/arm64/include/asm/efi.h +1 -2
index 4bc1e89671ab..6f041ae446d2 100644
@@ -97,8 +97,7 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 #define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
 #define efi_is_native()			(true)
 
-#define efi_table_attr(table, attr, instance)				\
-	instance->attr
+#define efi_table_attr(inst, attr)	(inst->attr)
 
 #define efi_call_proto(inst, func, ...) inst->func(inst, ##__VA_ARGS__)
 
arch/x86/boot/compressed/eboot.c +4 -4
index da3e7509bbe7..a3000283dc7a 100644
@@ -54,8 +54,8 @@ preserve_pci_rom_image(efi_pci_io_protocol_t *pci, struct pci_setup_rom **__rom)
 	 * large romsize. The UEFI spec limits the size of option ROMs to 16
 	 * MiB so we reject any ROMs over 16 MiB in size to catch this.
 	 */
-	romimage = efi_table_attr(efi_pci_io_protocol, romimage, pci);
-	romsize = efi_table_attr(efi_pci_io_protocol, romsize, pci);
+	romimage = efi_table_attr(pci, romimage);
+	romsize = efi_table_attr(pci, romsize);
 	if (!romimage || !romsize || romsize > SZ_16M)
 		return EFI_INVALID_PARAMETER;
 
@@ -190,7 +190,7 @@ static void retrieve_apple_device_properties(struct boot_params *boot_params)
 	if (status != EFI_SUCCESS)
 		return;
 
-	if (efi_table_attr(apple_properties_protocol, version, p) != 0x10000) {
+	if (efi_table_attr(p, version) != 0x10000) {
 		efi_printk("Unsupported properties proto version\n");
 		return;
 	}
@@ -233,7 +233,7 @@ static const efi_char16_t apple[] = L"Apple";
 static void setup_quirks(struct boot_params *boot_params)
 {
 	efi_char16_t *fw_vendor = (efi_char16_t *)(unsigned long)
-		efi_table_attr(efi_system_table, fw_vendor, sys_table);
+		efi_table_attr(efi_system_table(), fw_vendor);
 
 	if (!memcmp(fw_vendor, apple, sizeof(apple))) {
 		if (IS_ENABLED(CONFIG_APPLE_PROPERTIES))
arch/x86/include/asm/efi.h +9 -16
index 7bdd2fb11e7c..593a8cda8361 100644
@@ -225,16 +225,11 @@ static inline bool efi_is_native(void)
 		__builtin_types_compatible_p(u32, __typeof__(attr)),	\
 			(unsigned long)(attr), (attr))
 
-#define efi_table_attr(table, attr, instance) ({			\
-	__typeof__(instance->attr) __ret;				\
-	if (efi_is_native()) {						\
-		__ret = instance->attr;					\
-	} else {							\
-		__ret = (__typeof__(__ret))				\
-			efi_mixed_mode_cast(instance->mixed_mode.attr);	\
-	}								\
-	__ret;								\
-})
+#define efi_table_attr(inst, attr)					\
+	(efi_is_native()						\
+		? inst->attr						\
+		: (__typeof__(inst->attr))				\
+			efi_mixed_mode_cast(inst->mixed_mode.attr))
 
 #define efi_call_proto(inst, func, ...)					\
 	(efi_is_native()						\
@@ -244,16 +239,14 @@ static inline bool efi_is_native(void)
 #define efi_call_early(f, ...)						\
 	(efi_is_native()						\
 		? efi_system_table()->boottime->f(__VA_ARGS__)		\
-		: efi64_thunk(efi_table_attr(efi_boot_services,		\
-			boottime, efi_system_table())->mixed_mode.f,	\
-			__VA_ARGS__))
+		: efi64_thunk(efi_table_attr(efi_system_table(),	\
+				boottime)->mixed_mode.f, __VA_ARGS__))
 
 #define efi_call_runtime(f, ...)					\
 	(efi_is_native()						\
 		? efi_system_table()->runtime->f(__VA_ARGS__)		\
-		: efi64_thunk(efi_table_attr(efi_runtime_services,	\
-			runtime, efi_system_table())->mixed_mode.f,	\
-			__VA_ARGS__))
+		: efi64_thunk(efi_table_attr(efi_system_table(),	\
+				runtime)->mixed_mode.f, __VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
drivers/firmware/efi/libstub/efi-stub-helper.c +5 -8
index 4117813a1707..59c273cb7a92 100644
@@ -429,7 +429,7 @@ static efi_status_t efi_open_volume(efi_loaded_image_t *image,
 	efi_file_handle_t *fh;
 	efi_guid_t fs_proto = EFI_FILE_SYSTEM_GUID;
 	efi_status_t status;
-	void *handle = efi_table_attr(efi_loaded_image, device_handle, image);
+	void *handle = efi_table_attr(image, device_handle);
 
 	status = efi_call_early(handle_protocol, handle,
 				&fs_proto, (void **)&io);
@@ -947,17 +947,15 @@ efi_status_t efi_exit_boot_services(void *handle,
 
 void *get_efi_config_table(efi_guid_t guid)
 {
-	unsigned long tables = efi_table_attr(efi_system_table, tables,
-					      efi_system_table());
-	int nr_tables = efi_table_attr(efi_system_table, nr_tables,
-				       efi_system_table());
+	unsigned long tables = efi_table_attr(efi_system_table(), tables);
+	int nr_tables = efi_table_attr(efi_system_table(), nr_tables);
 	int i;
 
 	for (i = 0; i < nr_tables; i++) {
 		efi_config_table_t *t = (void *)tables;
 
 		if (efi_guidcmp(t->guid, guid) == 0)
-			return efi_table_attr(efi_config_table, table, t);
+			return efi_table_attr(t, table);
 
 		tables += efi_is_native() ? sizeof(efi_config_table_t)
 					  : sizeof(efi_config_table_32_t);
@@ -967,7 +965,6 @@ void *get_efi_config_table(efi_guid_t guid)
 
 void efi_char16_printk(efi_char16_t *str)
 {
-	efi_call_proto(efi_table_attr(efi_system_table, con_out,
-				      efi_system_table()),
+	efi_call_proto(efi_table_attr(efi_system_table(), con_out),
 		       output_string, str);
 }
drivers/firmware/efi/libstub/gop.c +3 -6
index c3afe8d4a688..8f746282c219 100644
@@ -85,9 +85,6 @@ setup_pixel_info(struct screen_info *si, u32 pixels_per_scan_line,
 	}
 }
 
-#define efi_gop_attr(table, attr, instance) \
-	(efi_table_attr(efi_graphics_output_protocol##table, attr, instance))
-
 static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
 			      unsigned long size, void **handles)
 {
@@ -123,9 +120,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
 		if (status == EFI_SUCCESS)
 			conout_found = true;
 
-		mode = (void *)(unsigned long)efi_gop_attr(, mode, gop);
-		info = (void *)(unsigned long)efi_gop_attr(_mode, info, mode);
-		current_fb_base = efi_gop_attr(_mode, frame_buffer_base, mode);
+		mode = efi_table_attr(gop, mode);
+		info = efi_table_attr(mode, info);
+		current_fb_base = efi_table_attr(mode, frame_buffer_base);
 
 		if ((!first_gop || conout_found) &&
 		    info->pixel_format != PIXEL_BLT_ONLY) {
Signature · 1 line
2.17.1
author maintainer
#53 lore raw

PATCH v2 18/21 efi/libstub: use 'func' not 'f' as macro parameter

In reply to Ard Biesheuvel

Use 'func' as the macro parameter name for the efi_call() macros which is less likely to collide and cause weird build errors.

---
 arch/arm64/include/asm/efi.h |  4 ++--
 arch/x86/include/asm/efi.h   | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)
arch/arm64/include/asm/efi.h +2 -2
index 6f041ae446d2..d06305590ac5 100644
@@ -93,8 +93,8 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 	return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define efi_call_early(f, ...)		efi_system_table()->boottime->f(__VA_ARGS__)
-#define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
+#define efi_call_early(func, ...)	efi_system_table()->boottime->func(__VA_ARGS__)
+#define efi_call_runtime(func, ...)	efi_system_table()->runtime->func(__VA_ARGS__)
 #define efi_is_native()			(true)
 
 #define efi_table_attr(inst, attr)	(inst->attr)
arch/x86/include/asm/efi.h +6 -6
index 593a8cda8361..ba3f8a98e156 100644
@@ -236,17 +236,17 @@ static inline bool efi_is_native(void)
 		? inst->func(inst, ##__VA_ARGS__)			\
 		: efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__))
 
-#define efi_call_early(f, ...)						\
+#define efi_call_early(func, ...)					\
 	(efi_is_native()						\
-		? efi_system_table()->boottime->f(__VA_ARGS__)		\
+		? efi_system_table()->boottime->func(__VA_ARGS__)	\
 		: efi64_thunk(efi_table_attr(efi_system_table(),	\
-				boottime)->mixed_mode.f, __VA_ARGS__))
+				boottime)->mixed_mode.func, __VA_ARGS__))
 
-#define efi_call_runtime(f, ...)					\
+#define efi_call_runtime(func, ...)					\
 	(efi_is_native()						\
-		? efi_system_table()->runtime->f(__VA_ARGS__)		\
+		? efi_system_table()->runtime->func(__VA_ARGS__)	\
 		: efi64_thunk(efi_table_attr(efi_system_table(),	\
-				runtime)->mixed_mode.f, __VA_ARGS__))
+				runtime)->mixed_mode.func, __VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
Signature · 1 line
2.17.1
#54 lore raw

Re: [PATCH v2 18/21] efi/libstub: use 'func' not 'f' as macro parameter

Quoted 3 lines · Ard Biesheuvel

Use 'func' as the macro parameter name for the efi_call() macros which is less likely to collide and cause weird build errors.

For my education, what are the possible collisions/build errors that might happen?

Thanks

Quoted 52 lines
---
 arch/arm64/include/asm/efi.h |  4 ++--
 arch/x86/include/asm/efi.h   | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)
arch/arm64/include/asm/efi.h +2 -2
index 6f041ae446d2..d06305590ac5 100644
@@ -93,8 +93,8 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
 	return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define efi_call_early(f, ...)		efi_system_table()->boottime->f(__VA_ARGS__)
-#define efi_call_runtime(f, ...)	efi_system_table()->runtime->f(__VA_ARGS__)
+#define efi_call_early(func, ...)	efi_system_table()->boottime->func(__VA_ARGS__)
+#define efi_call_runtime(func, ...)	efi_system_table()->runtime->func(__VA_ARGS__)
 #define efi_is_native()			(true)
 
 #define efi_table_attr(inst, attr)	(inst->attr)
arch/x86/include/asm/efi.h +6 -6
index 593a8cda8361..ba3f8a98e156 100644
@@ -236,17 +236,17 @@ static inline bool efi_is_native(void)
 		? inst->func(inst, ##__VA_ARGS__)			\
 		: efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__))
 
-#define efi_call_early(f, ...)						\
+#define efi_call_early(func, ...)					\
 	(efi_is_native()						\
-		? efi_system_table()->boottime->f(__VA_ARGS__)		\
+		? efi_system_table()->boottime->func(__VA_ARGS__)	\
 		: efi64_thunk(efi_table_attr(efi_system_table(),	\
-				boottime)->mixed_mode.f, __VA_ARGS__))
+				boottime)->mixed_mode.func, __VA_ARGS__))
 
-#define efi_call_runtime(f, ...)					\
+#define efi_call_runtime(func, ...)					\
 	(efi_is_native()						\
-		? efi_system_table()->runtime->f(__VA_ARGS__)		\
+		? efi_system_table()->runtime->func(__VA_ARGS__)	\
 		: efi64_thunk(efi_table_attr(efi_system_table(),	\
-				runtime)->mixed_mode.f, __VA_ARGS__))
+				runtime)->mixed_mode.func, __VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
Signature · 1 line
2.17.1
#55 lore raw

Re: [PATCH v2 18/21] efi/libstub: use 'func' not 'f' as macro parameter

Quoted 8 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Use 'func' as the macro parameter name for the efi_call() macros which is less likely to collide and cause weird build errors.

For my education, what are the possible collisions/build errors that might happen?

I was concerned about variables called 'f' being passed as one of the arguments, but now that I think of it, I'm not sure how that could break.

In any case, I'd like to keep this patch since it also increases legibility.

Quoted 52 lines
Quoted 52 lines
---
 arch/arm64/include/asm/efi.h |  4 ++--
 arch/x86/include/asm/efi.h   | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)
arch/arm64/include/asm/efi.h +2 -2
index 6f041ae446d2..d06305590ac5 100644
@@ -93,8 +93,8 @@ static inline unsigned long efi_get_max_initrd_addr(unsigned long dram_base,
      return (image_addr & ~(SZ_1G - 1UL)) + (1UL << (VA_BITS_MIN - 1));
 }
 
-#define efi_call_early(f, ...)               efi_system_table()->boottime->f(__VA_ARGS__)
-#define efi_call_runtime(f, ...)     efi_system_table()->runtime->f(__VA_ARGS__)
+#define efi_call_early(func, ...)    efi_system_table()->boottime->func(__VA_ARGS__)
+#define efi_call_runtime(func, ...)  efi_system_table()->runtime->func(__VA_ARGS__)
 #define efi_is_native()                      (true)
 
 #define efi_table_attr(inst, attr)   (inst->attr)
arch/x86/include/asm/efi.h +6 -7
index 593a8cda8361..ba3f8a98e156 100644
@@ -236,17 +236,17 @@ static inline bool efi_is_native(void)
              ? inst->func(inst, ##__VA_ARGS__)                       \
              : efi64_thunk(inst->mixed_mode.func, inst, ##__VA_ARGS__))
 
-#define efi_call_early(f, ...)                                               \
+#define efi_call_early(func, ...)                                    \
      (efi_is_native()                                                \
-             ? efi_system_table()->boottime->f(__VA_ARGS__)          \
+             ? efi_system_table()->boottime->func(__VA_ARGS__)       \
              : efi64_thunk(efi_table_attr(efi_system_table(),        \
-                             boottime)->mixed_mode.f, __VA_ARGS__))
+                             boottime)->mixed_mode.func, __VA_ARGS__))
 
-#define efi_call_runtime(f, ...)                                     \
+#define efi_call_runtime(func, ...)                                  \
      (efi_is_native()                                                \
-             ? efi_system_table()->runtime->f(__VA_ARGS__)           \
+             ? efi_system_table()->runtime->func(__VA_ARGS__)        \
              : efi64_thunk(efi_table_attr(efi_system_table(),        \
-                             runtime)->mixed_mode.f, __VA_ARGS__))
+                             runtime)->mixed_mode.func, __VA_ARGS__))
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);
--

2.17.1

#56 lore raw

Re: [PATCH v2 18/21] efi/libstub: use 'func' not 'f' as macro parameter

Quoted 7 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Use 'func' as the macro parameter name for the efi_call() macros which is less likely to collide and cause weird build errors.

Quoted 9 lines
Quoted 2 lines

For my education, what are the possible collisions/build errors that might happen?

I was concerned about variables called 'f' being passed as one of the arguments, but now that I think of it, I'm not sure how that could break.

In any case, I'd like to keep this patch since it also increases legibility.

No objections to the patch, I just wanted to check if I was missing something. Probably update the commit message though.

author maintainer
#57 lore raw

PATCH v2 19/21 efi/libstub: tidy up types and names of global cmdline variables

In reply to Ard Biesheuvel

Drop leading underscores and use bool not int for true/false variables set on the command line.

---
 drivers/firmware/efi/libstub/efi-stub-helper.c | 34 ++++++++++----------
 drivers/firmware/efi/libstub/efistub.h         |  6 ++--
 2 files changed, 20 insertions(+), 20 deletions(-)
drivers/firmware/efi/libstub/efi-stub-helper.c +17 -17
index 59c273cb7a92..8b0d59f5c8cd 100644
@@ -27,24 +27,24 @@
  */
 #define EFI_READ_CHUNK_SIZE	(1024 * 1024)
 
-static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE;
+static unsigned long efi_chunk_size = EFI_READ_CHUNK_SIZE;
 
-static int __section(.data) __nokaslr;
-static int __section(.data) __quiet;
-static int __section(.data) __novamap;
+static bool __section(.data) efi_nokaslr;
+static bool __section(.data) efi_quiet;
+static bool __section(.data) efi_novamap;
 static bool __section(.data) efi_nosoftreserve;
 
-int __pure nokaslr(void)
+bool __pure nokaslr(void)
 {
-	return __nokaslr;
+	return efi_nokaslr;
 }
-int __pure is_quiet(void)
+bool __pure is_quiet(void)
 {
-	return __quiet;
+	return efi_quiet;
 }
-int __pure novamap(void)
+bool __pure novamap(void)
 {
-	return __novamap;
+	return efi_novamap;
 }
 bool __pure __efi_soft_reserve_enabled(void)
 {
@@ -461,11 +461,11 @@ efi_status_t efi_parse_options(char const *cmdline)
 
 	str = strstr(cmdline, "nokaslr");
 	if (str == cmdline || (str && str > cmdline && *(str - 1) == ' '))
-		__nokaslr = 1;
+		efi_nokaslr = true;
 
 	str = strstr(cmdline, "quiet");
 	if (str == cmdline || (str && str > cmdline && *(str - 1) == ' '))
-		__quiet = 1;
+		efi_quiet = true;
 
 	/*
 	 * If no EFI parameters were specified on the cmdline we've got
@@ -485,18 +485,18 @@ efi_status_t efi_parse_options(char const *cmdline)
 	while (*str && *str != ' ') {
 		if (!strncmp(str, "nochunk", 7)) {
 			str += strlen("nochunk");
-			__chunk_size = -1UL;
+			efi_chunk_size = -1UL;
 		}
 
 		if (!strncmp(str, "novamap", 7)) {
 			str += strlen("novamap");
-			__novamap = 1;
+			efi_novamap = true;
 		}
 
 		if (IS_ENABLED(CONFIG_EFI_SOFT_RESERVE) &&
 		    !strncmp(str, "nosoftreserve", 7)) {
 			str += strlen("nosoftreserve");
-			efi_nosoftreserve = 1;
+			efi_nosoftreserve = true;
 		}
 
 		/* Group words together, delimited by "," */
@@ -661,8 +661,8 @@ efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
 			while (size) {
 				unsigned long chunksize;
 
-				if (IS_ENABLED(CONFIG_X86) && size > __chunk_size)
-					chunksize = __chunk_size;
+				if (IS_ENABLED(CONFIG_X86) && size > efi_chunk_size)
+					chunksize = efi_chunk_size;
 				else
 					chunksize = size;
 
drivers/firmware/efi/libstub/efistub.h +3 -3
index b5d9c9e65213..72e60b6afbf8 100644
@@ -25,9 +25,9 @@
 #define EFI_ALLOC_ALIGN		EFI_PAGE_SIZE
 #endif
 
-extern int __pure nokaslr(void);
-extern int __pure is_quiet(void);
-extern int __pure novamap(void);
+extern bool __pure nokaslr(void);
+extern bool __pure is_quiet(void);
+extern bool __pure novamap(void);
 
 extern __pure efi_system_table_t  *efi_system_table(void);
 
Signature · 1 line
2.17.1
author maintainer
#58 lore raw

PATCH v2 20/21 efi/libstub: import type definitions for creating and signalling events

In reply to Ard Biesheuvel

We will use the EFI event handling infrastructure in a future patch to register a notification function that executes at the very end of ExitBootServices() to disable DMA on all PCI bridges in the system.

So import the missing macros and typedefs from the UEFI spec.

---
 include/linux/efi.h | 23 ++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)
include/linux/efi.h +21 -2
index f97f48a3dc81..81bd1e8c201f 100644
@@ -270,6 +270,23 @@ typedef struct {
 	u32 create_event_ex;
 } __packed efi_boot_services_32_t;
 
+typedef unsigned long efi_tpl_t;
+
+#define TPL_APPLICATION				4
+#define TPL_CALLBACK				8
+#define TPL_NOTIFY				16
+#define TPL_HIGH_LEVEL				31
+
+typedef void *efi_event_t;
+typedef void (__efiapi *efi_event_notify_t)(efi_event_t, void *);
+
+#define EVT_TIMER				0x80000000
+#define EVT_RUNTIME				0x40000000
+#define EVT_NOTIFY_WAIT				0x00000100
+#define EVT_NOTIFY_SIGNAL			0x00000200
+#define EVT_SIGNAL_EXIT_BOOT_SERVICES		0x00000201
+#define EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE	0x60000202
+
 /*
  * EFI Boot Services table
  */
@@ -288,10 +305,12 @@ typedef union {
 		efi_status_t (__efiapi *allocate_pool)(int, unsigned long,
 						       void **);
 		efi_status_t (__efiapi *free_pool)(void *);
-		void *create_event;
+		efi_status_t (__efiapi *create_event)(u32 type, efi_tpl_t,
+						      efi_event_notify_t,
+						      void *, efi_event_t *);
 		void *set_timer;
 		void *wait_for_event;
-		void *signal_event;
+		efi_status_t (__efiapi *signal_event)(efi_event_t);
 		void *close_event;
 		void *check_event;
 		void *install_protocol_interface;
Signature · 1 line
2.17.1
author maintainer
#59 lore raw

PATCH v2 21/21 efi: Allow disabling PCI busmastering on bridges during boot

In reply to Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]
---
 Documentation/admin-guide/kernel-parameters.txt |   4 +
 arch/x86/boot/compressed/eboot.c                |   2 +
 drivers/firmware/efi/Kconfig                    |  22 ++++
 drivers/firmware/efi/libstub/Makefile           |   2 +-
 drivers/firmware/efi/libstub/arm-stub.c         |   1 +
 drivers/firmware/efi/libstub/efi-stub-helper.c  |  17 +++
 drivers/firmware/efi/libstub/efistub.h          |   1 +
 drivers/firmware/efi/libstub/pci.c              | 116 ++++++++++++++++++++
 include/linux/efi.h                             |   2 +
 9 files changed, 166 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +4 -0
index 5a92d89a1bd4..06fd8d2c9a2c 100644
@@ -1180,6 +1180,10 @@
 			claim. Specify efi=nosoftreserve to disable this
 			reservation and treat the memory by its base type
 			(i.e. EFI_CONVENTIONAL_MEMORY / "System RAM").
+			disable_pci_dma: Disable the busmaster bit on all
+			PCI bridges while in the EFI boot stub
+			no_disable_pci_dma: Leave the busmaster bit set on all
+			PCI bridges while in the EFI boot stub
 
 	efi_no_storage_paranoia [EFI; X86]
 			Using this parameter you can use more than 50% of
arch/x86/boot/compressed/eboot.c +2 -0
index a3000283dc7a..79c3c3308195 100644
@@ -126,6 +126,8 @@ static void setup_efi_pci(struct boot_params *params)
 	efi_handle_t h;
 	int i;
 
+	efi_pci_disable_bridge_busmaster();
+
 	status = efi_call_early(locate_handle,
 				EFI_LOCATE_BY_PROTOCOL,
 				&pci_proto, NULL, &size, pci_handle);
drivers/firmware/efi/Kconfig +22 -0
index bcc378c19ebe..da2f5fc862aa 100644
@@ -215,6 +215,28 @@ config EFI_RCI2_TABLE
 
 	  Say Y here for Dell EMC PowerEdge systems.
 
+config EFI_DISABLE_PCI_DMA
+       bool "Clear Busmaster bit on PCI bridges during ExitBootServices()"
+       help
+	  Disable the busmaster bit in the control register on all PCI bridges
+	  while calling ExitBootServices() and passing control to the runtime
+	  kernel. System firmware may configure the IOMMU to prevent malicious
+	  PCI devices from being able to attack the OS via DMA. However, since
+	  firmware can't guarantee that the OS is IOMMU-aware, it will tear
+	  down IOMMU configuration when ExitBootServices() is called. This
+	  leaves a window between where a hostile device could still cause
+	  damage before Linux configures the IOMMU again.
+
+	  If you say Y here, the EFI stub will clear the busmaster bit on all
+	  PCI bridges before ExitBootServices() completes. This will prevent
+	  any malicious PCI devices from being able to perform DMA until the
+	  kernel reenables busmastering after configuring the IOMMU.
+
+	  This option will cause failures with some poorly behaved hardware
+	  and should not be enabled without testing. The kernel commandline
+	  options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be
+	  used to override this option.
+
 endmenu
 
 config UEFI_CPER
drivers/firmware/efi/libstub/Makefile +1 -1
index c35f893897e1..98a81576213d 100644
@@ -39,7 +39,7 @@ OBJECT_FILES_NON_STANDARD	:= y
 KCOV_INSTRUMENT			:= n
 
 lib-y				:= efi-stub-helper.o gop.o secureboot.o tpm.o \
-				   random.o
+				   random.o pci.o
 
 # include the stub's generic dependencies from lib/ when building for ARM/arm64
 arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
drivers/firmware/efi/libstub/arm-stub.c +1 -0
index e1ec0b2cde29..aa2929098d60 100644
@@ -257,6 +257,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
 	}
 
 	install_memreserve_table();
+	efi_pci_disable_bridge_busmaster();
 
 	new_fdt_addr = fdt_addr;
 	status = allocate_new_fdt_and_exit_boot(handle,
drivers/firmware/efi/libstub/efi-stub-helper.c +17 -0
index 8b0d59f5c8cd..4b79bfff1fd4 100644
@@ -33,6 +33,8 @@ static bool __section(.data) efi_nokaslr;
 static bool __section(.data) efi_quiet;
 static bool __section(.data) efi_novamap;
 static bool __section(.data) efi_nosoftreserve;
+static bool __section(.data) efi_disable_pci_dma =
+					IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
 
 bool __pure nokaslr(void)
 {
@@ -51,6 +53,11 @@ bool __pure __efi_soft_reserve_enabled(void)
 	return !efi_nosoftreserve;
 }
 
+bool __pure disable_pci_dma(void)
+{
+	return efi_disable_pci_dma;
+}
+
 #define EFI_MMAP_NR_SLACK_SLOTS	8
 
 struct file_info {
@@ -499,6 +506,16 @@ efi_status_t efi_parse_options(char const *cmdline)
 			efi_nosoftreserve = true;
 		}
 
+		if (!strncmp(str, "disable_pci_dma", 15)) {
+			str += strlen("disable_pci_dma");
+			efi_disable_pci_dma = true;
+		}
+
+		if (!strncmp(str, "no_disable_pci_dma", 18)) {
+			str += strlen("no_disable_pci_dma");
+			efi_disable_pci_dma = false;
+		}
+
 		/* Group words together, delimited by "," */
 		while (*str && *str != ' ' && *str != ',')
 			str++;
drivers/firmware/efi/libstub/efistub.h +1 -0
index 72e60b6afbf8..679268f67521 100644
@@ -28,6 +28,7 @@
 extern bool __pure nokaslr(void);
 extern bool __pure is_quiet(void);
 extern bool __pure novamap(void);
+extern bool __pure disable_pci_dma(void);
 
 extern __pure efi_system_table_t  *efi_system_table(void);
 
drivers/firmware/efi/libstub/pci.c new file +116 -0

116 lines not on this page · raw message

include/linux/efi.h +2 -0
index 81bd1e8c201f..2864633abff0 100644
@@ -1786,4 +1786,6 @@ struct linux_efi_memreserve {
 #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \
 	/ sizeof(((struct linux_efi_memreserve *)0)->entry[0]))
 
+void efi_pci_disable_bridge_busmaster(void);
+
 #endif /* _LINUX_EFI_H */
Signature · 1 line
2.17.1
#60 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 18 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

Quoted 8 lines

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Could this maybe be efi=disable_early_pci_dma?

As it stands, it reads like busmastering is turned off and stays off, which is not the case. I'm dreading the silly forum posts saying "your system is slow, and busmastering is leet, so use efi=no_disable_pci_dma to allow busmastering and make it OMG fast".

--Andy

#61 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 29 lines · Andy Lutomirski
Quoted 18 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

Quoted 8 lines

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 7 lines

Could this maybe be efi=disable_early_pci_dma?

As it stands, it reads like busmastering is turned off and stays off, which is not the case. I'm dreading the silly forum posts saying "your system is slow, and busmastering is leet, so use efi=no_disable_pci_dma to allow busmastering and make it OMG fast".

Fair enough. I'll change this to [no_]disable_early_pci_dma (assuming Matthew doesn't object to the exact shade of this bikeshed)

#62 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 2 lines · Ard Biesheuvel

Fair enough. I'll change this to [no_]disable_early_pci_dma (assuming Matthew doesn't object to the exact shade of this bikeshed)

I'm fine with that.

#63 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

In reply to Ard Biesheuvel

drivers/firmware/efi/libstub/pci.c line 110

Quoted 7 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

#64 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 10 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 7 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }
Quoted 3 lines

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

So this approach is what i am willing to accept for mainline, and I would even start out by enabling it by default to get the most coverage. If you need something stronger, I understand but I don't think it belongs in mainline.

#65 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 19 lines · Ard Biesheuvel
Quoted 10 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 9 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }
Quoted 3 lines

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 6 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Can you clarify (in the changelog or a comment perhaps) why you’re doing this instead of turning off busmastering before calling ExitBootServices()? Maybe this was covered in this thread, but I missed it.

Also, surely this whole mess is a a design error in EFI, at least when SEV is involved, and there should be an EFI extension to keep IOMMU enabled. Or a specified way to *guarantee* that DMA is off when we exit boot services without hackery.

#66 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 20 lines · Andy Lutomirski
Quoted 20 lines · Ard Biesheuvel
Quoted 13 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 9 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 9 lines
Quoted 6 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Can you clarify (in the changelog or a comment perhaps) why you’re doing this instead of turning off busmastering before calling ExitBootServices()? Maybe this was covered in this thread, but I missed it.

Sure. The problem is that EBS() is the place where drivers tear down rings etc and gracefully take down the device. So killing DMA for all of them by clearing the BM bit of every bridge is likely to cause problems, because the teardown code wasn't written with the idea in mind that DMA is no longer possible. On arm64 at least, it may result in the kernel being entered with a pending SError which will kill the OS as soon as they are unmasked. But the UEFI drivers themselves may simply hang or timeout on some DMA access.

Quoted 1 line

Also, surely this whole mess is a a design error in EFI, at least when SEV is involved, and there should be an EFI extension to keep IOMMU enabled. Or a specified way to *guarantee* that DMA is off when we exit boot services without hackery.

The UEFI spec requires that all devices stop doing DMA at ExitBootServices() time, which is why they register for this event and disable DMA in their drivers. So from this pov, the state of the IOMMU is irrelevant since no device should be doing DMA anyway. The UEFI spec does not reason at all about IOMMUs.

But indeed, the whole idea that the IOMMU is a 'feature' that you can ignore if you want since it will be in passthrough mode otherwise is misguided, but I'm not sure this is a firmware problem. The desire to be able to run yesterday's OS on today's hardware, especially in the Windows world, resulted in a situation where many security and hardening features are opt-in rather than opt-out, with the adoption you might expect.

#67 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 21 lines · Ard Biesheuvel
Quoted 20 lines · Andy Lutomirski
Quoted 20 lines · Ard Biesheuvel
Quoted 13 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 9 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Could we add a comment to describe why we have the two-step event notification, i.e. to ensure the ordering?

Regarding SEV, from what Laszlo had said [1] I had understood that the SEV driver left everything blacklisted -- is this necessary at all for SEV or did I misunderstand his comment?

[1] https://lore.kernel.org/lkml/[email protected]/

Quoted 4 lines
Quoted 3 lines

Can you clarify (in the changelog or a comment perhaps) why you’re doing this instead of turning off busmastering before calling ExitBootServices()? Maybe this was covered in this thread, but I missed it.

Quoted 9 lines

Sure. The problem is that EBS() is the place where drivers tear down rings etc and gracefully take down the device. So killing DMA for all of them by clearing the BM bit of every bridge is likely to cause problems, because the teardown code wasn't written with the idea in mind that DMA is no longer possible. On arm64 at least, it may result in the kernel being entered with a pending SError which will kill the OS as soon as they are unmasked. But the UEFI drivers themselves may simply hang or timeout on some DMA access.

As I understand it, the order in which we want the bus-mastering disable to happen is
- all PCI device drivers are stopped
- bus mastering is disabled
- depending on firmware, iommu might get disabled (probably out of our control)

Instead of using the event notifier, could we not explicitly call DisconnectController() for all the PCI devices, then disable bus-mastering, and only then call ExitBootServices()?

Quoted 8 lines
Quoted 1 line

Also, surely this whole mess is a a design error in EFI, at least when SEV is involved, and there should be an EFI extension to keep IOMMU enabled. Or a specified way to *guarantee* that DMA is off when we exit boot services without hackery.

The UEFI spec requires that all devices stop doing DMA at ExitBootServices() time, which is why they register for this event and disable DMA in their drivers. So from this pov, the state of the IOMMU is irrelevant since no device should be doing DMA anyway. The UEFI spec does not reason at all about IOMMUs.

Quoted 7 lines

But indeed, the whole idea that the IOMMU is a 'feature' that you can ignore if you want since it will be in passthrough mode otherwise is misguided, but I'm not sure this is a firmware problem. The desire to be able to run yesterday's OS on today's hardware, especially in the Windows world, resulted in a situation where many security and hardening features are opt-in rather than opt-out, with the adoption you might expect.

Looking at the intel iommu driver [2] there is a PCD entry to control whether to leave the IOMMU enabled. Is it possible to check its value to see whether the bus-master disabling is necessary -- ie IOMMU was enabled during boot and its going to get turned off during ExitBootServices, or even check whether it's settable and if so set it to leave the IOMMU enabled?

[2] https://github.com/tianocore/edk2-platforms/blob/0d4d661b5a7cf3114a7d81e1c59e5cb57ceaf139/Silicon/Intel/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c#L601

#68 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 29 lines · Arvind Sankar
Quoted 21 lines · Ard Biesheuvel
Quoted 20 lines · Andy Lutomirski
Quoted 20 lines · Ard Biesheuvel
Quoted 13 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 9 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Quoted 8 lines

Could we add a comment to describe why we have the two-step event notification, i.e. to ensure the ordering?

Regarding SEV, from what Laszlo had said [1] I had understood that the SEV driver left everything blacklisted -- is this necessary at all for SEV or did I misunderstand his comment?

[1] https://lore.kernel.org/lkml/[email protected]/

Leaving all guest RAM encrypted after EBS may not be a technical requirement for SEV. It is a security goal, however. The OS should inherit the most secure environment possible from the firmware, and not have to look for pages that were left unencrypted.

Quoted 20 lines
Quoted 12 lines
Quoted 2 lines

Can you clarify (in the changelog or a comment perhaps) why you’re doing this instead of turning off busmastering before calling ExitBootServices()? Maybe this was covered in this thread, but I missed it.

Sure. The problem is that EBS() is the place where drivers tear down rings etc and gracefully take down the device. So killing DMA for all of them by clearing the BM bit of every bridge is likely to cause problems, because the teardown code wasn't written with the idea in mind that DMA is no longer possible. On arm64 at least, it may result in the kernel being entered with a pending SError which will kill the OS as soon as they are unmasked. But the UEFI drivers themselves may simply hang or timeout on some DMA access.

As I understand it, the order in which we want the bus-mastering disable to happen is
- all PCI device drivers are stopped
- bus mastering is disabled
- depending on firmware, iommu might get disabled (probably out of our control)

Quoted 3 lines

Instead of using the event notifier, could we not explicitly call DisconnectController() for all the PCI devices, then disable bus-mastering, and only then call ExitBootServices()?

This sounds worth investigating to me.

Quoted 16 lines
Quoted 8 lines
Quoted 1 line

Also, surely this whole mess is a a design error in EFI, at least when SEV is involved, and there should be an EFI extension to keep IOMMU enabled. Or a specified way to *guarantee* that DMA is off when we exit boot services without hackery.

The UEFI spec requires that all devices stop doing DMA at ExitBootServices() time, which is why they register for this event and disable DMA in their drivers. So from this pov, the state of the IOMMU is irrelevant since no device should be doing DMA anyway. The UEFI spec does not reason at all about IOMMUs.

Quoted 7 lines

But indeed, the whole idea that the IOMMU is a 'feature' that you can ignore if you want since it will be in passthrough mode otherwise is misguided, but I'm not sure this is a firmware problem. The desire to be able to run yesterday's OS on today's hardware, especially in the Windows world, resulted in a situation where many security and hardening features are opt-in rather than opt-out, with the adoption you might expect.

Quoted 9 lines

Looking at the intel iommu driver [2] there is a PCD entry to control whether to leave the IOMMU enabled. Is it possible to check its value to see whether the bus-master disabling is necessary -- ie IOMMU was enabled during boot and its going to get turned off during ExitBootServices, or even check whether it's settable and if so set it to leave the IOMMU enabled?

[2] https://github.com/tianocore/edk2-platforms/blob/0d4d661b5a7cf3114a7d81e1c59e5cb57ceaf139/Silicon/Intel/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c#L601

The platform config database is a PI concept, not a UEFI concept. IOW, EFI_PCD_PROTOCOL is specified in the Platform Init spec, and not the UEFI spec. A UEFI OS should only rely on the UEFI spec.

Thanks
Laszlo

#69 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 65 lines · Laszlo Ersek
Quoted 29 lines · Arvind Sankar
Quoted 21 lines · Ard Biesheuvel
Quoted 20 lines · Andy Lutomirski
Quoted 20 lines · Ard Biesheuvel
Quoted 13 lines · Matthew Garrett

Review

Quoted 9 lines · Ard Biesheuvel
(quoted diff) +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Quoted 8 lines

Could we add a comment to describe why we have the two-step event notification, i.e. to ensure the ordering?

Regarding SEV, from what Laszlo had said [1] I had understood that the SEV driver left everything blacklisted -- is this necessary at all for SEV or did I misunderstand his comment?

[1] https://lore.kernel.org/lkml/[email protected]/

Leaving all guest RAM encrypted after EBS may not be a technical requirement for SEV. It is a security goal, however. The OS should inherit the most secure environment possible from the firmware, and not have to look for pages that were left unencrypted.

Quoted 20 lines
Quoted 12 lines
Quoted 2 lines

Can you clarify (in the changelog or a comment perhaps) why you’re doing this instead of turning off busmastering before calling ExitBootServices()? Maybe this was covered in this thread, but I missed it.

Sure. The problem is that EBS() is the place where drivers tear down rings etc and gracefully take down the device. So killing DMA for all of them by clearing the BM bit of every bridge is likely to cause problems, because the teardown code wasn't written with the idea in mind that DMA is no longer possible. On arm64 at least, it may result in the kernel being entered with a pending SError which will kill the OS as soon as they are unmasked. But the UEFI drivers themselves may simply hang or timeout on some DMA access.

As I understand it, the order in which we want the bus-mastering disable to happen is
- all PCI device drivers are stopped
- bus mastering is disabled
- depending on firmware, iommu might get disabled (probably out of our control)

Quoted 6 lines
Quoted 3 lines

Instead of using the event notifier, could we not explicitly call DisconnectController() for all the PCI devices, then disable bus-mastering, and only then call ExitBootServices()?

This sounds worth investigating to me.

I implemented this here:
https://lore.kernel.org/linux-efi/[email protected]/

It works on all the systems I tried, including a mixed mode iMac 24" and a mixed mode Intel Atom based tablet kindly provided by Hans.

The only thing to keep in mind is that some drivers implement Gop->Blt() using DMA in some cases, and so we should ensure that this is really the last thing that happens before ExitBootServices() is called, since a simple efi_printk() may trigger a DMA access by the graphics hardware.

Quoted 23 lines
Quoted 23 lines
Quoted 8 lines
Quoted 1 line

Also, surely this whole mess is a a design error in EFI, at least when SEV is involved, and there should be an EFI extension to keep IOMMU enabled. Or a specified way to *guarantee* that DMA is off when we exit boot services without hackery.

The UEFI spec requires that all devices stop doing DMA at ExitBootServices() time, which is why they register for this event and disable DMA in their drivers. So from this pov, the state of the IOMMU is irrelevant since no device should be doing DMA anyway. The UEFI spec does not reason at all about IOMMUs.

Quoted 7 lines

But indeed, the whole idea that the IOMMU is a 'feature' that you can ignore if you want since it will be in passthrough mode otherwise is misguided, but I'm not sure this is a firmware problem. The desire to be able to run yesterday's OS on today's hardware, especially in the Windows world, resulted in a situation where many security and hardening features are opt-in rather than opt-out, with the adoption you might expect.

Looking at the intel iommu driver [2] there is a PCD entry to control whether to leave the IOMMU enabled. Is it possible to check its value to see whether the bus-master disabling is necessary -- ie IOMMU was enabled during boot and its going to get turned off during ExitBootServices, or even check whether it's settable and if so set it to leave the IOMMU enabled?

Quoted 7 lines
Quoted 2 lines

[2] https://github.com/tianocore/edk2-platforms/blob/0d4d661b5a7cf3114a7d81e1c59e5cb57ceaf139/Silicon/Intel/IntelSiliconPkg/Feature/VTd/IntelVTdDxe/DmaProtection.c#L601

The platform config database is a PI concept, not a UEFI concept. IOW, EFI_PCD_PROTOCOL is specified in the Platform Init spec, and not the UEFI spec. A UEFI OS should only rely on the UEFI spec.

Agreed. And on top of that, PCDs can be FixedAtBuild or Patchable, in which case they cannot be controlled via the EFI_PCD_PROTOCOL.

#70 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

In reply to Ard Biesheuvel
Quoted 16 lines · Ard Biesheuvel
Quoted 10 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 7 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }
Quoted 3 lines

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 5 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Hm. In that case, I think this version fails to match the patch description - there's potentially still a window where DMA-capable PCI devices can attack the kernel, it's just smaller. If disconnecting drivers from the device would give us the same expectations then that seems like a better approach, but I think I probably wouldn't add my signed-off-by to this version.

#71 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 25 lines · Matthew Garrett
Quoted 16 lines · Ard Biesheuvel
Quoted 10 lines · Matthew Garrett

drivers/firmware/efi/libstub/pci.c line 110

Quoted 7 lines · Ard Biesheuvel
drivers/firmware/efi/libstub/pci.c new file +7 -0
+       status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+                               TPL_CALLBACK, handle_exit_boot_services_event,
+                               NULL, &exit_boot_services_event);
+       if (status != EFI_SUCCESS) {
+               pr_efi_err("Failed to register for EBS() event\n");
+               goto free_handle;
+       }
Quoted 3 lines

OVMF's SEV implementation appears to tear down AMD's IOMMU mappings at EVT_SIGNAL_EXIT_BOOT_SERVICES. How are we ensuring that this happens first?

Quoted 5 lines

It doesn't, and that is kind of the point. The only guarantee you have is that this runs before ExitBootServices() returns, but after any other callbacks that have been registered. I know this is not 100% what you're after, but it is the only way we can avoid poking devices behind the backs of their drivers.

Quoted 6 lines

Hm. In that case, I think this version fails to match the patch description - there's potentially still a window where DMA-capable PCI devices can attack the kernel, it's just smaller. If disconnecting drivers from the device would give us the same expectations then that seems like a better approach, but I think I probably wouldn't add my signed-off-by to this version.

Point taken. I like Arvind's suggestion but we need to take care not to disable the device producing the GOP. Beyond that, it should be possible to disable all devices that are behind PCI bridges. It does mean we need to decode the secondary/subordinate bus values from the config space and take them into account when iterating over the PCI handle list.

#72 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 6 lines · Ard Biesheuvel

Point taken. I like Arvind's suggestion but we need to take care not to disable the device producing the GOP. Beyond that, it should be possible to disable all devices that are behind PCI bridges. It does mean we need to decode the secondary/subordinate bus values from the config space and take them into account when iterating over the PCI handle list.

The device producing the GOP is the device that I'm most afraid of, to be honest.I'd naively expect that anything presenting a linear framebuffer will probably leave it up at disconnect time, and we're definitely not using DMA to access that framebuffer.

#73 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 8 lines · Matthew Garrett
Quoted 6 lines · Ard Biesheuvel

Point taken. I like Arvind's suggestion but we need to take care not to disable the device producing the GOP. Beyond that, it should be possible to disable all devices that are behind PCI bridges. It does mean we need to decode the secondary/subordinate bus values from the config space and take them into account when iterating over the PCI handle list.

Why do we need to look at the bus numbers? Shouldn't it be just looping and disabling everything that's a regular device in the first pass (possibly excluding devices that are producing GOP/UGA), and then going over it again to disable the bus-master settings on bridges?

Also, why do we restrict the bus-master disable to only PCI/PCI bridges? Shouldn't we also disable bus-mastering on all the other PCI-xxx bridges?

Quoted 5 lines

The device producing the GOP is the device that I'm most afraid of, to be honest.I'd naively expect that anything presenting a linear framebuffer will probably leave it up at disconnect time, and we're definitely not using DMA to access that framebuffer.

At least on my system, disconnecting the GOP device does leave the display up, and the framebuffer remains active (ie writing to it updates the display). It wouldn't be surprising if this doesn't work for all GPUs though.

#74 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 11 lines · Arvind Sankar
Quoted 8 lines · Matthew Garrett
Quoted 6 lines · Ard Biesheuvel

Point taken. I like Arvind's suggestion but we need to take care not to disable the device producing the GOP. Beyond that, it should be possible to disable all devices that are behind PCI bridges. It does mean we need to decode the secondary/subordinate bus values from the config space and take them into account when iterating over the PCI handle list.

Quoted 5 lines

Why do we need to look at the bus numbers? Shouldn't it be just looping and disabling everything that's a regular device in the first pass (possibly excluding devices that are producing GOP/UGA), and then going over it again to disable the bus-master settings on bridges?

Yeah, that would work I guess

Quoted 4 lines

Also, why do we restrict the bus-master disable to only PCI/PCI bridges? Shouldn't we also disable bus-mastering on all the other PCI-xxx bridges?

Practically, on PCIe systems, PCI/PCI bridges are the only thing we need to care about, since that is how PCIe root ports are modelled.

Quoted 11 lines
Quoted 5 lines

The device producing the GOP is the device that I'm most afraid of, to be honest.I'd naively expect that anything presenting a linear framebuffer will probably leave it up at disconnect time, and we're definitely not using DMA to access that framebuffer.

At least on my system, disconnecting the GOP device does leave the display up, and the framebuffer remains active (ie writing to it updates the display). It wouldn't be surprising if this doesn't work for all GPUs though.

Disconnecting a device should result in the protocols installed by the driver to be removed again, and this also applies to the GOP protocol. Whether or not the framebuffer is still live after that point is not entirely relevant.

But if the GOP it produces is not BltOnly and its framebuffer address doesn't intersect system memory, it should be safe to disable DMA on such devices.

So in summary, we'll need to do

for each PCI I/O handle {
if (bus == 0 || // not behind a bridge

      type == bridge)
    ignore;
  else
    disconnect();
}

for each PCI I/O handle {
if (type == bridge)

    disable BM bit;
}

Disregarding bus 0 means all the 'special' devices integrated into the root complex are disregarded as well, which seems safer to me.

#75 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 3 lines · Ard Biesheuvel

Practically, on PCIe systems, PCI/PCI bridges are the only thing we need to care about, since that is how PCIe root ports are modelled.

If I'm interpreting my lspci output correctly, I have a PCI/ISA bridge on bus 0 that's not behind a PCI/PCI bridge. Device 1f.0 below is the PCI/ISA bridge. Devices 1-3 are the CPU root ports and 1c.* are the chipset root ports.

Also, what about for eg, the USB or SATA controllers? I know that someone had said earlier that disabling BM on endpoints is pointless as malicious endpoints could just re-enable it, but is it not possible for malicious USB devices/SATA devices to try to use DMA through those controllers? ie if we trust the controllers since they're on-board, but not necessarily the devices behind them, wouldn't it still be worth it to disable BM on the controllers too?

$ lspci -tv -s 0:0:
-[0000:00]-+-00.0  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
           +-01.0-[01]--
           +-01.1-[02]--
           +-02.0-[03]--
           +-02.2-[04]--
           +-03.0-[05]--
           +-03.2-[06-09]--
           +-05.0  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management
           +-05.1  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug
           +-05.2  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors
           +-05.4  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC
           +-11.0  Intel Corporation C610/X99 series chipset SPSR
           +-11.4  Intel Corporation C610/X99 series chipset sSATA Controller [AHCI mode]
           +-14.0  Intel Corporation C610/X99 series chipset USB xHCI Host Controller
           +-16.0  Intel Corporation C610/X99 series chipset MEI Controller #1
           +-16.1  Intel Corporation C610/X99 series chipset MEI Controller #2
           +-1a.0  Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #2
           +-1b.0  Intel Corporation C610/X99 series chipset HD Audio Controller
           +-1c.0-[0a]--
           +-1c.2-[0b]--
           +-1c.3-[0c]--
           +-1c.4-[0d]--
           +-1c.7-[0e-0f]--
           +-1d.0  Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #1
           +-1f.0  Intel Corporation C610/X99 series chipset LPC Controller
           +-1f.2  Intel Corporation C610/X99 series chipset 6-Port SATA Controller [AHCI mode]
           \-1f.3  Intel Corporation C610/X99 series chipset SMBus Controller
#76 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 9 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Practically, on PCIe systems, PCI/PCI bridges are the only thing we need to care about, since that is how PCIe root ports are modelled.

If I'm interpreting my lspci output correctly, I have a PCI/ISA bridge on bus 0 that's not behind a PCI/PCI bridge. Device 1f.0 below is the PCI/ISA bridge.

Yeah, this is the LPC controller, right? What devices are actually on that bus that are not closely tied to the chipset?

Quoted 3 lines

Devices 1-3 are the CPU root ports and 1c.* are the chipset root ports.

Right.

Quoted 8 lines

Also, what about for eg, the USB or SATA controllers? I know that someone had said earlier that disabling BM on endpoints is pointless as malicious endpoints could just re-enable it, but is it not possible for malicious USB devices/SATA devices to try to use DMA through those controllers? ie if we trust the controllers since they're on-board, but not necessarily the devices behind them, wouldn't it still be worth it to disable BM on the controllers too?

At this point, it probably makes sense to clarify what the threat model is. I have been assuming (but Matthew should confirm) that we're primarily concerned with DMA attacks over, e.g., Thunderbolt ports, in which case managing the BM bit at the root port level should be sufficient.

If there are other things we want to address (or if my assumption was incorrect), we should clarify this first, before deciding what the right mitigation would be.

Quoted 29 lines
$ lspci -tv -s 0:0:
-[0000:00]-+-00.0  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D DMI2
           +-01.0-[01]--
           +-01.1-[02]--
           +-02.0-[03]--
           +-02.2-[04]--
           +-03.0-[05]--
           +-03.2-[06-09]--
           +-05.0  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Map/VTd_Misc/System Management
           +-05.1  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO Hot Plug
           +-05.2  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D IIO RAS/Control Status/Global Errors
           +-05.4  Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D I/O APIC
           +-11.0  Intel Corporation C610/X99 series chipset SPSR
           +-11.4  Intel Corporation C610/X99 series chipset sSATA Controller [AHCI mode]
           +-14.0  Intel Corporation C610/X99 series chipset USB xHCI Host Controller
           +-16.0  Intel Corporation C610/X99 series chipset MEI Controller #1
           +-16.1  Intel Corporation C610/X99 series chipset MEI Controller #2
           +-1a.0  Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #2
           +-1b.0  Intel Corporation C610/X99 series chipset HD Audio Controller
           +-1c.0-[0a]--
           +-1c.2-[0b]--
           +-1c.3-[0c]--
           +-1c.4-[0d]--
           +-1c.7-[0e-0f]--
           +-1d.0  Intel Corporation C610/X99 series chipset USB Enhanced Host Controller #1
           +-1f.0  Intel Corporation C610/X99 series chipset LPC Controller
           +-1f.2  Intel Corporation C610/X99 series chipset 6-Port SATA Controller [AHCI mode]
           \-1f.3  Intel Corporation C610/X99 series chipset SMBus Controller
#77 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 7 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 3 lines · Ard Biesheuvel

Practically, on PCIe systems, PCI/PCI bridges are the only thing we need to care about, since that is how PCIe root ports are modelled.

Quoted 7 lines
Quoted 3 lines

If I'm interpreting my lspci output correctly, I have a PCI/ISA bridge on bus 0 that's not behind a PCI/PCI bridge. Device 1f.0 below is the PCI/ISA bridge.

Yeah, this is the LPC controller, right? What devices are actually on that bus that are not closely tied to the chipset?

AFAICT it's my BMC's IPMI interface and some sort of watchdog I think -- don't have drivers but there are three other ACPI PNP* nodes under there as well.

#78 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

In reply to Arvind Sankar
Quoted 7 lines · Arvind Sankar

Also, what about for eg, the USB or SATA controllers? I know that someone had said earlier that disabling BM on endpoints is pointless as malicious endpoints could just re-enable it, but is it not possible for malicious USB devices/SATA devices to try to use DMA through those controllers? ie if we trust the controllers since they're on-board, but not necessarily the devices behind them, wouldn't it still be worth it to disable BM on the controllers too?

The concern is DMA initiated by a hostile device. SATA and USB don't permit the devices themselves to initiate DMA, whereas PCI does.

#79 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

In reply to Ard Biesheuvel

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Regards,

Hans

Quoted 286 lines
---
  Documentation/admin-guide/kernel-parameters.txt |   4 +
  arch/x86/boot/compressed/eboot.c                |   2 +
  drivers/firmware/efi/Kconfig                    |  22 ++++
  drivers/firmware/efi/libstub/Makefile           |   2 +-
  drivers/firmware/efi/libstub/arm-stub.c         |   1 +
  drivers/firmware/efi/libstub/efi-stub-helper.c  |  17 +++
  drivers/firmware/efi/libstub/efistub.h          |   1 +
  drivers/firmware/efi/libstub/pci.c              | 116 ++++++++++++++++++++
  include/linux/efi.h                             |   2 +
  9 files changed, 166 insertions(+), 1 deletion(-)
Documentation/admin-guide/kernel-parameters.txt +4 -0
index 5a92d89a1bd4..06fd8d2c9a2c 100644
@@ -1180,6 +1180,10 @@
  			claim. Specify efi=nosoftreserve to disable this
  			reservation and treat the memory by its base type
  			(i.e. EFI_CONVENTIONAL_MEMORY / "System RAM").
+			disable_pci_dma: Disable the busmaster bit on all
+			PCI bridges while in the EFI boot stub
+			no_disable_pci_dma: Leave the busmaster bit set on all
+			PCI bridges while in the EFI boot stub
 
  	efi_no_storage_paranoia [EFI; X86]
  			Using this parameter you can use more than 50% of
arch/x86/boot/compressed/eboot.c +2 -0
index a3000283dc7a..79c3c3308195 100644
@@ -126,6 +126,8 @@ static void setup_efi_pci(struct boot_params *params)
  	efi_handle_t h;
  	int i;
 
+	efi_pci_disable_bridge_busmaster();
+
  	status = efi_call_early(locate_handle,
  				EFI_LOCATE_BY_PROTOCOL,
  				&pci_proto, NULL, &size, pci_handle);
drivers/firmware/efi/Kconfig +22 -0
index bcc378c19ebe..da2f5fc862aa 100644
@@ -215,6 +215,28 @@ config EFI_RCI2_TABLE
 
  	  Say Y here for Dell EMC PowerEdge systems.
 
+config EFI_DISABLE_PCI_DMA
+       bool "Clear Busmaster bit on PCI bridges during ExitBootServices()"
+       help
+	  Disable the busmaster bit in the control register on all PCI bridges
+	  while calling ExitBootServices() and passing control to the runtime
+	  kernel. System firmware may configure the IOMMU to prevent malicious
+	  PCI devices from being able to attack the OS via DMA. However, since
+	  firmware can't guarantee that the OS is IOMMU-aware, it will tear
+	  down IOMMU configuration when ExitBootServices() is called. This
+	  leaves a window between where a hostile device could still cause
+	  damage before Linux configures the IOMMU again.
+
+	  If you say Y here, the EFI stub will clear the busmaster bit on all
+	  PCI bridges before ExitBootServices() completes. This will prevent
+	  any malicious PCI devices from being able to perform DMA until the
+	  kernel reenables busmastering after configuring the IOMMU.
+
+	  This option will cause failures with some poorly behaved hardware
+	  and should not be enabled without testing. The kernel commandline
+	  options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be
+	  used to override this option.
+
  endmenu
 
  config UEFI_CPER
drivers/firmware/efi/libstub/Makefile +1 -1
index c35f893897e1..98a81576213d 100644
@@ -39,7 +39,7 @@ OBJECT_FILES_NON_STANDARD	:= y
  KCOV_INSTRUMENT			:= n
 
  lib-y				:= efi-stub-helper.o gop.o secureboot.o tpm.o \
-				   random.o
+				   random.o pci.o
 
  # include the stub's generic dependencies from lib/ when building for ARM/arm64
  arm-deps-y := fdt_rw.c fdt_ro.c fdt_wip.c fdt.c fdt_empty_tree.c fdt_sw.c
drivers/firmware/efi/libstub/arm-stub.c +1 -0
index e1ec0b2cde29..aa2929098d60 100644
@@ -257,6 +257,7 @@ unsigned long efi_entry(void *handle, efi_system_table_t *sys_table_arg,
  	}
 
  	install_memreserve_table();
+	efi_pci_disable_bridge_busmaster();
 
  	new_fdt_addr = fdt_addr;
  	status = allocate_new_fdt_and_exit_boot(handle,
drivers/firmware/efi/libstub/efi-stub-helper.c +17 -0
index 8b0d59f5c8cd..4b79bfff1fd4 100644
@@ -33,6 +33,8 @@ static bool __section(.data) efi_nokaslr;
  static bool __section(.data) efi_quiet;
  static bool __section(.data) efi_novamap;
  static bool __section(.data) efi_nosoftreserve;
+static bool __section(.data) efi_disable_pci_dma =
+					IS_ENABLED(CONFIG_EFI_DISABLE_PCI_DMA);
 
  bool __pure nokaslr(void)
  {
@@ -51,6 +53,11 @@ bool __pure __efi_soft_reserve_enabled(void)
  	return !efi_nosoftreserve;
  }
 
+bool __pure disable_pci_dma(void)
+{
+	return efi_disable_pci_dma;
+}
+
  #define EFI_MMAP_NR_SLACK_SLOTS	8
 
  struct file_info {
@@ -499,6 +506,16 @@ efi_status_t efi_parse_options(char const *cmdline)
  			efi_nosoftreserve = true;
  		}
 
+		if (!strncmp(str, "disable_pci_dma", 15)) {
+			str += strlen("disable_pci_dma");
+			efi_disable_pci_dma = true;
+		}
+
+		if (!strncmp(str, "no_disable_pci_dma", 18)) {
+			str += strlen("no_disable_pci_dma");
+			efi_disable_pci_dma = false;
+		}
+
  		/* Group words together, delimited by "," */
  		while (*str && *str != ' ' && *str != ',')
  			str++;
drivers/firmware/efi/libstub/efistub.h +1 -0
index 72e60b6afbf8..679268f67521 100644
@@ -28,6 +28,7 @@
  extern bool __pure nokaslr(void);
  extern bool __pure is_quiet(void);
  extern bool __pure novamap(void);
+extern bool __pure disable_pci_dma(void);
 
  extern __pure efi_system_table_t  *efi_system_table(void);
 
drivers/firmware/efi/libstub/pci.c new file +116 -0
new file mode 100644
index 000000000000..e2f71f890dd1
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PCI-related functions used by the EFI stub on multiple
+ * architectures.
+ *
+ * Copyright 2019 Google, LLC
+ */
+
+#include <linux/efi.h>
+#include <linux/pci.h>
+
+#include <asm/efi.h>
+
+#include "efistub.h"
+
+static efi_event_t __section(.data) exit_boot_services_event;
+static efi_event_t __section(.data) disable_pci_dma_event;
+
+static efi_handle_t *__section(.data) pci_handle;
+static unsigned long __section(.data) pci_handle_size;
+
+static efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
+
+static void __efiapi handle_exit_boot_services_event(efi_event_t event,
+						     void *context)
+{
+	efi_call_early(signal_event, disable_pci_dma_event);
+}
+
+static void __efiapi handle_disable_pci_dma_event(efi_event_t event,
+						  void *context)
+{
+	efi_handle_t handle;
+	efi_status_t status;
+	u16 class, command;
+	int i;
+
+	for_each_efi_handle(handle, pci_handle, pci_handle_size, i) {
+		efi_pci_io_protocol_t *pci = NULL;
+
+		status = efi_call_early(handle_protocol, handle,
+					&pci_proto, (void **)&pci);
+		if (status != EFI_SUCCESS || !pci)
+			continue;
+
+		status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
+					PCI_CLASS_DEVICE, 1, &class);
+
+		if (status != EFI_SUCCESS || class != PCI_CLASS_BRIDGE_PCI)
+			continue;
+
+		/* Disable busmastering */
+		status = efi_call_proto(pci, pci.read, EfiPciIoWidthUint16,
+					PCI_COMMAND, 1, &command);
+		if (status != EFI_SUCCESS || !(command & PCI_COMMAND_MASTER))
+			continue;
+
+		command &= ~PCI_COMMAND_MASTER;
+		status = efi_call_proto(pci, pci.write, EfiPciIoWidthUint16,
+					PCI_COMMAND, 1, &command);
+		if (status != EFI_SUCCESS)
+			pr_efi_err("Failed to disable PCI busmastering\n");
+	}
+}
+
+void efi_pci_disable_bridge_busmaster(void)
+{
+	efi_status_t status;
+
+	if (!disable_pci_dma() || !efi_is_native())
+		return;
+
+	status = efi_call_early(locate_handle, EFI_LOCATE_BY_PROTOCOL,
+				&pci_proto, NULL, &pci_handle_size, pci_handle);
+
+	if (status != EFI_BUFFER_TOO_SMALL) {
+		if (status != EFI_SUCCESS && status != EFI_NOT_FOUND)
+			pr_efi_err("Failed to locate PCI I/O handles'\n");
+		return;
+	}
+
+	status = efi_call_early(allocate_pool, EFI_LOADER_DATA,
+				pci_handle_size, (void **)&pci_handle);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to allocate memory for 'pci_handle'\n");
+		return;
+	}
+
+	status = efi_call_early(locate_handle, EFI_LOCATE_BY_PROTOCOL,
+				&pci_proto, NULL, &pci_handle_size, pci_handle);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to locate PCI I/O handles'\n");
+		goto free_handle;
+	}
+
+	status = efi_call_early(create_event, EVT_NOTIFY_SIGNAL,
+				TPL_CALLBACK, handle_disable_pci_dma_event,
+				NULL, &disable_pci_dma_event);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to create event to disable PCI DMA\n");
+		goto free_handle;
+	}
+
+	status = efi_call_early(create_event, EVT_SIGNAL_EXIT_BOOT_SERVICES,
+				TPL_CALLBACK, handle_exit_boot_services_event,
+				NULL, &exit_boot_services_event);
+	if (status != EFI_SUCCESS) {
+		pr_efi_err("Failed to register for EBS() event\n");
+		goto free_handle;
+	}
+
+	return;
+
+free_handle:
+	efi_call_early(free_pool, pci_handle);
+}
include/linux/efi.h +2 -0
index 81bd1e8c201f..2864633abff0 100644
@@ -1786,4 +1786,6 @@ struct linux_efi_memreserve {
  #define EFI_MEMRESERVE_COUNT(size) (((size) - sizeof(struct linux_efi_memreserve)) \
  	/ sizeof(((struct linux_efi_memreserve *)0)->entry[0]))
 
+void efi_pci_disable_bridge_busmaster(void);
+
  #endif /* _LINUX_EFI_H */
#80 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 52 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

Quoted 9 lines

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Thanks Hans.

Can you run the UEFI shell on this system? If so, could you share the output of devtree, both in the docked and the undocked states?

That should help us pinpoint which device is throwing an error at ExitBootServices() time due to its driver having been disconnected.

#81 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Hi,

Quoted 63 lines · Ard Biesheuvel
Quoted 52 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

Quoted 9 lines

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Quoted 7 lines

Thanks Hans.

Can you run the UEFI shell on this system? If so, could you share the output of devtree, both in the docked and the undocked states?

That should help us pinpoint which device is throwing an error at ExitBootServices() time due to its driver having been disconnected.

Sorry for being slow to respond. Attached are the outputs of devtree in both states. Not sure if the list will accept this, but you should get a direct copy.

Regards,

Hans

#82 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 72 lines · Hans de Goede

Hi,

Quoted 68 lines · Ard Biesheuvel
Quoted 52 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

Quoted 9 lines

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Thanks Hans.

Can you run the UEFI shell on this system? If so, could you share the output of devtree, both in the docked and the undocked states?

Quoted 7 lines
Quoted 2 lines

That should help us pinpoint which device is throwing an error at ExitBootServices() time due to its driver having been disconnected.

Sorry for being slow to respond. Attached are the outputs of devtree in both states. Not sure if the list will accept this, but you should get a direct copy.

Interesting. The only difference that UEFI seems to know about in terms of device hierarchy is a XHCI controller with a Realtek USB NIC attached.

Could you try unloading the driver for that manually, or disconnecting it? Or disconnect the whole thing from the shell?

If just unloading the realtek driver does not make a difference, but unload/disconnecting the xhci makes it work, it is likely that it this feature will break a lot of systems.

devtree-docked 2020-03-04 19:18:08.856257969 +0100 renamed from devtree-not-docked 2020-03-04 19:18:14.596315280 +0100 +14 -0
@@ -11,6 +11,7 @@
    Ctrl[] PciRoot(0x0)/Pci(0x0,0x0)
    Ctrl[] Intel(R) Graphics Controller
      Ctrl[] EDP1
+     Ctrl[] DP1
        Ctrl[] Primary Console Output Device
    Ctrl[] PciRoot(0x0)/Pci(0x4,0x0)
    Ctrl[] PciRoot(0x0)/Pci(0x8,0x0)
@@ -44,6 +45,110 @@
    Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x0,0x0)
    Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x0,0x0)/Pci(0x0,0x0)
    Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)
+   Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)
+   Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)
+   Ctrl[] eXtensible Host Controller (USB 3.0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x4,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x2,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)/USB(0x1,0x0)
+       Ctrl[] Realtek USB Ethernet Controller

<lots of network stuff>

+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x1)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x2)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x3)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x2,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)/USB(0x1,0x0)
+     Ctrl[] Generic Usb Keyboard

+ Ctrl[] Primary Console Input Device
+ Ctrl[] Generic Usb Mouse
+ Ctrl[] Primary Console Input Device
+ Ctrl[] Generic Usb Mouse
+ Ctrl[] Primary Console Input Device
+ Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x4,0x0)

    Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x2,0x0)
    Ctrl[] eXtensible Host Controller (USB 3.0)
    Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x4,0x0)

@@ -69,5 +174,3 @@
Ctrl[] VenHw(2C0EEABD-4107-44A8-A787-2E38FFC52B2B) Ctrl[] VenHw(48B9FB42-B3B9-4A0E-BF66-36AE1C302369) Ctrl[] VenHw(A7F26116-CFDC-4296-8224-ED7D140170C7)
- Ctrl[] MemoryMapped(0xB,0xFF2E0000,0xFF37FFFF)
- Ctrl[] Fv(B92CF322-8AFA-4AA4-B946-005DF1D69778)

#83 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Hi,

Quoted 81 lines · Ard Biesheuvel
Quoted 72 lines · Hans de Goede

Hi,

Quoted 68 lines · Ard Biesheuvel
Quoted 52 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

Quoted 9 lines

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Thanks Hans.

Can you run the UEFI shell on this system? If so, could you share the output of devtree, both in the docked and the undocked states?

Quoted 7 lines
Quoted 2 lines

That should help us pinpoint which device is throwing an error at ExitBootServices() time due to its driver having been disconnected.

Sorry for being slow to respond. Attached are the outputs of devtree in both states. Not sure if the list will accept this, but you should get a direct copy.

Quoted 6 lines

Interesting. The only difference that UEFI seems to know about in terms of device hierarchy is a XHCI controller with a Realtek USB NIC attached.

Could you try unloading the driver for that manually, or disconnecting it? Or disconnect the whole thing from the shell?

How would I go about that / do that ?

Quoted 3 lines

If just unloading the realtek driver does not make a difference, but unload/disconnecting the xhci makes it work, it is likely that it this feature will break a lot of systems.

Notice that it is not just the XHCI controller which gets added though, there also is an extra PCI-e switch added to the route to the XHCI controller, I've attached both docked and undocked lspci output under Linux. I guess this might be transparent from a UEFI pov though.

Regards,

Hans

Quoted 47 lines
devtree-docked 2020-03-04 19:18:08.856257969 +0100 renamed from devtree-not-docked 2020-03-04 19:18:14.596315280 +0100 +14 -0
@@ -11,6 +11,7 @@
     Ctrl[] PciRoot(0x0)/Pci(0x0,0x0)
     Ctrl[] Intel(R) Graphics Controller
       Ctrl[] EDP1
+     Ctrl[] DP1
         Ctrl[] Primary Console Output Device
     Ctrl[] PciRoot(0x0)/Pci(0x4,0x0)
     Ctrl[] PciRoot(0x0)/Pci(0x8,0x0)
@@ -44,6 +45,110 @@
     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x0,0x0)
     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x0,0x0)/Pci(0x0,0x0)
     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)
+   Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)
+   Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)
+   Ctrl[] eXtensible Host Controller (USB 3.0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x4,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x2,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)/USB(0x1,0x0)
+       Ctrl[] Realtek USB Ethernet Controller

<lots of network stuff>

devtree-docked 2020-03-04 19:18:08.856257969 +0100 renamed from devtree-not-docked 2020-03-04 19:18:14.596315280 +0100 +14 -0
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x1)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x2)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x0,0x0)/USB(0x3,0x3)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x2,0x0)/USB(0x3,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)/USB(0x0,0x0)
+     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x2,0x0)/Pci(0x0,0x0)/USB(0x1,0x0)/USB(0x0,0x0)/USB(0x3,0x0)/USB(0x1,0x0)
+     Ctrl[] Generic Usb Keyboard
+       Ctrl[] Primary Console Input Device
+     Ctrl[] Generic Usb Mouse
+       Ctrl[] Primary Console Input Device
+     Ctrl[] Generic Usb Mouse
+       Ctrl[] Primary Console Input Device
+   Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x1,0x0)/Pci(0x0,0x0)/Pci(0x4,0x0)
     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x2,0x0)
     Ctrl[] eXtensible Host Controller (USB 3.0)
     Ctrl[] PciRoot(0x0)/Pci(0x1D,0x4)/Pci(0x0,0x0)/Pci(0x4,0x0)

devtree-docked 2020-03-04 19:18:08.856257969 +0100 line 73

Quoted 5 lines
devtree-docked 2020-03-04 19:18:08.856257969 +0100 renamed from devtree-not-docked 2020-03-04 19:18:14.596315280 +0100 +0 -2
@@ -69,5 +174,3 @@
   Ctrl[] VenHw(2C0EEABD-4107-44A8-A787-2E38FFC52B2B)
   Ctrl[] VenHw(48B9FB42-B3B9-4A0E-BF66-36AE1C302369)
   Ctrl[] VenHw(A7F26116-CFDC-4296-8224-ED7D140170C7)
- Ctrl[] MemoryMapped(0xB,0xFF2E0000,0xFF37FFFF)
- Ctrl[] Fv(B92CF322-8AFA-4AA4-B946-005DF1D69778)

00:00.0 Host bridge: Intel Corporation Coffee Lake HOST and DRAM Controller (rev 0c)
00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (Whiskey Lake) (rev 02)
00:04.0 Signal processing controller: Intel Corporation Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (rev 0c) 00:08.0 System peripheral: Intel Corporation Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model 00:12.0 Signal processing controller: Intel Corporation Cannon Point-LP Thermal Controller (rev 11)
00:14.0 USB controller: Intel Corporation Cannon Point-LP USB 3.1 xHCI Controller (rev 11)
00:14.2 RAM memory: Intel Corporation Cannon Point-LP Shared SRAM (rev 11)
00:14.3 Network controller: Intel Corporation Cannon Point-LP CNVi [Wireless-AC] (rev 11)
00:15.0 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP Serial IO I2C Controller #0 (rev 11) 00:15.1 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP Serial IO I2C Controller #1 (rev 11) 00:16.0 Communication controller: Intel Corporation Cannon Point-LP MEI Controller #1 (rev 11)
00:16.3 Serial controller: Intel Corporation Cannon Point-LP Keyboard and Text (KT) Redirection (rev 11) 00:1d.0 PCI bridge: Intel Corporation Cannon Point-LP PCI Express Root Port #9 (rev f1)
00:1d.4 PCI bridge: Intel Corporation Cannon Point-LP PCI Express Root Port #13 (rev f1)
00:1f.0 ISA bridge: Intel Corporation Cannon Point-LP LPC Controller (rev 11)
00:1f.3 Audio device: Intel Corporation Cannon Point-LP High Definition Audio Controller (rev 11)
00:1f.4 SMBus: Intel Corporation Cannon Point-LP SMBus Controller (rev 11)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP SPI Controller (rev 11)
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (6) I219-LM (rev 11)
03:00.0 Non-Volatile memory controller: Sandisk Corp WD Black 2018/PC SN720 NVMe SSD
05:00.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:00.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:01.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:02.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:04.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 07:00.0 System peripheral: Intel Corporation JHL6540 Thunderbolt 3 NHI (C step) [Alpine Ridge 4C 2016] (rev 02) 08:00.0 PCI bridge: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge DD 2018] (rev 06)
09:02.0 PCI bridge: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge DD 2018] (rev 06)
09:04.0 PCI bridge: Intel Corporation JHL7540 Thunderbolt 3 Bridge [Titan Ridge DD 2018] (rev 06)
0a:00.0 USB controller: Intel Corporation JHL7540 Thunderbolt 3 USB Controller [Titan Ridge 4C 2018] (rev 06) 2d:00.0 USB controller: Intel Corporation JHL6540 Thunderbolt 3 USB Controller (C step) [Alpine Ridge 4C 2016] (rev 02)

00:00.0 Host bridge: Intel Corporation Coffee Lake HOST and DRAM Controller (rev 0c)
00:02.0 VGA compatible controller: Intel Corporation UHD Graphics 620 (Whiskey Lake) (rev 02)
00:04.0 Signal processing controller: Intel Corporation Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Thermal Subsystem (rev 0c) 00:08.0 System peripheral: Intel Corporation Xeon E3-1200 v5/v6 / E3-1500 v5 / 6th/7th/8th Gen Core Processor Gaussian Mixture Model 00:12.0 Signal processing controller: Intel Corporation Cannon Point-LP Thermal Controller (rev 11)
00:14.0 USB controller: Intel Corporation Cannon Point-LP USB 3.1 xHCI Controller (rev 11)
00:14.2 RAM memory: Intel Corporation Cannon Point-LP Shared SRAM (rev 11)
00:14.3 Network controller: Intel Corporation Cannon Point-LP CNVi [Wireless-AC] (rev 11)
00:15.0 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP Serial IO I2C Controller #0 (rev 11) 00:15.1 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP Serial IO I2C Controller #1 (rev 11) 00:16.0 Communication controller: Intel Corporation Cannon Point-LP MEI Controller #1 (rev 11)
00:16.3 Serial controller: Intel Corporation Cannon Point-LP Keyboard and Text (KT) Redirection (rev 11) 00:1d.0 PCI bridge: Intel Corporation Cannon Point-LP PCI Express Root Port #9 (rev f1)
00:1d.4 PCI bridge: Intel Corporation Cannon Point-LP PCI Express Root Port #13 (rev f1)
00:1f.0 ISA bridge: Intel Corporation Cannon Point-LP LPC Controller (rev 11)
00:1f.3 Audio device: Intel Corporation Cannon Point-LP High Definition Audio Controller (rev 11)
00:1f.4 SMBus: Intel Corporation Cannon Point-LP SMBus Controller (rev 11)
00:1f.5 Serial bus controller [0c80]: Intel Corporation Cannon Point-LP SPI Controller (rev 11)
00:1f.6 Ethernet controller: Intel Corporation Ethernet Connection (6) I219-LM (rev 11)
03:00.0 Non-Volatile memory controller: Sandisk Corp WD Black 2018/PC SN720 NVMe SSD
05:00.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:00.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:01.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:02.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 06:04.0 PCI bridge: Intel Corporation JHL6540 Thunderbolt 3 Bridge (C step) [Alpine Ridge 4C 2016] (rev 02) 07:00.0 System peripheral: Intel Corporation JHL6540 Thunderbolt 3 NHI (C step) [Alpine Ridge 4C 2016] (rev 02) 2d:00.0 USB controller: Intel Corporation JHL6540 Thunderbolt 3 USB Controller (C step) [Alpine Ridge 4C 2016] (rev 02)

-[0000:00]-+-00.0

           +-02.0
           +-04.0
           +-08.0
           +-12.0
           +-14.0
           +-14.2
           +-14.3
           +-15.0
           +-15.1
           +-16.0
           +-16.3
           +-1d.0-[03]----00.0
           +-1d.4-[05-52]----00.0-[06-52]--+-00.0-[07]----00.0
           |                               +-01.0-[08-2c]----00.0-[09-2c]--+-02.0-[0a]----00.0
           |                               |                               \-04.0-[0b-2c]--
           |                               +-02.0-[2d]----00.0
           |                               \-04.0-[2e-52]--
           +-1f.0
           +-1f.3
           +-1f.4
           +-1f.5
           \-1f.6
-[0000:00]-+-00.0
           +-02.0
           +-04.0
           +-08.0
           +-12.0
           +-14.0
           +-14.2
           +-14.3
           +-15.0
           +-15.1
           +-16.0
           +-16.3
           +-1d.0-[03]----00.0
           +-1d.4-[05-52]----00.0-[06-52]--+-00.0-[07]----00.0
           |                               +-01.0-[08-2c]--
           |                               +-02.0-[2d]----00.0
           |                               \-04.0-[2e-52]--
           +-1f.0
           +-1f.3
           +-1f.4
           +-1f.5
           \-1f.6
#84 lore raw

Re: [PATCH v2 21/21] efi: Allow disabling PCI busmastering on bridges during boot

Quoted 94 lines · Hans de Goede

Hi,

Quoted 81 lines · Ard Biesheuvel
Quoted 72 lines · Hans de Goede

Hi,

Quoted 68 lines · Ard Biesheuvel
Quoted 52 lines · Hans de Goede

Hi,

Quoted 26 lines · Ard Biesheuvel

From: Matthew Garrett <[email protected]>

Add an option to disable the busmaster bit in the control register on all PCI bridges during the invocation of ExitBootServices() and passing control to the runtime kernel. System firmware may configure the IOMMU to prevent malicious PCI devices from being able to attack the OS via DMA. However, since firmware can't guarantee that the OS is IOMMU-aware, it will tear down IOMMU configuration when ExitBootServices() is called. This leaves a window between where a hostile device could still cause damage before Linux configures the IOMMU again.

If CONFIG_EFI_DISABLE_PCI_DMA is enabled or the "efi=disable_pci_dma" command line argument is passed, the EFI stub will clear the busmaster bit on all PCI bridges before ExitBootServices() completes. This will prevent any malicious PCI devices from being able to perform DMA until the kernel reenables busmastering after configuring the IOMMU.

This option is disabled when in EFI mixed mode environments (ie, 64-bit kernels with a 32-bit EFI implementation), given that the use of EFI events is not supported in this case.

This option may cause failures with some poorly behaved hardware and should not be enabled without testing. The kernel commandline options "efi=disable_pci_dma" or "efi=no_disable_pci_dma" may be used to override the default.

Quoted 4 lines
[ardb: use EFI events to defer DMA disabling to the end of ExitBootServices()]

I guess this might not be the latest version of this patch, but this does seem to be the thread where most discussion on it has happened.

My personal kernel tree atm consists of v5.5 + efi/next + my own patches and yesterday I noticed that will not boot on a Lenovo X1 7th gen connected to a Lenovo thunderbolt 3 gen 2 dock.

My first hunch was that I have CONFIG_EFI_DISABLE_PCI_DMA=y and that that was causing it and indeed that is the problem.

So as (somewhat) expected CONFIG_EFI_DISABLE_PCI_DMA=y indeed stops the kernel from booting on some systems.

When I hit this problem the efistub prints 2 messages and then the system just hangs:

Quoted 9 lines

exit_boot() failed!
efi_main() failed!

When I boot the system without it being connected to the thunderbolt dock then efi=disable_pci_dma works fine.

Let me know if I can do anything to help and getting booting while connected to the dock to work with efi=disable_pci_dma.

Thanks Hans.

Can you run the UEFI shell on this system? If so, could you share the output of devtree, both in the docked and the undocked states?

Quoted 7 lines
Quoted 2 lines

That should help us pinpoint which device is throwing an error at ExitBootServices() time due to its driver having been disconnected.

Sorry for being slow to respond. Attached are the outputs of devtree in both states. Not sure if the list will accept this, but you should get a direct copy.

Quoted 6 lines

Interesting. The only difference that UEFI seems to know about in terms of device hierarchy is a XHCI controller with a Realtek USB NIC attached.

Could you try unloading the driver for that manually, or disconnecting it? Or disconnect the whole thing from the shell?

How would I go about that / do that ?

Quoted 9 lines
Quoted 3 lines

If just unloading the realtek driver does not make a difference, but unload/disconnecting the xhci makes it work, it is likely that it this feature will break a lot of systems.

Notice that it is not just the XHCI controller which gets added though, there also is an extra PCI-e switch added to the route to the XHCI controller, I've attached both docked and undocked lspci output under Linux. I guess this might be transparent from a UEFI pov though.

Yeah, what matters is drivers that are actually called when the EFI stub calls ExitBootServices(), and looking at the dump, the XHCI driver and the Realtek driver are the only ones that stand out to me (and the Realtek one is probably the only 3rd party driver, so that one looks the most suspect to me)

#85 lore raw
In reply to Ard Biesheuvel

Hi,

Quoted 37 lines · Ard Biesheuvel

Currently, we support mixed mode (64-bit Linux running on 32-bit firmware) by explicitly reasoning about pointer sizes for every call into the
firmware: on x86, there are 32-bit and 64-bit versions of each protocol interface, and each call gets routed via one of the two, depending on the native size of the firmware.

There is a lot of casting and pointer mangling involved in this, and as a result, we end up with much less coverage in terms of type checking by the compiler, due to the indirection via an anonymous, variadic thunking routine.

This peculiarity of x86 is also leaking into generic EFI code, which is shared with ia64, arm64, ARM and likely RiscV in the future. So let's try to clean this up a bit.

The approach taken by this series is to replace the 32/64 bit distinction with a distinction between native calls and mixed mode calls, where the former can be either 32 or 64 bit [depending on the platform] and use the ordinary native protocol definitions, while mixed mode calls retain the existing casting/thunking approach based on the 32-bit protocol definitions.

Given that GCC now supports emitting function calls using the MS calling convention, we can get rid of all the wrapping and casting, and emit the indirect calls directly.

Changes since v1:
- Substantially more cleanup work to simplify and hide the mixed mode handling in generic EFI code.
- Get rid of all the pointless passing around of sys_table_arg
- Incorporate an updated version of Matthew's PCI DMA disable patch using EFI events to defer poking the busmaster bits until after all the ordinary ExitBootServices() callbacks have executed.

Code can be found here
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efistub-x86-cleanup-v2

Quoted 6 lines
Cc Hans de Goede <[email protected]>
Cc Matthew Garrett <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Andy Lutomirski <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Arvind Sankar <[email protected]>

I've merged ardb/efistub-x86-cleanup-v3 into my local tree for testing I will let you know if I see any unexpected side-effects. I've set CONFIG_EFI_DISABLE_PCI_DMA=n for now.

I did notice one small cosmetic issue with the last patch:

drivers/firmware/efi/Kconfig config EFI_RCI2_TABLE · line 220

Quoted 11 lines
drivers/firmware/efi/Kconfig +3 -0
index bcc378c19ebe..da2f5fc862aa 100644
@@ -215,6 +215,28 @@ config EFI_RCI2_TABLE
 
  	  Say Y here for Dell EMC PowerEdge systems.
 
+config EFI_DISABLE_PCI_DMA
+       bool "Clear Busmaster bit on PCI bridges during ExitBootServices()"
+       help

The bool and help here seem to be indented by 7 spaces rather then by a tab.

Regards,

Hans

#86 lore raw
Quoted 48 lines · Hans de Goede

Hi,

Quoted 37 lines · Ard Biesheuvel

Currently, we support mixed mode (64-bit Linux running on 32-bit firmware) by explicitly reasoning about pointer sizes for every call into the
firmware: on x86, there are 32-bit and 64-bit versions of each protocol interface, and each call gets routed via one of the two, depending on the native size of the firmware.

There is a lot of casting and pointer mangling involved in this, and as a result, we end up with much less coverage in terms of type checking by the compiler, due to the indirection via an anonymous, variadic thunking routine.

This peculiarity of x86 is also leaking into generic EFI code, which is shared with ia64, arm64, ARM and likely RiscV in the future. So let's try to clean this up a bit.

The approach taken by this series is to replace the 32/64 bit distinction with a distinction between native calls and mixed mode calls, where the former can be either 32 or 64 bit [depending on the platform] and use the ordinary native protocol definitions, while mixed mode calls retain the existing casting/thunking approach based on the 32-bit protocol definitions.

Given that GCC now supports emitting function calls using the MS calling convention, we can get rid of all the wrapping and casting, and emit the indirect calls directly.

Changes since v1:
- Substantially more cleanup work to simplify and hide the mixed mode handling in generic EFI code.
- Get rid of all the pointless passing around of sys_table_arg
- Incorporate an updated version of Matthew's PCI DMA disable patch using EFI events to defer poking the busmaster bits until after all the ordinary ExitBootServices() callbacks have executed.

Code can be found here
https://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux.git/log/?h=efistub-x86-cleanup-v2

Quoted 6 lines
Cc Hans de Goede <[email protected]>
Cc Matthew Garrett <[email protected]>
Cc Ingo Molnar <[email protected]>
Cc Andy Lutomirski <[email protected]>
Cc Thomas Gleixner <[email protected]>
Cc Arvind Sankar <[email protected]>
Quoted 4 lines

I've merged ardb/efistub-x86-cleanup-v3 into my local tree for testing I will let you know if I see any unexpected side-effects. I've set CONFIG_EFI_DISABLE_PCI_DMA=n for now.

Thanks Hans.

The CONFIG should not make a difference on mixed-mode - all the code is there anyway, and only the default value changes, but it is overridden to 'false' on mixed mode systems. The reason is that my version of this patch uses an event callback, which would mean 32-bit firmware calling back into the 64-bit kernel in the mixed mode case, which we don't support.

Quoted 21 lines

I did notice one small cosmetic issue with the last patch:

drivers/firmware/efi/Kconfig config EFI_RCI2_TABLE · line 220

Quoted 11 lines
drivers/firmware/efi/Kconfig +3 -0
index bcc378c19ebe..da2f5fc862aa 100644
@@ -215,6 +215,28 @@ config EFI_RCI2_TABLE
 
       Say Y here for Dell EMC PowerEdge systems.
 
+config EFI_DISABLE_PCI_DMA
+       bool "Clear Busmaster bit on PCI bridges during ExitBootServices()"
+       help

The bool and help here seem to be indented by 7 spaces rather then by a tab.

Regards,

Hans