Skip to contentSkip to conversation

efi/x86: confine type unsafe casting to mixed mode

PATCH Superseded Ard Biesheuvel 4 participants 29 messages Dec 14, 2019 – Dec 17, 2019

Subsystems (4)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore

Ard Biesheuvel

author maintainer
#0 lore raw

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.

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

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 (10):
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: add missing apple_properties_protocol_t definition 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 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

 arch/arm/include/asm/efi.h                    |   3 +-
 arch/arm64/include/asm/efi.h                  |   3 +-
 arch/x86/Kconfig                              |   1 +
 arch/x86/boot/compressed/Makefile             |   2 +-
 arch/x86/boot/compressed/eboot.c              |  51 ++--
 arch/x86/boot/compressed/eboot.h              |  11 +-
 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            |  12 -
 arch/x86/include/asm/efi.h                    |  64 ++--
 arch/x86/platform/efi/efi.c                   |  12 +-
 arch/x86/platform/efi/efi_64.c                |   6 +-
 arch/x86/platform/efi/quirks.c                |   2 +-
 .../firmware/efi/libstub/efi-stub-helper.c    |  46 ++-
 drivers/firmware/efi/libstub/gop.c            |   9 +-
 drivers/firmware/efi/libstub/pci.c            |   9 +-
 drivers/firmware/efi/libstub/random.c         |  13 +-
 drivers/firmware/efi/libstub/tpm.c            |   4 +-
 include/linux/efi.h                           | 278 ++++++------------
 20 files changed, 195 insertions(+), 431 deletions(-)
 delete mode 100644 arch/x86/boot/compressed/efi_stub_32.S
 delete mode 100644 arch/x86/boot/compressed/efi_stub_64.S
Signature · 1 line
2.17.1
author maintainer
#1 lore raw

PATCH 1/10 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 2/10 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 3/10 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 ++-------
 drivers/firmware/efi/libstub/pci.c |  9 +++------
 include/linux/efi.h                | 10 ++++++++++
 4 files changed, 20 insertions(+), 22 deletions(-)
arch/x86/boot/compressed/eboot.c +5 -9
index 0f1edd2c49fc..34962f2f3337 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;
 
 	efi_pci_disable_bridge_busmaster(sys_table);
@@ -166,14 +167,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;
@@ -268,6 +266,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,
@@ -285,13 +284,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,
drivers/firmware/efi/libstub/pci.c +3 -6
index f792b241383d..d144551e36eb 100644
@@ -19,8 +19,8 @@ void efi_pci_disable_bridge_busmaster(efi_system_table_t *sys_table_arg)
 	void **pci_handle = NULL;
 	efi_guid_t pci_proto = EFI_PCI_IO_PROTOCOL_GUID;
 	unsigned long size = 0;
-	unsigned long nr_pci;
 	u16 class, command;
+	efi_handle_t handle;
 	int i;
 
 	if (!nobusmaster())
@@ -49,13 +49,10 @@ void efi_pci_disable_bridge_busmaster(efi_system_table_t *sys_table_arg)
 	if (status != EFI_SUCCESS)
 		goto free_handle;
 
