linux-efi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor
@ 2025-01-08 18:22 Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 1/6] x86/efi/mixed: Check CPU compatibility without relying on verify_cpu() Ard Biesheuvel
                   ` (6 more replies)
  0 siblings, 7 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

Since commit

  a1b87d54f4e4 ("x86/efistub: Avoid legacy decompressor when doing EFI boot")

booting via the EFI stub no longer relies on the legacy decompressor,
and instead, the kernel proper is decompressed by code executing in the
context of the EFI boot services, and subsequently invoked directly.

The only remaining dependency is the EFI mixed mode startup code, which
makes a detour via the legacy decompressor's 32-bit entrypoint, in order
to obtain a 1:1 mapping of memory, which is a prerequisite for 64-bit
execution on x86.

This detour requires some fiddly setup on the part of the mixed mode
startup code, which has to stash the firmware stack pointer and boot
arguments in memory, and create a fake struct boot_params to trick the
code in startup_32 to behave as intended.

This dependency also impedes reuse of the EFI stub code in other
contexts, such as generic EFI zboot, which will reuse the EFI stub but
not the legacy decompressor.

So remove this dependency, by replacing this detour with a minimal
reimplementation of the 1:1 mapping code. With some further cleanup
applied on top, the line count barely changes, but the resulting code
can operate independently from the legacy decompressor, and is therefore
moved out of arch/x86/boot/compressed and into the EFI libstub/
directory.

Ard Biesheuvel (6):
  x86/efi/mixed: Check CPU compatibility without relying on verify_cpu()
  x86/efi/mixed: Remove dependency on legacy startup_32 code
  x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors
  x86/efi/mixed: Simplify and document thunking logic
  x86/efi/mixed: Reduce padding by moving some code around
  x86/efi/mixed: Move mixed mode startup code into libstub

 arch/x86/boot/compressed/Makefile                                                |   1 -
 arch/x86/boot/compressed/head_64.S                                               |   7 -
 drivers/firmware/efi/libstub/Makefile                                            |   3 +
 arch/x86/boot/compressed/efi_mixed.S => drivers/firmware/efi/libstub/x86-mixed.S | 320 ++++++++++----------
 4 files changed, 169 insertions(+), 162 deletions(-)
 rename arch/x86/boot/compressed/efi_mixed.S => drivers/firmware/efi/libstub/x86-mixed.S (54%)

-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/6] x86/efi/mixed: Check CPU compatibility without relying on verify_cpu()
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 2/6] x86/efi/mixed: Remove dependency on legacy startup_32 code Ard Biesheuvel
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

In order for the EFI mixed mode startup code to be reusable in a context
where the legacy decompressor is not used, replace the call to
verify_cpu() [which performs an elaborate set of checks] with a simple
check against the 'long mode' bit in the appropriate CPUID leaf.

This is reasonable, given that EFI support is implied when booting in
this manner, and so there is no need to consider very old CPUs when
performing this check.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/efi_mixed.S | 22 ++++++++------------
 1 file changed, 9 insertions(+), 13 deletions(-)

diff --git a/arch/x86/boot/compressed/efi_mixed.S b/arch/x86/boot/compressed/efi_mixed.S
index 876fc6d46a13..067e10eb7897 100644
--- a/arch/x86/boot/compressed/efi_mixed.S
+++ b/arch/x86/boot/compressed/efi_mixed.S
@@ -293,24 +293,20 @@ SYM_FUNC_END(efi32_entry)
  *			       efi_system_table_32_t *sys_table)
  */
 SYM_FUNC_START(efi32_pe_entry)
-	pushl	%ebp
-	movl	%esp, %ebp
 	pushl	%ebx				// save callee-save registers
-	pushl	%edi
-
-	call	verify_cpu			// check for long mode support
-	testl	%eax, %eax
-	movl	$0x80000003, %eax		// EFI_UNSUPPORTED
-	jnz	2f
 
-	movl	8(%ebp), %ecx			// image_handle
-	movl	12(%ebp), %edx			// sys_table
+	/* Check whether the CPU supports long mode */
+	movl	$0x80000001, %eax		// assume extended info support
+	cpuid
+	btl	$29, %edx			// check long mode bit
+	jnc	1f
+	leal	8(%esp), %esp			// preserve stack alignment
+	movl	(%esp), %ecx			// image_handle
+	movl	4(%esp), %edx			// sys_table
 	jmp	efi32_entry			// pass %ecx, %edx
 						// no other registers remain live
-
-2:	popl	%edi				// restore callee-save registers
+1:	movl	$0x80000003, %eax		// EFI_UNSUPPORTED
 	popl	%ebx
-	leave
 	RET
 SYM_FUNC_END(efi32_pe_entry)
 
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/6] x86/efi/mixed: Remove dependency on legacy startup_32 code
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 1/6] x86/efi/mixed: Check CPU compatibility without relying on verify_cpu() Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 3/6] x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors Ard Biesheuvel
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

The EFI mixed mode startup code calls into startup_32 in the legacy
decompressor with a mocked up boot_params struct, only to get it to set
up the 1:1 mapping of the lower 4 GiB of memory and switch to a GDT that
supports 64-bit mode.

In order to be able to reuse the EFI mixed mode startup code in EFI
zboot images, which do not incorporate the legacy decompressor code,
decouple it, by building the 1:1 map and installing the GDT directly.

This also removes the need to preserve and restore the stack pointer and
the arguments passed to the entrypoint, and instead, the firmware stack
can be retained, and the arguments can be loaded from it at the point
where they are needed, i.e., before calling into the EFI stub C code.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/efi_mixed.S | 143 ++++++++++++--------
 arch/x86/boot/compressed/head_64.S   |   7 -
 2 files changed, 86 insertions(+), 64 deletions(-)

diff --git a/arch/x86/boot/compressed/efi_mixed.S b/arch/x86/boot/compressed/efi_mixed.S
index 067e10eb7897..c010aba7a0d0 100644
--- a/arch/x86/boot/compressed/efi_mixed.S
+++ b/arch/x86/boot/compressed/efi_mixed.S
@@ -15,15 +15,18 @@
  */
 
 #include <linux/linkage.h>
-#include <asm/asm-offsets.h>
 #include <asm/msr.h>
 #include <asm/page_types.h>
+#include <asm/pgtable_types.h>
 #include <asm/processor-flags.h>
 #include <asm/segment.h>
-#include <asm/setup.h>
 
 	.code64
 	.text
+	.balign	8
+SYM_DATA_LOCAL(gdt, .quad 0x0, 0x0, 0xaf9a000000ffff)	/* __KERNEL_CS */
+	.set	gdt_size, . - gdt
+
 /*
  * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode()
  * is the first thing that runs after switching to long mode. Depending on
@@ -35,30 +38,34 @@
  * pointer is used to disambiguate.
  *
  *                                                             +--------------+
- *  +------------------+     +------------+            +------>| efi_pe_entry |
- *  | efi32_pe_entry   |---->|            |            |       +-----------+--+
- *  +------------------+     |            |     +------+----------------+  |
- *                           | startup_32 |---->| startup_64_mixed_mode |  |
- *  +------------------+     |            |     +------+----------------+  |
- *  | efi32_stub_entry |---->|            |            |                   |
- *  +------------------+     +------------+            |                   |
+ *  +------------------+    +-------------+            +------>| efi_pe_entry |
+ *  | efi32_pe_entry   |--->|             |            |       +-----------+--+
+ *  +------------------+    |             |     +------+----------------+  |
+ *                          | efi32_entry |---->| startup_64_mixed_mode |  |
+ *  +------------------+    |             |     +------+----------------+  |
+ *  | efi32_stub_entry |--->|             |            |                   |
+ *  +------------------+    +-------------+            |                   |
  *                                                     V                   |
- *                           +------------+     +----------------+         |
- *                           | startup_64 |<----| efi_stub_entry |<--------+
- *                           +------------+     +----------------+
+ *                          +-------------+     +----------------+         |
+ *                          |   vmlinux   |<----| efi_stub_entry |<--------+
+ *                          +-------------+     +----------------+
  */
-SYM_FUNC_START(startup_64_mixed_mode)
-	lea	efi32_boot_args(%rip), %rdx
-	mov	0(%rdx), %edi
-	mov	4(%rdx), %esi
+SYM_FUNC_START_LOCAL_NOALIGN(startup_64_mixed_mode)
+	xorl	%eax, %eax
+	movl	%eax, %ds
+	movl	%eax, %es
+	movl	%eax, %ss
+	movl	%eax, %fs
+	movl	%eax, %gs
 
-	/* Switch to the firmware's stack */
-	movl	efi32_boot_sp(%rip), %esp
-	andl	$~7, %esp
+	movl	0(%rsp), %ecx		// MS calling convention
+	movl	4(%rsp), %edx
 
 #ifdef CONFIG_EFI_HANDOVER_PROTOCOL
-	mov	8(%rdx), %edx		// saved bootparams pointer
-	test	%edx, %edx
+	test	%edi, %edi		// struct boot_params provided?
+	movl	%edx, %esi		// SysV calling convention
+	cmovnzl	%edi, %edx
+	movl	%ecx, %edi
 	jnz	efi_stub_entry
 #endif
 	/*
@@ -69,8 +76,6 @@ SYM_FUNC_START(startup_64_mixed_mode)
 	 * the correct stack alignment for entry.
 	 */
 	sub	$40, %rsp
-	mov	%rdi, %rcx		// MS calling convention
-	mov	%rsi, %rdx
 	jmp	efi_pe_entry
 SYM_FUNC_END(startup_64_mixed_mode)
 
@@ -151,7 +156,6 @@ SYM_FUNC_END(__efi64_thunk)
 SYM_FUNC_START(efi32_stub_entry)
 	call	1f
 1:	popl	%ecx
-	leal	(efi32_boot_args - 1b)(%ecx), %ebx
 
 	/* Clear BSS */
 	xorl	%eax, %eax
@@ -163,10 +167,7 @@ SYM_FUNC_START(efi32_stub_entry)
 	rep	stosl
 
 	add	$0x4, %esp		/* Discard return address */
-	popl	%ecx
-	popl	%edx
-	popl	%esi
-	movl	%esi, 8(%ebx)
+	movl	8(%esp), %edi		/* struct boot_params pointer */
 	jmp	efi32_entry
 SYM_FUNC_END(efi32_stub_entry)
 #endif
@@ -241,8 +242,9 @@ SYM_FUNC_END(efi_enter32)
 /*
  * This is the common EFI stub entry point for mixed mode.
  *
- * Arguments:	%ecx	image handle
- * 		%edx	EFI system table pointer
+ * Arguments:	0(%esp)	image handle
+ * 		4(%esp)	EFI system table pointer
+ *		%edi	struct boot_params pointer (or NULL)
  *
  * Since this is the point of no return for ordinary execution, no registers
  * are considered live except for the function parameters. [Note that the EFI
@@ -261,31 +263,58 @@ SYM_FUNC_START_LOCAL(efi32_entry)
 	/* Store firmware IDT descriptor */
 	sidtl	(efi32_boot_idt - 1b)(%ebx)
 
-	/* Store firmware stack pointer */
-	movl	%esp, (efi32_boot_sp - 1b)(%ebx)
-
-	/* Store boot arguments */
-	leal	(efi32_boot_args - 1b)(%ebx), %ebx
-	movl	%ecx, 0(%ebx)
-	movl	%edx, 4(%ebx)
-	movb	$0x0, 12(%ebx)          // efi_is64
-
-	/*
-	 * Allocate some memory for a temporary struct boot_params, which only
-	 * needs the minimal pieces that startup_32() relies on.
-	 */
-	subl	$PARAM_SIZE, %esp
-	movl	%esp, %esi
-	movl	$PAGE_SIZE, BP_kernel_alignment(%esi)
-	movl	$_end - 1b, BP_init_size(%esi)
-	subl	$startup_32 - 1b, BP_init_size(%esi)
+	/* Record mixed mode entry */
+	movb	$0x0, (efi_is64 - 1b)(%ebx)
 
 	/* Disable paging */
 	movl	%cr0, %eax
 	btrl	$X86_CR0_PG_BIT, %eax
 	movl	%eax, %cr0
 
-	jmp	startup_32
+	/* Set up 1:1 mapping */
+	leal	(pte - 1b)(%ebx), %eax
+	movl	$_PAGE_PRESENT | _PAGE_RW | _PAGE_PSE, %ecx
+	leal	(_PAGE_PRESENT | _PAGE_RW)(%eax), %edx
+2:	movl	%ecx, (%eax)
+	addl	$8, %eax
+	addl	$PMD_SIZE, %ecx
+	jnc	2b
+
+	xor	%ecx, %ecx
+	movl	$PAGE_SIZE, %esi
+3:	movl	%edx, (%eax,%ecx,8)
+	addl	%esi, %edx
+	inc	%ecx
+	cmp	$4, %ecx
+	jl	3b
+
+	addl	%esi, %eax
+	movl	%edx, (%eax)
+	movl	%eax, %cr3
+
+	movl	%cr4, %eax
+	orl	$X86_CR4_PAE, %eax
+	movl	%eax, %cr4
+
+	movl	$MSR_EFER, %ecx
+	rdmsr
+	btsl	$_EFER_LME, %eax
+	wrmsr
+
+	leal	(gdt - 1b)(%ebx), %ecx
+	pushl	%ecx
+	pushw	$gdt_size - 1
+	lgdtl	(%esp)
+	lea	6(%esp), %esp
+
+	/* Enable paging and jump to long mode */
+	leal	(startup_64_mixed_mode - 1b)(%ebx), %ecx
+	pushl	$__KERNEL_CS
+	pushl	%ecx
+	movl	%cr0, %eax
+	btsl	$X86_CR0_PG_BIT, %eax
+	movl	%eax, %cr0
+	lret
 SYM_FUNC_END(efi32_entry)
 
 /*
@@ -301,10 +330,8 @@ SYM_FUNC_START(efi32_pe_entry)
 	btl	$29, %edx			// check long mode bit
 	jnc	1f
 	leal	8(%esp), %esp			// preserve stack alignment
-	movl	(%esp), %ecx			// image_handle
-	movl	4(%esp), %edx			// sys_table
-	jmp	efi32_entry			// pass %ecx, %edx
-						// no other registers remain live
+	xor	%edi, %edi			// no struct boot_params in EDI
+	jmp	efi32_entry			// only ESP and EDI remain live
 1:	movl	$0x80000003, %eax		// EFI_UNSUPPORTED
 	popl	%ebx
 	RET
@@ -318,8 +345,10 @@ SYM_FUNC_START_NOALIGN(efi64_stub_entry)
 SYM_FUNC_END(efi64_stub_entry)
 #endif
 
-	.data
-	.balign	8
+	.bss
+	.balign PAGE_SIZE
+SYM_DATA_LOCAL(pte, .fill 6 * PAGE_SIZE, 1, 0)
+
 SYM_DATA_START_LOCAL(efi32_boot_gdt)
 	.word	0
 	.quad	0
@@ -330,8 +359,8 @@ SYM_DATA_START_LOCAL(efi32_boot_idt)
 	.quad	0
 SYM_DATA_END(efi32_boot_idt)
 
+	.data
+	.balign	4
 SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
 SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
-SYM_DATA_LOCAL(efi32_boot_sp, .long 0)
-SYM_DATA_LOCAL(efi32_boot_args, .long 0, 0, 0)
 SYM_DATA(efi_is64, .byte 1)
diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S
index 1dcb794c5479..5db6495a3bb9 100644
--- a/arch/x86/boot/compressed/head_64.S
+++ b/arch/x86/boot/compressed/head_64.S
@@ -263,13 +263,6 @@ SYM_FUNC_START(startup_32)
 	 * used to perform that far jump.
 	 */
 	leal	rva(startup_64)(%ebp), %eax
-#ifdef CONFIG_EFI_MIXED
-	cmpb	$1, rva(efi_is64)(%ebp)
-	je	1f
-	leal	rva(startup_64_mixed_mode)(%ebp), %eax
-1:
-#endif
-
 	pushl	$__KERNEL_CS
 	pushl	%eax
 
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 3/6] x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 1/6] x86/efi/mixed: Check CPU compatibility without relying on verify_cpu() Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 2/6] x86/efi/mixed: Remove dependency on legacy startup_32 code Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 4/6] x86/efi/mixed: Simplify and document thunking logic Ard Biesheuvel
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

Commit

  e10848a26a96 ("x86/efi: Preserve segment registers in mixed mode")

added code to preserve and restore the caller's data segment selector
values when temporarily switching back to the firmware's GDT and segment
layout to perform an EFI runtime call.

At that point, this code was shared between the boot-time and runtime
mixed mode implementations, and the issue this commit aimed to address
only affected the latter.

Since commit

  96738c69a7fc ("x86/efi: Avoid triple faults during EFI mixed mode calls")

the runtime version of the mixed mode handling is entirely separate, and
relies on IA-32e compatibility mode to call into the 32-bit firmware
while retaining the OS's GDT/IDT and segment layout.

At boot-time, preserving/restoring the values is pointless, and the data
segment selectors can now simply be zeroed after (re)entering 64-bit
mode.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/efi_mixed.S | 24 ++++++--------------
 1 file changed, 7 insertions(+), 17 deletions(-)

diff --git a/arch/x86/boot/compressed/efi_mixed.S b/arch/x86/boot/compressed/efi_mixed.S
index c010aba7a0d0..066f4365af4f 100644
--- a/arch/x86/boot/compressed/efi_mixed.S
+++ b/arch/x86/boot/compressed/efi_mixed.S
@@ -83,17 +83,10 @@ SYM_FUNC_START(__efi64_thunk)
 	push	%rbp
 	push	%rbx
 
-	movl	%ds, %eax
-	push	%rax
-	movl	%es, %eax
-	push	%rax
-	movl	%ss, %eax
-	push	%rax
-
 	/* Copy args passed on stack */
-	movq	0x30(%rsp), %rbp
-	movq	0x38(%rsp), %rbx
-	movq	0x40(%rsp), %rax
+	movq	0x18(%rsp), %rbp
+	movq	0x20(%rsp), %rbx
+	movq	0x28(%rsp), %rax
 
 	/*
 	 * Convert x86-64 ABI params to i386 ABI
@@ -135,14 +128,11 @@ SYM_FUNC_START(__efi64_thunk)
 1:	addq	$64, %rsp
 	movq	%rdi, %rax
 
-	pop	%rbx
-	movl	%ebx, %ss
-	pop	%rbx
-	movl	%ebx, %es
-	pop	%rbx
-	movl	%ebx, %ds
-	/* Clear out 32-bit selector from FS and GS */
+	/* Clear out 32-bit segment selectors */
 	xorl	%ebx, %ebx
+	movl	%ebx, %ds
+	movl	%ebx, %es
+	movl	%ebx, %ss
 	movl	%ebx, %fs
 	movl	%ebx, %gs
 
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 4/6] x86/efi/mixed: Simplify and document thunking logic
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
                   ` (2 preceding siblings ...)
  2025-01-08 18:22 ` [PATCH 3/6] x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 5/6] x86/efi/mixed: Reduce padding by moving some code around Ard Biesheuvel
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

The current boot-time version of the thunk into 32-bit mode, to invoke
EFI services and protocols using the 32-bit calling convention, is a
jumble of argument marshalling code and GDT/IDT and segment register
handling, with an undocumented de facto calling convention that passes
the data segment descriptor, the return address and the GDT/IDT base
pointer for the return in registers EDX, EBP and EBX respectively.

Let's clean this up, and replace this with documented logic that
separates the handling of the segment registers into the 64-bit caller,
and the marshalling of the arguments into the 32-bit callee. Also,
replace the open coded far call with an actual one, removing the need to
pass the return address via a register.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/efi_mixed.S | 92 ++++++++++----------
 1 file changed, 44 insertions(+), 48 deletions(-)

diff --git a/arch/x86/boot/compressed/efi_mixed.S b/arch/x86/boot/compressed/efi_mixed.S
index 066f4365af4f..a44e522bbbed 100644
--- a/arch/x86/boot/compressed/efi_mixed.S
+++ b/arch/x86/boot/compressed/efi_mixed.S
@@ -80,52 +80,36 @@ SYM_FUNC_START_LOCAL_NOALIGN(startup_64_mixed_mode)
 SYM_FUNC_END(startup_64_mixed_mode)
 
 SYM_FUNC_START(__efi64_thunk)
-	push	%rbp
 	push	%rbx
 
-	/* Copy args passed on stack */
-	movq	0x18(%rsp), %rbp
-	movq	0x20(%rsp), %rbx
-	movq	0x28(%rsp), %rax
-
-	/*
-	 * Convert x86-64 ABI params to i386 ABI
-	 */
-	subq	$64, %rsp
-	movl	%esi, 0x0(%rsp)
-	movl	%edx, 0x4(%rsp)
-	movl	%ecx, 0x8(%rsp)
-	movl	%r8d, 0xc(%rsp)
-	movl	%r9d, 0x10(%rsp)
-	movl	%ebp, 0x14(%rsp)
-	movl	%ebx, 0x18(%rsp)
-	movl	%eax, 0x1c(%rsp)
-
-	leaq	0x20(%rsp), %rbx
-	sgdt	(%rbx)
-	sidt	16(%rbx)
-
-	leaq	1f(%rip), %rbp
+	/* Store live GDT and IDT descriptors */
+	subq	$16, %rsp
+	sgdt	(%rsp)
+	sidt	6(%rsp)
 
 	/*
 	 * Switch to IDT and GDT with 32-bit segments. These are the firmware
 	 * GDT and IDT that were installed when the kernel started executing.
 	 * The pointers were saved by the efi32_entry() routine below.
-	 *
-	 * Pass the saved DS selector to the 32-bit code, and use far return to
-	 * restore the saved CS selector.
 	 */
 	lidt	efi32_boot_idt(%rip)
 	lgdt	efi32_boot_gdt(%rip)
 
-	movzwl	efi32_boot_ds(%rip), %edx
-	movzwq	efi32_boot_cs(%rip), %rax
-	pushq	%rax
-	leaq	efi_enter32(%rip), %rax
-	pushq	%rax
-	lretq
+	/* Reload firmware's data segment selectors */
+	movw	efi32_boot_ds(%rip), %bx
+	movl	%ebx, %ds
+	movl	%ebx, %es
+	movl	%ebx, %ss
+	movl	%ebx, %fs
+	movl	%ebx, %gs
+
+	/* Move args #5 and #6 into 32-bit accessible registers */
+	movl	%r8d, %eax
+	movl	%r9d, %ebx
 
-1:	addq	$64, %rsp
+	lcalll	*efi32_thunk(%rip)
+
+	addq	$16, %rsp
 	movq	%rdi, %rax
 
 	/* Clear out 32-bit segment selectors */
@@ -137,7 +121,6 @@ SYM_FUNC_START(__efi64_thunk)
 	movl	%ebx, %gs
 
 	pop	%rbx
-	pop	%rbp
 	RET
 SYM_FUNC_END(__efi64_thunk)
 
@@ -163,17 +146,27 @@ SYM_FUNC_END(efi32_stub_entry)
 #endif
 
 /*
- * EFI service pointer must be in %edi.
+ * Called using a far call from 64-bit code, using the x86_64 SysV ABI (except
+ * for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are used instead).
+ *
+ * The first argument (EDI) is a pointer to the boot service or protocol, to
+ * which the remaining arguments are passed, each truncated to 32 bits.
  *
- * The stack should represent the 32-bit calling convention.
+ * Entered with ESP+40 pointing to the arguments passed via the stack, and with
+ * the 64-bit mode GDT and IDT descriptors at ESP+8 and ESP+14, respectively.
  */
 SYM_FUNC_START_LOCAL(efi_enter32)
-	/* Load firmware selector into data and stack segment registers */
-	movl	%edx, %ds
-	movl	%edx, %es
-	movl	%edx, %fs
-	movl	%edx, %gs
-	movl	%edx, %ss
+	/*
+	 * Convert x86-64 SysV ABI params to i386 ABI
+	 */
+	pushl	56(%esp)	/* Up to 3 args passed via the caller's stack */
+	pushl	52(%esp)
+	pushl	48(%esp)
+	pushl	%ebx		/* R9 */
+	pushl	%eax		/* R8 */
+	pushl	%ecx
+	pushl	%edx
+	pushl	%esi
 
 	/* Reload pgtables */
 	movl	%cr3, %eax
@@ -201,8 +194,9 @@ SYM_FUNC_START_LOCAL(efi_enter32)
 	 */
 	cli
 
-	lidtl	16(%ebx)
-	lgdtl	(%ebx)
+	addl	$32, %esp
+	lidtl	14(%esp)
+	lgdtl	8(%esp)
 
 	movl	%cr4, %eax
 	btsl	$(X86_CR4_PAE_BIT), %eax
@@ -219,9 +213,6 @@ SYM_FUNC_START_LOCAL(efi_enter32)
 	xorl	%eax, %eax
 	lldt	%ax
 
-	pushl	$__KERNEL_CS
-	pushl	%ebp
-
 	/* Enable paging */
 	movl	%cr0, %eax
 	btsl	$X86_CR0_PG_BIT, %eax
@@ -250,6 +241,10 @@ SYM_FUNC_START_LOCAL(efi32_entry)
 	movw	%cs, (efi32_boot_cs - 1b)(%ebx)
 	movw	%ds, (efi32_boot_ds - 1b)(%ebx)
 
+	/* Fix up absolute reference */
+	leal	(efi32_thunk - 1b)(%ebx), %eax
+	addl	%eax, (%eax)
+
 	/* Store firmware IDT descriptor */
 	sidtl	(efi32_boot_idt - 1b)(%ebx)
 
@@ -351,6 +346,7 @@ SYM_DATA_END(efi32_boot_idt)
 
 	.data
 	.balign	4
+SYM_DATA_LOCAL(efi32_thunk, .long efi_enter32 - .)
 SYM_DATA_LOCAL(efi32_boot_cs, .word 0)
 SYM_DATA_LOCAL(efi32_boot_ds, .word 0)
 SYM_DATA(efi_is64, .byte 1)
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 5/6] x86/efi/mixed: Reduce padding by moving some code around
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
                   ` (3 preceding siblings ...)
  2025-01-08 18:22 ` [PATCH 4/6] x86/efi/mixed: Simplify and document thunking logic Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-01-08 18:22 ` [PATCH 6/6] x86/efi/mixed: Move mixed mode startup code into libstub Ard Biesheuvel
  2025-02-25 20:56 ` [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ingo Molnar
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

The 32-bit and 64-bit entrypoints for the EFI handover protocol need to
be exactly 0x200 bytes apart, and so the 64-bit version is placed at the
end, using padding to move it into place.

This padding can be reduced now, since the code in between has reduced
in size as well. So move efi32_stub_entry() to the beginning of the
source file.

Note that this code is outside of the compressed kernel image, and so
the padding is not compressed.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/efi_mixed.S | 51 ++++++++++----------
 1 file changed, 26 insertions(+), 25 deletions(-)

diff --git a/arch/x86/boot/compressed/efi_mixed.S b/arch/x86/boot/compressed/efi_mixed.S
index a44e522bbbed..b5a46a4ce083 100644
--- a/arch/x86/boot/compressed/efi_mixed.S
+++ b/arch/x86/boot/compressed/efi_mixed.S
@@ -21,12 +21,32 @@
 #include <asm/processor-flags.h>
 #include <asm/segment.h>
 
-	.code64
 	.text
 	.balign	8
 SYM_DATA_LOCAL(gdt, .quad 0x0, 0x0, 0xaf9a000000ffff)	/* __KERNEL_CS */
 	.set	gdt_size, . - gdt
 
+#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
+	.code32
+SYM_FUNC_START_NOALIGN(efi32_stub_entry)
+	call	1f
+1:	popl	%ecx
+
+	/* Clear BSS */
+	xorl	%eax, %eax
+	leal	(_bss - 1b)(%ecx), %edi
+	leal	(_ebss - 1b)(%ecx), %ecx
+	subl	%edi, %ecx
+	shrl	$2, %ecx
+	cld
+	rep	stosl
+
+	add	$0x4, %esp		/* Discard return address */
+	movl	8(%esp), %edi		/* struct boot_params pointer */
+	jmp	efi32_entry
+SYM_FUNC_END(efi32_stub_entry)
+#endif
+
 /*
  * When booting in 64-bit mode on 32-bit EFI firmware, startup_64_mixed_mode()
  * is the first thing that runs after switching to long mode. Depending on
@@ -50,6 +70,7 @@ SYM_DATA_LOCAL(gdt, .quad 0x0, 0x0, 0xaf9a000000ffff)	/* __KERNEL_CS */
  *                          |   vmlinux   |<----| efi_stub_entry |<--------+
  *                          +-------------+     +----------------+
  */
+	.code64
 SYM_FUNC_START_LOCAL_NOALIGN(startup_64_mixed_mode)
 	xorl	%eax, %eax
 	movl	%eax, %ds
@@ -79,7 +100,7 @@ SYM_FUNC_START_LOCAL_NOALIGN(startup_64_mixed_mode)
 	jmp	efi_pe_entry
 SYM_FUNC_END(startup_64_mixed_mode)
 
-SYM_FUNC_START(__efi64_thunk)
+SYM_FUNC_START_NOALIGN(__efi64_thunk)
 	push	%rbx
 
 	/* Store live GDT and IDT descriptors */
@@ -125,26 +146,6 @@ SYM_FUNC_START(__efi64_thunk)
 SYM_FUNC_END(__efi64_thunk)
 
 	.code32
-#ifdef CONFIG_EFI_HANDOVER_PROTOCOL
-SYM_FUNC_START(efi32_stub_entry)
-	call	1f
-1:	popl	%ecx
-
-	/* Clear BSS */
-	xorl	%eax, %eax
-	leal	(_bss - 1b)(%ecx), %edi
-	leal	(_ebss - 1b)(%ecx), %ecx
-	subl	%edi, %ecx
-	shrl	$2, %ecx
-	cld
-	rep	stosl
-
-	add	$0x4, %esp		/* Discard return address */
-	movl	8(%esp), %edi		/* struct boot_params pointer */
-	jmp	efi32_entry
-SYM_FUNC_END(efi32_stub_entry)
-#endif
-
 /*
  * Called using a far call from 64-bit code, using the x86_64 SysV ABI (except
  * for R8/R9 which are inaccessible to 32-bit code - EAX/EBX are used instead).
@@ -155,7 +156,7 @@ SYM_FUNC_END(efi32_stub_entry)
  * Entered with ESP+40 pointing to the arguments passed via the stack, and with
  * the 64-bit mode GDT and IDT descriptors at ESP+8 and ESP+14, respectively.
  */
-SYM_FUNC_START_LOCAL(efi_enter32)
+SYM_FUNC_START_LOCAL_NOALIGN(efi_enter32)
 	/*
 	 * Convert x86-64 SysV ABI params to i386 ABI
 	 */
@@ -232,7 +233,7 @@ SYM_FUNC_END(efi_enter32)
  * stub may still exit and return to the firmware using the Exit() EFI boot
  * service.]
  */
-SYM_FUNC_START_LOCAL(efi32_entry)
+SYM_FUNC_START_LOCAL_NOALIGN(efi32_entry)
 	call	1f
 1:	pop	%ebx
 
@@ -306,7 +307,7 @@ SYM_FUNC_END(efi32_entry)
  * efi_status_t efi32_pe_entry(efi_handle_t image_handle,
  *			       efi_system_table_32_t *sys_table)
  */
-SYM_FUNC_START(efi32_pe_entry)
+SYM_FUNC_START_NOALIGN(efi32_pe_entry)
 	pushl	%ebx				// save callee-save registers
 
 	/* Check whether the CPU supports long mode */
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 6/6] x86/efi/mixed: Move mixed mode startup code into libstub
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
                   ` (4 preceding siblings ...)
  2025-01-08 18:22 ` [PATCH 5/6] x86/efi/mixed: Reduce padding by moving some code around Ard Biesheuvel
@ 2025-01-08 18:22 ` Ard Biesheuvel
  2025-02-25 20:56 ` [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ingo Molnar
  6 siblings, 0 replies; 8+ messages in thread
From: Ard Biesheuvel @ 2025-01-08 18:22 UTC (permalink / raw)
  To: linux-efi; +Cc: x86, kees, Ard Biesheuvel

From: Ard Biesheuvel <ardb@kernel.org>

The EFI mixed mode code has been decoupled from the legacy decompressor,
in order to be able to reuse it with generic EFI zboot images for x86.

Move the source file into the libstub source directory to facilitate
this.

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
---
 arch/x86/boot/compressed/Makefile                                                | 1 -
 drivers/firmware/efi/libstub/Makefile                                            | 3 +++
 arch/x86/boot/compressed/efi_mixed.S => drivers/firmware/efi/libstub/x86-mixed.S | 0
 3 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index f2051644de94..fc5563704466 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -104,7 +104,6 @@ vmlinux-objs-$(CONFIG_INTEL_TDX_GUEST) += $(obj)/tdx.o $(obj)/tdcall.o $(obj)/td
 vmlinux-objs-$(CONFIG_UNACCEPTED_MEMORY) += $(obj)/mem.o
 
 vmlinux-objs-$(CONFIG_EFI) += $(obj)/efi.o
-vmlinux-objs-$(CONFIG_EFI_MIXED) += $(obj)/efi_mixed.o
 vmlinux-libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
 
 $(obj)/vmlinux: $(vmlinux-objs-y) $(vmlinux-libs-y) FORCE
diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile
index e04285a7a6b9..f8cbd4557131 100644
--- a/drivers/firmware/efi/libstub/Makefile
+++ b/drivers/firmware/efi/libstub/Makefile
@@ -62,6 +62,8 @@ KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO), $(KBUILD_CFLAGS))
 # `-fdata-sections` flag from KBUILD_CFLAGS_KERNEL
 KBUILD_CFLAGS_KERNEL := $(filter-out -fdata-sections, $(KBUILD_CFLAGS_KERNEL))
 
+KBUILD_AFLAGS			:= $(KBUILD_CFLAGS) -D__ASSEMBLY__
+
 lib-y				:= efi-stub-helper.o gop.o secureboot.o tpm.o \
 				   file.o mem.o random.o randomalloc.o pci.o \
 				   skip_spaces.o lib-cmdline.o lib-ctype.o \
@@ -83,6 +85,7 @@ lib-$(CONFIG_EFI_GENERIC_STUB)	+= efi-stub.o string.o intrinsics.o systable.o \
 lib-$(CONFIG_ARM)		+= arm32-stub.o
 lib-$(CONFIG_ARM64)		+= kaslr.o arm64.o arm64-stub.o smbios.o
 lib-$(CONFIG_X86)		+= x86-stub.o smbios.o
+lib-$(CONFIG_EFI_MIXED)		+= x86-mixed.o
 lib-$(CONFIG_X86_64)		+= x86-5lvl.o
 lib-$(CONFIG_RISCV)		+= kaslr.o riscv.o riscv-stub.o
 lib-$(CONFIG_LOONGARCH)		+= loongarch.o loongarch-stub.o
diff --git a/arch/x86/boot/compressed/efi_mixed.S b/drivers/firmware/efi/libstub/x86-mixed.S
similarity index 100%
rename from arch/x86/boot/compressed/efi_mixed.S
rename to drivers/firmware/efi/libstub/x86-mixed.S
-- 
2.47.1.613.gc27f4b7a9f-goog


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor
  2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
                   ` (5 preceding siblings ...)
  2025-01-08 18:22 ` [PATCH 6/6] x86/efi/mixed: Move mixed mode startup code into libstub Ard Biesheuvel
@ 2025-02-25 20:56 ` Ingo Molnar
  6 siblings, 0 replies; 8+ messages in thread
