From 396f1a08db212138418b38f784e4bbe516d2fdb2 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 10 Apr 2014 13:30:13 +0100 Subject: [PATCH 1/3] x86/efi: Fix boot failure with EFI stub commit 54b52d872680 ("x86/efi: Build our own EFI services pointer table") introduced a regression because the 64-bit file_size() implementation passed a pointer to a 32-bit data object, instead of a pointer to a 64-bit object. Because the firmware treats the object as 64-bits regardless it was reading random values from the stack for the upper 32-bits. This resulted in people being unable to boot their machines, after seeing the following error messages, Failed to get file info size Failed to alloc highmem for files Reported-by: Dzmitry Sledneu Reported-by: Koen Kooi Tested-by: Koen Kooi Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 1e6146137f8e..280165524ee4 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -112,7 +112,7 @@ __file_size64(void *__fh, efi_char16_t *filename_16, efi_file_info_t *info; efi_status_t status; efi_guid_t info_guid = EFI_FILE_INFO_ID; - u32 info_sz; + u64 info_sz; status = efi_early->call((unsigned long)fh->open, fh, &h, filename_16, EFI_FILE_MODE_READ, (u64)0); From 7e8213c1f3acc064aef37813a39f13cbfe7c3ce7 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Tue, 8 Apr 2014 13:14:00 +0100 Subject: [PATCH 2/3] x86/efi: Correct EFI boot stub use of code32_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit code32_start should point at the start of the protected mode code, and *not* at the beginning of the bzImage. This is much easier to do in assembly so document that callers of make_boot_params() need to fill out code32_start. The fallout from this bug is that we would end up relocating the image but copying the image at some offset, resulting in what appeared to be memory corruption. Reported-by: Thomas Bächler Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 5 +++-- arch/x86/boot/compressed/head_32.S | 8 ++------ arch/x86/boot/compressed/head_64.S | 9 +++------ 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 280165524ee4..91d17007323b 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -1016,6 +1016,9 @@ void setup_graphics(struct boot_params *boot_params) * Because the x86 boot code expects to be passed a boot_params we * need to create one ourselves (usually the bootloader would create * one for us). + * + * The caller is responsible for filling out ->code32_start in the + * returned boot_params. */ struct boot_params *make_boot_params(struct efi_config *c) { @@ -1081,8 +1084,6 @@ struct boot_params *make_boot_params(struct efi_config *c) hdr->vid_mode = 0xffff; hdr->boot_flag = 0xAA55; - hdr->code32_start = (__u64)(unsigned long)image->image_base; - hdr->type_of_loader = 0x21; /* Convert unicode cmdline to ascii */ diff --git a/arch/x86/boot/compressed/head_32.S b/arch/x86/boot/compressed/head_32.S index de9d4200d305..cbed1407a5cd 100644 --- a/arch/x86/boot/compressed/head_32.S +++ b/arch/x86/boot/compressed/head_32.S @@ -59,6 +59,7 @@ ENTRY(efi_pe_entry) call make_boot_params cmpl $0, %eax je fail + movl %esi, BP_code32_start(%eax) popl %ecx pushl %eax pushl %ecx @@ -90,12 +91,7 @@ fail: hlt jmp fail 2: - call 3f -3: - popl %eax - subl $3b, %eax - subl BP_pref_address(%esi), %eax - add BP_code32_start(%esi), %eax + movl BP_code32_start(%esi), %eax leal preferred_addr(%eax), %eax jmp *%eax diff --git a/arch/x86/boot/compressed/head_64.S b/arch/x86/boot/compressed/head_64.S index 57e58a5fa210..0d558ee899ae 100644 --- a/arch/x86/boot/compressed/head_64.S +++ b/arch/x86/boot/compressed/head_64.S @@ -261,6 +261,8 @@ ENTRY(efi_pe_entry) cmpq $0,%rax je fail mov %rax, %rsi + leaq startup_32(%rip), %rax + movl %eax, BP_code32_start(%rsi) jmp 2f /* Skip the relocation */ handover_entry: @@ -284,12 +286,7 @@ fail: hlt jmp fail 2: - call 3f -3: - popq %rax - subq $3b, %rax - subq BP_pref_address(%rsi), %rax - add BP_code32_start(%esi), %eax + movl BP_code32_start(%esi), %eax leaq preferred_addr(%rax), %rax jmp *%rax From 47514c996fac5e6f13ef3a4c5e23f1c5cffabb7b Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Thu, 10 Apr 2014 14:11:45 +0100 Subject: [PATCH 3/3] efi: Pass correct file handle to efi_file_{read,close} We're currently passing the file handle for the root file system to efi_file_read() and efi_file_close(), instead of the file handle for the file we wish to read/close. While this has worked up until now, it seems that it has only been by pure luck. Olivier explains, "The issue is the UEFI Fat driver might return the same function for 'fh->read()' and 'h->read()'. While in our case it does not work with a different implementation of EFI_SIMPLE_FILE_SYSTEM_PROTOCOL. In our case, we return a different pointer when reading a directory and reading a file." Fixing this actually clears up the two functions because we can drop one of the arguments, and instead only pass a file 'handle' argument. Reported-by: Olivier Martin Reviewed-by: Olivier Martin Reviewed-by: Mark Rutland Cc: Leif Lindholm Signed-off-by: Matt Fleming --- arch/x86/boot/compressed/eboot.c | 12 ++++++------ drivers/firmware/efi/efi-stub-helper.c | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 91d17007323b..4703a6c4b8e3 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -167,31 +167,31 @@ efi_file_size(efi_system_table_t *sys_table, void *__fh, } static inline efi_status_t -efi_file_read(void *__fh, void *handle, unsigned long *size, void *addr) +efi_file_read(void *handle, unsigned long *size, void *addr) { unsigned long func; if (efi_early->is64) { - efi_file_handle_64_t *fh = __fh; + efi_file_handle_64_t *fh = handle; func = (unsigned long)fh->read; return efi_early->call(func, handle, size, addr); } else { - efi_file_handle_32_t *fh = __fh; + efi_file_handle_32_t *fh = handle; func = (unsigned long)fh->read; return efi_early->call(func, handle, size, addr); } } -static inline efi_status_t efi_file_close(void *__fh, void *handle) +static inline efi_status_t efi_file_close(void *handle) { if (efi_early->is64) { - efi_file_handle_64_t *fh = __fh; + efi_file_handle_64_t *fh = handle; return efi_early->call((unsigned long)fh->close, handle); } else { - efi_file_handle_32_t *fh = __fh; + efi_file_handle_32_t *fh = handle; return efi_early->call((unsigned long)fh->close, handle); } diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index ff50aeebf0d9..2c41eaece2c1 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -397,7 +397,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, else chunksize = size; - status = efi_file_read(fh, files[j].handle, + status = efi_file_read(files[j].handle, &chunksize, (void *)addr); if (status != EFI_SUCCESS) { @@ -408,7 +408,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, size -= chunksize; } - efi_file_close(fh, files[j].handle); + efi_file_close(files[j].handle); } } @@ -425,7 +425,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, close_handles: for (k = j; k < i; k++) - efi_file_close(fh, files[k].handle); + efi_file_close(files[k].handle); free_files: efi_call_early(free_pool, files); fail: