aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2025-11-14 17:46:50 +0100
committerAlexander Potapenko <glider@google.com>2025-11-19 08:59:40 +0000
commit825f9f21aae8dd655126b50de70210bacbd330f9 (patch)
tree89714b46f8fe5f200ef0ba9422ace70aaa27bdd0 /executor
parent6157b0280f1054052c9a36acb4fbae22288dc966 (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.h39
-rw-r--r--executor/kvm.h2
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)