From: Ingo Molnar @ 2025-02-25 20:56 UTC (permalink / raw)
  To: Ard Biesheuvel; +Cc: linux-efi, x86, kees, Ard Biesheuvel


* Ard Biesheuvel <ardb+git@google.com> wrote:

> From: Ard Biesheuvel <ardb@kernel.org>
> 
> Since commit
> 
>   a1b87d54f4e4 ("x86/efistub: Avoid legacy decompressor when doing EFI boot")
> 
> booting via the EFI stub no longer relies on the legacy decompressor,
> and instead, the kernel proper is decompressed by code executing in the
> context of the EFI boot services, and subsequently invoked directly.
> 
> The only remaining dependency is the EFI mixed mode startup code, which
> makes a detour via the legacy decompressor's 32-bit entrypoint, in order
> to obtain a 1:1 mapping of memory, which is a prerequisite for 64-bit
> execution on x86.
> 
> This detour requires some fiddly setup on the part of the mixed mode
> startup code, which has to stash the firmware stack pointer and boot
> arguments in memory, and create a fake struct boot_params to trick the
> code in startup_32 to behave as intended.
> 
> This dependency also impedes reuse of the EFI stub code in other
> contexts, such as generic EFI zboot, which will reuse the EFI stub but
> not the legacy decompressor.
> 
> So remove this dependency, by replacing this detour with a minimal
> reimplementation of the 1:1 mapping code. With some further cleanup
> applied on top, the line count barely changes, but the resulting code
> can operate independently from the legacy decompressor, and is therefore
> moved out of arch/x86/boot/compressed and into the EFI libstub/
> directory.
> 
> Ard Biesheuvel (6):
>   x86/efi/mixed: Check CPU compatibility without relying on verify_cpu()
>   x86/efi/mixed: Remove dependency on legacy startup_32 code
>   x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors
>   x86/efi/mixed: Simplify and document thunking logic
>   x86/efi/mixed: Reduce padding by moving some code around
>   x86/efi/mixed: Move mixed mode startup code into libstub
> 
>  arch/x86/boot/compressed/Makefile                                                |   1 -
>  arch/x86/boot/compressed/head_64.S                                               |   7 -
>  drivers/firmware/efi/libstub/Makefile                                            |   3 +
>  arch/x86/boot/compressed/efi_mixed.S => drivers/firmware/efi/libstub/x86-mixed.S | 320 ++++++++++----------
>  4 files changed, 169 insertions(+), 162 deletions(-)
>  rename arch/x86/boot/compressed/efi_mixed.S => drivers/firmware/efi/libstub/x86-mixed.S (54%)

A belated:

    Acked-by: Ingo Molnar <mingo@kernel.org>

Thanks,

	Ingo

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2025-02-25 20:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-08 18:22 [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 1/6] x86/efi/mixed: Check CPU compatibility without relying on verify_cpu() Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 2/6] x86/efi/mixed: Remove dependency on legacy startup_32 code Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 3/6] x86/efi/mixed: Don't bother preserving 64-bit mode segment selectors Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 4/6] x86/efi/mixed: Simplify and document thunking logic Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 5/6] x86/efi/mixed: Reduce padding by moving some code around Ard Biesheuvel
2025-01-08 18:22 ` [PATCH 6/6] x86/efi/mixed: Move mixed mode startup code into libstub Ard Biesheuvel
2025-02-25 20:56 ` [PATCH 0/6] x86/efi/mixed: Decouple from legacy decompressor Ingo Molnar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).