Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b8ff87a6 authored by Matt Fleming's avatar Matt Fleming
Browse files

x86/efi: Firmware agnostic handover entry points



The EFI handover code only works if the "bitness" of the firmware and
the kernel match, i.e. 64-bit firmware and 64-bit kernel - it is not
possible to mix the two. This goes against the tradition that a 32-bit
kernel can be loaded on a 64-bit BIOS platform without having to do
anything special in the boot loader. Linux distributions, for one thing,
regularly run only 32-bit kernels on their live media.

Despite having only one 'handover_offset' field in the kernel header,
EFI boot loaders use two separate entry points to enter the kernel based
on the architecture the boot loader was compiled for,

    (1) 32-bit loader: handover_offset
    (2) 64-bit loader: handover_offset + 512

Since we already have two entry points, we can leverage them to infer
the bitness of the firmware we're running on, without requiring any boot
loader modifications, by making (1) and (2) valid entry points for both
CONFIG_X86_32 and CONFIG_X86_64 kernels.

To be clear, a 32-bit boot loader will always use (1) and a 64-bit boot
loader will always use (2). It's just that, if a single kernel image
supports (1) and (2) that image can be used with both 32-bit and 64-bit
boot loaders, and hence both 32-bit and 64-bit EFI.

(1) and (2) must be 512 bytes apart at all times, but that is already
part of the boot ABI and we could never change that delta without
breaking existing boot loaders anyhow.

Signed-off-by: default avatarMatt Fleming <matt.fleming@intel.com>
parent c116e8d6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -80,7 +80,7 @@ targets += voffset.h
$(obj)/voffset.h: vmlinux FORCE
	$(call if_changed,voffset)

sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi_pe_entry\|efi_stub_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'
sed-zoffset := -e 's/^\([0-9a-fA-F]*\) . \(startup_32\|startup_64\|efi32_stub_entry\|efi64_stub_entry\|efi_pe_entry\|input_data\|_end\|z_.*\)$$/\#define ZO_\2 0x\1/p'

quiet_cmd_zoffset = ZOFFSET $@
      cmd_zoffset = $(NM) $< | sed -n $(sed-zoffset) > $@
+6 −3
Original line number Diff line number Diff line
@@ -1256,12 +1256,13 @@ static efi_status_t alloc_e820ext(u32 nr_desc, struct setup_data **e820ext,
}

