Skip to content

Commit 495a071

Browse files
committed
x86/slaunch: support EFI boot
When running on an EFI-enabled system, Xen needs to have access to Boot Services in order to initialize itself properly and reach a state in which a dom0 kernel can operate without issues. This means that DRTM must be started in the middle of Xen's initialization process. This effect is achieved via a callback into bootloader (GRUB) which is responsible for initiating DRTM and continuing Xen's initialization process. The latter is done by branching in Slaunch entry point on a flag to switch back into long mode before calling the same function which Xen would execute as the next step without DRTM. Signed-off-by: Krystian Hebel <[email protected]> Signed-off-by: Sergii Dmytruk <[email protected]>
1 parent 4e788a9 commit 495a071

File tree

11 files changed

+431
-12
lines changed

11 files changed

+431
-12
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ xen/.xen.elf32
201201
xen/System.map
202202
xen/arch/x86/efi.lds
203203
xen/arch/x86/efi/check.efi
204+
xen/arch/x86/efi/fixmlehdr
204205
xen/arch/x86/efi/mkreloc
205206
xen/arch/x86/include/asm/asm-macros.h
206207
xen/arch/*/xen.lds

docs/hypervisor-guide/x86/how-xen-boots.rst

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ If ``CONFIG_PVH_GUEST`` was selected at build time, an Elf note is included
5555
which indicates the ability to use the PVH boot protocol, and registers
5656
``__pvh_start`` as the entrypoint, entered in 32bit mode.
5757

58-
A combination of Multiboot 2 and MLE headers is used to implement DRTM for
59-
legacy (BIOS) boot. The separate entry point is used mainly to differentiate
60-
from other kinds of boots. It moves a magic number to EAX before jumping into
61-
common startup code.
58+
A combination of Multiboot 2 and MLE headers is used to implement DRTM. The
59+
separate entry point is used mainly to differentiate from other kinds of boots.
60+
For a legacy (BIOS) boot, it moves a magic number to EAX before jumping into
61+
common startup code. For a EFI boot, it resumes execution of Xen.efi which was
62+
paused by handing control to a part of a bootloader responsible for initiating
63+
DRTM sequence.
6264

6365

6466
xen.gz

xen/arch/x86/Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ extra-y += xen.lds
9090

9191
hostprogs-y += boot/mkelf32
9292
hostprogs-y += efi/mkreloc
93+
hostprogs-y += efi/fixmlehdr
9394

9495
$(obj)/efi/mkreloc: HOSTCFLAGS += -I$(srctree)/include
9596

@@ -141,6 +142,10 @@ $(TARGET): $(TARGET)-syms $(efi-y) $(obj)/boot/mkelf32
141142

142143
CFLAGS-$(XEN_BUILD_EFI) += -DXEN_BUILD_EFI
143144

145+
ifeq ($(XEN_BUILD_EFI),y)
146+
XEN_AFLAGS += -DXEN_BUILD_EFI
147+
endif
148+
144149
$(TARGET)-syms: $(objtree)/prelink.o $(obj)/xen.lds
145150
$(LD) $(XEN_LDFLAGS) -T $(obj)/xen.lds -N $< $(build_id_linker) \
146151
$(objtree)/common/symbols-dummy.o -o $(dot-target).0
@@ -210,7 +215,7 @@ note_file_option ?= $(note_file)
210215

211216
extra-$(XEN_BUILD_PE) += efi.lds
212217
ifeq ($(XEN_BUILD_PE),y)
213-
$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds $(obj)/efi/relocs-dummy.o $(obj)/efi/mkreloc
218+
$(TARGET).efi: $(objtree)/prelink.o $(note_file) $(obj)/efi.lds $(obj)/efi/relocs-dummy.o $(obj)/efi/mkreloc $(obj)/efi/fixmlehdr
214219
ifeq ($(CONFIG_DEBUG_INFO),y)
215220
$(if $(filter --strip-debug,$(EFI_LDFLAGS)),echo,:) "Will strip debug info from $(@F)"
216221
endif
@@ -237,6 +242,8 @@ endif
237242
$(LD) $(call EFI_LDFLAGS,$(VIRT_BASE)) -T $(obj)/efi.lds -N $< \
238243
$(dot-target).1r.o $(dot-target).1s.o $(orphan-handling-y) \
239244
$(note_file_option) -o $@
245+
# take image offset into account
246+
$(obj)/efi/fixmlehdr $@ $(XEN_IMG_OFFSET)
240247
$(NM) -pa --format=sysv $@ \
241248
| $(objtree)/tools/symbols --all-symbols --xensyms --sysv --sort \
242249

xen/arch/x86/boot/head.S

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,12 @@ slaunch_stub_entry:
397397
mov %ebx, %esi
398398
sub $sym_offs(slaunch_stub_entry), %esi
399399

400+
#ifdef XEN_BUILD_EFI
401+
/* If the flag is already set, then Xen should continue execution. */
402+
cmpb $0, sym_esi(slaunch_active)
403+
jne slaunch_efi_jumpback
404+
#endif
405+
400406
/* On AMD, %ebp holds the base address of SLB, save it for later. */
401407
mov %ebp, %ebx
402408