-	nr_pci = size / (efi_is_64bit() ? sizeof(u64) : sizeof(u32));
-	for (i = 0; i < nr_pci; i++) {
+	for_each_efi_handle(handle, pci_handle, size, i) {
 		efi_pci_io_protocol_t *pci = NULL;
-		unsigned long handle = efi_is_64bit() ? ((u64 *)pci_handle)[i]
-						      : ((u32 *)pci_handle)[i];
 
-		status = efi_call_early(handle_protocol, (efi_handle_t)handle,
+		status = efi_call_early(handle_protocol, handle,
 					&pci_proto, (void **)&pci);
 		if (status != EFI_SUCCESS || !pci)
 			continue;
include/linux/efi.h +10 -0
index de2087149089..d7ca0b85b2b5 100644
@@ -48,6 +48,16 @@ typedef u16 efi_char16_t;		/* UNICODE character */
 typedef u64 efi_physical_addr_t;
 typedef void *efi_handle_t;
 
+#define for_each_efi_handle(handle, array, size, i)			\
+	for (i = 1, handle = efi_is_64bit()				\
+		? (efi_handle_t)(unsigned long)((u64 *)(array))[0]	\
+		: (efi_handle_t)(unsigned long)((u32 *)(array))[0];	\
+	    i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)	\
+					     : sizeof(u32));		\
+	    handle = efi_is_64bit()					\
+		? (efi_handle_t)(unsigned long)((u64 *)(array))[i]	\
+		: (efi_handle_t)(unsigned long)((u32 *)(array))[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
#4 lore raw

Re: [PATCH 03/10] efi/libstub: use a helper to iterate over a EFI handle array

Quoted 26 lines · 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.

---

include/linux/efi.h +10 -0
+#define for_each_efi_handle(handle, array, size, i)			\
+	for (i = 1, handle = efi_is_64bit()				\
+		? (efi_handle_t)(unsigned long)((u64 *)(array))[0]	\
+		: (efi_handle_t)(unsigned long)((u32 *)(array))[0];	\
+	    i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)	\
+					     : sizeof(u32));		\
+	    handle = efi_is_64bit()					\
+		? (efi_handle_t)(unsigned long)((u64 *)(array))[i]	\
+		: (efi_handle_t)(unsigned long)((u32 *)(array))[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

This would access one past the array, no? Eg if the array has one handle, i is incremented to 2 the first time the condition is checked, then the loop increment will access array[2] before the condition is checked again. There seem to be at least a couple of other for_each macros that might have similar issues.

How about the below instead?

#define for_each_efi_handle(handle, array, size, i)			\
	for (i = 0;							\
	    (i < (size) / (efi_is_64bit() ? sizeof(efi_handle_t)	\
					  : sizeof(u32))) &&		\
	    ((handle = efi_is_64bit()					\
		? ((efi_handle_t *)(array))[i]				\
		: (efi_handle_t)(unsigned long)((u32 *)(array))[i]), 1);\
	    i++)
#5 lore raw

Re: [PATCH 03/10] efi/libstub: use a helper to iterate over a EFI handle array

Quoted 26 lines · Arvind Sankar
Quoted 24 lines · 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.

---

include/linux/efi.h +10 -1
+#define for_each_efi_handle(handle, array, size, i)                  \
+     for (i = 1, handle = efi_is_64bit()                             \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[0]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[0];     \
+         i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)      \
+                                          : sizeof(u32));            \
+         handle = efi_is_64bit()                                     \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[i]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[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
--
Quoted 9 lines
Quoted 2 lines

2.17.1

This would access one past the array, no? Eg if the array has one handle, i is incremented to 2 the first time the condition is checked, then the loop increment will access array[2] before the condition is checked again. There seem to be at least a couple of other for_each macros that might have similar issues.

Indeed.

Quoted 11 lines

How about the below instead?

#define for_each_efi_handle(handle, array, size, i)                     \
        for (i = 0;                                                     \
            (i < (size) / (efi_is_64bit() ? sizeof(efi_handle_t)        \
                                          : sizeof(u32))) &&            \
            ((handle = efi_is_64bit()                                   \
                ? ((efi_handle_t *)(array))[i]                          \
                : (efi_handle_t)(unsigned long)((u32 *)(array))[i]), 1);\
            i++)

Yeah, that looks correct to me, but perhaps we can come up with something slightly more readable? :-)
(Not saying my code was better in that respect)

#6 lore raw

Re: [PATCH 03/10] efi/libstub: use a helper to iterate over a EFI handle array

Quoted 52 lines · Ard Biesheuvel
Quoted 26 lines · Arvind Sankar
Quoted 24 lines · 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.

---

include/linux/efi.h +10 -1
+#define for_each_efi_handle(handle, array, size, i)                  \
+     for (i = 1, handle = efi_is_64bit()                             \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[0]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[0];     \
+         i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)      \
+                                          : sizeof(u32));            \
+         handle = efi_is_64bit()                                     \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[i]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[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
--
Quoted 9 lines
Quoted 2 lines

2.17.1

This would access one past the array, no? Eg if the array has one handle, i is incremented to 2 the first time the condition is checked, then the loop increment will access array[2] before the condition is checked again. There seem to be at least a couple of other for_each macros that might have similar issues.

Indeed.

Quoted 11 lines

How about the below instead?

#define for_each_efi_handle(handle, array, size, i)                     \
        for (i = 0;                                                     \
            (i < (size) / (efi_is_64bit() ? sizeof(efi_handle_t)        \
                                          : sizeof(u32))) &&            \
            ((handle = efi_is_64bit()                                   \
                ? ((efi_handle_t *)(array))[i]                          \
                : (efi_handle_t)(unsigned long)((u32 *)(array))[i]), 1);\
            i++)
Quoted 3 lines

Yeah, that looks correct to me, but perhaps we can come up with something slightly more readable? :-)
(Not saying my code was better in that respect)

How about

#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))[i])
#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++)
#7 lore raw

Re: [PATCH 03/10] efi/libstub: use a helper to iterate over a EFI handle array

Quoted 64 lines · Ard Biesheuvel
Quoted 52 lines · Ard Biesheuvel
Quoted 26 lines · Arvind Sankar
Quoted 24 lines · 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.

---

include/linux/efi.h +10 -1
+#define for_each_efi_handle(handle, array, size, i)                  \
+     for (i = 1, handle = efi_is_64bit()                             \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[0]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[0];     \
+         i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)      \
+                                          : sizeof(u32));            \
+         handle = efi_is_64bit()                                     \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[i]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[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
--
Quoted 9 lines
Quoted 2 lines

2.17.1

This would access one past the array, no? Eg if the array has one handle, i is incremented to 2 the first time the condition is checked, then the loop increment will access array[2] before the condition is checked again. There seem to be at least a couple of other for_each macros that might have similar issues.

Indeed.

Quoted 11 lines

How about the below instead?

#define for_each_efi_handle(handle, array, size, i)                     \
        for (i = 0;                                                     \
            (i < (size) / (efi_is_64bit() ? sizeof(efi_handle_t)        \
                                          : sizeof(u32))) &&            \
            ((handle = efi_is_64bit()                                   \
                ? ((efi_handle_t *)(array))[i]                          \
                : (efi_handle_t)(unsigned long)((u32 *)(array))[i]), 1);\
            i++)
Quoted 3 lines

Yeah, that looks correct to me, but perhaps we can come up with something slightly more readable? :-)
(Not saying my code was better in that respect)

How about

#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))[i])
Quoted 8 lines
#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++)

Heh, I came up with almost the same thing, but yours is slightly better, You have a typo in efi_get_handle_at (i instead of idx in the second line).

#8 lore raw

