arm64: boot BE kernels from UEFI
cover arm64: boot BE kernels from UEFI
This series adds support for booting BE kernels from UEFI. As UEFI is defined to be strictly little endian, some workarounds are required to combine a little endian EFI stub with a big endian kernel. Also, runtime services need to be wrapped so they can be executed in little endian mode.
Patches #1 and #2 have been sent to the list before, but are included for completeness.
Patch #3, #4 and #5 modify the PE/COFF header definition so it is always emitted in little endian, regardless of the endianness of the kernel.
Patch #6 removes references to linker defined symbols like _text and _edata from the stub, as the stub is now a standalone little endian binary that is wrapped in a big endian object file, and _text and _edata are unavailable or meaningless in that context.
Patch #7 adds the Makefile changes and some code to build the standalone stub and wrap it.
Patch #8 updates the kernel code that references data in UEFI memory to byte reverse it if required.
Patch #9 adds runtime services wrappers for the variable store services so they can be called from the big endian kernel. Other runtime services are not implemented for now.
Patch #10 enables everything by adding the Kconfig logic.
This is tested on Foundation model and FVP Base model. Booting works fine on both, however, enumerating the variable store works only on FVP Base, and is not debuggable on Foundation Model, so I would really appreciate any insights into the code in patch #9 that may be causing this. There is some trickery regarding en-/disabling caches and MMU and surely I have gotten something wrong there.
Ard Biesheuvel (10):
arm64/efi: efistub: jump to 'stext' directly, not through the header arm64/efi: efistub: cover entire static mem footprint in PE/COFF .text
arm64: add macros to emit little endian ASM constants
arm64: add EFI little endian constants to linker script arm64/efi: update the PE/COFF header to be endian agnostic arm64/efi: efistub: avoid using linker defined constants arm64/efi: efistub: add support for booting a BE kernel arm64/efi: use LE accessors to access UEFI data
arm64/efi: enable minimal UEFI Runtime Services for big endian
arm64: Kconfig: enable UEFI on BE kernels
arch/arm64/Kconfig | 10 ++- arch/arm64/include/asm/assembler.h | 18 +++++ arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/Makefile | 7 +- arch/arm64/kernel/efi-be-call.S | 55 +++++++++++++++ arch/arm64/kernel/efi-be-runtime.c | 104 ++++++++++++++++++++++++++++ arch/arm64/kernel/efi-entry.S | 41 ++++++++--- arch/arm64/kernel/efi-stub.c | 11 ++- arch/arm64/kernel/efi.c | 68 +++++++++++------- arch/arm64/kernel/efistub-le/Makefile | 52 ++++++++++++++ arch/arm64/kernel/efistub-le/efi-le-entry.S | 13 ++++ arch/arm64/kernel/efistub-le/efistub-le.lds | 35 ++++++++++ arch/arm64/kernel/efistub-le/le.h | 12 ++++ arch/arm64/kernel/efistub-le/strstr.c | 20 ++++++ arch/arm64/kernel/head.S | 50 +++++++------ arch/arm64/kernel/image.h | 16 ++++- drivers/firmware/efi/efi.c | 26 ++++--- drivers/firmware/efi/efivars.c | 2 +- drivers/firmware/efi/libstub/fdt.c | 4 ++ 19 files changed, 466 insertions(+), 80 deletions(-) create mode 100644 arch/arm64/kernel/efi-be-call.S create mode 100644 arch/arm64/kernel/efi-be-runtime.c create mode 100644 arch/arm64/kernel/efistub-le/Makefile create mode 100644 arch/arm64/kernel/efistub-le/efi-le-entry.S create mode 100644 arch/arm64/kernel/efistub-le/efistub-le.lds create mode 100644 arch/arm64/kernel/efistub-le/le.h create mode 100644 arch/arm64/kernel/efistub-le/strstr.c
Signature · 1 line
1.8.3.2
RFC PATCH 1/10 arm64/efi: efistub: jump to 'stext' directly, not through the header
After the EFI stub has done its business, it jumps into the kernel by branching to offset #0 of the loaded Image, which is where it expects to find the header containing a 'branch to stext' instruction.
However, the header is not covered by any PE/COFF section, so the header may not actually be loaded at the expected offset. So instead, jump to 'stext' directly, which is at the base of the PE/COFF .text section, by supplying a symbol 'stext_offset' to efi-entry.o which contains the relative offset of stext into the Image. Also replace other open coded calculations of the same value with a reference to 'stext_offset'
--- arch/arm64/kernel/efi-entry.S | 3 ++- arch/arm64/kernel/head.S | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-)
arch/arm64/kernel/efi-entry.S +2 -1
@@ -61,7 +61,8 @@ ENTRY(efi_stub_entry) */ mov x20, x0 // DTB address ldr x0, [sp, #16] // relocated _text address - mov x21, x0 + ldr x21, =stext_offset + add x21, x0, x21 /* * Flush dcache covering current runtime addresses
arch/arm64/kernel/head.S +6 -4
@@ -129,6 +129,8 @@ efi_head: #endif #ifdef CONFIG_EFI + .globl stext_offset + .set stext_offset, stext - efi_head .align 3 pe_header: .ascii "PE"
@@ -152,7 +154,7 @@ optional_header: .long 0 // SizeOfInitializedData .long 0 // SizeOfUninitializedData .long efi_stub_entry - efi_head // AddressOfEntryPoint - .long stext - efi_head // BaseOfCode + .long stext_offset // BaseOfCode extra_header_fields: .quad 0 // ImageBase
@@ -169,7 +171,7 @@ extra_header_fields: .long _edata - efi_head // SizeOfImage // Everything before the kernel image is considered part of the header - .long stext - efi_head // SizeOfHeaders + .long stext_offset // SizeOfHeaders .long 0 // CheckSum .short 0xa // Subsystem (EFI application) .short 0 // DllCharacteristics
@@ -214,9 +216,9 @@ section_table: .byte 0 .byte 0 // end of 0 padding of section name .long _edata - stext // VirtualSize - .long stext - efi_head // VirtualAddress + .long stext_offset // VirtualAddress .long _edata - stext // SizeOfRawData - .long stext - efi_head // PointerToRawData + .long stext_offset // PointerToRawData .long 0 // PointerToRelocations (0 for executables) .long 0 // PointerToLineNumbers (0 for executables)
Signature · 1 line
1.8.3.2
RFC PATCH 2/10 arm64/efi: efistub: cover entire static mem footprint in PE/COFF .text
In reply to Ard BiesheuvelThe static memory footprint of a kernel Image at boot is larger than the Image file itself. Things like .bss data and initial page tables are allocated statically but populated dynamically so their content is not contained in the Image file.
However, if EFI has loaded the Image at precisely the desired offset of base of DRAM + TEXT_OFFSET, the Image will be booted in place, and we have to make sure that the allocation done by the EFI loader is large enough.
Fix this by growing the PE/COFF .text section to cover the entire static memory footprint. The part of the section that is not covered by the payload will be zero initialised by the EFI loader.
--- arch/arm64/kernel/head.S | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/arm64/kernel/head.S +3 -3
@@ -150,7 +150,7 @@ optional_header: .short 0x20b // PE32+ format .byte 0x02 // MajorLinkerVersion .byte 0x14 // MinorLinkerVersion - .long _edata - stext // SizeOfCode + .long _end - stext // SizeOfCode .long 0 // SizeOfInitializedData .long 0 // SizeOfUninitializedData .long efi_stub_entry - efi_head // AddressOfEntryPoint
@@ -168,7 +168,7 @@ extra_header_fields: .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue - .long _edata - efi_head // SizeOfImage + .long _end - efi_head // SizeOfImage // Everything before the kernel image is considered part of the header .long stext_offset // SizeOfHeaders
@@ -215,7 +215,7 @@ section_table: .byte 0 .byte 0 .byte 0 // end of 0 padding of section name - .long _edata - stext // VirtualSize + .long _end - stext // VirtualSize .long stext_offset // VirtualAddress .long _edata - stext // SizeOfRawData .long stext_offset // PointerToRawData
Signature · 1 line
1.8.3.2
RFC PATCH 3/10 arm64: add macros to emit little endian ASM constants
In reply to Ard BiesheuvelThe Image header contains many constants that should be emitted in little endian regardless of the endianness of the kernel. Add helper macros le16, le32 and le64 to <asm/assembler.h> to aid with this.
--- arch/arm64/include/asm/assembler.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
arch/arm64/include/asm/assembler.h +18 -0
@@ -155,3 +155,21 @@ lr .req x30 // link register#endif orr \rd, \lbits, \hbits, lsl #32 .endm + + /* + * Define LE constants + */ + .macro le16, x + .byte \x & 0xff + .byte (\x >> 8) & 0xff + .endm + + .macro le32, x + le16 \x + le16 \x >> 16 + .endm + + .macro le64, x + le32 \x + le32 \x >> 32 + .endm
Signature · 1 line
1.8.3.2
RFC PATCH 4/10 arm64: add EFI little endian constants to linker script
In reply to Ard BiesheuvelSimilar to how text offset and kernel size are mangled to produce little endian constants for the Image header regardless of the endianness of the kernel, this adds a number of constants used in the EFI PE/COFF header which can only be calculated (and byte swapped) by the linker.
--- arch/arm64/kernel/image.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
arch/arm64/kernel/image.h +15 -1
@@ -37,8 +37,10 @@ (((data) & 0x0000ff0000000000) >> 24) | \ (((data) & 0x00ff000000000000) >> 40) | \ (((data) & 0xff00000000000000) >> 56)) +#define DATA_LE32 (data) (DATA_LE64(data) >> 32)#else #define DATA_LE64 (data) ((data) & 0xffffffffffffffff) +#define DATA_LE32 (data) ((data) & 0xffffffff)#endif #ifdef CONFIG_CPU_BIG_ENDIAN
@@ -57,6 +59,18 @@#define HEAD_SYMBOLS \ _kernel_size_le = DATA_LE64(_end - _text); \ _kernel_offset_le = DATA_LE64(TEXT_OFFSET); \ - _kernel_flags_le = DATA_LE64(__HEAD_FLAGS); + _kernel_flags_le = DATA_LE64(__HEAD_FLAGS); \ + EFI_HEAD_SYMBOLS + +#ifdef CONFIG_EFI +#define EFI_HEAD_SYMBOLS \ + _efi_stext_offset_le = DATA_LE32(stext_offset); \ + _efi_code_virtsize_le = DATA_LE32(_end - _text - stext_offset); \ + _efi_code_rawsize_le = DATA_LE32(_edata - _text - stext_offset); \ + _efi_image_size_le = DATA_LE32(_end - _text); \ + _efi_entry_point_le = DATA_LE32(efi_stub_entry - _text); +#else +#define EFI_HEAD_SYMBOLS +#endif #endif /* __ASM_IMAGE_H */
Signature · 1 line
1.8.3.2
RFC PATCH 4/10 arm64: add EFI little endian constants to linker script
Quoted 9 lines · Ard Biesheuvel
Similar to how text offset and kernel size are mangled to produce little endian constants for the Image header regardless of the endianness of the kernel, this adds a number of constants used in the EFI PE/COFF header which can only be calculated (and byte swapped) by the linker.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/kernel/image.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)
Where is this file? I can't find it in Linus' tree.
Signature · 1 line
Matt Fleming, Intel Open Source Technology Center
RFC PATCH 4/10 arm64: add EFI little endian constants to linker script
Quoted 6 lines · Matt Fleming
Quoted 5 lines · Ard Biesheuvel
Similar to how text offset and kernel size are mangled to produce little endian constants for the Image header regardless of the endianness of the kernel, this adds a number of constants used in the EFI PE/COFF header which can only be calculated (and byte swapped) by the linker.
Quoted 7 lines
Quoted 4 lines
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/kernel/image.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)Where is this file? I can't find it in Linus' tree.
Apologies, I failed to mention that this series depends on arm64 for-next/core
https://git.kernel.org/cgit/linux/kernel/git/arm64/linux.git/log/?h=for-next/core
Signature · 1 line
Ard.
RFC PATCH 4/10 arm64: add EFI little endian constants to linker script
In reply to Matt FlemingQuoted 6 lines · Matt Fleming
Quoted 5 lines · Ard Biesheuvel
Similar to how text offset and kernel size are mangled to produce little endian constants for the Image header regardless of the endianness of the kernel, this adds a number of constants used in the EFI PE/COFF header which can only be calculated (and byte swapped) by the linker.
Quoted 6 lines
Quoted 4 lines
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/kernel/image.h | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-)Where is this file? I can't find it in Linus' tree.
It's queued in the arm64 tree for 3.17 (should also appear in -next at the moment).
See commit a2c1d73b94ed ("arm64: Update the Image header").
Will
RFC PATCH 5/10 arm64/efi: update the PE/COFF header to be endian agnostic
In reply to Ard BiesheuvelUpdate the PE/COFF header to use explicit little endian constants and use explicit little endian linker symbols so that the PE/COFF header is always emitted in little endian regardless of the endiannes of the kernel.
--- arch/arm64/kernel/head.S | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-)
arch/arm64/kernel/head.S +26 -22
@@ -123,7 +123,10 @@ efi_head: .byte 0x4d .byte 0x64 #ifdef CONFIG_EFI - .long pe_header - efi_head // Offset to the PE header. + .byte pe_header - efi_head // Offset to the PE header. + .byte 0 + .byte 0 + .byte 0 #else .word 0 // reserved #endif
@@ -136,30 +139,31 @@ pe_header: .ascii "PE" .short 0 coff_header: - .short 0xaa64 // AArch64 - .short 2 // nr_sections + le16 0xaa64 // AArch64 + le16 2 // nr_sections .long 0 // TimeDateStamp .long 0 // PointerToSymbolTable - .long 1 // NumberOfSymbols - .short section_table - optional_header // SizeOfOptionalHeader - .short 0x206 // Characteristics. + le32 1 // NumberOfSymbols + .byte section_table - optional_header // SizeOfOptionalHeader + .byte 0 + le16 0x206 // Characteristics. // IMAGE_FILE_DEBUG_STRIPPED | // IMAGE_FILE_EXECUTABLE_IMAGE | // IMAGE_FILE_LINE_NUMS_STRIPPED optional_header: - .short 0x20b // PE32+ format + le16 0x20b // PE32+ format .byte 0x02 // MajorLinkerVersion .byte 0x14 // MinorLinkerVersion - .long _end - stext // SizeOfCode + .long _efi_code_virtsize_le // SizeOfCode .long 0 // SizeOfInitializedData .long 0 // SizeOfUninitializedData - .long efi_stub_entry - efi_head // AddressOfEntryPoint - .long stext_offset // BaseOfCode + .long _efi_entry_point_le // AddressOfEntryPoint + .long _efi_stext_offset_le // BaseOfCode extra_header_fields: .quad 0 // ImageBase - .long 0x20 // SectionAlignment - .long 0x8 // FileAlignment + le32 0x20 // SectionAlignment + le32 0x8 // FileAlignment .short 0 // MajorOperatingSystemVersion .short 0 // MinorOperatingSystemVersion .short 0 // MajorImageVersion
@@ -168,19 +172,19 @@ extra_header_fields: .short 0 // MinorSubsystemVersion .long 0 // Win32VersionValue - .long _end - efi_head // SizeOfImage + .long _efi_image_size_le // SizeOfImage // Everything before the kernel image is considered part of the header - .long stext_offset // SizeOfHeaders + .long _efi_stext_offset_le // SizeOfHeaders .long 0 // CheckSum - .short 0xa // Subsystem (EFI application) + le16 0xa // Subsystem (EFI application) .short 0 // DllCharacteristics .quad 0 // SizeOfStackReserve .quad 0 // SizeOfStackCommit .quad 0 // SizeOfHeapReserve .quad 0 // SizeOfHeapCommit .long 0 // LoaderFlags - .long 0x6 // NumberOfRvaAndSizes + le32 0x6 // NumberOfRvaAndSizes .quad 0 // ExportTable .quad 0 // ImportTable
@@ -208,23 +212,23 @@ section_table: .long 0 // PointerToLineNumbers .short 0 // NumberOfRelocations .short 0 // NumberOfLineNumbers - .long 0x42100040 // Characteristics (section flags) + le32 0x42100040 // Characteristics (section flags) .ascii ".text" .byte 0 .byte 0 .byte 0 // end of 0 padding of section name - .long _end - stext // VirtualSize - .long stext_offset // VirtualAddress - .long _edata - stext // SizeOfRawData - .long stext_offset // PointerToRawData + .long _efi_code_virtsize_le // VirtualSize + .long _efi_stext_offset_le // VirtualAddress + .long _efi_code_rawsize_le // SizeOfRawData + .long _efi_stext_offset_le // PointerToRawData .long 0 // PointerToRelocations (0 for executables) .long 0 // PointerToLineNumbers (0 for executables) .short 0 // NumberOfRelocations (0 for executables) .short 0 // NumberOfLineNumbers (0 for executables) - .long 0xe0500020 // Characteristics (section flags) + le32 0xe0500020 // Characteristics (section flags) .align 5 #endif
Signature · 1 line
1.8.3.2
RFC PATCH 6/10 arm64/efi: efistub: avoid using linker defined constants
In reply to Ard BiesheuvelWhen we build the stub as a separate executable, we cannot refer to symbols like _edata or _end to find out how large the kernel is. Use image->image_size instead, this covers the entire static memory footprint including BSS.
--- arch/arm64/kernel/efi-stub.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-)
arch/arm64/kernel/efi-stub.c +4 -7
@@ -11,7 +11,6 @@*/ #include <linux/efi.h> #include <asm/efi.h> -#include <asm/sections.h> efi_status_t handle_kernel_image(efi_system_table_t *sys_table, unsigned long *image_addr,
@@ -22,14 +21,12 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, efi_loaded_image_t *image) {efi_status_t status; -unsigned long kernel_size, kernel_memsize =0 ;/* Relocate the image, if required. */ - kernel_size = _edata - _text;if (*image_addr != (dram_base +TEXT_OFFSET )) { - kernel_memsize = kernel_size + (_end - _edata); status =efi_relocate_kernel (sys_table, image_addr, - kernel_size, kernel_memsize, + image->image_size, + image->image_size, dram_base +TEXT_OFFSET ,PAGE_SIZE );if (status !=EFI_SUCCESS ) {
@@ -38,10 +35,10 @@ efi_status_t handle_kernel_image(efi_system_table_t *sys_table, }if (*image_addr != (dram_base +TEXT_OFFSET )) {pr_efi_err (sys_table,"Failed to alloc kernel memory\n" ); -efi_free (sys_table, kernel_memsize, *image_addr); +efi_free (sys_table, image->image_size, *image_addr);return EFI_LOAD_ERROR ; } - *image_size = kernel_memsize; + *image_size = image->image_size; }
Signature · 1 line
1.8.3.2
RFC PATCH 7/10 arm64/efi: efistub: add support for booting a BE kernel
In reply to Ard BiesheuvelThis adds support to boot a big endian kernel from UEFI firmware, which is always little endian. The EFI stub itself is built as little endian, and embedded into a big endian kernel image.
To enable this, we need to build all the stub's dependencies as little endian, including FDT parsing code and string functions. This is accomplished by building little endian versions of those support files and link them into a static library which is used by the inner stub build.
--- arch/arm64/kernel/Makefile | 7 +++- arch/arm64/kernel/efi-entry.S | 42 +++++++++++++++++------ arch/arm64/kernel/efistub-le/Makefile | 52 +++++++++++++++++++++++++++++ arch/arm64/kernel/efistub-le/efi-le-entry.S | 13 ++++++++ arch/arm64/kernel/efistub-le/efistub-le.lds | 35 +++++++++++++++++++ arch/arm64/kernel/efistub-le/le.h | 12 +++++++ arch/arm64/kernel/efistub-le/strstr.c | 20 +++++++++++ drivers/firmware/efi/libstub/fdt.c | 4 +++ 8 files changed, 173 insertions(+), 12 deletions(-) create mode 100644 arch/arm64/kernel/efistub-le/Makefile create mode 100644 arch/arm64/kernel/efistub-le/efi-le-entry.S create mode 100644 arch/arm64/kernel/efistub-le/efistub-le.lds create mode 100644 arch/arm64/kernel/efistub-le/le.h create mode 100644 arch/arm64/kernel/efistub-le/strstr.c
arch/arm64/kernel/Makefile +6 -1
@@ -27,7 +27,12 @@ arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o arm64-obj-$(CONFIG_ARM64_CPU_SUSPEND) += sleep.o suspend.o arm64-obj-$(CONFIG_JUMP_LABEL) += jump_label.o arm64-obj-$(CONFIG_KGDB) += kgdb.o -arm64-obj-$(CONFIG_EFI) += efi.o efi-stub.o efi-entry.o + +arm64-efi-obj-y := efi.o +arm64-efi-obj-$(CONFIG_EFI_STUB) += efi-stub.o efi-entry.o +arm64-efi-obj-$(CONFIG_EFI_LE_STUB) += efistub-le/ +arm64-efi-obj-$(CONFIG_CPU_BIG_ENDIAN) += efi-be-runtime.o efi-be-call.o +arm64-obj-$(CONFIG_EFI) += $(arm64-efi-obj-y) obj-y += $(arm64-obj-y) vdso/ obj-m += $(arm64-obj-m)
arch/arm64/kernel/efi-entry.S +31 -11
72 lines not on this page · raw message
arch/arm64/kernel/efistub-le/Makefile new file +52 -0
52 lines not on this page · raw message
arch/arm64/kernel/efistub-le/efi-le-entry.S new file +13 -0
@@ -0,0 +1,13 @@ + +#include <linux/linkage.h> + + .text + .align 12 + .long _text - efi_stub_entry + .long linux_banner - efi_stub_entry + .quad _kernel_size_le + .quad stext_offset + .quad 0 +ENTRY(efi_stub_entry) + .incbin "arch/arm64/kernel/efistub-le/efi-le-stub.bin" +ENDPROC(efi_stub_entry)
arch/arm64/kernel/efistub-le/efistub-le.lds new file +35 -0
@@ -0,0 +1,35 @@ + +ENTRY(efi_stub_entry) + +SECTIONS { + /* + * The inner and outer alignment of this chunk of code need to be the + * same so that PC relative references using adrp/add or adrp/ldr pairs + * will work correctly. + * Skip 32 bytes here, so we can put the binary blob at an offset of + * 4k + 0x20 in the outer image, and use the gap to share constants + * emitted by the outer linker but required in the stub. + */ + .text 0x20 : { + arch/arm64/kernel/efistub-le/efi-entry.o(.init.text) + *(.init.text) + *(.text) + *(.text*) + } + .rodata : { + . = ALIGN(16); + *(.rodata) + *(.rodata*) + *(.init.rodata) + } + .data : { + . = ALIGN(16); + *(.data) + *(.data*) + le_linux_banner = .; + . += 8; + } + /DISCARD/ : { + *(__ex_table) + } +}
arch/arm64/kernel/efistub-le/le.h new file +12 -0
@@ -0,0 +1,12 @@ + +/* +* This is a bit of a hack, but it is necessary to correctly compile .S files +* that contain CPU_LE()/CPU_BE() statements, as these are defined to depend on +* CONFIG_ symbols and not on the endianness of the compiler. +*/ +#ifdef CONFIG_CPU_BIG_ENDIAN +#define STUB_BE(code...) code +#else +#define STUB_BE(code...) +#endif +#undef CONFIG_CPU_BIG_ENDIAN
arch/arm64/kernel/efistub-le/strstr.c new file +20 -0
@@ -0,0 +1,20 @@ + +#include <linux/types.h> +#include <linux/string.h> + +char *strstr (const char *s1,const char *s2) +{ +size_t l1, l2; + + l2 =strlen (s2); +if (!l2) +return (char *)s1; + l1 =strlen (s1); +while (l1 >= l2) { + l1--; +if (!memcmp (s1, s2, l2)) +return (char *)s1; + s1++; + } +return NULL ; +}
drivers/firmware/efi/libstub/fdt.c +4 -0
@@ -22,6 +22,10 @@ efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, unsigned long map_size, unsigned long desc_size, u32 desc_ver) { +#ifdef CONFIG_EFI_LE_STUB + extern char const *le_linux_banner; +char const *linux_banner = le_linux_banner; +#endif int node, prev;int status;u32 fdt_val32;
Signature · 1 line
1.8.3.2
RFC PATCH 8/10 arm64/efi: use LE accessors to access UEFI data
In reply to Ard BiesheuvelIf we are running a BE kernel, we need to byte reverse all data that UEFI keeps, as UEFI is strictly little endian.
--- arch/arm64/kernel/efi.c | 53 +++++++++++++++++++++++------------------- drivers/firmware/efi/efi.c | 26 ++++++++++++--------- drivers/firmware/efi/efivars.c | 2 +- 3 files changed, 45 insertions(+), 36 deletions(-)
arch/arm64/kernel/efi.c +29 -24
@@ -42,7 +42,7 @@ early_param("uefi_debug", uefi_debug_setup);static int __init is_normal_ram (efi_memory_desc_t *md) { -if (md->attribute &EFI_MEMORY_WB ) +if (le64_to_cpu (md->attribute) &EFI_MEMORY_WB )return 1 ;return 0 ; }
@@ -58,10 +58,11 @@ static void __init efi_setup_idmap(void)/* map runtime io spaces */ for_each_efi_memory_desc (&memmap, md) { -if (!(md->attribute &EFI_MEMORY_RUNTIME ) ||is_normal_ram (md)) +if (!(le64_to_cpu (md->attribute) &EFI_MEMORY_RUNTIME ) || +is_normal_ram (md))continue ; - paddr = md->phys_addr; - npages = md->num_pages; + paddr =le64_to_cpu (md->phys_addr); + npages =le64_to_cpu (md->num_pages);memrange_efi_to_native (&paddr, &npages); size = npages <<PAGE_SHIFT ;create_id_mapping (paddr, size,1 );
@@ -87,27 +88,27 @@ static int __init uefi_init(void)/* * Verify the EFI Table */ -if (efi.systab->hdr.signature !=EFI_SYSTEM_TABLE_SIGNATURE ) { +if (le64_to_cpu (efi.systab->hdr.signature) !=EFI_SYSTEM_TABLE_SIGNATURE ) {pr_err ("System table signature incorrect\n" );return -EINVAL ; } -if ((efi.systab->hdr.revision >>16 ) <2 ) +if ((le32_to_cpu (efi.systab->hdr.revision) >>16 ) <2 )pr_warn ("Warning: EFI system table version %d.%02d, expected 2.00 or greater\n" , efi.systab->hdr.revision >>16 , efi.systab->hdr.revision &0xffff );/* Show what we know for posterity */ - c16 =early_memremap (efi.systab->fw_vendor, + c16 =early_memremap (le64_to_cpu (efi.systab->fw_vendor),sizeof (vendor));if (c16) {for (i =0 ; i < (int )sizeof (vendor) -1 && *c16; ++i) - vendor[i] = c16[i]; + vendor[i] =le16_to_cpu (c16[i]); vendor[i] ='\0' ; }pr_info ("EFI v%u.%.02u by %s\n" , - efi.systab->hdr.revision >>16 , - efi.systab->hdr.revision &0xffff , vendor); +le32_to_cpu (efi.systab->hdr.revision) >>16 , +le32_to_cpu (efi.systab->hdr.revision) &0xffff , vendor); retval =efi_config_init (NULL );if (retval ==0 )
@@ -144,11 +145,11 @@ static __init int is_reserve_region(efi_memory_desc_t *md)if (!is_normal_ram (md))return 0 ; -if (md->attribute &EFI_MEMORY_RUNTIME ) +if (le64_to_cpu (md->attribute) &EFI_MEMORY_RUNTIME )return 1 ; -if (md->type ==EFI_ACPI_RECLAIM_MEMORY || - md->type ==EFI_RESERVED_TYPE ) +if (le32_to_cpu (md->type) ==EFI_ACPI_RECLAIM_MEMORY || +le32_to_cpu (md->type) ==EFI_RESERVED_TYPE )return 1 ;return 0 ;
@@ -163,13 +164,15 @@ static __init void reserve_regions(void)pr_info ("Processing EFI memory map:\n" );for_each_efi_memory_desc (&memmap, md) { - paddr = md->phys_addr; - npages = md->num_pages; +u32 md_type =le32_to_cpu (md->type); + + paddr =le64_to_cpu (md->phys_addr); + npages =le64_to_cpu (md->num_pages);if (uefi_debug)pr_info (" 0x%012llx-0x%012llx [%s]" , paddr, paddr + (npages <<EFI_PAGE_SHIFT ) -1 , - memory_type_name[md->type]); + memory_type_name[md_type]);memrange_efi_to_native (&paddr, &npages); size = npages <<PAGE_SHIFT ;
@@ -178,8 +181,8 @@ static __init void reserve_regions(void)early_init_dt_add_memory_arch (paddr, size);if (is_reserve_region (md) || - md->type ==EFI_BOOT_SERVICES_CODE || - md->type ==EFI_BOOT_SERVICES_DATA ) { + md_type ==EFI_BOOT_SERVICES_CODE || + md_type ==EFI_BOOT_SERVICES_DATA ) {memblock_reserve (paddr, size);if (uefi_debug)pr_cont ("*" );
@@ -258,17 +261,18 @@ static void __init free_boot_services(void)*/ if (free_start) {/* adjust free_end then free region */ -if (free_end > md->phys_addr) +if (free_end >le64_to_cpu (md->phys_addr)) free_end -=PAGE_SIZE ; total_freed +=free_region (free_start, free_end); free_start =0 ; } - keep_end = md->phys_addr + (md->num_pages <<EFI_PAGE_SHIFT ); + keep_end =le64_to_cpu (md->phys_addr) + + (le64_to_cpu (md->num_pages) <<EFI_PAGE_SHIFT );continue ; } -if (md->type !=EFI_BOOT_SERVICES_CODE && - md->type !=EFI_BOOT_SERVICES_DATA ) { +if (le32_to_cpu (md->type) !=EFI_BOOT_SERVICES_CODE && +le32_to_cpu (md->type) !=EFI_BOOT_SERVICES_DATA ) {/* no need to free this region */ continue ; }
@@ -276,8 +280,8 @@ static void __init free_boot_services(void)/* * We want to free memory from this region. */ - paddr = md->phys_addr; - npages = md->num_pages; + paddr =le64_to_cpu (md->phys_addr); + npages =le64_to_cpu (md->num_pages);memrange_efi_to_native (&paddr, &npages); size = npages <<PAGE_SHIFT ;
@@ -475,3 +479,4 @@ err_unmap:return -1 ; }early_initcall (arm64_enter_virtual_mode); +
drivers/firmware/efi/efi.c +15 -11
@@ -270,18 +270,23 @@ static __init int match_config_table(efi_guid_t *guid,int __init efi_config_init (efi_config_table_type_t *arch_tables) {void *config_tables, *tablep; -int i, sz; +unsigned long __tables; +int i, sz, nr_tables; -if (efi_enabled (EFI_64BIT )) +if (efi_enabled (EFI_64BIT )) { sz =sizeof (efi_config_table_64_t); -else + nr_tables =le64_to_cpu ((__force __le64 )efi.systab->nr_tables); + __tables =le64_to_cpu ((__force __le64 )efi.systab->tables); + }else { sz =sizeof (efi_config_table_32_t); + nr_tables =le32_to_cpu ((__force __le32 )efi.systab->nr_tables); + __tables =le32_to_cpu ((__force __le32 )efi.systab->tables); + }/* * Let's see what config tables the firmware passed to us. */ - config_tables =early_memremap (efi.systab->tables, - efi.systab->nr_tables * sz); + config_tables =early_memremap (__tables, nr_tables * sz);if (config_tables ==NULL ) {pr_err ("Could not map Configuration table!\n" );return -ENOMEM ;
@@ -289,21 +294,20 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) tablep = config_tables;pr_info ("" ); -for (i =0 ; i < efi.systab->nr_tables; i++) { +for (i =0 ; i < nr_tables; i++) {efi_guid_t guid;unsigned long table;if (efi_enabled (EFI_64BIT )) {u64 table64; guid = ((efi_config_table_64_t *)tablep)->guid; - table64 = ((efi_config_table_64_t *)tablep)->table; - table = table64; + table = table64 =le64_to_cpu ((__force __le64) + ((efi_config_table_64_t *)tablep)->table);#ifndef CONFIG_64BIT if (table64 >>32 ) {pr_cont ("\n" );pr_err ("Table located above 4GB, disabling EFI.\n" ); -early_memunmap (config_tables, - efi.systab->nr_tables * sz); +early_memunmap (config_tables, nr_tables * sz);return -EINVAL ; }#endif
@@ -318,7 +322,7 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) tablep += sz; }pr_cont ("\n" ); -early_memunmap (config_tables, efi.systab->nr_tables * sz); +early_memunmap (config_tables, nr_tables * sz);set_bit (EFI_CONFIG_TABLES , &efi.flags);
drivers/firmware/efi/efivars.c +1 -1
@@ -563,7 +563,7 @@ efivar_create_sysfs_entry(struct efivar_entry *new_var)/* Convert Unicode to normal chars (assume top bits are 0), ala UTF-8 */ for (i=0 ; i < (int )(variable_name_size /sizeof (efi_char16_t)); i++) { - short_name[i] = variable_name[i] &0xFF ; + short_name[i] =le16_to_cpu ((__force __le16 )variable_name[i]); }/* This is ugly, but necessary to separate one vendor's private variables from another's. */
Signature · 1 line
1.8.3.2
RFC PATCH 9/10 arm64/efi: enable minimal UEFI Runtime Services for big endian
In reply to Ard BiesheuvelThis enables the UEFI Runtime Services needed to manipulate the non-volatile variable store when running under a BE kernel.
--- arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/efi-be-call.S | 55 ++++++++++++++++++++ arch/arm64/kernel/efi-be-runtime.c | 104 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/efi.c | 15 ++++++ 4 files changed, 176 insertions(+) create mode 100644 arch/arm64/kernel/efi-be-call.S create mode 100644 arch/arm64/kernel/efi-be-runtime.c
arch/arm64/include/asm/efi.h +2 -0
@@ -44,4 +44,6 @@ extern void efi_idmap_init(void);#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +extern void efi_be_runtime_setup (void ); +#endif /* _ASM_EFI_H */
arch/arm64/kernel/efi-be-call.S new file +55 -0
55 lines not on this page · raw message
arch/arm64/kernel/efi-be-runtime.c new file +104 -0
104 lines not on this page · raw message
arch/arm64/kernel/efi.c +15 -0
@@ -406,6 +406,21 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap; +if (IS_ENABLED (CONFIG_CPU_BIG_ENDIAN )) { + efi.systab =ioremap_cache (efi_system_table, +sizeof (efi_system_table_t)); +if (!efi.systab) { +pr_err ("Failed to remap EFI system table!\n" ); +return -1 ; + } +free_boot_services (); +efi_be_runtime_setup (); + +set_bit (EFI_SYSTEM_TABLES , &efi.flags); +set_bit (EFI_RUNTIME_SERVICES , &efi.flags); +return 0 ; + } +/* Map the runtime regions */ virtmap =kmalloc (mapsize,GFP_KERNEL );if (!virtmap) {
Signature · 1 line
1.8.3.2
Mark Rutland
reviewerRFC PATCH 9/10 arm64/efi: enable minimal UEFI Runtime Services for big endian
Hi Ard,
This is certainly a neat feature, and I definitely want to be able to boot BE kernels via UEFI.
However, I'm wary of calling EFI in a physical (i.e. idmap with dcaches off) context. I'm not sure anyone else does that, and I'm not sure whether that's going to work (both because of the cache maintenance requirements and the expectations of a given UEFI implementation w.r.t. memory cacheability).
I'd hoped we'd be able to use a LE EL0 context to call the runtime services in, but I'm not sure that's possible by the spec :(
As I understand it, we shouldn't need these runtime services to simply boot a BE kernel.
Quoted 52 lines · Ard Biesheuvel
This enables the UEFI Runtime Services needed to manipulate the non-volatile variable store when running under a BE kernel.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/efi-be-call.S | 55 ++++++++++++++++++++ arch/arm64/kernel/efi-be-runtime.c | 104 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/efi.c | 15 ++++++ 4 files changed, 176 insertions(+) create mode 100644 arch/arm64/kernel/efi-be-call.S create mode 100644 arch/arm64/kernel/efi-be-runtime.carch/arm64/include/asm/efi.h +2
-0@@ -44,4 +44,6 @@ extern void efi_idmap_init(void);#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +extern void efi_be_runtime_setup (void ); +#endif /* _ASM_EFI_H */ arch/arm64/kernel/efi-be-call.S new file +22
-0@@ -0,0 +1,55 @@ + +#include <linux/linkage.h> + + .text + .align 3 +ENTRY(efi_be_phys_call) + /* + * Entered at physical address with 1:1 mapping enabled. + */ + stp x29, x30, [sp, #-96]! + mov x29, sp + str x27, [sp, #16] + + ldr x8, =efi_be_phys_call // virt address of this function + adr x9, efi_be_phys_call // phys address of this function + sub x9, x8, x9 // calculate virt to phys offset in x9 + + /* preserve all inputs */ + stp x0, x1, [sp, #32] + stp x2, x3, [sp, #48] + stp x4, x5, [sp, #64] + stp x6, x7, [sp, #80]
arch/arm64/kernel/efi-be-call.S line 32
Quoted 10 lines
arch/arm64/kernel/efi-be-call.S new file +10
-0+ + /* get phys address of stack */ + sub sp, sp, x9 + + /* switch to LE, disable MMU and D-cache but leave I-cache enabled */ + mrs x27, sctlr_el1 + bic x8, x27, #1 << 2 // clear SCTLR.C + msr sctlr_el1, x8 + + bl flush_cache_all
What is the cache flush for?
The only thing that flush_cache_all can do is empty the local architected caches, and it can only do that when said caches are disabled. Any other use is unsafe; we have no guarantee that the cache is empty (or even clean), and we have no guarantee that prior writes have made it to the PoC.
Even when the caches are disabled, flush_cache_all can only guarantee that the local architected caches are empty. There is no guarantee that the dirty data made it to the PoC.
Quoted 4 lines
arch/arm64/kernel/efi-be-call.S new file +4
-0+ + /* restore inputs but rotated by 1 register */ + ldp x7, x0, [sp, #32] + ldp x1, x2, [sp, #48]
arch/arm64/kernel/efi-be-call.S line 46
Quoted 10 lines
arch/arm64/kernel/efi-be-call.S new file +10
-0+ ldp x3, x4, [sp, #64] + ldp x5, x6, [sp, #80] + + bic x8, x27, #1 << 2 // clear SCTLR.C + bic x8, x8, #1 << 0 // clear SCTLR.M + bic x8, x8, #1 << 25 // clear SCTLR.EE + msr sctlr_el1, x8 + isb + + blr x7
Is it safe to call EFI functions with the D-cache disabled?
Do the functions not care about the memory attributes for their own sue (e.g. for exclusives)?
Do they not care about IO? If IO to/from storage for variables is cache-coherent EFI and the device won't have a coherent view of memory.
arch/arm64/kernel/efi-be-call.S line 50
Quoted 4 lines
arch/arm64/kernel/efi-be-call.S new file +4
-0+ + /* switch back to BE and reenable MMU and D-cache */ + msr sctlr_el1, x27 +
Missing ISB?
Quoted 19 lines
arch/arm64/kernel/efi-be-call.S new file +5
-0+ mov sp, x29 + ldr x27, [sp, #16] + ldp x29, x30, [sp], #96 + ret +ENDPROC(efi_be_phys_call)arch/arm64/kernel/efi-be-runtime.c new file +8
-0@@ -0,0 +1,104 @@ + +#include <linux/efi.h> +#include <linux/spinlock.h> +#include <asm/efi.h> +#include <asm/neon.h> +#include <asm/tlbflush.h> + +static efi_runtime_services_t *runtime;
arch/arm64/kernel/efi-be-runtime.c line 18
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+static efi_status_t (*efi_be_call)(phys_addr_t func, ...); + +static DEFINE_SPINLOCK (efi_be_rt_lock); + +static unsigned long efi_be_call_pre (void ) +{ +unsigned long flags; + +kernel_neon_begin (); +spin_lock_irqsave (&efi_be_rt_lock, flags);
At this point we might still have DA_F unmasked, and I don't think we expect to be able to handle any of those when the CPU is in the wrong endianness for the kernel.
Quoted 22 lines
arch/arm64/kernel/efi-be-runtime.c new file +22
-0+cpu_switch_mm (idmap_pg_dir, &init_mm); +flush_tlb_all (); +return flags; +} + +static void efi_be_call_post (unsigned long flags) +{ +cpu_switch_mm (current, current->active_mm); +flush_tlb_all (); +spin_unlock_irqrestore (&efi_be_rt_lock, flags); +kernel_neon_end (); +} + +static efi_status_t efi_be_get_variable (efi_char16_t *name, +efi_guid_t *vendor, +u32 *attr, +unsigned long *data_size, +void *data) +{ +unsigned long flags; +efi_status_t status; +
arch/arm64/kernel/efi-be-runtime.c line 50
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+ *data_size =cpu_to_le64 (*data_size); + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->get_variable), +virt_to_phys (name),virt_to_phys (vendor), +virt_to_phys (attr),virt_to_phys (data_size), +virt_to_phys (data)); +efi_be_call_post (flags); + *attr =le32_to_cpu (*attr); + *data_size =le64_to_cpu (*data_size); +return status;
No cache maintenance? EFI Could have written to a physical mapping which could be shadowed by old cache entries. Similarly any buffers that we pass to UEFI aren't guaranteed to have been hit the PoC, so UEFI might read stale data.
I think that's true for all of the efi_be_* calls below.
Quoted 37 lines
arch/arm64/kernel/efi-be-runtime.c new file +37
-0+} + +static efi_status_t efi_be_get_next_variable (unsigned long *name_size, +efi_char16_t *name, +efi_guid_t *vendor) +{ +unsigned long flags; +efi_status_t status; + + *name_size =cpu_to_le64 (*name_size); + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->get_next_variable), +virt_to_phys (name_size),virt_to_phys (name), +virt_to_phys (vendor)); +efi_be_call_post (flags); + *name_size =le64_to_cpu (*name_size); +return status; +} + +static efi_status_t efi_be_set_variable (efi_char16_t *name, +efi_guid_t *vendor, +u32 attr, +unsigned long data_size, +void *data) +{ +unsigned long flags; +efi_status_t status; + + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->set_variable), +virt_to_phys (name),virt_to_phys (vendor), +cpu_to_le32 (attr),cpu_to_le64 (data_size), +virt_to_phys (data)); +efi_be_call_post (flags); +return status; +} +
arch/arm64/kernel/efi-be-runtime.c line 97
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+void efi_be_runtime_setup (void ) +{ +extern u8 efi_be_phys_call[]; + + runtime =ioremap_cache (le64_to_cpu (efi.systab->runtime), +sizeof (efi_runtime_services_t)); +if (!runtime) { +pr_err ("Failed to set up BE wrappers for UEFI Runtime Services!\n" ); +return ; + }
Might it be worth propagating the error code?
Quoted 14 lines
arch/arm64/kernel/efi-be-runtime.c new file +7
-0+ + efi_be_call = (void *)virt_to_phys (efi_be_phys_call); + + efi.get_variable = efi_be_get_variable; + efi.get_next_variable = efi_be_get_next_variable; + efi.set_variable = efi_be_set_variable; +}arch/arm64/kernel/efi.c +0
-0@@ -406,6 +406,21 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap;
arch/arm64/kernel/efi.c static int __init arm64_enter_virtual_mode(void) · line 417
Quoted 10 lines
arch/arm64/kernel/efi.c +9
-0+if (IS_ENABLED (CONFIG_CPU_BIG_ENDIAN )) { + efi.systab =ioremap_cache (efi_system_table, +sizeof (efi_system_table_t)); +if (!efi.systab) { +pr_err ("Failed to remap EFI system table!\n" ); +return -1 ; + } +free_boot_services (); +efi_be_runtime_setup ();
We can fail here, but we'll still set the bits below, which doesn't seem right.
Quoted 11 lines
arch/arm64/kernel/efi.c +6
-0+ +set_bit (EFI_SYSTEM_TABLES , &efi.flags); +set_bit (EFI_RUNTIME_SERVICES , &efi.flags); +return 0 ; + } +/* Map the runtime regions */ virtmap =kmalloc (mapsize,GFP_KERNEL );if (!virtmap) {Signature · 1 line
1.8.3.2
Cheers,
Mark.
RFC PATCH 9/10 arm64/efi: enable minimal UEFI Runtime Services for big endian
Quoted 5 lines · Mark Rutland
Hi Ard,
This is certainly a neat feature, and I definitely want to be able to boot BE kernels via UEFI.
Good!
Quoted 6 lines
However, I'm wary of calling EFI in a physical (i.e. idmap with dcaches off) context. I'm not sure anyone else does that, and I'm not sure whether that's going to work (both because of the cache maintenance requirements and the expectations of a given UEFI implementation w.r.t. memory cacheability).
I have developed an alternate version in the mean time that switches to a LE idmap (so with D-cache enabled), but this is an imperfect solution as well, as (like in the MMU off case), the vector base virtual address cannot be resolved when the EE bit is cleared (as TTBR1 points to a BE page table) so any exception taken locks the machine hard. I am not sure if this can be solved in any way other than changing exception levels. Or install an alternate vector table for the duration of the runtime services call that flips the EE bit back, restores VBAR to its original address, and jumps into it. None of this is very sexy, though ...
Quoted 3 lines
I'd hoped we'd be able to use a LE EL0 context to call the runtime services in, but I'm not sure that's possible by the spec :(
Nope, they should be called at the exception level UEFI was started in (as Leif tells me)
Quoted 3 lines
As I understand it, we shouldn't need these runtime services to simply boot a BE kernel.
Well, the significance of the variable store related Runtime Services is that they are used by an installer (through efibootmgr) to program the kernel command line. Hence the choice for just these services in the minimal implementation.
Signature · 1 line
Ard.
Quoted 272 lines · Ard Biesheuvel
Quoted 52 lines
This enables the UEFI Runtime Services needed to manipulate the non-volatile variable store when running under a BE kernel.
Signed-off-by Ard Biesheuvel <[email protected]>--- arch/arm64/include/asm/efi.h | 2 + arch/arm64/kernel/efi-be-call.S | 55 ++++++++++++++++++++ arch/arm64/kernel/efi-be-runtime.c | 104 +++++++++++++++++++++++++++++++++++++ arch/arm64/kernel/efi.c | 15 ++++++ 4 files changed, 176 insertions(+) create mode 100644 arch/arm64/kernel/efi-be-call.S create mode 100644 arch/arm64/kernel/efi-be-runtime.carch/arm64/include/asm/efi.h +2
-0@@ -44,4 +44,6 @@ extern void efi_idmap_init(void);#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +extern void efi_be_runtime_setup (void ); +#endif /* _ASM_EFI_H */ arch/arm64/kernel/efi-be-call.S new file +22
-0@@ -0,0 +1,55 @@ + +#include <linux/linkage.h> + + .text + .align 3 +ENTRY(efi_be_phys_call) + /* + * Entered at physical address with 1:1 mapping enabled. + */ + stp x29, x30, [sp, #-96]! + mov x29, sp + str x27, [sp, #16] + + ldr x8, =efi_be_phys_call // virt address of this function + adr x9, efi_be_phys_call // phys address of this function + sub x9, x8, x9 // calculate virt to phys offset in x9 + + /* preserve all inputs */ + stp x0, x1, [sp, #32] + stp x2, x3, [sp, #48] + stp x4, x5, [sp, #64] + stp x6, x7, [sp, #80]arch/arm64/kernel/efi-be-call.S line 32
Quoted 10 lines
arch/arm64/kernel/efi-be-call.S new file +10
-0+ + /* get phys address of stack */ + sub sp, sp, x9 + + /* switch to LE, disable MMU and D-cache but leave I-cache enabled */ + mrs x27, sctlr_el1 + bic x8, x27, #1 << 2 // clear SCTLR.C + msr sctlr_el1, x8 + + bl flush_cache_allWhat is the cache flush for?
The only thing that
flush_cache_allcan do is empty the local architected caches, and it can only do that when said caches are disabled. Any other use is unsafe; we have no guarantee that the cache is empty (or even clean), and we have no guarantee that prior writes have made it to the PoC.Even when the caches are disabled,
flush_cache_allcan only guarantee that the local architected caches are empty. There is no guarantee that the dirty data made it to the PoC.Quoted 4 lines
arch/arm64/kernel/efi-be-call.S new file +4
-0+ + /* restore inputs but rotated by 1 register */ + ldp x7, x0, [sp, #32] + ldp x1, x2, [sp, #48]arch/arm64/kernel/efi-be-call.S line 46
Quoted 10 lines
arch/arm64/kernel/efi-be-call.S new file +10
-0+ ldp x3, x4, [sp, #64] + ldp x5, x6, [sp, #80] + + bic x8, x27, #1 << 2 // clear SCTLR.C + bic x8, x8, #1 << 0 // clear SCTLR.M + bic x8, x8, #1 << 25 // clear SCTLR.EE + msr sctlr_el1, x8 + isb + + blr x7Is it safe to call EFI functions with the D-cache disabled?
Do the functions not care about the memory attributes for their own sue (e.g. for exclusives)?
Do they not care about IO? If IO to/from storage for variables is cache-coherent EFI and the device won't have a coherent view of memory.
arch/arm64/kernel/efi-be-call.S line 50
Quoted 4 lines
arch/arm64/kernel/efi-be-call.S new file +4
-0+ + /* switch back to BE and reenable MMU and D-cache */ + msr sctlr_el1, x27 +Missing ISB?
Quoted 19 lines
arch/arm64/kernel/efi-be-call.S new file +5
-0+ mov sp, x29 + ldr x27, [sp, #16] + ldp x29, x30, [sp], #96 + ret +ENDPROC(efi_be_phys_call)arch/arm64/kernel/efi-be-runtime.c new file +8
-0@@ -0,0 +1,104 @@ + +#include <linux/efi.h> +#include <linux/spinlock.h> +#include <asm/efi.h> +#include <asm/neon.h> +#include <asm/tlbflush.h> + +static efi_runtime_services_t *runtime;arch/arm64/kernel/efi-be-runtime.c line 18
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+static efi_status_t (*efi_be_call)(phys_addr_t func, ...); + +static DEFINE_SPINLOCK (efi_be_rt_lock); + +static unsigned long efi_be_call_pre (void ) +{ +unsigned long flags; + +kernel_neon_begin (); +spin_lock_irqsave (&efi_be_rt_lock, flags);At this point we might still have
DA_Funmasked, and I don't think we expect to be able to handle any of those when the CPU is in the wrong endianness for the kernel.Quoted 22 lines
arch/arm64/kernel/efi-be-runtime.c new file +22
-0+cpu_switch_mm (idmap_pg_dir, &init_mm); +flush_tlb_all (); +return flags; +} + +static void efi_be_call_post (unsigned long flags) +{ +cpu_switch_mm (current, current->active_mm); +flush_tlb_all (); +spin_unlock_irqrestore (&efi_be_rt_lock, flags); +kernel_neon_end (); +} + +static efi_status_t efi_be_get_variable (efi_char16_t *name, +efi_guid_t *vendor, +u32 *attr, +unsigned long *data_size, +void *data) +{ +unsigned long flags; +efi_status_t status; +arch/arm64/kernel/efi-be-runtime.c line 50
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+ *data_size =cpu_to_le64 (*data_size); + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->get_variable), +virt_to_phys (name),virt_to_phys (vendor), +virt_to_phys (attr),virt_to_phys (data_size), +virt_to_phys (data)); +efi_be_call_post (flags); + *attr =le32_to_cpu (*attr); + *data_size =le64_to_cpu (*data_size); +return status;No cache maintenance? EFI Could have written to a physical mapping which could be shadowed by old cache entries. Similarly any buffers that we pass to UEFI aren't guaranteed to have been hit the PoC, so UEFI might read stale data.
I think that's true for all of the efi_be_* calls below.
Quoted 37 lines
arch/arm64/kernel/efi-be-runtime.c new file +37
-0+} + +static efi_status_t efi_be_get_next_variable (unsigned long *name_size, +efi_char16_t *name, +efi_guid_t *vendor) +{ +unsigned long flags; +efi_status_t status; + + *name_size =cpu_to_le64 (*name_size); + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->get_next_variable), +virt_to_phys (name_size),virt_to_phys (name), +virt_to_phys (vendor)); +efi_be_call_post (flags); + *name_size =le64_to_cpu (*name_size); +return status; +} + +static efi_status_t efi_be_set_variable (efi_char16_t *name, +efi_guid_t *vendor, +u32 attr, +unsigned long data_size, +void *data) +{ +unsigned long flags; +efi_status_t status; + + flags =efi_be_call_pre (); + status =efi_be_call (le64_to_cpu (runtime->set_variable), +virt_to_phys (name),virt_to_phys (vendor), +cpu_to_le32 (attr),cpu_to_le64 (data_size), +virt_to_phys (data)); +efi_be_call_post (flags); +return status; +} +arch/arm64/kernel/efi-be-runtime.c line 97
Quoted 10 lines
arch/arm64/kernel/efi-be-runtime.c new file +10
-0+void efi_be_runtime_setup (void ) +{ +extern u8 efi_be_phys_call[]; + + runtime =ioremap_cache (le64_to_cpu (efi.systab->runtime), +sizeof (efi_runtime_services_t)); +if (!runtime) { +pr_err ("Failed to set up BE wrappers for UEFI Runtime Services!\n" ); +return ; + }Might it be worth propagating the error code?
Quoted 14 lines
arch/arm64/kernel/efi-be-runtime.c new file +7
-0+ + efi_be_call = (void *)virt_to_phys (efi_be_phys_call); + + efi.get_variable = efi_be_get_variable; + efi.get_next_variable = efi_be_get_next_variable; + efi.set_variable = efi_be_set_variable; +}arch/arm64/kernel/efi.c +0
-0@@ -406,6 +406,21 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap;arch/arm64/kernel/efi.c static int __init arm64_enter_virtual_mode(void) · line 417
Quoted 10 lines
arch/arm64/kernel/efi.c +9
-0+if (IS_ENABLED (CONFIG_CPU_BIG_ENDIAN )) { + efi.systab =ioremap_cache (efi_system_table, +sizeof (efi_system_table_t)); +if (!efi.systab) { +pr_err ("Failed to remap EFI system table!\n" ); +return -1 ; + } +free_boot_services (); +efi_be_runtime_setup ();We can fail here, but we'll still set the bits below, which doesn't seem right.
Quoted 11 lines
arch/arm64/kernel/efi.c +6
-1+ +set_bit (EFI_SYSTEM_TABLES , &efi.flags); +set_bit (EFI_RUNTIME_SERVICES , &efi.flags); +return 0 ; + } +/* Map the runtime regions */ virtmap =kmalloc (mapsize,GFP_KERNEL );if (!virtmap) { --1.8.3.2
Cheers,
Mark.
RFC PATCH 9/10 arm64/efi: enable minimal UEFI Runtime Services for big endian
Quoted 34 lines · Ard Biesheuvel
Quoted 5 lines · Mark Rutland
Hi Ard,
This is certainly a neat feature, and I definitely want to be able to boot BE kernels via UEFI.
Good!
Quoted 6 lines
However, I'm wary of calling EFI in a physical (i.e. idmap with dcaches off) context. I'm not sure anyone else does that, and I'm not sure whether that's going to work (both because of the cache maintenance requirements and the expectations of a given UEFI implementation w.r.t. memory cacheability).
I have developed an alternate version in the mean time that switches to a LE idmap (so with D-cache enabled), but this is an imperfect solution as well, as (like in the MMU off case), the vector base virtual address cannot be resolved when the EE bit is cleared (as TTBR1 points to a BE page table) so any exception taken locks the machine hard. I am not sure if this can be solved in any way other than changing exception levels. Or install an alternate vector table for the duration of the runtime services call that flips the EE bit back, restores VBAR to its original address, and jumps into it. None of this is very sexy, though ...
Quoted 3 lines
I'd hoped we'd be able to use a LE EL0 context to call the runtime services in, but I'm not sure that's possible by the spec :(
Nope, they should be called at the exception level UEFI was started in (as Leif tells me)
Quoted 9 lines
Quoted 3 lines
As I understand it, we shouldn't need these runtime services to simply boot a BE kernel.
Well, the significance of the variable store related Runtime Services is that they are used by an installer (through efibootmgr) to program the kernel command line. Hence the choice for just these services in the minimal implementation.
The below patch is an alternate approach with a LE id mapping in efi_pg_dir. (Patch that sets it up omitted).
This dodges all the concerns related to caching, hopefully, as the LE id mapping and the BE id mapping in idmap_pg_dir should agree on the memory attributes of all common mappings.
This also addresses the FIQ and exception concerns, although I fully realise that this is likely too controversial. Suggestions for less controversial approaches are highly appreciated. As said, booting a BE kernel is useful by itself, but without being able to use efibootmgr it is a bit crippled.
Signature · 1 line
Ard.
arch/arm64/include/asm/efi.h +2 -0
@@ -44,4 +44,6 @@ extern void efi_idmap_init(void);#define efi_call_early (f, ...) sys_table_arg->boottime->f(__VA_ARGS__) +extern int efi_be_runtime_setup (void ); +#endif /* _ASM_EFI_H */
(quoted diff) +0 -0
b/arch/arm64/kernel/efi-be-call.S
new file mode 100644
index 000000000000..8da53a225fab
arch/arm64/kernel/efi-be-call.S new file +129 -0
129 lines not on this page · raw message
(quoted diff) +0 -0
b/arch/arm64/kernel/efi-be-runtime.c
new file mode 100644
index 000000000000..abcc275481bd
arch/arm64/kernel/efi-be-runtime.c new file +105 -0
105 lines not on this page · raw message
arch/arm64/kernel/efi.c +14 -0
@@ -426,6 +426,20 @@ static int __init arm64_enter_virtual_mode(void) efi.memmap = &memmap; +if (IS_ENABLED (CONFIG_CPU_BIG_ENDIAN )) { + efi.systab =ioremap_cache (efi_system_table, +sizeof (efi_system_table_t)); +if (!efi.systab) { +pr_err ("Failed to remap EFI system table!\n" ); +return -1 ; + } +free_boot_services (); +set_bit (EFI_SYSTEM_TABLES , &efi.flags); +if (efi_be_runtime_setup () ==0 ) +set_bit (EFI_RUNTIME_SERVICES , &efi.flags); +return 0 ; + } +/* Map the runtime regions */ virtmap =kmalloc (mapsize,GFP_KERNEL );if (!virtmap) {
RFC PATCH 10/10 arm64: Kconfig: enable UEFI on BE kernels
In reply to Ard BiesheuvelThis changes the Kconfig logic to allow EFI to be enabled on a BE kernel build.
--- arch/arm64/Kconfig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-)
arch/arm64/Kconfig +7 -3
@@ -301,16 +301,20 @@ config CMDLINE_FORCE config EFI_STUB bool +config EFI_LE_STUB + bool + config EFI bool "UEFI runtime support" - depends on OF && !CPU_BIG_ENDIAN + depends on OF select LIBFDT select UCS2_STRING select EFI_PARAMS_FROM_FDT select EFI_RUNTIME_WRAPPERS - select EFI_STUB + select EFI_STUB if !CPU_BIG_ENDIAN + select EFI_LE_STUB if CPU_BIG_ENDIAN select EFI_ARMSTUB - default y + default y if !CPU_BIG_ENDIAN help This option provides support for runtime services provided by UEFI firmware (such as non-volatile variables, realtime
Signature · 1 line
1.8.3.2