Skip to content

Commit

Permalink
Merge tag 'efi-next-for-v6.4' of git://git.kernel.org/pub/scm/linux/k…
Browse files Browse the repository at this point in the history
…ernel/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
  • Loading branch information
Linus Torvalds committed Apr 30, 2023
2 parents 17d4ded + 026b857 commit 825a071
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 97 deletions.
4 changes: 4 additions & 0 deletions arch/arm64/boot/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
71 changes: 43 additions & 28 deletions arch/arm64/kernel/efi-header.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions arch/arm64/kernel/image-vars.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
3 changes: 1 addition & 2 deletions drivers/firmware/efi/libstub/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down
43 changes: 29 additions & 14 deletions drivers/firmware/efi/libstub/Makefile.zboot
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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)
Expand All @@ -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
19 changes: 13 additions & 6 deletions drivers/firmware/efi/libstub/arm64.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include <linux/efi.h>
#include <asm/efi.h>
#include <asm/image.h>
#include <asm/memory.h>
#include <asm/sysreg.h>

Expand Down Expand Up @@ -88,26 +89,32 @@ 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,
CTR_EL0_DminLine_SHIFT);

/* 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)
Expand Down
3 changes: 1 addition & 2 deletions drivers/firmware/efi/libstub/efistub.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
24 changes: 7 additions & 17 deletions drivers/firmware/efi/libstub/loongarch-stub.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,16 @@ 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;

*image_addr = EFI_KIMG_PREFERRED_ADDRESS;
*image_size = kernel_asize;
kernel_addr = (unsigned long)&kernel_offset - kernel_offset;

status = efi_relocate_kernel(&kernel_addr, kernel_fsize, kernel_asize,
EFI_KIMG_PREFERRED_ADDRESS, efi_get_kimg_min_align(), 0x0);

memcpy((void *)EFI_KIMG_PREFERRED_ADDRESS,
(void *)&kernel_offset - kernel_offset,
kernel_fsize);
*image_addr = kernel_addr;
*image_size = kernel_asize;

return status;
}
Expand Down
51 changes: 33 additions & 18 deletions drivers/firmware/efi/libstub/zboot-header.S
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down
13 changes: 3 additions & 10 deletions drivers/firmware/efi/libstub/zboot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);

Expand Down
Loading

0 comments on commit 825a071

Please sign in to comment.