diff options
| author | Alexander Potapenko <glider@google.com> | 2025-11-14 17:46:50 +0100 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2025-11-19 08:59:40 +0000 |
| commit | 825f9f21aae8dd655126b50de70210bacbd330f9 (patch) | |
| tree | 89714b46f8fe5f200ef0ba9422ace70aaa27bdd0 /executor | |
| parent | 6157b0280f1054052c9a36acb4fbae22288dc966 (diff) | |
executor: x86: Configure L1 guest TSS for nested virtualization
Set up the L1 guest's 64-bit Task State Segment (TSS), a prerequisite for VMX/SVM.
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_kvm_amd64.h | 39 | ||||
| -rw-r--r-- | executor/kvm.h | 2 |
2 files changed, 38 insertions, 3 deletions
diff --git a/executor/common_kvm_amd64.h b/executor/common_kvm_amd64.h index 818dc8125..0c49f07a0 100644 --- a/executor/common_kvm_amd64.h +++ b/executor/common_kvm_amd64.h @@ -399,11 +399,21 @@ static void setup_gdt_64(struct gdt_entry* gdt) // P=1, DPL=0, S=1, Type=Read/Write, DB=1, G=1 gdt[X86_SYZOS_SEL_DATA >> 3] = (struct gdt_entry){ .limit_low = 0xFFFF, - .base_low = 0, - .base_mid = 0, + .base_low = (uint16)(X86_SYZOS_ADDR_VAR_TSS & 0xFFFF), + .base_mid = (uint8)((X86_SYZOS_ADDR_VAR_TSS >> 16) & 0xFF), .access = 0x92, // Present, DPL=0, S=1, Type=Read/Write, Accessed .limit_high_and_flags = 0xCF, // Granularity=1, DB=1, Limit=0xF + .base_high = (uint8)((X86_SYZOS_ADDR_VAR_TSS >> 24) & 0xFF)}; + // Entry 3 (selector 0x18): 64-bit TSS Segment + gdt[X86_SYZOS_SEL_TSS64 >> 3] = (struct gdt_entry){ + .limit_low = 0x67, // Minimal TSS limit + .base_low = 0, + .base_mid = 0, + .access = 0x89, // Present, DPL=0, 64-bit TSS (Available) + .limit_high_and_flags = 0x00, // G=0, Limit High = 0 .base_high = 0}; + // NOTE: A 64-bit TSS descriptor actually needs a second GDT entry for the high 32 bits of the base. + // We'll keep the base 0 for simplicity, so the second entry (index 4) can remain 0. } // This only sets up a 64-bit VCPU. @@ -414,7 +424,7 @@ static void setup_gdt_ldt_pg(struct kvm_syz_vm* vm, int cpufd) ioctl(cpufd, KVM_GET_SREGS, &sregs); sregs.gdt.base = X86_SYZOS_ADDR_GDT; - sregs.gdt.limit = 3 * sizeof(struct gdt_entry) - 1; + sregs.gdt.limit = 5 * sizeof(struct gdt_entry) - 1; struct gdt_entry* gdt = (struct gdt_entry*)((uint64)vm->host_mem + sregs.gdt.base); struct kvm_segment seg_cs64; @@ -446,6 +456,29 @@ static void setup_gdt_ldt_pg(struct kvm_syz_vm* vm, int cpufd) sregs.gs = seg_ds64; sregs.ss = seg_ds64; + // The L1 guest (the host for L2) MUST have a valid TR + // pointing to the 64-bit TSS in the GDT. + struct kvm_segment seg_tr; + memset(&seg_tr, 0, sizeof(seg_tr)); + seg_tr.selector = X86_SYZOS_SEL_TSS64; // 0x18 + seg_tr.type = 11; // 64-bit TSS (Busy) + seg_tr.base = X86_SYZOS_ADDR_VAR_TSS; + seg_tr.limit = 0x67; // Limit of the TSS descriptor + seg_tr.present = 1; + seg_tr.s = 0; // System segment + sregs.tr = seg_tr; + + // The L1 TSS memory is at (vm->host_mem + X86_SYZOS_ADDR_VAR_TSS) + volatile uint8* l1_tss = + (volatile uint8*)((uint64)vm->host_mem + X86_SYZOS_ADDR_VAR_TSS); + + // Zero out the TSS (104 bytes for 64-bit) + memset((void*)l1_tss, 0, 104); + + // Set the critical RSP0 field to the L1 guest's main stack. + // RSP0 is at offset +4 bytes in a 64-bit TSS. + *(volatile uint64*)(l1_tss + 4) = X86_SYZOS_ADDR_STACK0; + setup_gdt_64(gdt); syzos_setup_idt(vm, &sregs); diff --git a/executor/kvm.h b/executor/kvm.h index 835d717ae..ec6fdccc3 100644 --- a/executor/kvm.h +++ b/executor/kvm.h @@ -48,6 +48,7 @@ // Pool of 32 pages for dynamic PT/PD allocations. #define X86_SYZOS_ADDR_PT_POOL 0x5000 #define X86_SYZOS_ADDR_VAR_IDT 0x25000 +#define X86_SYZOS_ADDR_VAR_TSS 0x26000 #define X86_SYZOS_ADDR_SMRAM 0x30000 // Write to this page to trigger a page fault and stop KVM_RUN. @@ -124,6 +125,7 @@ // SYZOS segment selectors #define X86_SYZOS_SEL_CODE 0x8 #define X86_SYZOS_SEL_DATA 0x10 +#define X86_SYZOS_SEL_TSS64 0x18 #define X86_CR0_PE 1ULL #define X86_CR0_MP (1ULL << 1) |
