EFI updates for v6.4:

- relocate the LoongArch kernel if the preferred address is already
   occupied;
 
 - implement BTI annotations for arm64 EFI stub and zboot images;
 
 - clean up arm64 zboot Kbuild rules for injecting the kernel code size.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYIAB0WIQQQm/3uucuRGn1Dmh0wbglWLn0tXAUCZEwUOwAKCRAwbglWLn0t
 XMNzAQChdPim0N+l2G4XLa1g8WCGany/+6/B9GHPJVcmQ25zLQD/UaNvAofkHwjR
 Y3P3ZEY1SPEA+UJBL/BTI0wO9/XgpAA=
 =hGWP
 -----END PGP SIGNATURE-----

Merge tag 'efi-next-for-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi

Pull EFI updates from Ard Biesheuvel:

 - relocate the LoongArch kernel if the preferred address is already
   occupied

 - implement BTI annotations for arm64 EFI stub and zboot images

 - clean up arm64 zboot Kbuild rules for injecting the kernel code size

* tag 'efi-next-for-v6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/efi/efi:
  efi/zboot: arm64: Grab code size from ELF symbol in payload
  efi/zboot: arm64: Inject kernel code size symbol into the zboot payload
  efi/zboot: Set forward edge CFI compat header flag if supported
  efi/zboot: Add BSS padding before compression
  arm64: efi: Enable BTI codegen and add PE/COFF annotation
  efi/pe: Import new BTI/IBT header flags from the spec
  efi/loongarch: Reintroduce efi_relocate_kernel() to relocate kernel
This commit is contained in:
Linus Torvalds 2023-04-29 17:42:33 -07:00
commit 825a0714d2
12 changed files with 148 additions and 96 deletions

View File

@ -42,5 +42,9 @@ $(obj)/Image.zst: $(obj)/Image FORCE
EFI_ZBOOT_PAYLOAD := Image
EFI_ZBOOT_BFD_TARGET := elf64-littleaarch64
EFI_ZBOOT_MACH_TYPE := ARM64
EFI_ZBOOT_FORWARD_CFI := $(CONFIG_ARM64_BTI_KERNEL)
EFI_ZBOOT_OBJCOPY_FLAGS = --add-symbol zboot_code_size=0x$(shell \
$(NM) vmlinux|grep _kernel_codesize|cut -d' ' -f1)
include $(srctree)/drivers/firmware/efi/libstub/Makefile.zboot

View File

@ -81,9 +81,47 @@
.quad 0 // CertificationTable
.quad 0 // BaseRelocationTable
#ifdef CONFIG_DEBUG_EFI
#if defined(CONFIG_DEBUG_EFI) || defined(CONFIG_ARM64_BTI_KERNEL)
.long .Lefi_debug_table - .L_head // DebugTable
.long .Lefi_debug_table_size
/*
* The debug table is referenced via its Relative Virtual Address (RVA),
* which is only defined for those parts of the image that are covered
* by a section declaration. Since this header is not covered by any
* section, the debug table must be emitted elsewhere. So stick it in
* the .init.rodata section instead.
*
* Note that the payloads themselves are permitted to have zero RVAs,
* which means we can simply put those right after the section headers.
*/
__INITRODATA
.align 2
.Lefi_debug_table:
#ifdef CONFIG_DEBUG_EFI
// EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_CODEVIEW // Type
.long .Lefi_debug_entry_size // SizeOfData
.long 0 // RVA
.long .Lefi_debug_entry - .L_head // FileOffset
#endif
#ifdef CONFIG_ARM64_BTI_KERNEL
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS // Type
.long 4 // SizeOfData
.long 0 // RVA
.long .Lefi_dll_characteristics_ex - .L_head // FileOffset
#endif
.set .Lefi_debug_table_size, . - .Lefi_debug_table
.previous
#endif
// Section table
@ -119,33 +157,6 @@
.set .Lsection_count, (. - .Lsection_table) / 40
#ifdef CONFIG_DEBUG_EFI
/*
* The debug table is referenced via its Relative Virtual Address (RVA),
* which is only defined for those parts of the image that are covered
* by a section declaration. Since this header is not covered by any
* section, the debug table must be emitted elsewhere. So stick it in
* the .init.rodata section instead.
*
* Note that the EFI debug entry itself may legally have a zero RVA,
* which means we can simply put it right after the section headers.
*/
__INITRODATA
.align 2
.Lefi_debug_table:
// EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_CODEVIEW // Type
.long .Lefi_debug_entry_size // SizeOfData
.long 0 // RVA
.long .Lefi_debug_entry - .L_head // FileOffset
.set .Lefi_debug_table_size, . - .Lefi_debug_table
.previous
.Lefi_debug_entry:
// EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY
.ascii "NB10" // Signature
@ -157,6 +168,10 @@
.set .Lefi_debug_entry_size, . - .Lefi_debug_entry
#endif
#ifdef CONFIG_ARM64_BTI_KERNEL
.Lefi_dll_characteristics_ex:
.long IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT
#endif
.balign SEGMENT_ALIGN
.Lefi_header_end:

