Message ID | 20210827031222.2778522-15-zixuanwang@google.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | x86_64 UEFI and AMD SEV/SEV-ES support | expand |
On 27/08/21 05:12, Zixuan Wang wrote: > + * > + * 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. Is 0x30/0x38 the same as kvm-unit-tests's 0x08/0x10? Can kvm-unit-tests simply change its ring-0 64-bit CS/DS to 0x30 and 0x38 instead of 0x08 and 0x10? I can help with that too, since there would be some more shuffling to keep similar descriptors together: * 0x00 NULL descriptor NULL descriptor * 0x08 intr_alt_stack TSS ring-0 code segment (32-bit) * 0x10 (0x13) **unused** ring-3 code segment (64-bit) * 0x18 ring-0 code segment (P=0) ring-0 code segment (64-bit, P=0) * 0x20 ring-0 code segment (16-bit) same * 0x28 ring-0 data segment (16-bit) same * 0x30 ring-0 code segment (32-bit) ring-0 code segment (64-bit) * 0x38 ring-0 data segment (32-bit) ring-0 data segment (32/64-bit) * 0x40 (0x43) ring-3 code segment (32-bit) same * 0x48 (0x4b) ring-3 data segment (32-bit) ring-3 data segment (32/64-bit) * 0x50-0x78 free to use for test cases same or: old new ---- ---- 0x00 0x00 0x20 0x08 0x48 0x10 0x18 0x18 0x28 0x20 0x30 0x28 0x08 0x30 0x10 0x38 0x38 0x40 0x40 0x48 Thanks, Paolo
On Mon, Sep 20, 2021 at 6:27 PM Paolo Bonzini <pbonzini@redhat.com> wrote: > > On 27/08/21 05:12, Zixuan Wang wrote: > > + * > > + * 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. > > Is 0x30/0x38 the same as kvm-unit-tests's 0x08/0x10? Can kvm-unit-tests > simply change its ring-0 64-bit CS/DS to 0x30 and 0x38 instead of 0x08 > and 0x10? I can help with that too, since there would be some more > shuffling to keep similar descriptors together: > > * 0x00 NULL descriptor NULL descriptor > * 0x08 intr_alt_stack TSS ring-0 code segment (32-bit) > * 0x10 (0x13) **unused** ring-3 code segment (64-bit) > * 0x18 ring-0 code segment (P=0) ring-0 code segment (64-bit, P=0) > * 0x20 ring-0 code segment (16-bit) same > * 0x28 ring-0 data segment (16-bit) same > * 0x30 ring-0 code segment (32-bit) ring-0 code segment (64-bit) > * 0x38 ring-0 data segment (32-bit) ring-0 data segment (32/64-bit) > * 0x40 (0x43) ring-3 code segment (32-bit) same > * 0x48 (0x4b) ring-3 data segment (32-bit) ring-3 data segment (32/64-bit) > * 0x50-0x78 free to use for test cases same > > or: > > old new > ---- ---- > 0x00 0x00 > 0x20 0x08 > 0x48 0x10 > 0x18 0x18 > 0x28 0x20 > 0x30 0x28 > 0x08 0x30 > 0x10 0x38 > 0x38 0x40 > 0x40 0x48 > > Thanks, > > Paolo > Thank you for the detailed explanation! Updating KVM-unit-tests GDT is one way to solve the problem, but we found a more straightforward solution [1]: We found it possible to update the 'code segment selector' field in the #VC IDT entry and point it to the KVM-unit-tests code segment. This allows the UEFI #VC handler to use KVM-unit-tests segments, and we do not need to copy the UEFI segments. I will update this into the next version. [1] https://github.com/TheNetAdmin/KVM-Unit-Tests-dev-fork/commit/ab480fd0fbad813c2922526a0bccadf121cb9240 Best regards, Zixuan
diff --git a/lib/x86/amd_sev.c b/lib/x86/amd_sev.c index 8d4df8c..c9fabc4 100644 --- a/lib/x86/amd_sev.c +++ b/lib/x86/amd_sev.c @@ -91,6 +91,47 @@ bool amd_sev_es_enabled(void) return sev_es_enabled; } +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) +{ + if (!amd_sev_es_enabled()) { + return; + } + + /* 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()); +} + unsigned long long get_amd_sev_c_bit_mask(void) { if (amd_sev_enabled()) { diff --git a/lib/x86/amd_sev.h b/lib/x86/amd_sev.h index b73a872..0b4ff8c 100644 --- a/lib/x86/amd_sev.h +++ b/lib/x86/amd_sev.h @@ -40,6 +40,7 @@ bool amd_sev_enabled(void); efi_status_t setup_amd_sev(void); bool amd_sev_es_enabled(void); +void copy_uefi_segments(void); unsigned long long get_amd_sev_c_bit_mask(void); unsigned long long get_amd_sev_addr_upperbound(void); diff --git a/lib/x86/setup.c b/lib/x86/setup.c index bdda337..c6eb3e9 100644 --- a/lib/x86/setup.c +++ b/lib/x86/setup.c @@ -324,6 +324,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); + if (amd_sev_es_enabled()) { + copy_uefi_segments(); + } + load_gdt_tss(tss_offset); }
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@google.com> --- lib/x86/amd_sev.c | 41 +++++++++++++++++++++++++++++++++++++++++ lib/x86/amd_sev.h | 1 + lib/x86/setup.c | 4 ++++ 3 files changed, 46 insertions(+)