kvm-unit-tests produces tests as ELF binaries that can't be directly loaded with EFI. One way of doing so however, is to build the tests as shared libs (-pic, -shared), and then objcopy-ing out the relevant sections into a PE32+. This adds support to compile the tests as an intermediary .so lib, which is linked via x86/efi.lds to contain 4K aligned COFF-compatible sections. The linker script sets up _efi_pe_entry as the image entrypoint, and the .so sections get repackaged to an EFI binary via `objcopy --target efi-app-x86_64`. The 32-bit / long mode transition / multiboot / AP setup code within cstart64.S being incompatible with EFI builds is now hidden behind !CONFIG_EFI. It stays enabled without `configure --efi`. Some tests that don't support building with -fpic / -shared (and need some cleanups) are also moved to build on non-EFI only for now. This gets us dud EFI binaries that enter into _efi_pe_entry and die. No change to non- --efi builds. Signed-off-by: Varad Gautam <varad.gautam@xxxxxxxx> --- .gitignore | 2 ++ Makefile | 16 +++++++++-- configure | 11 ++++++++ x86/Makefile.common | 52 +++++++++++++++++++++++------------ x86/Makefile.x86_64 | 51 +++++++++++++++++++++------------- x86/cstart64.S | 27 +++++++++++++++++- x86/efi.lds | 67 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 186 insertions(+), 40 deletions(-) create mode 100644 x86/efi.lds diff --git a/.gitignore b/.gitignore index b3cf2cb..f83bdf6 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,9 @@ tags *.a *.d *.o +*.so *.flat +*.efi *.elf .pc patches diff --git a/Makefile b/Makefile index f7b9f28..273b1df 100644 --- a/Makefile +++ b/Makefile @@ -50,7 +50,11 @@ fomit_frame_pointer := $(call cc-option, $(frame-pointer-flag), "") fno_stack_protector := $(call cc-option, -fno-stack-protector, "") fno_stack_protector_all := $(call cc-option, -fno-stack-protector-all, "") wno_frame_address := $(call cc-option, -Wno-frame-address, "") -fno_pic := $(call cc-option, -fno-pic, "") +ifeq ($(CONFIG_EFI), y) +opt_pic := -fpic +else +opt_pic := $(call cc-option, -fno-pic, "") +endif no_pie := $(call cc-option, -no-pie, "") wclobbered := $(call cc-option, -Wclobbered, "") wunused_but_set_parameter := $(call cc-option, -Wunused-but-set-parameter, "") @@ -62,10 +66,15 @@ COMMON_CFLAGS += $(fno_stack_protector) COMMON_CFLAGS += $(fno_stack_protector_all) COMMON_CFLAGS += $(wno_frame_address) COMMON_CFLAGS += $(if $(U32_LONG_FMT),-D__U32_LONG_FMT__,) -COMMON_CFLAGS += $(fno_pic) $(no_pie) +COMMON_CFLAGS += $(opt_pic) $(no_pie) COMMON_CFLAGS += $(wclobbered) COMMON_CFLAGS += $(wunused_but_set_parameter) +ifeq ($(CONFIG_EFI),y) +COMMON_CFLAGS += -mno-red-zone -fshort-wchar -DCONFIG_EFI -ffreestanding \ + -fno-stack-check +endif + CFLAGS += $(COMMON_CFLAGS) CFLAGS += $(wmissing_parameter_type) CFLAGS += $(wold_style_declaration) @@ -74,6 +83,9 @@ CFLAGS += -Woverride-init -Wmissing-prototypes -Wstrict-prototypes autodepend-flags = -MMD -MF $(dir $*).$(notdir $*).d LDFLAGS += $(CFLAGS) +ifeq ($(CONFIG_EFI),y) +LDFLAGS += -nostdlib --warn-common --no-undefined --fatal-warnings +endif $(libcflat): $(cflatobjs) $(AR) rcs $@ $^ diff --git a/configure b/configure index 1d4d855..2d1b1bc 100755 --- a/configure +++ b/configure @@ -28,6 +28,7 @@ erratatxt="$srcdir/errata.txt" host_key_document= page_size= earlycon= +config_efi= usage() { cat <<-EOF @@ -69,6 +70,7 @@ usage() { pl011,mmio32,ADDR Specify a PL011 compatible UART at address ADDR. Supported register stride is 32 bit only. + --efi Build with EFI support (x86_64 only). EOF exit 1 } @@ -133,6 +135,9 @@ while [[ "$1" = -* ]]; do --earlycon) earlycon="$arg" ;; + --efi) + config_efi=y + ;; --help) usage ;; @@ -192,6 +197,11 @@ elif [ "$processor" = "arm" ]; then processor="cortex-a15" fi +if [ "$config_efi" = y ] && [ "$arch" != "x86_64" ]; then + echo "--efi only supported on x86_64" + usage +fi + if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then testdir=x86 elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then @@ -341,6 +351,7 @@ U32_LONG_FMT=$u32_long WA_DIVIDE=$wa_divide GENPROTIMG=${GENPROTIMG-genprotimg} HOST_KEY_DOCUMENT=$host_key_document +CONFIG_EFI=$config_efi EOF if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then echo "TARGET=$target" >> config.mak diff --git a/x86/Makefile.common b/x86/Makefile.common index 52bb7aa..fc9a693 100644 --- a/x86/Makefile.common +++ b/x86/Makefile.common @@ -38,7 +38,7 @@ COMMON_CFLAGS += -O1 KEEP_FRAME_POINTER := y # We want to keep intermediate file: %.elf and %.o -.PRECIOUS: %.elf %.o +.PRECIOUS: %.elf %.o %.so FLATLIBS = lib/libcflat.a %.elf: %.o $(FLATLIBS) $(SRCDIR)/x86/flat.lds $(cstart.o) @@ -50,17 +50,33 @@ FLATLIBS = lib/libcflat.a $(OBJCOPY) -O elf32-i386 $^ $@ @chmod a-x $@ -tests-common = $(TEST_DIR)/vmexit.flat $(TEST_DIR)/tsc.flat \ - $(TEST_DIR)/smptest.flat \ - $(TEST_DIR)/realmode.flat $(TEST_DIR)/msr.flat \ - $(TEST_DIR)/hypercall.flat $(TEST_DIR)/sieve.flat \ - $(TEST_DIR)/kvmclock_test.flat $(TEST_DIR)/eventinj.flat \ - $(TEST_DIR)/s3.flat $(TEST_DIR)/pmu.flat $(TEST_DIR)/setjmp.flat \ - $(TEST_DIR)/tsc_adjust.flat $(TEST_DIR)/asyncpf.flat \ - $(TEST_DIR)/init.flat $(TEST_DIR)/smap.flat \ - $(TEST_DIR)/hyperv_synic.flat $(TEST_DIR)/hyperv_stimer.flat \ - $(TEST_DIR)/hyperv_connections.flat \ - $(TEST_DIR)/umip.flat $(TEST_DIR)/tsx-ctrl.flat +%.so: %.o $(FLATLIBS) $(cstart.o) + $(LD) -shared -nostdlib -znocombreloc -Bsymbolic -T $(SRCDIR)/x86/efi.lds $^ \ + -o $@ $(FLATLIBS) + @chmod a-x $@ + +%.efi: %.so + $(OBJCOPY) -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel \ + -j .rela -j .rel.* -j .rela.* -j .rel* -j .rela* \ + -j .reloc -j .init --target efi-app-x86_64 $*.so $@ + @chmod a-x $@ + +tests-flatonly = $(TEST_DIR)/realmode.$(out) $(TEST_DIR)/eventinj.$(out) \ + $(TEST_DIR)/smap.$(out) $(TEST_DIR)/umip.$(out) + +tests-common = $(TEST_DIR)/vmexit.$(out) $(TEST_DIR)/tsc.$(out) \ + $(TEST_DIR)/smptest.$(out) $(TEST_DIR)/msr.$(out) \ + $(TEST_DIR)/hypercall.$(out) $(TEST_DIR)/sieve.$(out) \ + $(TEST_DIR)/kvmclock_test.$(out) $(TEST_DIR)/s3.$(out) \ + $(TEST_DIR)/pmu.$(out) $(TEST_DIR)/setjmp.$(out) \ + $(TEST_DIR)/tsc_adjust.$(out) $(TEST_DIR)/asyncpf.$(out) \ + $(TEST_DIR)/init.$(out) $(TEST_DIR)/hyperv_synic.$(out) \ + $(TEST_DIR)/hyperv_stimer.$(out) $(TEST_DIR)/hyperv_connections.$(out) \ + $(TEST_DIR)/tsx-ctrl.$(out) + +ifneq ($(CONFIG_EFI),y) +tests-common += $(tests-flatonly) +endif test_cases: $(tests-common) $(tests) @@ -72,14 +88,14 @@ $(TEST_DIR)/realmode.elf: $(TEST_DIR)/realmode.o $(TEST_DIR)/realmode.o: bits = $(if $(call cc-option,-m16,""),16,32) -$(TEST_DIR)/kvmclock_test.elf: $(TEST_DIR)/kvmclock.o +$(TEST_DIR)/kvmclock_test.$(bin): $(TEST_DIR)/kvmclock.o -$(TEST_DIR)/hyperv_synic.elf: $(TEST_DIR)/hyperv.o +$(TEST_DIR)/hyperv_synic.$(bin): $(TEST_DIR)/hyperv.o -$(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o +$(TEST_DIR)/hyperv_stimer.$(bin): $(TEST_DIR)/hyperv.o -$(TEST_DIR)/hyperv_connections.elf: $(TEST_DIR)/hyperv.o +$(TEST_DIR)/hyperv_connections.$(bin): $(TEST_DIR)/hyperv.o arch_clean: - $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.flat $(TEST_DIR)/*.elf \ - $(TEST_DIR)/.*.d lib/x86/.*.d \ + $(RM) $(TEST_DIR)/*.o $(TEST_DIR)/*.$(out) $(TEST_DIR)/*.elf \ + $(TEST_DIR)/.*.d lib/x86/.*.d $(TEST_DIR)/*.so \ diff --git a/x86/Makefile.x86_64 b/x86/Makefile.x86_64 index 8134952..f6c7bd7 100644 --- a/x86/Makefile.x86_64 +++ b/x86/Makefile.x86_64 @@ -5,33 +5,46 @@ ldarch = elf64-x86-64 fcf_protection_full := $(call cc-option, -fcf-protection=full,) COMMON_CFLAGS += -mno-red-zone -mno-sse -mno-sse2 $(fcf_protection_full) +ifeq ($(CONFIG_EFI),y) +out = efi +bin = so +else +out = flat +bin = elf +endif + cflatobjs += lib/x86/setjmp64.o cflatobjs += lib/x86/intel-iommu.o cflatobjs += lib/x86/usermode.o -tests = $(TEST_DIR)/access.flat $(TEST_DIR)/apic.flat \ - $(TEST_DIR)/emulator.flat $(TEST_DIR)/idt_test.flat \ - $(TEST_DIR)/xsave.flat $(TEST_DIR)/rmap_chain.flat \ - $(TEST_DIR)/pcid.flat $(TEST_DIR)/debug.flat \ - $(TEST_DIR)/ioapic.flat $(TEST_DIR)/memory.flat \ - $(TEST_DIR)/pku.flat $(TEST_DIR)/hyperv_clock.flat -tests += $(TEST_DIR)/syscall.flat -tests += $(TEST_DIR)/svm.flat -tests += $(TEST_DIR)/vmx.flat -tests += $(TEST_DIR)/tscdeadline_latency.flat -tests += $(TEST_DIR)/intel-iommu.flat -tests += $(TEST_DIR)/vmware_backdoors.flat -tests += $(TEST_DIR)/rdpru.flat -tests += $(TEST_DIR)/pks.flat -tests += $(TEST_DIR)/pmu_lbr.flat +# Tests that have relocation / PIC problems and need more attention for EFI. +tests_flatonly = $(TEST_DIR)/access.$(out) $(TEST_DIR)/emulator.$(out) \ + $(TEST_DIR)/svm.$(out) $(TEST_DIR)/vmx.$(out) \ + $(TEST_DIR)/vmware_backdoors.$(out) + +tests = $(TEST_DIR)/apic.$(out) $(TEST_DIR)/idt_test.$(out) \ + $(TEST_DIR)/xsave.$(out) $(TEST_DIR)/rmap_chain.$(out) \ + $(TEST_DIR)/pcid.$(out) $(TEST_DIR)/debug.$(out) \ + $(TEST_DIR)/ioapic.$(out) $(TEST_DIR)/memory.$(out) \ + $(TEST_DIR)/pku.$(out) $(TEST_DIR)/hyperv_clock.$(out) +tests += $(TEST_DIR)/syscall.$(out) +tests += $(TEST_DIR)/tscdeadline_latency.$(out) +tests += $(TEST_DIR)/intel-iommu.$(out) +tests += $(TEST_DIR)/rdpru.$(out) +tests += $(TEST_DIR)/pks.$(out) +tests += $(TEST_DIR)/pmu_lbr.$(out) ifneq ($(fcf_protection_full),) -tests += $(TEST_DIR)/cet.flat +tests_flatonly += $(TEST_DIR)/cet.$(out) +endif + +ifneq ($(CONFIG_EFI),y) +tests += $(tests_flatonly) endif include $(SRCDIR)/$(TEST_DIR)/Makefile.common -$(TEST_DIR)/hyperv_clock.elf: $(TEST_DIR)/hyperv_clock.o +$(TEST_DIR)/hyperv_clock.$(bin): $(TEST_DIR)/hyperv_clock.o -$(TEST_DIR)/vmx.elf: $(TEST_DIR)/vmx_tests.o -$(TEST_DIR)/svm.elf: $(TEST_DIR)/svm_tests.o +$(TEST_DIR)/vmx.$(bin): $(TEST_DIR)/vmx_tests.o +$(TEST_DIR)/svm.$(bin): $(TEST_DIR)/svm_tests.o diff --git a/x86/cstart64.S b/x86/cstart64.S index 5c6ad38..404fcac 100644 --- a/x86/cstart64.S +++ b/x86/cstart64.S @@ -101,20 +101,26 @@ i = i + 1 .endr tss_end: +#ifndef CONFIG_EFI mb_boot_info: .quad 0 +#endif pt_root: .quad ptl4 +#ifndef CONFIG_EFI .section .init +#endif .code32 +#ifndef CONFIG_EFI mb_magic = 0x1BADB002 mb_flags = 0x0 # multiboot header .long mb_magic, mb_flags, 0 - (mb_magic + mb_flags) mb_cmdline = 16 +#endif MSR_GS_BASE = 0xc0000101 @@ -140,6 +146,7 @@ MSR_GS_BASE = 0xc0000101 wrmsr .endm +#ifndef CONFIG_EFI .globl start start: mov %ebx, mb_boot_info @@ -231,16 +238,20 @@ ap_start32: setup_percpu_area call prepare_64 ljmpl $8, $ap_start64 +#endif /* CONFIG_EFI */ .code64 save_id: +#ifndef CONFIG_EFI movl $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax movl (%rax), %eax shrl $24, %eax lock btsl %eax, online_cpus +#endif retq ap_start64: +#ifndef CONFIG_EFI call reset_apic call load_tss call enable_apic @@ -249,11 +260,18 @@ ap_start64: sti nop lock incw cpu_online_count - +#endif 1: hlt jmp 1b +#ifdef CONFIG_EFI +.globl _efi_pe_entry +_efi_pe_entry: + ret +#endif + start64: +#ifndef CONFIG_EFI call reset_apic call load_tss call mask_pic_interrupts @@ -277,9 +295,11 @@ start64: call main mov %eax, %edi call exit +#endif .globl setup_5level_page_table setup_5level_page_table: +#ifndef CONFIG_EFI /* Check if 5-level paging has already enabled */ mov %cr4, %rax test $0x1000, %eax @@ -287,6 +307,7 @@ setup_5level_page_table: pushq $32 pushq $switch_to_5level +#endif lretq lvl5: retq @@ -299,6 +320,7 @@ online_cpus: .fill (max_cpus + 7) / 8, 1, 0 load_tss: +#ifndef CONFIG_EFI lidtq idt_descr mov $(APIC_DEFAULT_PHYS_BASE + APIC_ID), %eax mov (%rax), %eax @@ -317,9 +339,11 @@ load_tss: mov %eax, tss_descr+8(%rbx) lea tss_descr-gdt64(%rbx), %rax ltr %ax +#endif ret ap_init: +#ifndef CONFIG_EFI cld lea sipi_entry, %rsi xor %rdi, %rdi @@ -332,6 +356,7 @@ ap_init: 1: pause cmpw %ax, cpu_online_count jne 1b +#endif ret cpu_online_count: .word 1 diff --git a/x86/efi.lds b/x86/efi.lds new file mode 100644 index 0000000..9ed1272 --- /dev/null +++ b/x86/efi.lds @@ -0,0 +1,67 @@ +/* Same as gnu-efi's elf_x86_64_fbsd_efi.lds. */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_efi_pe_entry) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } + .gnu.hash : { *(.gnu.hash) } + . = ALIGN(4096); + .text : + { + _text = .; + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + . = ALIGN(16); + } + _etext = .; + _text_size = . - _text; + . = ALIGN(4096); + .reloc : + { + LONG(_data); + LONG(10); + SHORT(0); + *(.reloc) + } + . = ALIGN(4096); + .data : + { + _data = .; + exception_table_start = .; + *(.data.ex) + exception_table_end = .; + *(.rodata*) + *(.got.plt) + *(.got) + *(.data*) + *(.sdata) + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + *(.rel.local) + } + .note.gnu.build-id : { *(.note.gnu.build-id) } + edata = .; + _data_size = . - _etext; + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.data*) + *(.rela.got) + *(.rela.stab) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + .comment 0 : { *(.comment) } +} -- 2.30.2