• Ard Biesheuvel's avatar
    x86/efistub: Add missing boot_params for mixed mode compat entry · d21f5a59
    Ard Biesheuvel authored
    The pure EFI stub entry point does not take a struct boot_params from
    the boot loader, but creates it from scratch, and populates only the
    fields that still have meaning in this context (command line, initrd
    base and size, etc)
    
    The original mixed mode implementation used the EFI handover protocol
    instead, where the boot loader (i.e., GRUB) populates a boot_params
    struct and passes it to a special Linux specific EFI entry point that
    takes the boot_params pointer as its third argument.
    
    When the new mixed mode implementation was introduced, using a special
    32-bit PE entrypoint in the 64-bit kernel, it adopted the pure approach,
    and relied on the EFI stub to create the struct boot_params.  This is
    preferred because it makes the bootloader side much easier to implement,
    as it does not need any x86-specific knowledge on how struct boot_params
    and struct setup_header are put together. This mixed mode implementation
    was adopted by systemd-boot version 252 and later.
    
    When commit
    
      e2ab9eab ("x86/boot/compressed: Move 32-bit entrypoint code into .text section")
    
    refactored this code and moved it out of head_64.S, the fact that ESI
    was populated with the address of the base of the image was overlooked,
    and to simplify the code flow, ESI is now zeroed and stored to memory
    unconditionally in shared code, so that the NULL-ness of that variable
    can still be used later to determine which mixed mode boot protocol is
    in use.
    
    With ESI pointing to the base of the image, it can serve as a struct
    boot_params pointer for startup_32(), which only accesses the init_data
    and kernel_alignment fields (and the scratch field as a temporary
    stack). Zeroing ESI means that those accesses produce garbage now, even
    though things appear to work if the first page of memory happens to be
    zeroed, and the region right before LOAD_PHYSICAL_ADDR (== 16 MiB)
    happens to be free.
    
    The solution is to pass a special, temporary struct boot_params to
    startup_32() via ESI, one that is sufficient for getting it to create
    the page tables correctly and is discarded right after. This involves
    setting a minimal alignment of 4k, only to get the statically allocated
    page tables line up correctly, and setting init_size to the executable
    image size (_end - startup_32). This ensures that the page tables are
    covered by the static footprint of the PE image.
    
    Given that EFI boot no longer calls the decompressor and no longer pads
    the image to permit the decompressor to execute in place, the same
    temporary struct boot_params should be used in the EFI handover protocol
    based mixed mode implementation as well, to prevent the page tables from
    being placed outside of allocated memory.
    
    Fixes: e2ab9eab ("x86/boot/compressed: Move 32-bit entrypoint code into .text section")
    Cc: <stable@kernel.org> # v6.1+
    Closes: https://lore.kernel.org/all/20240321150510.GI8211@craftyguy.net/Reported-by: default avatarClayton Craft <clayton@craftyguy.net>
    Tested-by: default avatarClayton Craft <clayton@craftyguy.net>
    Tested-by: default avatarHans de Goede <hdegoede@redhat.com>
    Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
    d21f5a59
efi_mixed.S 8.22 KB