diff options
author | Matt Fleming <matt.fleming@intel.com> | 2014-01-10 15:54:31 +0000 |
---|---|---|
committer | Matt Fleming <matt.fleming@intel.com> | 2014-03-04 21:25:06 +0000 |
commit | b8ff87a6158886771677e6dc8139bac6e3cba717 (patch) | |
tree | c781f609a9949236ae88a302d330e5d319ea9e1a /arch/x86/boot/tools/build.c | |
parent | c116e8d60adabfd545a269fccab85e77febc1643 (diff) | |
download | linux-b8ff87a6158886771677e6dc8139bac6e3cba717.tar.bz2 |
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: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'arch/x86/boot/tools/build.c')
-rw-r--r-- | arch/x86/boot/tools/build.c | 22 |
1 files changed, 15 insertions, 7 deletions
diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index bf262077ec92..4f07df5ac5d9 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -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); |