summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArd Biesheuvel <ardb@kernel.org>2019-12-24 18:10:20 +0300
committerIngo Molnar <mingo@kernel.org>2019-12-25 12:49:23 +0300
commit23e60394046a831d3245f83c0f5d46dee7d83326 (patch)
tree28751309158d404e2a85293e84e97d5788f5bff5
parentcd33a5c1d53e43bef1683c70dc3b68b6d9e8eca6 (diff)
downloadlinux-23e60394046a831d3245f83c0f5d46dee7d83326.tar.xz
efi/libstub/x86: Work around page freeing issue in mixed mode
Mixed mode translates calls from the 64-bit kernel into the 32-bit firmware by wrapping them in a call to a thunking routine that pushes a 32-bit word onto the stack for each argument passed to the function, regardless of the argument type. This works surprisingly well for most services and protocols, with the exception of ones that take explicit 64-bit arguments. efi_free() invokes the FreePages() EFI boot service, which takes a efi_physical_addr_t as its address argument, and this is one of those 64-bit types. This means that the 32-bit firmware will interpret the (addr, size) pair as a single 64-bit quantity, and since it is guaranteed to have the high word set (as size > 0), it will always fail due to the fact that EFI memory allocations are always < 4 GB on 32-bit firmware. So let's fix this by giving the thunking code a little hand, and pass two values for the address, and a third one for the size. Signed-off-by: Ard Biesheuvel <ardb@kernel.org> Cc: Arvind Sankar <nivedita@alum.mit.edu> Cc: Borislav Petkov <bp@alien8.de> Cc: James Morse <james.morse@arm.com> Cc: Matt Fleming <matt@codeblueprint.co.uk> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: linux-efi@vger.kernel.org Link: https://lkml.kernel.org/r/20191224151025.32482-21-ardb@kernel.org Signed-off-by: Ingo Molnar <mingo@kernel.org>
-rw-r--r--arch/x86/boot/compressed/eboot.c16
-rw-r--r--drivers/firmware/efi/libstub/efi-stub-helper.c5
2 files changed, 20 insertions, 1 deletions
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index f81dd66626ce..ec92c4decc86 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -901,3 +901,19 @@ fail:
for (;;)
asm("hlt");
}
+
+#ifdef CONFIG_EFI_MIXED
+void efi_free_native(unsigned long size, unsigned long addr);
+
+void efi_free(unsigned long size, unsigned long addr)
+{
+ if (!size)
+ return;
+
+ if (efi_is_native())
+ efi_free_native(size, addr);
+ else
+ efi64_thunk(efi_system_table()->boottime->mixed_mode.free_pages,
+ addr, 0, DIV_ROUND_UP(size, EFI_PAGE_SIZE));
+}
+#endif
diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c
index d4215571f05a..b715ac6a0c94 100644
--- a/drivers/firmware/efi/libstub/efi-stub-helper.c
+++ b/drivers/firmware/efi/libstub/efi-stub-helper.c
@@ -346,6 +346,9 @@ fail:
}
void efi_free(unsigned long size, unsigned long addr)
+ __weak __alias(efi_free_native);
+
+void efi_free_native(unsigned long size, unsigned long addr)
{
unsigned long nr_pages;
@@ -353,7 +356,7 @@ void efi_free(unsigned long size, unsigned long addr)
return;
nr_pages = round_up(size, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
- efi_call_early(free_pages, addr, nr_pages);
+ efi_system_table()->boottime->free_pages(addr, nr_pages);
}
static efi_status_t efi_file_size(void *__fh, efi_char16_t *filename_16,