View File

@ -108,4 +108,8 @@ KVM_NVHE_ALIAS(kvm_protected_mode_initialized);
#endif /* CONFIG_KVM */
#ifdef CONFIG_EFI_ZBOOT
_kernel_codesize = ABSOLUTE(__inittext_end - _text);
#endif
#endif /* __ARM64_KERNEL_IMAGE_VARS_H */

View File

@ -23,8 +23,7 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ \
# arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly
# disable the stackleak plugin
cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \
-fno-unwind-tables -fno-asynchronous-unwind-tables \
$(call cc-option,-mbranch-protection=none)
-fno-unwind-tables -fno-asynchronous-unwind-tables
cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \
-DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \
-DEFI_HAVE_STRCMP -fno-builtin -fpic \

View File

@ -1,7 +1,16 @@
# SPDX-License-Identifier: GPL-2.0
# to be include'd by arch/$(ARCH)/boot/Makefile after setting
# EFI_ZBOOT_PAYLOAD, EFI_ZBOOT_BFD_TARGET and EFI_ZBOOT_MACH_TYPE
# EFI_ZBOOT_PAYLOAD, EFI_ZBOOT_BFD_TARGET, EFI_ZBOOT_MACH_TYPE and
# EFI_ZBOOT_FORWARD_CFI
quiet_cmd_copy_and_pad = PAD $@
cmd_copy_and_pad = cp $< $@ && \
truncate -s $(shell hexdump -s16 -n4 -e '"%u"' $<) $@
# Pad the file to the size of the uncompressed image in memory, including BSS
$(obj)/vmlinux.bin: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE
$(call if_changed,copy_and_pad)
comp-type-$(CONFIG_KERNEL_GZIP) := gzip
comp-type-$(CONFIG_KERNEL_LZ4) := lz4
@ -10,26 +19,32 @@ comp-type-$(CONFIG_KERNEL_LZO) := lzo
comp-type-$(CONFIG_KERNEL_XZ) := xzkern
comp-type-$(CONFIG_KERNEL_ZSTD) := zstd22
# Copy the SizeOfHeaders, SizeOfCode and SizeOfImage fields from the payload to
# the end of the compressed image. Note that this presupposes a PE header
# offset of 64 bytes, which is what arm64, RISC-V and LoongArch use.
quiet_cmd_compwithsize = $(quiet_cmd_$(comp-type-y))
cmd_compwithsize = $(cmd_$(comp-type-y)) && ( \
dd status=none if=$< bs=4 count=1 skip=37 ; \
dd status=none if=$< bs=4 count=1 skip=23 ; \
dd status=none if=$< bs=4 count=1 skip=36 ) >> $@
# in GZIP, the appended le32 carrying the uncompressed size is part of the
# format, but in other cases, we just append it at the end for convenience,
# causing the original tools to complain when checking image integrity.
# So disregard it when calculating the payload size in the zimage header.
zboot-method-y := $(comp-type-y)_with_size
zboot-size-len-y := 4
$(obj)/vmlinuz: $(obj)/$(EFI_ZBOOT_PAYLOAD) FORCE
$(call if_changed,compwithsize)
zboot-method-$(CONFIG_KERNEL_GZIP) := gzip
zboot-size-len-$(CONFIG_KERNEL_GZIP) := 0
OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) \
$(obj)/vmlinuz: $(obj)/vmlinux.bin FORCE
$(call if_changed,$(zboot-method-y))
OBJCOPYFLAGS_vmlinuz.o := -I binary -O $(EFI_ZBOOT_BFD_TARGET) $(EFI_ZBOOT_OBJCOPY_FLAGS) \
--rename-section .data=.gzdata,load,alloc,readonly,contents
$(obj)/vmlinuz.o: $(obj)/vmlinuz FORCE
$(call if_changed,objcopy)
aflags-zboot-header-$(EFI_ZBOOT_FORWARD_CFI) := \
-DPE_DLL_CHAR_EX=IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT
AFLAGS_zboot-header.o += -DMACHINE_TYPE=IMAGE_FILE_MACHINE_$(EFI_ZBOOT_MACH_TYPE) \
-DZBOOT_EFI_PATH="\"$(realpath $(obj)/vmlinuz.efi.elf)\"" \
-DCOMP_TYPE="\"$(comp-type-y)\""
-DZBOOT_SIZE_LEN=$(zboot-size-len-y) \
-DCOMP_TYPE="\"$(comp-type-y)\"" \
$(aflags-zboot-header-y)
$(obj)/zboot-header.o: $(srctree)/drivers/firmware/efi/libstub/zboot-header.S FORCE
$(call if_changed_rule,as_o_S)
@ -44,4 +59,4 @@ OBJCOPYFLAGS_vmlinuz.efi := -O binary
$(obj)/vmlinuz.efi: $(obj)/vmlinuz.efi.elf FORCE
$(call if_changed,objcopy)
targets += zboot-header.o vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi
targets += zboot-header.o vmlinux.bin vmlinuz vmlinuz.o vmlinuz.efi.elf vmlinuz.efi