Re: [PATCH 03/10] efi/libstub: use a helper to iterate over a EFI handle array

In reply to Ard Biesheuvel
Quoted 51 lines · Ard Biesheuvel
Quoted 26 lines · Arvind Sankar
Quoted 24 lines · 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.

---

include/linux/efi.h +10 -1
+#define for_each_efi_handle(handle, array, size, i)                  \
+     for (i = 1, handle = efi_is_64bit()                             \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[0]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[0];     \
+         i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)      \
+                                          : sizeof(u32));            \
+         handle = efi_is_64bit()                                     \
+             ? (efi_handle_t)(unsigned long)((u64 *)(array))[i]      \
+             : (efi_handle_t)(unsigned long)((u32 *)(array))[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
--
Quoted 9 lines
Quoted 2 lines

2.17.1

This would access one past the array, no? Eg if the array has one handle, i is incremented to 2 the first time the condition is checked, then the loop increment will access array[2] before the condition is checked again. There seem to be at least a couple of other for_each macros that might have similar issues.

Indeed.

Quoted 11 lines

How about the below instead?

#define for_each_efi_handle(handle, array, size, i)                     \
        for (i = 0;                                                     \
            (i < (size) / (efi_is_64bit() ? sizeof(efi_handle_t)        \
                                          : sizeof(u32))) &&            \
            ((handle = efi_is_64bit()                                   \
                ? ((efi_handle_t *)(array))[i]                          \
                : (efi_handle_t)(unsigned long)((u32 *)(array))[i]), 1);\
            i++)
Quoted 3 lines

Yeah, that looks correct to me, but perhaps we can come up with something slightly more readable? :-)
(Not saying my code was better in that respect)

:) The idiom of the && with , operator is copied from for_each_bvec in bvec.h.

Perhaps more readable with helper macros:

#define __efi_handle_size (efi_is_64bit() ? sizeof(efi_handle_t)	\
					  : sizeof(u32))
#define __efi_handle_elem(array, i)					\
	(efi_is_64bit() ? ((efi_handle_t *)(array))[i]			\
			: (efi_handle_t)(uintptr_t)((u32 *)(array))[i])
#define for_each_efi_handle(handle, array, size, i)			\
	for (i = 0; i < (size) / __efi_handle_size &&			\
		    (handle = __efi_handle_elem(array, i), 1); i++)
author maintainer
#9 lore raw

PATCH 4/10 efi/libstub: add missing apple_properties_protocol_t definition

In reply to Ard Biesheuvel

As apple_properties_protocol_t is only used on x86, we never bothered to define the native apple_properties_protocol_t struct, but only added the explicit 32-bit and 64-bit ones. We'll need the native one for the next patch so let's add it, based on the prototypes that can be found in commit 58c5475aba67706b31d9237808d5d3d54074e5ea.

---
 include/linux/efi.h | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)
include/linux/efi.h +16 -0
index d7ca0b85b2b5..735388ea7012 100644
@@ -518,6 +518,22 @@ typedef struct {
 	u64 get_all;
 } apple_properties_protocol_64_t;
 
