Misc EFI fixes, including the boot failure regression caused by the BSS section not being cleared.

-----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl6WzYwRHG1pbmdvQGtl
 cm5lbC5vcmcACgkQEnMQ0APhK1jsFRAArMSO7A9NvpkP2rVpI6mHB2tIBDwLoXMC
 VNoCB9h0oiV7TDKIDv1f5nhtsdQwIqxOtGVjVAraUoEQPi6Ef55pmE4kSlPklGxU
 4+W5Ch1g3gbQCK8leB0cxQW/LingG2LEXM6zzAWtmonkIu1t2F3b+8XrSGNPe56e
 BFnK9/Y9QNKox14ITtv8u9tOXWc7hkZ8W+fpKmQr1PWtZ0FaUDXmxrjJ4FyYwvnT
 hUNmGfKAngT13WU7glxhh5B9W161XtlMt0sJ62ZQ4I7t77FErFB81fOQ0wmJwnDu
 Qa9cQAvFEHvXxnQxhq1pmK+w8vyzdtRZD5eF4kLz2lz5tCiduVSHIutM6xXAqVhD
 eAVL59hSPGwP1+Tc3JS5d1O2smuWclUWsXKN21cTLgOnNltn2Ri4Ij5FTJ1lOAOh
 RNekYQJLbiVdSDn8cVtmOzx91q9xeTuu9w1NUAXFM+eVTQbRl7lqA1vln56Ccekn
 dRPJQiGIUDObjY/8T6AtySc1/tDskDrsQkPF9zw90GXDNITX/G3MluUsQ3m1UA4X
 JBJQs+ORjoD1ujNNe12G823XGssOrY0vOriY4L1vvJnnarFd2Fku8RKa9ZOLa0Ia
 YrguIVe63guitBoshJHPoSbglgGgKVWtfN5v0cgPpW3YepUG7xMQGdw1TDQlDNV7
 H0hiMRtYdVM=
 =dC6l
 -----END PGP SIGNATURE-----

Merge tag 'efi-urgent-2020-04-15' of git://git.kernel.org/pub/scm/linux/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
This commit is contained in:
Linus Torvalds 2020-04-15 17:37:48 -07:00
commit 00086336a8
8 changed files with 61 additions and 30 deletions

View File

@ -1399,8 +1399,8 @@ 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 must be __BOOT_DS; interrupt must be disabled; %rsi must hold the base
address of the struct boot_params. address of the struct boot_params.
EFI Handover Protocol EFI Handover Protocol (deprecated)
===================== ==================================
This protocol allows boot loaders to defer initialisation to the EFI This protocol allows boot loaders to defer initialisation to the EFI
boot stub. The boot loader is required to load the kernel/initrd(s) boot stub. The boot loader is required to load the kernel/initrd(s)
@ -1408,6 +1408,12 @@ from the boot media and jump to the EFI handover protocol entry point
which is hdr->handover_offset bytes from the beginning of which is hdr->handover_offset bytes from the beginning of
startup_{32,64}. 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:: The function prototype for the handover entry point looks like this::
efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp) efi_main(void *handle, efi_system_table_t *table, struct boot_params *bp)
@ -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:: The boot loader *must* fill out the following fields in bp::
- hdr.code32_start
- hdr.cmd_line_ptr - hdr.cmd_line_ptr
- hdr.ramdisk_image (if applicable) - hdr.ramdisk_image (if applicable)
- hdr.ramdisk_size (if applicable) - hdr.ramdisk_size (if applicable)
All other fields should be zero. 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

View File

@ -1450,7 +1450,8 @@ ENTRY(efi_enter_kernel)
@ running beyond the PoU, and so calling cache_off below from @ running beyond the PoU, and so calling cache_off below from
@ inside the PE/COFF loader allocated region is unsafe unless @ inside the PE/COFF loader allocated region is unsafe unless
@ we explicitly clean it to the PoC. @ 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 adr r1, 0f @ run with MMU off
bl cache_clean_flush bl cache_clean_flush
bl cache_off bl cache_off

View File

