Skip to content

Commit

Permalink
Merge tag 'efi-urgent-2020-04-15' of git://git.kernel.org/pub/scm/lin…
Browse files Browse the repository at this point in the history
…ux/kernel/git/tip/tip

Pull EFI fixes from Ingo Molnar:
 "Misc EFI fixes, including the boot failure regression caused by the
  BSS section not being cleared by the loaders"

* tag 'efi-urgent-2020-04-15' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  efi/x86: Revert struct layout change to fix kexec boot regression
  efi/x86: Don't remap text<->rodata gap read-only for mixed mode
  efi/x86: Fix the deletion of variables in mixed mode
  efi/libstub/file: Merge file name buffers to reduce stack usage
  Documentation/x86, efi/x86: Clarify EFI handover protocol and its requirements
  efi/arm: Deal with ADR going out of range in efi_enter_kernel()
  efi/x86: Always relocate the kernel for EFI handover entry
  efi/x86: Move efi stub globals from .bss to .data
  efi/libstub/x86: Remove redundant assignment to pointer hdr
  efi/cper: Use scnprintf() for avoiding potential buffer overflow
  • Loading branch information
torvalds committed Apr 16, 2020
2 parents 8632e9b + a088b85 commit 0008633
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 30 deletions.
21 changes: 18 additions & 3 deletions Documentation/x86/boot.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1399,15 +1399,21 @@ must have read/write permission; CS must be __BOOT_CS and DS, ES, SS
must be __BOOT_DS; interrupt must be disabled; %rsi must hold the base
address of the struct boot_params.

EFI Handover Protocol
=====================
EFI Handover Protocol (deprecated)
==================================

This protocol allows boot loaders to defer initialisation to the EFI
boot stub. The boot loader is required to load the kernel/initrd(s)
from the boot media and jump to the EFI handover protocol entry point
which is hdr->handover_offset bytes from the beginning of
startup_{32,64}.

The boot loader MUST respect the kernel's PE/COFF metadata when it comes
to section alignment, the memory footprint of the executable image beyond
the size of the file itself, and any other aspect of the PE/COFF header
that may affect correct operation of the image as a PE/COFF binary in the
execution context provided by the EFI firmware.

The function prototype for the handover entry point looks like this::

efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp)
Expand All @@ -1419,9 +1425,18 @@ UEFI specification. 'bp' is the boot loader-allocated boot params.

The boot loader *must* fill out the following fields in bp::

- hdr.code32_start
- hdr.cmd_line_ptr
- hdr.ramdisk_image (if applicable)
- hdr.ramdisk_size (if applicable)

All other fields should be zero.

NOTE: The EFI Handover Protocol is deprecated in favour of the ordinary PE/COFF
entry point, combined with the LINUX_EFI_INITRD_MEDIA_GUID based initrd
loading protocol (refer to [0] for an example of the bootloader side of
this), which removes the need for any knowledge on the part of the EFI
bootloader regarding the internal representation of boot_params or any
requirements/limitations regarding the placement of the command line
and ramdisk in memory, or the placement of the kernel image itself.

[0] https://github.com/u-boot/u-boot/commit/ec80b4735a593961fe701cc3a5d717d4739b0fd0
3 changes: 2 additions & 1 deletion arch/arm/boot/compressed/head.S
Original file line number Diff line number Diff line change
Expand Up @@ -1450,7 +1450,8 @@ ENTRY(efi_enter_kernel)
@ running beyond the PoU, and so calling cache_off below from
@ inside the PE/COFF loader allocated region is unsafe unless
@ we explicitly clean it to the PoC.
adr r0, call_cache_fn @ region of code we will
ARM( adrl r0, call_cache_fn )
THUMB( adr r0, call_cache_fn ) @ region of code we will
adr r1, 0f @ run with MMU off
bl cache_clean_flush
bl cache_off
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/include/asm/efi.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,10 @@ extern void efi_free_boot_services(void);
extern pgd_t * __init efi_uv1_memmap_phys_prolog(void);
extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd);

/* kexec external ABI */
struct efi_setup_data {
u64 fw_vendor;
u64 __unused;
u64 tables;
u64 smbios;
u64 reserved[8];
Expand Down
16 changes: 12 additions & 4 deletions arch/x86/platform/efi/efi_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ virt_to_phys_or_null_size(void *va, unsigned long size)

int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
{
unsigned long pfn, text, pf;
unsigned long pfn, text, pf, rodata;
struct page *page;
unsigned npages;
pgd_t *pgd = efi_mm.pgd;
Expand Down Expand Up @@ -256,7 +256,7 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)

efi_scratch.phys_stack = page_to_phys(page + 1); /* stack grows down */

npages = (__end_rodata_aligned - _text) >> PAGE_SHIFT;
npages = (_etext - _text) >> PAGE_SHIFT;
text = __pa(_text);
pfn = text >> PAGE_SHIFT;

Expand All @@ -266,6 +266,14 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
return 1;
}

npages = (__end_rodata - __start_rodata) >> PAGE_SHIFT;
rodata = __pa(__start_rodata);
pfn = rodata >> PAGE_SHIFT;
if (kernel_map_pages_in_pgd(pgd, pfn, rodata, npages, pf)) {
pr_err("Failed to map kernel rodata 1:1\n");
return 1;
}

return 0;
}

Expand Down Expand Up @@ -638,7 +646,7 @@ efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor,
phys_vendor = virt_to_phys_or_null(vnd);
phys_data = virt_to_phys_or_null_size(data, data_size);

