diff mbox series

[2/4] x86: Add support for building a multiboot2 PE binary

Message ID 20240313150439.791213-3-ross.lagerwall@citrix.com (mailing list archive)
State Superseded
Headers show
Series x86: Multiboot PE support | expand

Commit Message

Ross Lagerwall March 13, 2024, 3:04 p.m. UTC
Add a new config option, CONFIG_MULTIBOOT_PE, that when set changes
xen.gz to be a compressed PE binary instead of a compressed ELF binary.
This requires use with a multiboot2 loader that supports the PE load
type.

Using this option allows the binary to be signed and verified by Shim.
This means the same xen.gz can then be used for BIOS boot, UEFI Boot and
UEFI boot with Secure Boot verification (all with the convenience of
GRUB2 as a bootloader).

Signed-off-by: Ross Lagerwall <ross.lagerwall@citrix.com>
---
 xen/Makefile             |  1 +
 xen/arch/x86/Kconfig     |  6 +++++
 xen/arch/x86/Makefile    | 48 ++++++++++++++++++++++++++++++++++++++++
 xen/arch/x86/boot/head.S | 33 +++++++++++++++++++++++++++
 4 files changed, 88 insertions(+)
diff mbox series

Patch

diff --git a/xen/Makefile b/xen/Makefile
index 21832d640225..c9461c9778cc 100644
--- a/xen/Makefile
+++ b/xen/Makefile
@@ -581,6 +581,7 @@  _clean:
 		-o -name ".*.cmd" -o -name "lib.a" \) -exec rm -f {} \;
 	rm -f include/asm $(TARGET) $(TARGET).gz $(TARGET)-syms $(TARGET)-syms.map
 	rm -f $(TARGET).efi $(TARGET).efi.map $(TARGET).efi.elf $(TARGET).efi.stripped
+	rm -f $(TARGET).elf $(TARGET).map
 	rm -f asm-offsets.s arch/*/include/asm/asm-offsets.h
 	rm -f .banner .allconfig.tmp include/xen/compile.h
 	rm -rf $(objtree)/arch/*/include/generated
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index 1acdffc51c22..2d8f6e687e58 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -348,6 +348,12 @@  config REQUIRE_NX
 	  was unavailable. However, if enabled, Xen will no longer boot on
 	  any CPU which is lacking NX support.
 
+config MULTIBOOT_PE
+	bool "Build the multiboot binary as PE"
+	help
+	  Build the multiboot xen.gz binary as a PE binary rather than ELF.
+	  This allows it to be signed and verified when using Secure Boot
+	  with Shim and a bootloader.
 endmenu
 
 source "common/Kconfig"
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 26d87405297b..e26b1cb35036 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -123,6 +123,53 @@  syms-warn-dup-$(CONFIG_ENFORCE_UNIQUE_SYMBOLS) := --error-dup
 
 orphan-handling-$(call ld-option,--orphan-handling=warn) += --orphan-handling=warn
 
+ifeq ($(CONFIG_MULTIBOOT_PE),y)
+ifneq ($(XEN_BUILD_PE),y)
+$(TARGET): FORCE
+	rm -f $@
+	echo 'PE build not supported'
+else
+$(TARGET): VIRT_BASE = 0x$(shell $(NM) $(obj)/efi/relocs-dummy.o | sed -n 's, A VIRT_START$$,,p')
+$(TARGET): $(objtree)/prelink.o $(note_file) $(obj)/efi.lds \
+           $(obj)/efi/relocs-dummy.o $(obj)/efi/set-coff-flags $(TARGET)-syms $(efi-y)
+ifeq ($(CONFIG_DEBUG_INFO),y)
+	$(if $(filter --strip-debug,$(EFI_LDFLAGS)),echo,:) "Will strip debug info from $(@F)"
+endif
+	$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) --disable-reloc-section \
+	      -T $(obj)/efi.lds -N $< $(relocs-dummy) \
+	      $(objtree)/common/symbols-dummy.o $(note_file_option) \
+	      -o $(dot-target).$(VIRT_BASE).0
+	$(NM) -pa --format=sysv $(dot-target).$(VIRT_BASE).0 \
+		| $(objtree)/tools/symbols $(all_symbols) --sysv --sort \
+		> $(dot-target).0s.S
+	$(MAKE) $(build)=$(@D) .$(@F).0s.o
+	$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) --disable-reloc-section \
+	      -T $(obj)/efi.lds -N $< $(dot-target).0s.o $(note_file_option) \
+	      -o $(dot-target).$(VIRT_BASE).1
+	$(NM) -pa --format=sysv $(dot-target).$(VIRT_BASE).1 \
+		| $(objtree)/tools/symbols $(all_symbols) --sysv --sort \
+		> $(dot-target).1s.S
+	$(MAKE) $(build)=$(@D) .$(@F).1s.o
+	$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) --disable-reloc-section \
+	      -T $(obj)/efi.lds -N $< $(dot-target).1s.o $(orphan-handling-y) \
+	      $(note_file_option) -o $@.tmp
+	od -t x4 -N 8192 $@.tmp  | grep 1badb002 > /dev/null || \
+		{ echo "No Multiboot1 header found" >&2; false; }
+	od -t x4 -N 32768 $@.tmp | grep e85250d6 > /dev/null || \
+		{ echo "No Multiboot2 header found" >&2; false; }
+	mv $@.tmp $(TARGET)
+	$(NM) -pa --format=sysv $@ \
+		| $(objtree)/tools/symbols --all-symbols --xensyms --sysv --sort \
+		> $@.map
+ifeq ($(CONFIG_DEBUG_INFO),y)
+	$(if $(filter --strip-debug,$(EFI_LDFLAGS)),:$(space))$(OBJCOPY) -O elf64-x86-64 $@ $@.elf
+endif
+	rm -f $(dot-target).[0-9]* $(@D)/..$(@F).[0-9]*
+ifeq ($(CONFIG_XEN_IBT),y)
+	$(SHELL) $(srctree)/tools/check-endbr.sh $@
+endif
+endif
+else
 $(TARGET): TMP = $(dot-target).elf32
 $(TARGET): $(TARGET)-syms $(efi-y) $(obj)/boot/mkelf32
 	$(obj)/boot/mkelf32 $(notes_phdrs) $(TARGET)-syms $(TMP) $(XEN_IMG_OFFSET) \