@ -178,8 +178,10 @@ extern void efi_free_boot_services(void);
extern pgd_t * __init efi_uv1_memmap_phys_prolog(void); extern pgd_t * __init efi_uv1_memmap_phys_prolog(void);
extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd); extern void __init efi_uv1_memmap_phys_epilog(pgd_t *save_pgd);
/* kexec external ABI */
struct efi_setup_data { struct efi_setup_data {
u64 fw_vendor; u64 fw_vendor;
u64 __unused;
u64 tables; u64 tables;
u64 smbios; u64 smbios;
u64 reserved[8]; u64 reserved[8];

View File

@ -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) 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; struct page *page;
unsigned npages; unsigned npages;
pgd_t *pgd = efi_mm.pgd; pgd_t *pgd = efi_mm.pgd;
@ -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 */ 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); text = __pa(_text);
pfn = text >> PAGE_SHIFT; pfn = text >> PAGE_SHIFT;
@ -266,6 +266,14 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
return 1; 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; return 0;
} }
@ -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_vendor = virt_to_phys_or_null(vnd);
phys_data = virt_to_phys_or_null_size(data, data_size); 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; status = EFI_INVALID_PARAMETER;
else else
status = efi_thunk(set_variable, phys_name, phys_vendor, status = efi_thunk(set_variable, phys_name, phys_vendor,
@ -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_vendor = virt_to_phys_or_null(vnd);
phys_data = virt_to_phys_or_null_size(data, data_size); 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; status = EFI_INVALID_PARAMETER;
else else
status = efi_thunk(set_variable, phys_name, phys_vendor, status = efi_thunk(set_variable, phys_name, phys_vendor,

View File

@ -101,7 +101,7 @@ void cper_print_bits(const char *pfx, unsigned int bits,
if (!len) if (!len)
len = snprintf(buf, sizeof(buf), "%s%s", pfx, str); len = snprintf(buf, sizeof(buf), "%s%s", pfx, str);
else else
len += snprintf(buf+len, sizeof(buf)-len, ", %s", str); len += scnprintf(buf+len, sizeof(buf)-len, ", %s", str);
} }
if (len) if (len)
printk("%s\n", buf); printk("%s\n", buf);

View File

@ -25,7 +25,7 @@
#define EFI_ALLOC_ALIGN EFI_PAGE_SIZE #define EFI_ALLOC_ALIGN EFI_PAGE_SIZE
#endif #endif
#ifdef CONFIG_ARM #if defined(CONFIG_ARM) || defined(CONFIG_X86)
#define __efistub_global __section(.data) #define __efistub_global __section(.data)
#else #else
#define __efistub_global #define __efistub_global

View File

@ -29,30 +29,31 @@
*/ */
#define EFI_READ_CHUNK_SIZE SZ_1M #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, 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, efi_file_protocol_t **handle,
unsigned long *file_size) 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_guid_t info_guid = EFI_FILE_INFO_ID;
efi_file_protocol_t *fh; efi_file_protocol_t *fh;
unsigned long info_sz; unsigned long info_sz;
efi_status_t status; 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) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to open file: "); pr_efi_err("Failed to open file: ");
efi_char16_printk(filename_16); efi_char16_printk(fi->filename);
efi_printk("\n"); efi_printk("\n");
return status; return status;
} }
info_sz = sizeof(finfo); info_sz = sizeof(struct finfo);
status = fh->get_info(fh, &info_guid, &info_sz, &finfo); status = fh->get_info(fh, &info_guid, &info_sz, fi);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
pr_efi_err("Failed to get file info\n"); pr_efi_err("Failed to get file info\n");
fh->close(fh); fh->close(fh);
@ -60,7 +61,7 @@ static efi_status_t efi_open_file(efi_file_protocol_t *volume,
} }
*handle = fh; *handle = fh;
*file_size = finfo.info.file_size; *file_size = fi->info.file_size;
return EFI_SUCCESS; return EFI_SUCCESS;
} }
@ -146,13 +147,13 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
alloc_addr = alloc_size = 0; alloc_addr = alloc_size = 0;
do { do {
efi_char16_t filename[MAX_FILENAME_SIZE]; struct finfo fi;
unsigned long size; unsigned long size;
void *addr; void *addr;
offset = find_file_option(cmdline, cmdline_len, offset = find_file_option(cmdline, cmdline_len,
optstr, optstr_size, optstr, optstr_size,
filename, ARRAY_SIZE(filename)); fi.filename, ARRAY_SIZE(fi.filename));
if (!offset) if (!offset)
break; break;
@ -166,7 +167,7 @@ static efi_status_t handle_cmdline_files(efi_loaded_image_t *image,
return status; return status;
} }
status = efi_open_file(volume, filename, &file, &size); status = efi_open_file(volume, &fi, &file, &size);
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
goto err_close_volume; goto err_close_volume;

View File

@ -20,7 +20,7 @@
/* Maximum physical address for 64-bit kernel with 4-level paging */ /* Maximum physical address for 64-bit kernel with 4-level paging */
#define MAXMEM_X86_64_4LEVEL (1ull << 46) #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 const bool efi_is64;
extern u32 image_offset; extern u32 image_offset;
@ -392,8 +392,6 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
image_base = efi_table_attr(image, image_base); image_base = efi_table_attr(image, image_base);
image_offset = (void *)startup_32 - 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); status = efi_allocate_pages(0x4000, (unsigned long *)&boot_params, ULONG_MAX);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
efi_printk("Failed to allocate lowmem for boot params\n"); efi_printk("Failed to allocate lowmem for boot params\n");
@ -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 * now use KERNEL_IMAGE_SIZE, which will be 512MiB, the same as what
* KASLR uses. * KASLR uses.
* *
* Also relocate it if image_offset is zero, i.e. we weren't loaded by * Also relocate it if image_offset is zero, i.e. the kernel wasn't
* LoadImage, but we are not aligned correctly. * 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, buffer_start = ALIGN(bzimage_addr - image_offset,
@ -753,8 +758,7 @@ unsigned long efi_main(efi_handle_t handle,
if ((buffer_start < LOAD_PHYSICAL_ADDR) || if ((buffer_start < LOAD_PHYSICAL_ADDR) ||
(IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE) || (IS_ENABLED(CONFIG_X86_32) && buffer_end > KERNEL_IMAGE_SIZE) ||
(IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) || (IS_ENABLED(CONFIG_X86_64) && buffer_end > MAXMEM_X86_64_4LEVEL) ||
(image_offset == 0 && !IS_ALIGNED(bzimage_addr, (image_offset == 0)) {
hdr->kernel_alignment))) {
status = efi_relocate_kernel(&bzimage_addr, status = efi_relocate_kernel(&bzimage_addr,
hdr->init_size, hdr->init_size, hdr->init_size, hdr->init_size,
hdr->pref_address, hdr->pref_address,