+struct efi_dev_path;
+
+typedef struct apple_properties_protocol {
+	unsigned long version;
+	efi_status_t (*get)(struct apple_properties_protocol *,
+			    struct efi_dev_path *, efi_char16_t *,
+			    void *, u32 *);
+	efi_status_t (*set)(struct apple_properties_protocol *,
+			    struct efi_dev_path *, efi_char16_t *,
+			    void *, u32);
+	efi_status_t (*del)(struct apple_properties_protocol *,
+			    struct efi_dev_path *, efi_char16_t *);
+	efi_status_t (*get_all)(struct apple_properties_protocol *,
+				void *buffer, u32 *);
+} apple_properties_protocol_t;
+
 typedef struct {
 	u32 get_capability;
 	u32 get_event_log;
Signature · 1 line
2.17.1
author maintainer
#10 lore raw

PATCH 5/10 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/include/asm/efi.h                     | 31 +++++++++++----
 drivers/firmware/efi/libstub/efi-stub-helper.c | 41 +++++++-------------
 include/linux/efi.h                            | 10 ++---
 5 files changed, 44 insertions(+), 42 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/include/asm/efi.h +24 -7
index 6094e7f49a99..37364c43296e 100644
@@ -222,21 +222,38 @@ 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_table_attr(table, attr, instance) ({			\
+	__typeof__(((table##_t *)0)->attr) __ret;			\
+	if (efi_is_native()) {						\
+		__ret = ((table##_t *)instance)->attr;			\
+	} else {							\
+		__typeof__(((table##_32_t *)0)->attr) at;		\
+		at = (((table##_32_t *)(unsigned long)instance)->attr);	\
+		__ret = (__typeof__(__ret))(unsigned long)at;		\
+	}								\
+	__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 +14 -27

47 lines not on this page · raw message

include/linux/efi.h +4 -6
index 735388ea7012..7d7ea32a9990 100644
@@ -49,13 +49,11 @@ typedef u64 efi_physical_addr_t;
 typedef void *efi_handle_t;
 
 #define for_each_efi_handle(handle, array, size, i)			\
-	for (i = 1, handle = efi_is_64bit()				\
-		? (efi_handle_t)(unsigned long)((u64 *)(array))[0]	\
+	for (i = 1, handle = efi_is_native() ? (array)[0]		\
 		: (efi_handle_t)(unsigned long)((u32 *)(array))[0];	\
-	    i++ <= (size) / (efi_is_64bit() ? sizeof(efi_handle_t)	\
+	    i++ <= (size) / (efi_is_native() ? sizeof(efi_handle_t)	\
 					     : sizeof(u32));		\
-	    handle = efi_is_64bit()					\
-		? (efi_handle_t)(unsigned long)((u64 *)(array))[i]	\
+	    handle = efi_is_native() ? (array)[i]			\
 		: (efi_handle_t)(unsigned long)((u32 *)(array))[i])
 
 /*
@@ -753,7 +751,7 @@ typedef struct {
 
 typedef struct {
 	efi_guid_t guid;
-	unsigned long table;
+	void *table;
 } efi_config_table_t;
 
 typedef struct {
Signature · 1 line
2.17.1
#11 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 12 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +12 -0
+
+#define efi_table_attr(table, attr, instance) ({			\
+	__typeof__(((table##_t *)0)->attr) __ret;			\
+	if (efi_is_native()) {						\
+		__ret = ((table##_t *)instance)->attr;			\
+	} else {							\
+		__typeof__(((table##_32_t *)0)->attr) at;		\
+		at = (((table##_32_t *)(unsigned long)instance)->attr);	\
+		__ret = (__typeof__(__ret))(unsigned long)at;		\
+	}								\
+	__ret;								\
+})

The casting of `at' is appropriate if the attr is a pointer type which needs to be zero-extended to 64-bit, but for other fields it is unnecessary at best and possibly dangerous. There are probably no instances currently where it is called for a non-pointer field, but is it possible to detect if the type is pointer and avoid the cast if not?

#12 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 11 lines · Arvind Sankar
Quoted 10 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +10 -0
+
+#define efi_table_attr(table, attr, instance) ({			\
+	__typeof__(((table##_t *)0)->attr) __ret;			\
+	if (efi_is_native()) {						\
+		__ret = ((table##_t *)instance)->attr;			\
+	} else {							\
+		__typeof__(((table##_32_t *)0)->attr) at;		\
+		at = (((table##_32_t *)(unsigned long)instance)->attr);	\
+		__ret = (__typeof__(__ret))(unsigned long)at;		\
+	}								\
Quoted 8 lines

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 2 lines
arch/x86/include/asm/efi.h +2 -0
+	__ret;								\
+})

The casting of `at' is appropriate if the attr is a pointer type which needs to be zero-extended to 64-bit, but for other fields it is unnecessary at best and possibly dangerous. There are probably no instances currently where it is called for a non-pointer field, but is it possible to detect if the type is pointer and avoid the cast if not?

To clarify, I mean the casting via `unsigned long' -- casting to type of __ret should be ok. We could also use uintptr_t for cleanliness when the cast is required?

#13 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 22 lines · Arvind Sankar
Quoted 11 lines · Arvind Sankar
Quoted 10 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +10 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
Quoted 8 lines

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 2 lines
arch/x86/include/asm/efi.h +2 -0
+   __ret;                                                          \
+})

The casting of `at' is appropriate if the attr is a pointer type which needs to be zero-extended to 64-bit, but for other fields it is unnecessary at best and possibly dangerous. There are probably no instances currently where it is called for a non-pointer field, but is it possible to detect if the type is pointer and avoid the cast if not?

Quoted 3 lines

To clarify, I mean the casting via `unsigned long' -- casting to type of __ret should be ok. We could also use uintptr_t for cleanliness when the cast is required?

Could you give an example of how it could break?

#14 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 23 lines · Ard Biesheuvel
Quoted 22 lines · Arvind Sankar
Quoted 19 lines · Arvind Sankar

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 12 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +12 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
+   __ret;                                                          \
+})

The casting of `at' is appropriate if the attr is a pointer type which needs to be zero-extended to 64-bit, but for other fields it is unnecessary at best and possibly dangerous. There are probably no instances currently where it is called for a non-pointer field, but is it possible to detect if the type is pointer and avoid the cast if not?

Quoted 5 lines
Quoted 3 lines

To clarify, I mean the casting via `unsigned long' -- casting to type of __ret should be ok. We could also use uintptr_t for cleanliness when the cast is required?

Could you give an example of how it could break?

eg, if the field is actually a structure. Nobody seems to do this currently, but say for
efi_table_attr(efi_boot_services, hdr, instance)
you shouldn't cast hdr to unsigned long.

There's also the case of nested 32/64-bit structures that breaks, but that might be too hard to try to handle:
efi_table_attr(efi_pci_io_protocol, io, instance)
where io is a structure of two pointers which would need to be individually casted. It's properly accessible as io.read/io.write though.

In general though, everything is either a pointer or a u64, so if it's too messy to detect pointer type, the cast is still probably safe in practice.

#15 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 29 lines · Arvind Sankar
Quoted 27 lines · Ard Biesheuvel
Quoted 25 lines · Arvind Sankar
Quoted 11 lines · Arvind Sankar
Quoted 10 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +10 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
Quoted 8 lines

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 2 lines
arch/x86/include/asm/efi.h +2 -0
+   __ret;                                                          \
+})

The casting of `at' is appropriate if the attr is a pointer type which needs to be zero-extended to 64-bit, but for other fields it is unnecessary at best and possibly dangerous. There are probably no instances currently where it is called for a non-pointer field, but is it possible to detect if the type is pointer and avoid the cast if not?

To clarify, I mean the casting via `unsigned long' -- casting to type of __ret should be ok. We could also use uintptr_t for cleanliness when the cast is required?

Quoted 7 lines
Quoted 1 line

Could you give an example of how it could break?

eg, if the field is actually a structure. Nobody seems to do this currently, but say for
efi_table_attr(efi_boot_services, hdr, instance)
you shouldn't cast hdr to unsigned long.

Yes. the efi_guid_t in efi_config_table_t is another example - I had to work around that in get_efi_config_table().

Quoted 7 lines

There's also the case of nested 32/64-bit structures that breaks, but that might be too hard to try to handle:
efi_table_attr(efi_pci_io_protocol, io, instance)
where io is a structure of two pointers which would need to be individually casted. It's properly accessible as io.read/io.write though.

Yes. We already have calls to pci.read that use this.

Quoted 3 lines

In general though, everything is either a pointer or a u64, so if it's too messy to detect pointer type, the cast is still probably safe in practice.

Yes. I'm open to suggestions on how to improve this, but mixed mode is somewhat of a maintenance burden, so if new future functionality needs to leave mixed mode behind, I'm not too bothered.

Thanks,
Ard.

#16 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 20 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 4 lines · Ard Biesheuvel
Quoted 3 lines · Arvind Sankar
Quoted 1 line · Arvind Sankar

On Sat, Dec 14, 2019 at 06:57:30PM +0100, Ard Biesheuvel wrote:

Quoted 13 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
arch/x86/include/asm/efi.h +5 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

arch/x86/include/asm/efi.h static inline bool efi_is_64bit(void) · line 242

Quoted 7 lines
arch/x86/include/asm/efi.h +7 -0
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
+   __ret;                                                          \
+})
Quoted 4 lines

Yes. I'm open to suggestions on how to improve this, but mixed mode is somewhat of a maintenance burden, so if new future functionality needs to leave mixed mode behind, I'm not too bothered.

Maybe just do

	if (sizeof(at) < sizeof(__ret))
		__ret = (__typeof__(__ret))(uintptr_t)at;
	else
		__ret = (__typeof__(__ret))at;

That should cover most of the cases.

#17 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 27 lines · Arvind Sankar
Quoted 20 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 4 lines · Ard Biesheuvel
Quoted 3 lines · Arvind Sankar
Quoted 1 line · Arvind Sankar

On Sat, Dec 14, 2019 at 06:57:30PM +0100, Ard Biesheuvel wrote:

Quoted 13 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
(quoted diff) +5 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Review

Quoted 7 lines
(quoted diff) +7 -0
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
+   __ret;                                                          \
+})
Quoted 4 lines

Yes. I'm open to suggestions on how to improve this, but mixed mode is somewhat of a maintenance burden, so if new future functionality needs to leave mixed mode behind, I'm not too bothered.

Quoted 6 lines

Maybe just do

        if (sizeof(at) < sizeof(__ret))
                __ret = (__typeof__(__ret))(uintptr_t)at;
        else
                __ret = (__typeof__(__ret))at;

That should cover most of the cases.

But the compiler will still be unhappy about the else clause if __ret is a pointer type, since we'll be casting an u32 to a pointer,

#18 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 11 lines · Ard Biesheuvel
Quoted 7 lines · Arvind Sankar

Maybe just do

        if (sizeof(at) < sizeof(__ret))
                __ret = (__typeof__(__ret))(uintptr_t)at;
        else
                __ret = (__typeof__(__ret))at;

That should cover most of the cases.

But the compiler will still be unhappy about the else clause if __ret is a pointer type, since we'll be casting an u32 to a pointer,

Ugh, yeah it complains even though it can tell at compile time that that branch isn't taken.

#19 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

In reply to Ard Biesheuvel
Quoted 36 lines · Ard Biesheuvel
Quoted 27 lines · Arvind Sankar
Quoted 20 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 4 lines · Ard Biesheuvel
Quoted 3 lines · Arvind Sankar
Quoted 1 line · Arvind Sankar

On Sat, Dec 14, 2019 at 06:57:30PM +0100, Ard Biesheuvel wrote:

Quoted 13 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
(quoted diff) +5 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Review

Quoted 7 lines
(quoted diff) +7 -0
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
+   __ret;                                                          \
+})
Quoted 4 lines

Yes. I'm open to suggestions on how to improve this, but mixed mode is somewhat of a maintenance burden, so if new future functionality needs to leave mixed mode behind, I'm not too bothered.

Quoted 6 lines

Maybe just do

        if (sizeof(at) < sizeof(__ret))
                __ret = (__typeof__(__ret))(uintptr_t)at;
        else
                __ret = (__typeof__(__ret))at;

That should cover most of the cases.

Quoted 2 lines

But the compiler will still be unhappy about the else clause if __ret is a pointer type, since we'll be casting an u32 to a pointer,

I think the answer is to have efi_table_ptr() for pointers and efi_table_attr() for other types.

#20 lore raw

Re: [PATCH 05/10] efi/libstub: distinguish between native/mixed not 32/64 bit

Quoted 37 lines · Ard Biesheuvel
Quoted 36 lines · Ard Biesheuvel
Quoted 33 lines · Arvind Sankar
Quoted 20 lines · Ard Biesheuvel
Quoted 6 lines · Arvind Sankar
Quoted 4 lines · Ard Biesheuvel
Quoted 3 lines · Arvind Sankar
Quoted 1 line · Arvind Sankar

On Sat, Dec 14, 2019 at 06:57:30PM +0100, Ard Biesheuvel wrote:

Quoted 13 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
(quoted diff) +5 -0
+
+#define efi_table_attr(table, attr, instance) ({                   \
+   __typeof__(((table##_t *)0)->attr) __ret;                       \
+   if (efi_is_native()) {                                          \
+           __ret = ((table##_t *)instance)->attr;                  \
Quoted 8 lines
Quoted 8 lines
Quoted 8 lines

Review

Quoted 7 lines
(quoted diff) +7 -0
+   } else {                                                        \
+           __typeof__(((table##_32_t *)0)->attr) at;               \
+           at = (((table##_32_t *)(unsigned long)instance)->attr); \
+           __ret = (__typeof__(__ret))(unsigned long)at;           \
+   }                                                               \
+   __ret;                                                          \
+})
Quoted 4 lines

Yes. I'm open to suggestions on how to improve this, but mixed mode is somewhat of a maintenance burden, so if new future functionality needs to leave mixed mode behind, I'm not too bothered.

Maybe just do

        if (sizeof(at) < sizeof(__ret))
                __ret = (__typeof__(__ret))(uintptr_t)at;
        else
                __ret = (__typeof__(__ret))at;

That should cover most of the cases.

Quoted 5 lines
Quoted 2 lines

But the compiler will still be unhappy about the else clause if __ret is a pointer type, since we'll be casting an u32 to a pointer,

I think the answer is to have efi_table_ptr() for pointers and efi_table_attr() for other types.

Using __builtin_choose_expr avoids the warning:

	__ret = (__typeof__(__ret))
		__builtin_choose_expr(sizeof(at) < sizeof(ret),
				      (uintptr_t)at, at);

But having different efi_table_ macros sounds cleaner.

author maintainer
#21 lore raw

PATCH 6/10 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 34962f2f3337..6d833f93a878 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)
 {
@@ -399,10 +392,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);
@@ -761,10 +751,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 37364c43296e..2244232108a0 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 7d7ea32a9990..e17c16d8d523 100644
@@ -810,7 +810,7 @@ typedef struct {
 	unsigned long con_in_handle;
 	unsigned long con_in;
 	unsigned long con_out_handle;
-	unsigned long con_out;
+	struct efi_simple_text_output_protocol *con_out;
 	unsigned long stderr_handle;
 	unsigned long stderr;
 	efi_runtime_services_t *runtime;
@@ -1445,11 +1445,11 @@ typedef struct {
 	u64 test_string;
 } efi_simple_text_output_protocol_64_t;
 
-struct efi_simple_text_output_protocol {
+typedef struct efi_simple_text_output_protocol {
 	void *reset;
 	efi_status_t (*output_string)(void *, void *);
 	void *test_string;
-};
+} efi_simple_text_output_protocol_t;
 
 #define PIXEL_RGB_RESERVED_8BIT_PER_COLOR		0
 #define PIXEL_BGR_RESERVED_8BIT_PER_COLOR		1
Signature · 1 line
2.17.1
author maintainer
#22 lore raw

PATCH 7/10 efi/libstub: drop explicit 64-bit protocol definitions

In reply to Ard Biesheuvel

Mixed mode involves casting 64-bit native calls to 32-bit EFI calls, which requires explicit 32-bit protocol definitions. Native calls are always done using the protocol definitions that use native types, and so the explicit 64-bit ones are redundant. Remove them.

---
 arch/x86/boot/compressed/eboot.h      |   6 -
 drivers/firmware/efi/libstub/random.c |   5 -
 include/linux/efi.h                   | 142 --------------------
 3 files changed, 153 deletions(-)
arch/x86/boot/compressed/eboot.h +0 -6
index 8297387c4676..1cf384ba1b0e 100644
@@ -18,12 +18,6 @@ typedef struct {
 	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 struct {
 	void *get_mode;
 	void *set_mode;
drivers/firmware/efi/libstub/random.c +0 -5
index 97378cf96a2e..3b85883fb312 100644
@@ -16,11 +16,6 @@ typedef struct {
 	u32 get_rng;
 } efi_rng_protocol_32_t;
 
-typedef struct {
-	u64 get_info;
-	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 *);
include/linux/efi.h +0 -142

202 lines not on this page · raw message

Signature · 1 line
2.17.1
author maintainer
#23 lore raw

PATCH 8/10 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               |  5 +++--
 drivers/firmware/efi/libstub/efi-stub-helper.c |  5 +++--
 drivers/firmware/efi/libstub/tpm.c             |  4 ++--
 include/linux/efi.h                            |  6 +++++-
 5 files changed, 19 insertions(+), 11 deletions(-)
arch/x86/boot/compressed/eboot.c +6 -4
index 6d833f93a878..052852eaa076 100644
@@ -64,7 +64,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;
@@ -191,9 +192,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;
 
@@ -208,7 +209,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 +3 -2
index 1cf384ba1b0e..318531f128c2 100644
@@ -18,8 +18,9 @@ typedef struct {
 	u32 blt;
 } efi_uga_draw_protocol_32_t;
 
-typedef struct {
-	void *get_mode;
+typedef struct efi_uga_draw_protocol {
+	efi_status_t (*get_mode)(struct efi_uga_draw_protocol *,
+				 u32*, u32*, u32*, u32*);
 	void *set_mode;
 	void *blt;
 } efi_uga_draw_protocol_t;
drivers/firmware/efi/libstub/efi-stub-helper.c +3 -2
index ff3266b9f673..5bb1715cb154 100644
@@ -423,12 +423,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 0354d35ebaa7..83cf204bfe8f 100644
@@ -59,11 +59,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 87c2537b4543..7f1a99bd2c05 100644
@@ -399,7 +399,11 @@ struct efi_pci_io_protocol {
 	void *allocate_buffer;
 	void *free_buffer;
 	void *flush;
-	void *get_location;
+	efi_status_t (*get_location)(struct efi_pci_io_protocol *,
+				     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
#24 lore raw

PATCH 9/10 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 |   8 +-
 include/linux/efi.h                   | 130 +++++++++++---------
 4 files changed, 79 insertions(+), 64 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 318531f128c2..512d2143a94f 100644
@@ -19,8 +19,8 @@ typedef struct {
 } efi_uga_draw_protocol_32_t;
 
 typedef struct efi_uga_draw_protocol {
-	efi_status_t (*get_mode)(struct efi_uga_draw_protocol *,
-				 u32*, u32*, u32*, u32*);
+	efi_status_t (__efiapi *get_mode)(struct efi_uga_draw_protocol *,
+					  u32*, u32*, u32*, u32*);
 	void *set_mode;
 	void *blt;
 } efi_uga_draw_protocol_t;
drivers/firmware/efi/libstub/random.c +4 -4
index 3b85883fb312..872d2de3eaaf 100644
@@ -17,10 +17,10 @@ typedef struct {
 } efi_rng_protocol_32_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);
+	efi_status_t (__efiapi *get_info)(struct efi_rng_protocol *,
+					  unsigned long *, efi_guid_t *);
+	efi_status_t (__efiapi *get_rng)(struct efi_rng_protocol *,
+					 efi_guid_t *, unsigned long, u8 *out);
 };
 
 efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
include/linux/efi.h +72 -58

225 lines not on this page · raw message

Signature · 1 line
2.17.1
#25 lore raw

Re: [PATCH 09/10] efi/libstub: annotate firmware routines as __efiapi

Quoted 66 lines · 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 |   8 +-
 include/linux/efi.h                   | 130 +++++++++++---------
 4 files changed, 79 insertions(+), 64 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 318531f128c2..512d2143a94f 100644
@@ -19,8 +19,8 @@ typedef struct {
 } efi_uga_draw_protocol_32_t;
 
 typedef struct efi_uga_draw_protocol {
-       efi_status_t (*get_mode)(struct efi_uga_draw_protocol *,
-                                u32*, u32*, u32*, u32*);
+       efi_status_t (__efiapi *get_mode)(struct efi_uga_draw_protocol *,
+                                         u32*, u32*, u32*, u32*);
        void *set_mode;
        void *blt;
 } efi_uga_draw_protocol_t;
drivers/firmware/efi/libstub/random.c +4 -4
index 3b85883fb312..872d2de3eaaf 100644
@@ -17,10 +17,10 @@ typedef struct {
 } efi_rng_protocol_32_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);
+       efi_status_t (__efiapi *get_info)(struct efi_rng_protocol *,
+                                         unsigned long *, efi_guid_t *);
+       efi_status_t (__efiapi *get_rng)(struct efi_rng_protocol *,
+                                        efi_guid_t *, unsigned long, u8 *out);
 };
 
 efi_status_t efi_get_random_bytes(efi_system_table_t *sys_table_arg,
include/linux/efi.h +0 -0
index 7f1a99bd2c05..2ba13d901055 100644

include/linux/efi.h typedef u16 efi_char16_t; /* UNICODE character */ · line 54

Quoted 7 lines
include/linux/efi.h +4 -0
@@ -48,6 +48,12 @@ typedef u16 efi_char16_t;            /* UNICODE character */
 typedef u64 efi_physical_addr_t;
 typedef void *efi_handle_t;
 
+#ifdef CONFIG_X86_64
+#define __efiapi __attribute__((ms_abi))
+#else
+#define __efiapi

I think it would be better to explicitly use the regparm(0) attribute for 32-bit, even though that is currently the default.

--
Brian Gerst

author maintainer
#26 lore raw

PATCH 10/10 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             | 22 +++--
 arch/x86/platform/efi/efi_64.c         |  2 -
 7 files changed, 15 insertions(+), 121 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 +14 -8
index 2244232108a0..14a24bb01776 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;
 
@@ -232,7 +232,7 @@ static inline bool efi_is_native(void)
 #define efi_table_attr(table, attr, instance) ({			\
 	__typeof__(((table##_t *)0)->attr) __ret;			\
 	if (efi_is_native()) {						\
-		__ret = ((table##_t *)instance)->attr;			\
+		__ret = instance->attr;					\
 	} else {							\
 		__typeof__(((table##_32_t *)0)->attr) at;		\
 		at = (((table##_32_t *)(unsigned long)instance)->attr);	\
@@ -242,19 +242,25 @@ static inline bool efi_is_native(void)
 })
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? instance->f(instance, ##__VA_ARGS__)			\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(protocol, f, instance),	\
-		instance, ##__VA_ARGS__)
+			instance, ##__VA_ARGS__)
 
 #define efi_call_early(f, ...)						\
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? __efi_early()->boot_services->f(__VA_ARGS__)		\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(efi_boot_services, f,	\
-		__efi_early()->boot_services), __VA_ARGS__)
+			__efi_early()->boot_services), __VA_ARGS__)
 
 #define efi_call_runtime(f, ...)					\
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(efi_runtime_services, f,	\
-		__efi_early()->runtime_services), __VA_ARGS__)
+			__efi_early()->runtime_services), __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
#27 lore raw

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

arch/x86/include/asm/efi.h static inline bool efi_is_native(void) · line 238

Quoted 10 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +1 -1
@@ -232,7 +232,7 @@ static inline bool efi_is_native(void)
 #define efi_table_attr(table, attr, instance) ({			\
 	__typeof__(((table##_t *)0)->attr) __ret;			\
 	if (efi_is_native()) {						\
-		__ret = ((table##_t *)instance)->attr;			\
+		__ret = instance->attr;					\
 	} else {							\
 		__typeof__(((table##_32_t *)0)->attr) at;		\
 		at = (((table##_32_t *)(unsigned long)instance)->attr);	\

Is there a reason we didn't remove this cast for native-mode earlier in the series?

Quoted 22 lines
arch/x86/include/asm/efi.h +8 -4
@@ -242,19 +242,25 @@ static inline bool efi_is_native(void)
 })
 
 #define efi_call_proto(protocol, f, instance, ...)			\
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? instance->f(instance, ##__VA_ARGS__)			\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(protocol, f, instance),	\
-		instance, ##__VA_ARGS__)
+			instance, ##__VA_ARGS__)
 
 #define efi_call_early(f, ...)						\
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? __efi_early()->boot_services->f(__VA_ARGS__)		\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(efi_boot_services, f,	\
-		__efi_early()->boot_services), __VA_ARGS__)
+			__efi_early()->boot_services), __VA_ARGS__)
 
 #define efi_call_runtime(f, ...)					\

arch/x86/include/asm/efi.h static inline bool efi_is_native(void) · line 266

Quoted 10 lines
arch/x86/include/asm/efi.h +4 -2
-	__efi_early()->call((unsigned long)				\
+	efi_is_native()							\
+		? __efi_early()->runtime_services->f(__VA_ARGS__)	\
+		: efi64_thunk((unsigned long)				\
 				efi_table_attr(efi_runtime_services, f,	\
-		__efi_early()->runtime_services), __VA_ARGS__)
+			__efi_early()->runtime_services), __VA_ARGS__)
 
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);

For the efi_call macros, their definition should be enclosed in parentheses now that it's a ternary operator.

#28 lore raw

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

Quoted 16 lines · Arvind Sankar

arch/x86/include/asm/efi.h static inline bool efi_is_native(void) · line 238

Quoted 10 lines · Ard Biesheuvel
arch/x86/include/asm/efi.h +1 -1
@@ -232,7 +232,7 @@ static inline bool efi_is_native(void)
 #define efi_table_attr(table, attr, instance) ({                     \
      __typeof__(((table##_t *)0)->attr) __ret;                       \
      if (efi_is_native()) {                                          \
-             __ret = ((table##_t *)instance)->attr;                  \
+             __ret = instance->attr;                                 \
      } else {                                                        \
              __typeof__(((table##_32_t *)0)->attr) at;               \
              at = (((table##_32_t *)(unsigned long)instance)->attr); \

Is there a reason we didn't remove this cast for native-mode earlier in the series?

Yes. In patch 9/10, I fix a couple of occurrences where the protocol pointer is a void*, so without the cast, things break.

Quoted 30 lines
Quoted 30 lines
arch/x86/include/asm/efi.h +12 -6
@@ -242,19 +242,25 @@ static inline bool efi_is_native(void)
 })
 
 #define efi_call_proto(protocol, f, instance, ...)                   \
-     __efi_early()->call((unsigned long)                             \
+     efi_is_native()                                                 \
+             ? instance->f(instance, ##__VA_ARGS__)                  \
+             : efi64_thunk((unsigned long)                           \
                              efi_table_attr(protocol, f, instance),  \
-             instance, ##__VA_ARGS__)
+                     instance, ##__VA_ARGS__)
 
 #define efi_call_early(f, ...)                                               \
-     __efi_early()->call((unsigned long)                             \
+     efi_is_native()                                                 \
+             ? __efi_early()->boot_services->f(__VA_ARGS__)          \
+             : efi64_thunk((unsigned long)                           \
                              efi_table_attr(efi_boot_services, f,    \
-             __efi_early()->boot_services), __VA_ARGS__)
+                     __efi_early()->boot_services), __VA_ARGS__)
 
 #define efi_call_runtime(f, ...)                                     \
-     __efi_early()->call((unsigned long)                             \
+     efi_is_native()                                                 \
+             ? __efi_early()->runtime_services->f(__VA_ARGS__)       \
+             : efi64_thunk((unsigned long)                           \
                              efi_table_attr(efi_runtime_services, f, \
-             __efi_early()->runtime_services), __VA_ARGS__)
+                     __efi_early()->runtime_services), __VA_ARGS__)
 
Quoted 5 lines

arch/x86/include/asm/efi.h static inline bool efi_is_native(void) · line 266

Quoted 2 lines
arch/x86/include/asm/efi.h +0 -0
 extern bool efi_reboot_required(void);
 extern bool efi_is_table_address(unsigned long phys_addr);

For the efi_call macros, their definition should be enclosed in parentheses now that it's a ternary operator.

Ack