View File

@ -9,6 +9,7 @@
#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/image.h>
#include <asm/memory.h>
#include <asm/sysreg.h>
@ -88,9 +89,10 @@ efi_status_t check_platform_features(void)
#define DCTYPE "cvau"
#endif
u32 __weak code_size;
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size)
unsigned long alloc_size)
{
u32 ctr = read_cpuid_effective_cachetype();
u64 lsize = 4 << cpuid_feature_extract_unsigned_field(ctr,
@ -98,16 +100,21 @@ void efi_cache_sync_image(unsigned long image_base,
/* only perform the cache maintenance if needed for I/D coherency */
if (!(ctr & BIT(CTR_EL0_IDC_SHIFT))) {
unsigned long base = image_base;
unsigned long size = code_size;
do {
asm("dc " DCTYPE ", %0" :: "r"(image_base));
image_base += lsize;
code_size -= lsize;
} while (code_size >= lsize);
asm("dc " DCTYPE ", %0" :: "r"(base));
base += lsize;
size -= lsize;
} while (size >= lsize);
}
asm("ic ialluis");
dsb(ish);
isb();
efi_remap_image(image_base, alloc_size, code_size);
}
unsigned long __weak primary_entry_offset(void)

View File

@ -1066,8 +1066,7 @@ struct screen_info *__alloc_screen_info(void);
void free_screen_info(struct screen_info *si);
void efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size);
unsigned long alloc_size);
struct efi_smbios_record {
u8 type;

View File

@ -21,27 +21,17 @@ efi_status_t handle_kernel_image(unsigned long *image_addr,
efi_loaded_image_t *image,
efi_handle_t image_handle)
{
int nr_pages = round_up(kernel_asize, EFI_ALLOC_ALIGN) / EFI_PAGE_SIZE;
efi_physical_addr_t kernel_addr = EFI_KIMG_PREFERRED_ADDRESS;
efi_status_t status;
unsigned long kernel_addr = 0;
/*
* Allocate space for the kernel image at the preferred offset. This is
* the only location in memory from where we can execute the image, so
* no point in falling back to another allocation.
*/
status = efi_bs_call(allocate_pages, EFI_ALLOCATE_ADDRESS,
EFI_LOADER_DATA, nr_pages, &kernel_addr);
if (status != EFI_SUCCESS)
return status;
kernel_addr = (unsigned long)&kernel_offset - kernel_offset;
*image_addr = EFI_KIMG_PREFERRED_ADDRESS;
status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
EFI_KIMG_PREFERRED_ADDRESS, efi_get_kimg_min_align(), 0x0);
*image_addr = kernel_addr;
*image_size = kernel_asize;
memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS,
(void *)&kernel_offset - kernel_offset,
kernel_fsize);
return status;
}

View File

