furix
cute os
arch/x86_64/percpu.c
2396 bytes raw
1#include <percpu.h>
2
3#include <io.h>
4#include <types.h>
5
6/*
7 * 64-bit MSR numbers. the low-32 values often quoted for these (0xC5/0xC6) are
8 * NOT the GS base MSRs, and QEMU silently ignores writes to undefined MSRs,
9 * which is a confusing way to quietly lose your per-CPU base.
10 */
11#define MSR_IA32_GS_BASE 0xC0000101u
12#define MSR_IA32_KERNEL_GS_BASE 0xC0000102u
13
14struct per_cpu {
15 /* must stay first: this is what %gs:0 refers to */
16 uint64_t gs_probe;
17 uint32_t apic_id;
18 uint32_t online;
19};
20
21/* 64-byte aligned so a per-CPU struct never straddles a cache line */
22static struct per_cpu percpu[PERCPU_MAX_CPUS] __attribute__((aligned(64)));
23
24/*
25 * stacks live in one contiguous pool rather than inside struct per_cpu, so
26 * growing the per-CPU struct does not silently move every stack.
27 */
28static uint8_t percpu_stacks[PERCPU_MAX_CPUS][PERCPU_STACK_SIZE] __attribute__((aligned(16)));
29
30uint64_t percpu_address(void) {
31 return (uint64_t)&percpu[0];
32}
33
34uint64_t percpu_area(uint8_t slot) {
35 return (uint64_t)&percpu[slot];
36}
37
38uint64_t percpu_stack_top(uint8_t slot) {
39 return (uint64_t)&percpu_stacks[slot][PERCPU_STACK_SIZE];
40}
41
42void percpu_attach(uint32_t apic_id, uint8_t slot) {
43 /*
44 * deliberately NOT using CR4.FSGSBASE + wrgsbase. firmware leaves
45 * CR4.FSGSBASE clear and QEMU's default CPU model (qemu64) does not
46 * advertise CPUID.(7,0):EBX[0], so setting that CR4 bit does not raise a
47 * catchable #GP, the guest just stops. IA32_KERNEL_GS_BASE + swapgs gets
48 * the same result with no feature dependency.
49 *
50 * swapgs is safe on a freshly INIT'd AP: GS.base is still 0 there, so the
51 * swap cannot lose a kernel GS base.
52 */
53 wrmsr(MSR_IA32_KERNEL_GS_BASE, (uint64_t)&percpu[slot]);
54 swapgs();
55
56 /*
57 * record the identity AFTER the swap, so it is read and written through the
58 * GS base just installed. percpu_apic_id() is otherwise only meaningful on
59 * the BSP, which has no AP to be started by.
60 */
61 percpu[slot].apic_id = apic_id;
62 percpu[slot].online = 1;
63}
64
65void percpu_init(void) {
66 percpu_attach(0, 0);
67}
68
69void percpu_set_id(uint32_t apic_id) {
70 percpu[0].apic_id = apic_id;
71}
72
73uint32_t percpu_apic_id(void) {
74 return percpu[0].apic_id;
75}
76
77uint64_t percpu_gs_selftest(uint64_t value) {
78 gs_store(value);
79
80 return gs_load();
81}
82
83uint64_t percpu_gs_selftest_via_c(uint64_t value) {
84 gs_store(value);
85
86 return percpu[0].gs_probe;
87}