λ

furix

cute os

settings

arch/x86_64/percpu.c

2396 bytes raw

#include <percpu.h>

#include <io.h>
#include <types.h>

/*
 * 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;
}