@ -17,7 +17,7 @@ __efistub_efi_zboot_header:
.long MZ_MAGIC
.ascii "zimg" // image type
.long __efistub__gzdata_start - .Ldoshdr // payload offset
.long __efistub__gzdata_size - 12 // payload size
.long __efistub__gzdata_size - ZBOOT_SIZE_LEN // payload size
.long 0, 0 // reserved
.asciz COMP_TYPE // compression type
.org .Ldoshdr + 0x38
@ -78,9 +78,36 @@ __efistub_efi_zboot_header:
.quad 0 // ExceptionTable
.quad 0 // CertificationTable
.quad 0 // BaseRelocationTable
#ifdef CONFIG_DEBUG_EFI
#if defined(PE_DLL_CHAR_EX) || defined(CONFIG_DEBUG_EFI)
.long .Lefi_debug_table - .Ldoshdr // DebugTable
.long .Lefi_debug_table_size
.section ".rodata", "a"
.p2align 2
.Lefi_debug_table:
// EFI_IMAGE_DEBUG_DIRECTORY_ENTRY[]
#ifdef PE_DLL_CHAR_EX
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS // Type
.long 4 // SizeOfData
.long 0 // RVA
.long .Lefi_dll_characteristics_ex - .Ldoshdr // FileOffset
#endif
#ifdef CONFIG_DEBUG_EFI
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_CODEVIEW // Type
.long .Lefi_debug_entry_size // SizeOfData
.long 0 // RVA
.long .Lefi_debug_entry - .Ldoshdr // FileOffset
#endif
.set .Lefi_debug_table_size, . - .Lefi_debug_table
.previous
#endif
.Lsection_table:
@ -110,23 +137,11 @@ __efistub_efi_zboot_header:
.set .Lsection_count, (. - .Lsection_table) / 40
#ifdef PE_DLL_CHAR_EX
.Lefi_dll_characteristics_ex:
.long PE_DLL_CHAR_EX
#endif
#ifdef CONFIG_DEBUG_EFI
.section ".rodata", "a"
.align 2
.Lefi_debug_table:
// EFI_IMAGE_DEBUG_DIRECTORY_ENTRY
.long 0 // Characteristics
.long 0 // TimeDateStamp
.short 0 // MajorVersion
.short 0 // MinorVersion
.long IMAGE_DEBUG_TYPE_CODEVIEW // Type
.long .Lefi_debug_entry_size // SizeOfData
.long 0 // RVA
.long .Lefi_debug_entry - .Ldoshdr // FileOffset
.set .Lefi_debug_table_size, . - .Lefi_debug_table
.previous
.Lefi_debug_entry:
// EFI_IMAGE_DEBUG_CODEVIEW_NB10_ENTRY
.ascii "NB10" // Signature

View File

@ -50,8 +50,7 @@ static unsigned long alloc_preferred_address(unsigned long alloc_size)
}
void __weak efi_cache_sync_image(unsigned long image_base,
unsigned long alloc_size,
unsigned long code_size)
unsigned long alloc_size)
{
// Provided by the arch to perform the cache maintenance necessary for
// executable code loaded into memory to be safe for execution.
@ -66,7 +65,7 @@ asmlinkage efi_status_t __efiapi
efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
{
unsigned long compressed_size = _gzdata_end - _gzdata_start;
unsigned long image_base, alloc_size, code_size;
unsigned long image_base, alloc_size;
efi_loaded_image_t *image;
efi_status_t status;
char *cmdline_ptr;
@ -94,10 +93,6 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
alloc_size = round_up(get_unaligned_le32(_gzdata_end - 4),
EFI_ALLOC_ALIGN);
// SizeOfHeaders and SizeOfCode from the compressee's PE/COFF header
code_size = get_unaligned_le32(_gzdata_end - 8) +
get_unaligned_le32(_gzdata_end - 12);
// If the architecture has a preferred address for the image,
// try that first.
image_base = alloc_preferred_address(alloc_size);
@ -140,9 +135,7 @@ efi_zboot_entry(efi_handle_t handle, efi_system_table_t *systab)
goto free_image;
}
efi_cache_sync_image(image_base, alloc_size, code_size);
efi_remap_image(image_base, alloc_size, code_size);
efi_cache_sync_image(image_base, alloc_size);
status = efi_stub_common(handle, image, image_base, cmdline_ptr);

View File

@ -2,6 +2,8 @@
ENTRY(__efistub_efi_zboot_header);
PROVIDE(zboot_code_size = ABSOLUTE(0));
SECTIONS
{
.head : ALIGN(4096) {
@ -17,6 +19,11 @@ SECTIONS
*(.gzdata)
__efistub__gzdata_end = .;
*(.rodata* .init.rodata* .srodata*)
. = ALIGN(4);
__efistub_code_size = .;
LONG(zboot_code_size);
_etext = ALIGN(4096);
. = _etext;
}

View File

@ -118,6 +118,9 @@
#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000
#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000
#define IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT 0x0001
#define IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT 0x0040
/* they actually defined 0x00000000 as well, but I think we'll skip that one. */
#define IMAGE_SCN_RESERVED_0 0x00000001
#define IMAGE_SCN_RESERVED_1 0x00000002
@ -165,6 +168,7 @@
#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */
#define IMAGE_DEBUG_TYPE_CODEVIEW 2
#define IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS 20
#ifndef __ASSEMBLY__