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)
