Before this commit, KVM-Unit-Tests set up process crashes when executing 'lgdt' instruction under SEV-ES. This is because lgdt triggers UEFI procedures (e.g. UEFI #VC handler) that require UEFI's code and data segments. But these segments are are not compatible with KVM-Unit-Tests GDT: UEFI uses 0x30 as code segment and 0x38 as data segment, but in KVM-Unit-Tests' GDT, 0x30 is a data segment, and 0x38 is a code segment. This discrepancy crashes the UEFI procedures and thus crashes the 'lgdt' execution. This commit fixes this issue by copying UEFI GDT's code and data segments into KVM-Unit-Tests GDT, so that UEFI procedures (e.g. UEFI #VC handler) can work. In this commit, the guest VM passes setup_gdt_tss() but crashes in load_idt(), which will be fixed by follow-up commits. Signed-off-by: Zixuan Wang <zixuanwang@xxxxxxxxxx> --- lib/x86/amd_sev.c | 36 ++++++++++++++++++++++++++++++++++++ lib/x86/amd_sev.h | 1 + lib/x86/setup.c | 4 ++++ 3 files changed, 41 insertions(+) diff --git a/lib/x86/amd_sev.c b/lib/x86/amd_sev.c index a31d352..c2aebdf 100644 --- a/lib/x86/amd_sev.c +++ b/lib/x86/amd_sev.c @@ -53,6 +53,42 @@ EFI_STATUS setup_amd_sev_es(void){ return EFI_SUCCESS; } + +static void copy_gdt_entry(gdt_entry_t *dst, gdt_entry_t *src, unsigned segment) +{ + unsigned index; + + index = segment / sizeof(gdt_entry_t); + dst[index] = src[index]; +} + +/* Defined in x86/efi/efistart64.S */ +extern gdt_entry_t gdt64[]; + +/* Copy UEFI's code and data segments to KVM-Unit-Tests GDT. + * + * This is because KVM-Unit-Tests reuses UEFI #VC handler that requires UEFI + * code and data segments to run. The UEFI #VC handler crashes the guest VM if + * these segments are not available. So we need to copy these two UEFI segments + * into KVM-Unit-Tests GDT. + * + * UEFI uses 0x30 as code segment and 0x38 as data segment. Fortunately, these + * segments can be safely overridden in KVM-Unit-Tests as they are used as + * protected mode and real mode segments (see x86/efi/efistart64.S for more + * details), which are not used in EFI set up process. + */ +void copy_uefi_segments(void) +{ + /* GDT and GDTR in current UEFI */ + gdt_entry_t *gdt_curr; + struct descriptor_table_ptr gdtr_curr; + + /* Copy code and data segments from UEFI */ + sgdt(&gdtr_curr); + gdt_curr = (gdt_entry_t *)gdtr_curr.base; + copy_gdt_entry(gdt64, gdt_curr, read_cs()); + copy_gdt_entry(gdt64, gdt_curr, read_ds()); +} #endif /* CONFIG_AMD_SEV_ES */ unsigned long long get_amd_sev_c_bit_mask(void) diff --git a/lib/x86/amd_sev.h b/lib/x86/amd_sev.h index a2eccfc..4d81cae 100644 --- a/lib/x86/amd_sev.h +++ b/lib/x86/amd_sev.h @@ -39,6 +39,7 @@ EFI_STATUS setup_amd_sev(void); #ifdef CONFIG_AMD_SEV_ES EFI_STATUS setup_amd_sev_es(void); +void copy_uefi_segments(void); #endif /* CONFIG_AMD_SEV_ES */ unsigned long long get_amd_sev_c_bit_mask(void); diff --git a/lib/x86/setup.c b/lib/x86/setup.c index d29f415..d828638 100644 --- a/lib/x86/setup.c +++ b/lib/x86/setup.c @@ -348,6 +348,10 @@ static void setup_gdt_tss(void) tss_hi->limit_low = (u16)((curr_tss_addr >> 32) & 0xffff); tss_hi->base_low = (u16)((curr_tss_addr >> 48) & 0xffff); +#ifdef CONFIG_AMD_SEV_ES + copy_uefi_segments(); +#endif /* CONFIG_AMD_SEV_ES */ + load_gdt_tss(tss_offset); } -- 2.33.0.rc1.237.g0d66db33f3-goog