#include #include #include /* * 64-bit MSR numbers. the low-32 values often quoted for these (0xC5/0xC6) are * NOT the GS base MSRs, and QEMU silently ignores writes to undefined MSRs, * which is a confusing way to quietly lose your per-CPU base. */ #define MSR_IA32_GS_BASE 0xC0000101u #define MSR_IA32_KERNEL_GS_BASE 0xC0000102u struct per_cpu { /* must stay first: this is what %gs:0 refers to */ uint64_t gs_probe; uint32_t apic_id; uint32_t online; }; /* 64-byte aligned so a per-CPU struct never straddles a cache line */ static struct per_cpu percpu[PERCPU_MAX_CPUS] __attribute__((aligned(64))); /* * stacks live in one contiguous pool rather than inside struct per_cpu, so * growing the per-CPU struct does not silently move every stack. */ static uint8_t percpu_stacks[PERCPU_MAX_CPUS][PERCPU_STACK_SIZE] __attribute__((aligned(16))); uint64_t percpu_address(void) { return (uint64_t)&percpu[0]; } uint64_t percpu_area(uint8_t slot) { return (uint64_t)&percpu[slot]; } uint64_t percpu_stack_top(uint8_t slot) { return (uint64_t)&percpu_stacks[slot][PERCPU_STACK_SIZE]; } void percpu_attach(uint32_t apic_id, uint8_t slot) { /* * deliberately NOT using CR4.FSGSBASE + wrgsbase. firmware leaves * CR4.FSGSBASE clear and QEMU's default CPU model (qemu64) does not * advertise CPUID.(7,0):EBX[0], so setting that CR4 bit does not raise a * catchable #GP, the guest just stops. IA32_KERNEL_GS_BASE + swapgs gets * the same result with no feature dependency. * * swapgs is safe on a freshly INIT'd AP: GS.base is still 0 there, so the * swap cannot lose a kernel GS base. */ wrmsr(MSR_IA32_KERNEL_GS_BASE, (uint64_t)&percpu[slot]); swapgs(); /* * record the identity AFTER the swap, so it is read and written through the * GS base just installed. percpu_apic_id() is otherwise only meaningful on * the BSP, which has no AP to be started by. */ percpu[slot].apic_id = apic_id; percpu[slot].online = 1; } void percpu_init(void) { percpu_attach(0, 0); } void percpu_set_id(uint32_t apic_id) { percpu[0].apic_id = apic_id; } uint32_t percpu_apic_id(void) { return percpu[0].apic_id; } uint64_t percpu_gs_selftest(uint64_t value) { gs_store(value); return gs_load(); } uint64_t percpu_gs_selftest_via_c(uint64_t value) { gs_store(value); return percpu[0].gs_probe; }