diff --git a/.gitignore b/.gitignore
index 8534fb7..81ee499 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 395c809..e14b9ec 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
@@ -337,6 +347,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..837a8a5 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)
 
@@ -81,5 +97,5 @@ $(TEST_DIR)/hyperv_stimer.elf: $(TEST_DIR)/hyperv.o
 $(TEST_DIR)/hyperv_connections.elf: $(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..26970ab 100644
--- a/x86/Makefile.x86_64
+++ b/x86/Makefile.x86_64
@@ -5,28 +5,39 @@ 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
+else
+out = flat
+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
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) }
+}