@@ -836,6 +842,124 @@ trampoline_setup:
836842
/* Jump into the relocated trampoline. */
837843
lret
838844

845+
#ifdef XEN_BUILD_EFI
846+
847+
/*
848+
* The state matches that of slaunch_stub_entry above, but with %esi
849+
* already initialized.
850+
*/
851+
slaunch_efi_jumpback:
852+
lea STACK_SIZE - CPUINFO_sizeof + sym_esi(cpu0_stack), %esp
853+
854+
/* Prepare gdt and segments. */
855+
add %esi, sym_esi(gdt_boot_base)
856+
lgdt sym_esi(gdt_boot_descr)
857+
858+
mov $BOOT_DS, %ecx
859+
mov %ecx, %ds
860+
mov %ecx, %es
861+
mov %ecx, %ss
862+
863+
push $BOOT_CS32
864+
lea sym_esi(.Lgdt_is_set),%edx
865+
push %edx
866+
lret
867+
.Lgdt_is_set:
868+
869+
/*
870+
* Stash TSC as above because it was zeroed on jumping into bootloader
871+
* to not interfere with measurements.
872+
*/
873+
rdtsc
874+
mov %eax, sym_esi(boot_tsc_stamp)
875+
mov %edx, 4 + sym_esi(boot_tsc_stamp)
876+
877+
/*
878+
* Clear the pagetables before the use. We are loaded below 4GiB and
879+
* this avoids the need for writing to higher dword of each entry.
880+
* Additionally, this ensures those dwords are actually zero and the
881+
* mappings aren't manipulated from outside.
882+
*/
883+
lea sym_esi(bootmap_start), %edi
884+
lea sym_esi(bootmap_end), %ecx
885+
sub %edi, %ecx
886+
xor %eax, %eax
887+
shr $2, %ecx
888+
rep stosl
889+
890+
/* 1x L1 page, 512 entries mapping total of 2M. */
891+
lea sym_esi(l1_bootmap), %edi
892+
mov $512, %ecx
893+
mov $(__PAGE_HYPERVISOR + 512 * PAGE_SIZE), %edx
894+
.Lfill_l1_identmap:
895+
sub $PAGE_SIZE, %edx
896+
/* Loop runs for ecx=[512..1] for entries [511..0], hence -8. */
897+
mov %edx, -8(%edi,%ecx,8)
898+
loop .Lfill_l1_identmap
899+
900+
/* 4x L2 pages, each page mapping 1G of RAM. */
901+
lea sym_esi(l2_bootmap), %edi
902+
/* 1st entry points to L1. */
903+
lea (sym_offs(l1_bootmap) + __PAGE_HYPERVISOR)(%esi), %edx
904+
mov %edx, (%edi)
905+
/* Other entries are 2MB pages. */
906+
mov $(4 * 512 - 1), %ecx
907+
/*
908+
* Value below should be 4GB + flags, which wouldn't fit in 32b
909+
* register. To avoid warning from the assembler, 4GB is skipped here.
910+
* Substitution in first iteration makes the value roll over and point
911+
* to 4GB - 2MB + flags.
912+
*/
913+
mov $(_PAGE_PSE + __PAGE_HYPERVISOR), %edx
914+
.Lfill_l2_identmap:
915+
sub $(1 << L2_PAGETABLE_SHIFT), %edx
916+
/* Loop runs for ecx=[2047..1] for entries [2047..1]. */
917+
mov %edx, (%edi,%ecx,8)
918+
loop .Lfill_l2_identmap
919+
920+
/* 1x L3 page, mapping the 4x L2 pages. */
921+
lea sym_esi(l3_bootmap), %edi
922+
mov $4, %ecx
923+
lea (sym_offs(l2_bootmap) + 4 * PAGE_SIZE + __PAGE_HYPERVISOR)(%esi), %edx
924+
.Lfill_l3_identmap:
925+
sub $PAGE_SIZE, %edx
926+
/* Loop runs for ecx=[4..1] for entries [3..0], hence -8. */
927+
mov %edx, -8(%edi,%ecx,8)
928+
loop .Lfill_l3_identmap
929+
930+
/* 1x L4 page, mapping the L3 page. */
931+
lea (sym_offs(l3_bootmap) + __PAGE_HYPERVISOR)(%esi), %edx
932+
mov %edx, sym_esi(l4_bootmap)
933+
934+
/* Restore CR4, PAE must be enabled before IA-32e mode */
935+
mov %cr4, %ecx
936+
or $X86_CR4_PAE, %ecx
937+
mov %ecx, %cr4
938+
939+
/* Load PML4 table location into PT base register */
940+
lea sym_esi(l4_bootmap), %eax
941+
mov %eax, %cr3
942+
943+
/* Enable IA-32e mode and paging */
944+
mov $MSR_EFER, %ecx
945+
rdmsr
946+
or $EFER_LME >> 8, %ah
947+
wrmsr
948+
949+
mov %cr0, %eax
950+
or $X86_CR0_PG | X86_CR0_NE | X86_CR0_TS | X86_CR0_MP, %eax
951+
mov %eax, %cr0
952+
953+
/* Now in IA-32e compatibility mode, use lret to jump to 64b mode */
954+
lea sym_esi(start_xen_from_efi), %ecx
955+
push $BOOT_CS64
956+
push %ecx
957+
lret
958+
959+
.global start_xen_from_efi
960+
961+
#endif /* XEN_BUILD_EFI */
962+
839963
ENTRY(trampoline_start)
840964
#include "trampoline.S"
841965
ENTRY(trampoline_end)