if (!phys_name || !phys_data)
if (!phys_name || (data && !phys_data))
status = EFI_INVALID_PARAMETER;
else
status = efi_thunk(set_variable, phys_name, phys_vendor,
Expand Down Expand Up @@ -669,7 +677,7 @@ efi_thunk_set_variable_nonblocking(efi_char16_t *name, efi_guid_t *vendor,
phys_vendor = virt_to_phys_or_null(vnd);
phys_data = virt_to_phys_or_null_size(data, data_size);

if (!phys_name || !phys_data)
if (!phys_name || (data && !phys_data))
status = EFI_INVALID_PARAMETER;
else
status = efi_thunk(set_variable, phys_name, phys_vendor,
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/efi/cper.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ void cper_print_bits(const char *pfx, unsigned int bits,
if (!len)
len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
else
len += snprintf(buf+len, sizeof(buf)-len, ", %s", str);
len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
}
if (len)
printk("%s\n", buf);
Expand Down
2 changes: 1 addition & 1 deletion drivers/firmware/efi/libstub/efistub.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#define EFI_ALLOC_ALIGN EFI_PAGE_SIZE
#endif

#ifdef CONFIG_ARM
#if defined(CONFIG_ARM) || defined(CONFIG_X86)
#define __efistub_global __section(.data)
#else
#define __efistub_global
Expand Down
27 changes: 14 additions & 13 deletions drivers/firmware/efi/libstub/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,38 +29,39 @@
*/
#define EFI_READ_CHUNK_SIZE SZ_1M

struct finfo {
efi_file_info_t info;
efi_char16_t filename[MAX_FILENAME_SIZE];
};

static efi_status_t efi_open_file(efi_file_protocol_t *volume,
efi_char16_t *filename_16,
struct finfo *fi,
efi_file_protocol_t **handle,
unsigned long *file_size)
{
struct {
efi_file_info_t info;
efi_char16_t filename[MAX_FILENAME_SIZE];
} finfo;
efi_guid_t info_guid = EFI_FILE_INFO_ID;
efi_file_protocol_t *fh;
unsigned long info_sz;
efi_status_t status;

status = volume->open(volume, &fh, filename_16, EFI_FILE_MODE_READ, 0);
status = volume->open(volume, &fh, fi->filename, EFI_FILE_MODE_READ, 0);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to open file: ");
efi_char16_printk(filename_16);
efi_char16_printk(fi->filename);
efi_printk("\n");
return status;
}

info_sz = sizeof(finfo);
status = fh->get_info(fh, &info_guid, &info_sz, &finfo);
info_sz = sizeof(struct finfo);
status = fh->get_info(fh, &info_guid, &info_sz, fi);
if (status != EFI_SUCCESS) {
pr_efi_err("Failed to get file info\n");
fh->close(fh);
return status;
}

*handle = fh;
*file_size = finfo.info.file_size;
*file_size = fi->info.file_size;
return EFI_SUCCESS;
}

Expand Down Expand Up @@ -146,13 +147,13 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,

alloc_addr = alloc_size = 0;
do {
efi_char16_t filename[MAX_FILENAME_SIZE];
struct finfo fi;
unsigned long size;
void *addr;

offset = find_file_option(cmdline, cmdline_len,
optstr, optstr_size,
filename, ARRAY_SIZE(filename));
fi.filename, ARRAY_SIZE(fi.filename));

if (!offset)
break;
Expand All @@ -166,7 +167,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
return status;
}

status = efi_open_file(volume, filename, &file, &size);
status = efi_open_file(volume, &fi, &file, &size);
if (status != EFI_SUCCESS)
goto err_close_volume;

Expand Down
18 changes: 11 additions & 7 deletions drivers/firmware/efi/libstub/x86-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/* Maximum physical address for 64-bit kernel with 4-level paging */
#define MAXMEM_X86_64_4LEVEL (1ull << 46)

static efi_system_table_t *sys_table;
static efi_system_table_t *sys_table __efistub_global;
extern const bool efi_is64;
extern u32 image_offset;

Expand Down Expand Up @@ -392,8 +392,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
image_base = efi_table_attr(image, image_base);
image_offset = (void *)startup_32 - image_base;

hdr = &((struct boot_params *)image_base)->hdr;

status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate lowmem for boot params\n");
Expand Down Expand Up @@ -742,8 +740,15 @@ unsigned long efi_main(efi_handle_t handle,
* now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what
* KASLR uses.
*
* Also relocate it if image_offset is zero, i.e. we weren't loaded by
* LoadImage, but we are not aligned correctly.
* Also relocate it if image_offset is zero, i.e. the kernel wasn't
* loaded by LoadImage, but rather by a bootloader that called the
* handover entry. The reason we must always relocate in this case is
* to handle the case of systemd-boot booting a unified kernel image,
* which is a PE executable that contains the bzImage and an initrd as
* COFF sections. The initrd section is placed after the bzImage
* without ensuring that there are at least init_size bytes available
* for the bzImage, and thus the compressed kernel's startup code may
* overwrite the initrd unless it is moved out of the way.
*/

buffer_start = ALIGN(bzimage_addr - image_offset,
Expand All @@ -753,8 +758,7 @@ unsigned long efi_main(efi_handle_t handle,
if ((buffer_start < LOAD_PHYSICAL_ADDR) ||
(IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE) ||
(IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) ||
(image_offset == 0 && !IS_ALIGNED(bzimage_addr,
hdr->kernel_alignment))) {
(image_offset == 0)) {
status = efi_relocate_kernel(&bzimage_addr,
hdr->init_size, hdr->init_size,
hdr->pref_address,
Expand Down

0 comments on commit 0008633

Please sign in to comment.