static efi_status_t exit_boot(struct boot_params *boot_params,
			      void *handle)
			      void *handle, bool is64)
{
	struct efi_info *efi = &boot_params->efi_info;
	unsigned long map_sz, key, desc_size;
	efi_memory_desc_t *mem_map;
	struct setup_data *e820ext;
	const char *signature;
	__u32 e820ext_size;
	__u32 nr_desc, prev_nr_desc;
	efi_status_t status;
@@ -1295,7 +1296,9 @@ static efi_status_t exit_boot(struct boot_params *boot_params,
		goto get_map; /* Allocated memory, get map again */
	}

	memcpy(&efi->efi_loader_signature, EFI_LOADER_SIGNATURE, sizeof(__u32));
	signature = is64 ? EFI64_LOADER_SIGNATURE : EFI32_LOADER_SIGNATURE;
	memcpy(&efi->efi_loader_signature, signature, sizeof(__u32));

	efi->efi_systab = (unsigned long)sys_table;
	efi->efi_memdesc_size = desc_size;
	efi->efi_memdesc_version = desc_version;
@@ -1408,7 +1411,7 @@ struct boot_params *efi_main(struct efi_config *c,
		hdr->code32_start = bzimage_addr;
	}

	status = exit_boot(boot_params, handle);
	status = exit_boot(boot_params, handle, is64);
	if (status != EFI_SUCCESS)
		goto fail;

+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ ENTRY(efi_pe_entry)
	pushl	%ecx
	jmp	2f		/* Skip efi_config initialization */

ENTRY(efi_stub_entry)
ENTRY(efi32_stub_entry)
	add	$0x4, %esp
	popl	%ecx
	popl	%edx
+54 −8
Original line number Diff line number Diff line
@@ -178,6 +178,13 @@ ENTRY(startup_32)
	 */
	pushl	$__KERNEL_CS
	leal	startup_64(%ebp), %eax
#ifdef CONFIG_EFI_MIXED
	movl	efi32_config(%ebp), %ebx
	cmp	$0, %ebx
	jz	1f
	leal	handover_entry(%ebp), %eax
1:
#endif
	pushl	%eax

	/* Enter paged protected Mode, activating Long Mode */
@@ -188,6 +195,30 @@ ENTRY(startup_32)
	lret
ENDPROC(startup_32)

#ifdef CONFIG_EFI_MIXED
	.org 0x190
ENTRY(efi32_stub_entry)
	add	$0x4, %esp		/* Discard return address */
	popl	%ecx
	popl	%edx
	popl	%esi

	leal	(BP_scratch+4)(%esi), %esp
	call	1f
1:	pop	%ebp
	subl	$1b, %ebp

	movl	%ecx, efi32_config(%ebp)
	movl	%edx, efi32_config+8(%ebp)
	sgdtl	efi32_boot_gdt(%ebp)

	leal	efi32_config(%ebp), %eax
	movl	%eax, efi_config(%ebp)

	jmp	startup_32
ENDPROC(efi32_stub_entry)
#endif

	.code64
	.org 0x200
ENTRY(startup_64)
@@ -231,13 +262,7 @@ ENTRY(efi_pe_entry)
	mov	%rax, %rsi
	jmp	2f		/* Skip the relocation */

ENTRY(efi_stub_entry)
	movq	%rdi, efi64_config(%rip)	/* Handle */
	movq	%rsi, efi64_config+8(%rip) /* EFI System table pointer */

	leaq	efi64_config(%rip), %rax
	movq	%rax, efi_config(%rip)

handover_entry:
	call	1f
1:	popq	%rbp
	subq	$1b, %rbp
@@ -247,7 +272,6 @@ ENTRY(efi_stub_entry)
	 */
	movq	efi_config(%rip), %rax
	addq	%rbp, 88(%rax)
	movq	%rdx, %rsi
2:
	movq	efi_config(%rip), %rdi
	call	efi_main
@@ -336,6 +360,20 @@ preferred_addr:
	leaq	relocated(%rbx), %rax
	jmp	*%rax

#ifdef CONFIG_EFI_STUB
	.org 0x390
ENTRY(efi64_stub_entry)
	movq	%rdi, efi64_config(%rip)	/* Handle */
	movq	%rsi, efi64_config+8(%rip) /* EFI System table pointer */

	leaq	efi64_config(%rip), %rax
	movq	%rax, efi_config(%rip)

	movq	%rdx, %rsi
	jmp	handover_entry
ENDPROC(efi64_stub_entry)
#endif

	.text
relocated:

@@ -404,6 +442,14 @@ gdt_end:
efi_config:
	.quad	0

#ifdef CONFIG_EFI_MIXED
	.global efi32_config
efi32_config:
	.fill	11,8,0
	.quad	efi64_thunk
	.byte	0
#endif

	.global efi64_config
efi64_config:
	.fill	11,8,0
+15 −7
Original line number Diff line number Diff line
@@ -53,7 +53,8 @@ int is_big_kernel;

#define PECOFF_RELOC_RESERVE 0x20

unsigned long efi_stub_entry;
unsigned long efi32_stub_entry;
unsigned long efi64_stub_entry;
unsigned long efi_pe_entry;
unsigned long startup_64;

@@ -231,20 +232,26 @@ static void efi_stub_defaults(void)
	/* Defaults for old kernel */
#ifdef CONFIG_X86_32
	efi_pe_entry = 0x10;
	efi_stub_entry = 0x30;
#else
	efi_pe_entry = 0x210;
	efi_stub_entry = 0x230;
	startup_64 = 0x200;
#endif
}

static void efi_stub_entry_update(void)
{
#ifdef CONFIG_X86_64 /* Yes, this is really how we defined it :( */
	efi_stub_entry -= 0x200;
	unsigned long addr = efi32_stub_entry;

#ifdef CONFIG_X86_64
	/* Yes, this is really how we defined it :( */
	addr = efi64_stub_entry - 0x200;
#endif

#ifdef CONFIG_EFI_MIXED
	if (efi32_stub_entry != addr)
		die("32-bit and 64-bit EFI entry points do not match\n");
#endif
	put_unaligned_le32(efi_stub_entry, &buf[0x264]);
	put_unaligned_le32(addr, &buf[0x264]);
}

#else
@@ -289,7 +296,8 @@ static void parse_zoffset(char *fname)
	p = (char *)buf;

	while (p && *p) {
		PARSE_ZOFS(p, efi_stub_entry);
		PARSE_ZOFS(p, efi32_stub_entry);
		PARSE_ZOFS(p, efi64_stub_entry);
		PARSE_ZOFS(p, efi_pe_entry);
		PARSE_ZOFS(p, startup_64);

Loading