xen/arch/x86/boot/x86_64.S

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,22 @@ GLOBAL(__page_tables_end)
227227
/* Init pagetables. Enough page directories to map into 4GB. */
228228
.section .init.data, "aw", @progbits
229229

230-
DATA_LOCAL(l1_bootmap, PAGE_SIZE)
230+
bootmap_start:
231+
232+
DATA_LOCAL(l1_bootmap, PAGE_SIZE) /* 1x L1 page, mapping 2M of RAM. */
231233
.fill L1_PAGETABLE_ENTRIES, 8, 0
232234
END(l1_bootmap)
233235

234-
DATA(l2_bootmap, PAGE_SIZE)
236+
DATA(l2_bootmap, PAGE_SIZE) /* 4x L2 pages, each mapping 1G of RAM. */
235237
.fill 4 * L2_PAGETABLE_ENTRIES, 8, 0
236238
END(l2_bootmap)
237239

238-
DATA(l3_bootmap, PAGE_SIZE)
240+
DATA(l3_bootmap, PAGE_SIZE) /* 1x L3 page, mapping the 4x L2 pages. */
239241
.fill L3_PAGETABLE_ENTRIES, 8, 0
240242
END(l3_bootmap)
243+
244+
DATA_LOCAL(l4_bootmap, PAGE_SIZE) /* 1x L4 page, mapping the L3 page. */
245+
.fill L4_PAGETABLE_ENTRIES, 8, 0
246+
END(l4_bootmap)
247+
248+
bootmap_end:

xen/arch/x86/efi/efi-boot.h

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,18 @@
1313
#include <asm/setup.h>
1414
#include <asm/trampoline.h>
1515
#include <asm/efi.h>
16+
#include <asm/slaunch.h>
17+
18+
/*
19+
* Make <asm/intel_txt.h> access TXT registers without address translation which
20+
* is not set up at this point.
21+
*/
22+
#define __BOOT_DEFS_H__
23+
#include <asm/intel_txt.h>
24+
#undef __BOOT_DEFS_H__
1625

1726
static struct file __initdata ucode;
27+
static uint64_t __initdata xen_image_size;
1828
static multiboot_info_t __initdata mbi = {
1929
.flags = MBI_MODULES | MBI_LOADERNAME
2030
};
@@ -230,10 +240,29 @@ static void __init efi_arch_pre_exit_boot(void)
230240
}
231241
}
232242

