Stage MC-0a: per-core block on one core -- current VM moves behind the per-CPU register (FABRIC-3.10.8 §3.1)

- New sk_percpu_t { self, core_id, current_vm }, reached via GS base
  (amd64, %gs:0), TPIDR_EL1 (aarch64), tp (riscv64). Boot core's block is
  one static object installed right after arch_early_init(); secondary
  cores' blocks come with MC-1.
- switch.c's global g_switch_current_vm is gone; current VM is
  sk_percpu()->current_vm, re-read after resume in the context switch.
- Probe (removed): normal path and every heartbeat ISR found core 0's
  block on all three ISAs (ISR ok 889/1877/1625, bad 0).
- Standard sequence on amd64/aarch64/riscv64: hashes unchanged from O-C,
  G A=40 B=40, MINT/WIREBIND/USE ok, conservation true, 0 idle growth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-25 13:56:48 -04:00
co-authored by Claude Opus 5.5
parent 8d33f7a959
commit 8409e376cf
17 changed files with 55013 additions and 8 deletions
+37 -1
View File
@@ -78,4 +78,40 @@ Everything that would break if two cores ran kernel or VM code at the same momen
## 3. Execution log
*(none yet)*
### 3.1 MC-0a — the per-core block, on one core (2026-09-25)
**Change.**
- `include/starkernel/percpu.h` (new): `sk_percpu_t { self, core_id, current_vm }`, with `self` first.
- `sk_percpu()` reads this core's block: `movq %gs:0` on amd64, `mrs tpidr_el1` on aarch64, `mv tp` on riscv64.
- `sk_percpu_install()` writes the register: `IA32_GS_BASE` via `wrmsr`, `TPIDR_EL1`, or `tp`.
- `src/starkernel/hal/percpu.c` (new): the boot core's block is one static object, not a table sized to a core count. Secondary cores get theirs as they come online (MC-1).
- `kernel_main.c`: `sk_percpu_boot_init()` runs straight after `arch_early_init()`, because on amd64 that call reloads GS, which would reset the base.
- `switch.c`: the global `g_switch_current_vm` is gone.
- `sk_vm_switch_current_vm()`, the setter, the idle point and `sk_vm_context_switch()` use `sk_percpu()->current_vm`.
- After resume, the context switch re-reads the block rather than trusting a pointer cached before the switch.
**Probe** (temporary; removed before commit).
- `kernel_main.c` checked, from the normal path after the Tripod came up, that:
- `sk_percpu()->self == sk_percpu()`;
- `core_id == 0`;
- `current_vm` is Hera;
- `sk_vm_switch_current_vm()` agrees.
- `heartbeat_tick()` ran the same self-check from interrupt context on every beat.
| ISA | log | normal path | core_id | current_vm is Hera | switch.c agrees | interrupt path ok / bad |
|---|---|---|---|---|---|---|
| amd64 | `logs/20260925-130335` | ok | 0 | 1 | 1 | 889 / 0 |
| aarch64 | `logs/20260925-130905` | ok | 0 | 1 | 1 | 1877 / 0 |
| riscv64 | `logs/20260925-131337` | ok | 0 | 1 | 1 | 1625 / 0 |
**Standard sequence** (clean build, with the probe reverted; the tree was checked byte-for-byte against the saved stage diff).
| ISA | log | O-A (G, PREC, LOAD) | M7.1a hash | MINT / WIREBIND / USE / `1 2 + .` | idle growth | UNKNOWN / faults |
|---|---|---|---|---|---|---|
| amd64 | `logs/20260925-131805` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
| aarch64 | `logs/20260925-133049` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
| riscv64 | `logs/20260925-134357` | A=40 B=40, HHH, ok | `0x99b01134c3d3049c` | all ok, `3` | 0 B / 20 s | 0 / 0 |
- The `dict_hash` values (Hera `0x00340a8b2c842eb7`, Hestia `0x78bf28439e4a643f`, Artemis `0x66886d174d5f3b5a`) are identical across ISAs and unchanged from O-C.
- The ledger line matches across ISAs: held=2048, pulled=258048, returned=255263, consumed=737, conserved=true.
- No stop condition was hit. **MC-0a done.**