@@ -132,6 +179,7 @@  $(TARGET): $(TARGET)-syms $(efi-y) $(obj)/boot/mkelf32
 	od -t x4 -N 32768 $(TMP) | grep e85250d6 > /dev/null || \
 		{ echo "No Multiboot2 header found" >&2; false; }
 	mv $(TMP) $(TARGET)
+endif
 
 CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
 
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index 015023915a72..84dc8b76b61d 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -44,6 +44,25 @@ 
 .Lmb2ht_init_end\@:
         .endm
 
+        .macro mb2ht_qargs arg:req, args:vararg
+        .quad \arg
+        .ifnb \args
+        mb2ht_args \args
+        .endif
+        .endm
+
+        .macro mb2ht_qinit type:req, req:req, args:vararg
+        .balign MULTIBOOT2_TAG_ALIGN, 0xc2 /* Avoid padding with long nops. */
+.Lmb2ht_init_start\@:
+        .short \type
+        .short \req
+        .long .Lmb2ht_init_end\@ - .Lmb2ht_init_start\@
+        .ifnb \args
+        mb2ht_qargs \args
+        .endif
+.Lmb2ht_init_end\@:
+        .endm
+
 ENTRY(start)
         jmp     __start
 
@@ -90,8 +109,14 @@  multiboot2_header:
                    0x200000, /* Load address alignment (2 MiB). */ \
                    MULTIBOOT2_LOAD_PREFERENCE_HIGH
 
+        /* Load type */
+#ifdef CONFIG_MULTIBOOT_PE
+        mb2ht_init MB2_HT(LOAD_TYPE), MB2_HT(OPTIONAL), \
+                   MULTIBOOT2_LOAD_TYPE_PE
+#else
         mb2ht_init MB2_HT(LOAD_TYPE), MB2_HT(OPTIONAL), \
                    MULTIBOOT2_LOAD_TYPE_ELF
+#endif
 
         /* Console flags tag. */
         mb2ht_init MB2_HT(CONSOLE_FLAGS), MB2_HT(OPTIONAL), \
@@ -107,8 +132,16 @@  multiboot2_header:
         mb2ht_init MB2_HT(EFI_BS), MB2_HT(OPTIONAL)
 
         /* EFI64 Multiboot2 entry point. */
+#ifdef CONFIG_MULTIBOOT_PE
+        mb2ht_qinit MB2_HT(ENTRY_ADDRESS_EFI64), MB2_HT(OPTIONAL), \
+                   __efi64_mb2_start
+
+        mb2ht_qinit MB2_HT(ENTRY_ADDRESS), MB2_HT(OPTIONAL), \
+                   start
+#else
         mb2ht_init MB2_HT(ENTRY_ADDRESS_EFI64), MB2_HT(OPTIONAL), \
                    sym_offs(__efi64_mb2_start)
+#endif
 
         /* Multiboot2 header end tag. */
         mb2ht_init MB2_HT(END), MB2_HT(REQUIRED)