efi/x86: Implement mixed mode boot without the handover protocol

Add support for booting 64-bit x86 kernels from 32-bit firmware running
on 64-bit capable CPUs without requiring the bootloader to implement
the EFI handover protocol or allocate the setup block, etc etc, all of
which can be done by the stub itself, using code that already exists.

Instead, create an ordinary EFI application entrypoint but implemented
in 32-bit code [so that it can be invoked by 32-bit firmware], and stash
the address of this 32-bit entrypoint in the .compat section where the
bootloader can find it.

Note that we use the setup block embedded in the binary to go through
startup_32(), but it gets reallocated and copied in efi_pe_entry(),
using the same code that runs when the x86 kernel is booted in EFI
mode from native firmware. This requires the loaded image protocol to
be installed on the kernel image's EFI handle, and point to the kernel
image itself and not to its loader. This, in turn, requires the
bootloader to use the LoadImage() boot service to load the 64-bit
image from 32-bit firmware, which is in fact supported by firmware
based on EDK2. (Only StartImage() will fail, and instead, the newly
added entrypoint needs to be invoked)

Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
This commit is contained in:
Ard Biesheuvel
2020-02-12 23:20:54 +01:00
förälder 3b8f44fc08
incheckning 17054f492d
2 ändrade filer med 73 tillägg och 15 borttagningar

Visa fil

@@ -424,21 +424,24 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
hdr->ramdisk_image = 0;
hdr->ramdisk_size = 0;
status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
goto fail2;
if (!noinitrd()) {
status = efi_load_initrd(image, &ramdisk_addr, &ramdisk_size,
hdr->initrd_addr_max,
above4g ? ULONG_MAX
: hdr->initrd_addr_max);
if (efi_is_native()) {
status = efi_parse_options(cmdline_ptr);
if (status != EFI_SUCCESS)
goto fail2;
hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
hdr->ramdisk_size = ramdisk_size & 0xffffffff;
boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32;
if (!noinitrd()) {
status = efi_load_initrd(image, &ramdisk_addr,
&ramdisk_size,
hdr->initrd_addr_max,
above4g ? ULONG_MAX
: hdr->initrd_addr_max);
if (status != EFI_SUCCESS)
goto fail2;
hdr->ramdisk_image = ramdisk_addr & 0xffffffff;
hdr->ramdisk_size = ramdisk_size & 0xffffffff;
boot_params->ext_ramdisk_image = (u64)ramdisk_addr >> 32;
boot_params->ext_ramdisk_size = (u64)ramdisk_size >> 32;
}
}
efi_stub_entry(handle, sys_table, boot_params);