233-
static void __init noreturn efi_arch_post_exit_boot(void)
243+
void __init noreturn start_xen_from_efi(void)
234244
{
235245
u64 cr4 = XEN_MINIMAL_CR4 & ~X86_CR4_PGE, efer;
236246

247+
if ( slaunch_active )
248+
{
249+
struct slr_table *slrt = (struct slr_table *)efi.slr;
250+
struct slr_entry_intel_info *intel_info;
251+
252+
intel_info = (struct slr_entry_intel_info *)
253+
slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_INTEL_INFO);
254+
if ( intel_info != NULL )
255+
{
256+
void *txt_heap = txt_init();
257+
struct txt_os_mle_data *os_mle = txt_os_mle_data_start(txt_heap);
258+
struct txt_os_sinit_data *os_sinit =
259+
txt_os_sinit_data_start(txt_heap);
260+
261+
txt_verify_pmr_ranges(os_mle, os_sinit, intel_info, xen_phys_start,
262+
xen_phys_start, xen_image_size);
263+
}
264+
}
265+
237266
efi_arch_relocate_image(__XEN_VIRT_START - xen_phys_start);
238267
memcpy(_p(trampoline_phys), trampoline_start, cfg.size);
239268

@@ -279,6 +308,63 @@ static void __init noreturn efi_arch_post_exit_boot(void)
279308
unreachable();
280309
}
281310

311+
static void __init attempt_secure_launch(void)
312+
{
313+
struct slr_table *slrt;
314+
struct slr_entry_dl_info *dlinfo;
315+
dl_handler_func handler_callback;
316+
317+
/* The presence of this table indicates a Secure Launch boot. */
318+
slrt = (struct slr_table *)efi.slr;
319+
if ( efi.slr == EFI_INVALID_TABLE_ADDR || slrt->magic != SLR_TABLE_MAGIC ||
320+
slrt->revision != SLR_TABLE_REVISION )
321+
return;
322+
323+
/* Avoid calls into firmware after DRTM. */
324+
__clear_bit(EFI_RS, &efi_flags);
325+
326+
/*
327+
* Make measurements less sensitive to hardware-specific details.
328+
*
329+
* Intentionally leaving efi_ct and efi_num_ct intact.
330+
*/
331+
efi_ih = 0;
332+
efi_bs = NULL;
333+
efi_bs_revision = 0;
334+
efi_rs = NULL;
335+
efi_version = 0;
336+
efi_fw_vendor = NULL;
337+
efi_fw_revision = 0;
338+
StdOut = NULL;
339+
StdErr = NULL;
340+
boot_tsc_stamp = 0;
341+
342+
slaunch_active = true;
343+
slaunch_slrt = efi.slr;
344+
345+
/* Jump through DL stub to initiate Secure Launch. */
346+
dlinfo = (struct slr_entry_dl_info *)
347+
slr_next_entry_by_tag(slrt, NULL, SLR_ENTRY_DL_INFO);
348+
349+
handler_callback = (dl_handler_func)dlinfo->dl_handler;
350+
handler_callback(&dlinfo->bl_context);
351+
352+
unreachable();
353+
}
354+
355+
static void __init noreturn efi_arch_post_exit_boot(void)
356+
{
357+
/*
358+
* If Secure Launch happens, attempt_secure_launch() doesn't return and
359+
* start_xen_from_efi() is invoked after DRTM has been initiated.
360+
* Otherwise, attempt_secure_launch() returns and execution continues as
361+
* usual.
362+
*/
363+
attempt_secure_launch();
364+
365+
start_xen_from_efi();
366+
}
367+
282368
static void __init efi_arch_cfg_file_early(const EFI_LOADED_IMAGE *image,
283369
EFI_FILE_HANDLE dir_handle,
284370
const char *section)
@@ -775,6 +861,7 @@ static void __init efi_arch_halt(void)
775861
static void __init efi_arch_load_addr_check(const EFI_LOADED_IMAGE *loaded_image)
776862
{
777863
xen_phys_start = (UINTN)loaded_image->ImageBase;
864+
xen_image_size = loaded_image->ImageSize;
778865
if ( (xen_phys_start + loaded_image->ImageSize - 1) >> 32 )
779866
blexit(L"Xen must be loaded below 4Gb.");
780867
if ( xen_phys_start & ((1 << L2_PAGETABLE_SHIFT) - 1) )

0 commit comments

Comments
 (0)