FABRIC-3.md §I.5: Milestone 7 trust tiers (QEMU-vs-real-hardware), closing it
Closes the contributor-capsule/trust-tier punch-list item. Decided direction: QEMU-vs-real-hardware conditional enforcement. Found before building on that decision: the obvious mechanism (expose TimerInfo.vm_mode) only works on amd64 -- aarch64 and riscv64 both had vm_mode hardcoded to 1 unconditionally, meaning they'd always report "running under QEMU" even on real hardware. Built real detection for both instead of shipping that: aarch64 checks the ACPI RSDP's OEM ID for QEMU's "BOCHS " SeaBIOS-heritage signature; riscv64 checks the devicetree root compatible property for "qemu". Confirmed vm_mode was otherwise unread anywhere else in either file first -- zero risk to existing timing behavior. CAPSULE_FLAG_CONTRIB (mkcapsule.c: FLAG_CONTRIB) path-matches on capsules/contrib/, mirroring FLAG_MAMA_INIT's exact-match pattern. contrib_capsule_refused() (capsule_birth.c) enforces: no additional check under QEMU (same WARN-only as everything else); on real hardware, a contrib capsule additionally requires CAPSULE_SIG_OK, since it has no other provenance to fall back on. Wired into capsule_birth_baby() and capsule_run_experiment(). Also updates §I.7 (Milestone 9): its stated precondition (Milestone 7 closing) is now met, flagged as stale rather than treated as a green light to design networking from nothing. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the foreground) -- compile/boot verification only; the real-hardware enforcement branch is unverifiable from this environment, same as all of §I.6. logs and DoE CSVs from this session's verification runs included per this repo's own audit-artifact convention. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5567d03c12
commit
eeceec21a5
+73
-16
@@ -990,20 +990,23 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
|
|||||||
|
|
||||||
### From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust tiers
|
### From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust tiers
|
||||||
|
|
||||||
- [ ] Create the `capsules/contrib/` directory (mechanically trivial, matches existing
|
- [x] Create the `capsules/contrib/` directory (mechanically trivial, matches existing
|
||||||
subdirectory convention — the directory itself is not the work).
|
subdirectory convention — the directory itself is not the work). **DONE 2026-09-04.**
|
||||||
|
|
||||||
- [ ] Add a `FLAG_CONTRIB` bit to `mkcapsule.c`'s flag system, assigned by path match
|
- [x] Add a `FLAG_CONTRIB` bit to `mkcapsule.c`'s flag system, assigned by path match
|
||||||
(`contrib/` prefix), same pattern as how `init.4th` already gets `FLAG_MAMA_INIT`.
|
(`contrib/` prefix), same pattern as how `init.4th` already gets `FLAG_MAMA_INIT`. **DONE
|
||||||
|
2026-09-04.**
|
||||||
|
|
||||||
- [ ] Decide and implement one of the four spitballed trust-tier directions (signature-
|
- [x] Decide and implement one of the four spitballed trust-tier directions (signature-
|
||||||
authority tiers / block-namespace sandboxing / QEMU-vs-real-hardware conditional
|
authority tiers / block-namespace sandboxing / QEMU-vs-real-hardware conditional
|
||||||
enforcement) — none chosen yet, this is a real decision point, not just an implementation
|
enforcement) — none chosen yet, this is a real decision point, not just an implementation
|
||||||
task.
|
task. **DECIDED + BUILT 2026-09-04: QEMU-vs-real-hardware conditional enforcement.** Required
|
||||||
|
building real hypervisor detection for aarch64/riscv64 first (both had it hardcoded to
|
||||||
|
"always QEMU"). See §I.5 for the full build note.
|
||||||
|
|
||||||
- [ ] If block-namespace sandboxing is chosen: extend `mkcapsule`'s existing conflict-
|
- [ ] If block-namespace sandboxing is chosen: extend `mkcapsule`'s existing conflict-
|
||||||
detection logic to also reject a `contrib/`-path capsule claiming blocks outside its
|
detection logic to also reject a `contrib/`-path capsule claiming blocks outside its
|
||||||
reserved range.
|
reserved range. **N/A 2026-09-04** — a different direction was chosen.
|
||||||
|
|
||||||
### From FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB
|
### From FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB
|
||||||
|
|
||||||
@@ -4467,15 +4470,64 @@ stale the way the original carry-forwards did.
|
|||||||
structural check, so not itself a new consumer) and Milestone 7's contrib-capsule validation
|
structural check, so not itself a new consumer) and Milestone 7's contrib-capsule validation
|
||||||
(§I.5, next) can reuse. *(Same location as above.)*
|
(§I.5, next) can reuse. *(Same location as above.)*
|
||||||
|
|
||||||
### I.5 — Milestone 7 (contributor capsules / trust tiers) — nothing started
|
### I.5 — Milestone 7 (contributor capsules / trust tiers) — CLOSED 2026-09-04
|
||||||
|
|
||||||
- [ ] Create `capsules/contrib/` (mechanically trivial once the real decision below is made).
|
- [x] Create `capsules/contrib/` (mechanically trivial once the real decision below is made).
|
||||||
- [ ] Add a `FLAG_CONTRIB` bit to `mkcapsule.c` (path-match, mirrors `FLAG_MAMA_INIT`).
|
**DONE**: exists with a `README.md` (recording the direction/enforcement rule below;
|
||||||
- [ ] **Real open decision, not just an implementation gap**: which of the four spitballed
|
also a live test capsule — `contrib:README.md` registers with `FLAG_CONTRIB` set, confirmed
|
||||||
|
via a standalone `mkcapsule` run, no content-type mismatch).
|
||||||
|
- [x] Add a `FLAG_CONTRIB` bit to `mkcapsule.c` (path-match, mirrors `FLAG_MAMA_INIT`). **DONE**:
|
||||||
|
`CAPSULE_FLAG_CONTRIB` (`0x00000080`, `include/starkernel/capsule.h`) and mirrored `FLAG_CONTRIB`
|
||||||
|
in `mkcapsule.c`; `flags_from_name()` sets it on any name starting with `contrib:`.
|
||||||
|
- [x] **Real open decision, not just an implementation gap**: which of the four spitballed
|
||||||
trust-tier directions (signature-authority tiers / block-namespace sandboxing / QEMU-vs-
|
trust-tier directions (signature-authority tiers / block-namespace sandboxing / QEMU-vs-
|
||||||
real-hardware conditional enforcement) — none chosen.
|
real-hardware conditional enforcement) — none chosen. **DECIDED (asked directly): QEMU-vs-
|
||||||
- [ ] If block-namespace sandboxing is the choice: extend `mkcapsule`'s conflict-detection to
|
real-hardware conditional enforcement.**
|
||||||
reject a `contrib/` capsule claiming blocks outside its reserved range.
|
|
||||||
|
**Real, load-bearing gap found before building on this decision**: the obvious mechanism
|
||||||
|
(expose `TimerInfo.vm_mode`, already existing "per-arch") turned out to only be a real signal
|
||||||
|
on amd64. `s_cal.vm_mode = 1;` was **hardcoded, unconditional**, on both aarch64 and riscv64
|
||||||
|
— using it as-is would mean those two architectures always report "running under QEMU" even
|
||||||
|
on real hardware, defeating the entire point of the tier. Asked directly how to handle it;
|
||||||
|
told to build real detection for both rather than ship amd64-only or swap mechanisms.
|
||||||
|
|
||||||
|
**Built 2026-09-04, real hypervisor-vs-hardware detection on all three architectures:**
|
||||||
|
- **amd64**: already real (`running_under_hypervisor()`, `CPUID.1:ECX[31]`) — unchanged.
|
||||||
|
- **aarch64**: new `running_under_hypervisor(boot_info)` (`arch/aarch64/timer.c`) reads the
|
||||||
|
ACPI RSDP's `oem_id` field via `boot_info->acpi_table`, checks for `"BOCHS "` — QEMU's
|
||||||
|
SeaBIOS-heritage ACPI signature on every machine type it emulates, aarch64 virt included;
|
||||||
|
real hardware vendors set their own OEMID. Confirmed `vm_mode` is otherwise unread anywhere
|
||||||
|
else in this file before changing its computed value (purely informational there, unlike
|
||||||
|
amd64 where it gates a real calibration-path choice) — zero risk of regressing aarch64's
|
||||||
|
own timing behavior.
|
||||||
|
- **riscv64**: new `running_under_hypervisor(boot_info)` (`arch/riscv64/timer.c`) reads the
|
||||||
|
devicetree root `compatible` property (`fdt_find_prop()`, already used one property over
|
||||||
|
for `timebase-frequency`) and substring-searches for `"qemu"` — QEMU's own virt-board
|
||||||
|
machine-model string; real hardware sets vendor-specific compatible strings. Same
|
||||||
|
"confirmed unread elsewhere first" safety check as aarch64.
|
||||||
|
|
||||||
|
Both now exposed identically via the pre-existing `timer_calibration_record()->vm_mode`
|
||||||
|
public accessor (all three architectures implement it; no new plumbing needed there).
|
||||||
|
|
||||||
|
**Enforcement rule, built the same pass**: `contrib_capsule_refused(flags, sr)`
|
||||||
|
(`capsule_birth.c`), called from `capsule_birth_baby()` and `capsule_run_experiment()`
|
||||||
|
(never `capsule_birth_mama()` — `FLAG_MAMA_INIT`/`FLAG_CONTRIB` are mutually exclusive by
|
||||||
|
construction). Under QEMU: no additional check — same WARN-only treatment every capsule
|
||||||
|
already gets. On real hardware: a `FLAG_CONTRIB` capsule additionally requires
|
||||||
|
`CAPSULE_SIG_OK` — `MISSING`/`NO_ROOT_KEY` (permanently WARN-only for every other capsule,
|
||||||
|
most machines lack the offline signing key) are refused here specifically, since a
|
||||||
|
contributor's capsule has no other provenance to fall back on. Additive to, never replacing,
|
||||||
|
the existing `CAPSULE_SIG_INVALID` refusal already enforced on every capsule.
|
||||||
|
|
||||||
|
Verified 3-arch boot to `ok>` (amd64/aarch64/riscv64, each in the foreground) — compile/boot
|
||||||
|
verification only; the real-hardware enforcement branch is, like all of §I.6, unverifiable
|
||||||
|
from this environment (no physical machine to actually trigger `vm_mode == 0` against), and
|
||||||
|
the `contrib:README.md` test capsule was never actually birthed this pass (registers at
|
||||||
|
build time; nothing calls `BIRTH`/experiment-run on it), so `contrib_capsule_refused()`
|
||||||
|
itself never executed live either way.
|
||||||
|
- [x] If block-namespace sandboxing is the choice: extend `mkcapsule`'s conflict-detection to
|
||||||
|
reject a `contrib/` capsule claiming blocks outside its reserved range. **N/A** — a different
|
||||||
|
direction was chosen; block-namespace sandboxing's own extension isn't needed.
|
||||||
*(Full entries: §A, "From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust
|
*(Full entries: §A, "From FABRIC-2.md §X, Milestone 7 — Contributor capsules / trust
|
||||||
tiers.")*
|
tiers.")*
|
||||||
|
|
||||||
@@ -4490,10 +4542,15 @@ serial-socket log available, confirm POST reaches the same 1012/0/0 result, conf
|
|||||||
a basic command work identically to QEMU, and document the outcome. *(Full entries: §A, "From
|
a basic command work identically to QEMU, and document the outcome. *(Full entries: §A, "From
|
||||||
FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB.")*
|
FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB.")*
|
||||||
|
|
||||||
### I.7 — Milestone 9 (networking / capsule distribution) — correctly still deferred
|
### I.7 — Milestone 9 (networking / capsule distribution) — precondition now met, still no work done
|
||||||
|
|
||||||
- [ ] Explicitly deferred until Milestone 7 closes — its own precondition isn't met, nothing
|
- [ ] Explicitly deferred until Milestone 7 closes — its own precondition isn't met, nothing
|
||||||
to do here yet. *(Full entry: §A, "From FABRIC-2.md §X, Milestone 9.")*
|
to do here yet. **Precondition met 2026-09-04**: §I.5 (Milestone 7) closed. This does **not**
|
||||||
|
mean Milestone 9 is ready to build in the sense of "go" — no network stack exists in any
|
||||||
|
form (confirmed absent, `FABRIC-2.md` §G), and Milestone 9 was never otherwise scoped beyond
|
||||||
|
"serve signed capsules, check against manifest metadata" as a rough concept. Flagging the
|
||||||
|
stale blocker only, same discipline §I.8 got in the 2026-09-03 sweep — not a design pass.
|
||||||
|
*(Full entry: §A, "From FABRIC-2.md §X, Milestone 9.")*
|
||||||
|
|
||||||
### I.8 — `EXPIRE` (ACL) — CLOSED 2026-09-04, re-scoped away from the Stadium-patron model
|
### I.8 — `EXPIRE` (ACL) — CLOSED 2026-09-04, re-scoped away from the Stadium-patron model
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Capsule Block Manifest — Auto-generated
|
# Capsule Block Manifest — Auto-generated
|
||||||
<!-- Generated by mkcapsule --manifest 2026-09-04T14:43:57Z -->
|
<!-- Generated by mkcapsule --manifest 2026-09-04T15:02:01Z -->
|
||||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||||
<!-- Hand-written justifications and immutability notes live -->
|
<!-- Hand-written justifications and immutability notes live -->
|
||||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# capsules/contrib/
|
||||||
|
|
||||||
|
Milestone 7 (contributor capsules / trust tiers), FABRIC-3.md §I.5.
|
||||||
|
|
||||||
|
Any `.4th` file placed here gets `FLAG_CONTRIB` in addition to the usual
|
||||||
|
`FLAG_PRODUCTION | FLAG_EXPERIMENT` pair — `tools/mkcapsule.c`'s
|
||||||
|
`flags_from_name()` path-matches on the colon-separated capsule name
|
||||||
|
starting with `contrib:`, mirroring `FLAG_MAMA_INIT`'s own exact-match
|
||||||
|
pattern one line up in that same function.
|
||||||
|
|
||||||
|
**Trust-tier direction, decided in conversation 2026-09-04:** QEMU-vs-real-
|
||||||
|
hardware conditional enforcement — a contributor capsule is validated more
|
||||||
|
strictly on real hardware than under QEMU, using
|
||||||
|
`timer_calibration_record()->vm_mode` (`include/starkernel/timer.h`) as the
|
||||||
|
signal. `vm_mode` is a real per-architecture hypervisor-vs-hardware
|
||||||
|
detection as of this same pass (amd64: `CPUID.1:ECX[31]`; aarch64: ACPI
|
||||||
|
RSDP OEM ID; riscv64: devicetree `compatible` string) — not a build-time
|
||||||
|
flag, so the same binary enforces differently depending on where it
|
||||||
|
actually boots.
|
||||||
|
|
||||||
|
**Enforcement rule, built 2026-09-04:** `contrib_capsule_refused()`
|
||||||
|
(`capsule_birth.c`), called from both `capsule_birth_baby()` and
|
||||||
|
`capsule_run_experiment()` (never `capsule_birth_mama()` — Mama's own init
|
||||||
|
can never carry `FLAG_CONTRIB`, mutually exclusive with `FLAG_MAMA_INIT` by
|
||||||
|
construction). Under QEMU (`vm_mode == 1`): no additional check, same
|
||||||
|
WARN-only treatment every other capsule gets. On real hardware
|
||||||
|
(`vm_mode == 0`): a contrib capsule additionally requires `CAPSULE_SIG_OK`
|
||||||
|
— `MISSING`/`NO_ROOT_KEY`, which stay WARN-only for every other capsule
|
||||||
|
(most machines lack the offline signing key), are refused here specifically
|
||||||
|
because a contributor's capsule has no other provenance to fall back on.
|
||||||
|
Additive to, never a replacement for, the existing `CAPSULE_SIG_INVALID`
|
||||||
|
refusal already enforced on every capsule regardless of `FLAG_CONTRIB`.
|
||||||
Binary file not shown.
@@ -87,6 +87,16 @@ typedef enum {
|
|||||||
/** Mama init flag (exactly one capsule must have this) */
|
/** Mama init flag (exactly one capsule must have this) */
|
||||||
#define CAPSULE_FLAG_MAMA_INIT 0x00000040 /* (m) Mama's init */
|
#define CAPSULE_FLAG_MAMA_INIT 0x00000040 /* (m) Mama's init */
|
||||||
|
|
||||||
|
/** Contributor capsule flag (FABRIC-3.md §I.5, 2026-09-04) -- path-match
|
||||||
|
* on capsules/contrib/, mirrors FLAG_MAMA_INIT's own exact-match pattern
|
||||||
|
* in mkcapsule.c's flags_from_name(). Trust-tier enforcement (QEMU-vs-
|
||||||
|
* real-hardware, decided in conversation) is a runtime check in
|
||||||
|
* capsule_validate()'s callers, not encoded in this bit itself -- the
|
||||||
|
* bit only marks "this capsule's provenance is a contributor, not this
|
||||||
|
* project's own source," same as CAPSULE_FLAG_PRODUCTION/_EXPERIMENT
|
||||||
|
* mark mode, not policy. */
|
||||||
|
#define CAPSULE_FLAG_CONTRIB 0x00000080 /* (c) contributor-submitted */
|
||||||
|
|
||||||
/** Validate mode flags.
|
/** Validate mode flags.
|
||||||
* Mama: neither (p) nor (e) may be set.
|
* Mama: neither (p) nor (e) may be set.
|
||||||
* Babies: at least one of (p) or (e) must be set (both is fine — D2). */
|
* Babies: at least one of (p) or (e) must be set (both is fine — D2). */
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -16,9 +16,35 @@
|
|||||||
#include "q48_16.h"
|
#include "q48_16.h"
|
||||||
#include "uefi.h"
|
#include "uefi.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
/* ─── ARM generic-timer helpers ─────────────────────────────────────── */
|
/* ─── ARM generic-timer helpers ─────────────────────────────────────── */
|
||||||
|
|
||||||
|
/* FABRIC-3.md §I.5, 2026-09-04: real hypervisor-vs-hardware detection.
|
||||||
|
* s_cal.vm_mode was hardcoded to 1 unconditionally below (comment:
|
||||||
|
* "QEMU SBSA always uses virtualised Generic Timer") -- true for the
|
||||||
|
* *timing policy* this file cares about, but wrong to reuse as a
|
||||||
|
* general "are we in QEMU" signal elsewhere (contrib-capsule trust-tier
|
||||||
|
* enforcement, §I.5), which is exactly what happened before this was
|
||||||
|
* caught: it would report "always QEMU" on real hardware too.
|
||||||
|
*
|
||||||
|
* ARM has no single CPUID-equivalent hypervisor-present bit the way
|
||||||
|
* amd64's CPUID.1:ECX[31] is (arch/amd64/timer.c's own
|
||||||
|
* running_under_hypervisor()). Checks the ACPI RSDP's OEM ID instead --
|
||||||
|
* "BOCHS " is the SeaBIOS-heritage signature QEMU's ACPI table
|
||||||
|
* generation has used since long before this project, on every machine
|
||||||
|
* type it emulates, aarch64 virt included. Real hardware vendors set
|
||||||
|
* their own OEMID, never this string. Mirrors pci.c's own private Rsdp2
|
||||||
|
* struct layout (signature[8], checksum, oem_id[6]) -- duplicated
|
||||||
|
* rather than shared, same "a few TLV-walking lines is simpler than a
|
||||||
|
* new header" precedent tools/pkcs8_ed25519.c already set. */
|
||||||
|
static int running_under_hypervisor(BootInfo *boot_info) {
|
||||||
|
if (!boot_info || !boot_info->acpi_table) return 0;
|
||||||
|
const uint8_t *rsdp = (const uint8_t *) boot_info->acpi_table;
|
||||||
|
if (memcmp(rsdp, "RSD PTR ", 8) != 0) return 0;
|
||||||
|
return memcmp(rsdp + 9, "BOCHS ", 6) == 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read the AArch64 Physical System Counter with ISB serialisation.
|
* @brief Read the AArch64 Physical System Counter with ISB serialisation.
|
||||||
*
|
*
|
||||||
@@ -101,15 +127,17 @@ static timer_calibration_record_t s_cal;
|
|||||||
* because the ARM Generic Timer is invariant and synchronised by
|
* because the ARM Generic Timer is invariant and synchronised by
|
||||||
* architecture — no drift-detection loop is needed.
|
* architecture — no drift-detection loop is needed.
|
||||||
*
|
*
|
||||||
* @param boot_info Kernel @c BootInfo (ACPI/memory map); unused on AArch64
|
* @param boot_info Kernel @c BootInfo -- GIC base address will be used in a
|
||||||
* for timer init (GIC base address will be used in a later
|
* later milestone for the interrupt controller; as of
|
||||||
* milestone for the interrupt controller).
|
* 2026-09-04 also consulted here for @c acpi_table, so
|
||||||
|
* @c s_cal.vm_mode can be a real hypervisor-vs-hardware
|
||||||
|
* detection instead of the hardcoded-1 timing-policy
|
||||||
|
* shortcut this field used to double as (see
|
||||||
|
* running_under_hypervisor()'s own doc comment above).
|
||||||
* @return 0 always.
|
* @return 0 always.
|
||||||
*/
|
*/
|
||||||
int timer_init(BootInfo *boot_info)
|
int timer_init(BootInfo *boot_info)
|
||||||
{
|
{
|
||||||
(void)boot_info;
|
|
||||||
|
|
||||||
uint64_t freq = cntfrq_read();
|
uint64_t freq = cntfrq_read();
|
||||||
if (freq == 0) {
|
if (freq == 0) {
|
||||||
/* Firmware did not set CNTFRQ; assume 62.5 MHz (Cortex-A57 default) */
|
/* Firmware did not set CNTFRQ; assume 62.5 MHz (Cortex-A57 default) */
|
||||||
@@ -127,7 +155,7 @@ int timer_init(BootInfo *boot_info)
|
|||||||
s_cal.hpet_hz = 0;
|
s_cal.hpet_hz = 0;
|
||||||
s_cal.pit_hz_mean = 0;
|
s_cal.pit_hz_mean = 0;
|
||||||
s_cal.converged = 1;
|
s_cal.converged = 1;
|
||||||
s_cal.vm_mode = 1;
|
s_cal.vm_mode = running_under_hypervisor(boot_info) ? 1 : 0;
|
||||||
s_cal.trust = TIMER_TRUST_ABSOLUTE;
|
s_cal.trust = TIMER_TRUST_ABSOLUTE;
|
||||||
|
|
||||||
console_println("Timer: AArch64 generic timer initialised.");
|
console_println("Timer: AArch64 generic timer initialised.");
|
||||||
@@ -209,7 +237,11 @@ int timer_check_drift_now(void)
|
|||||||
* - @c hpet_hz = 0 (no HPET on AArch64)
|
* - @c hpet_hz = 0 (no HPET on AArch64)
|
||||||
* - @c pit_hz_mean = 0 (no PIT on AArch64)
|
* - @c pit_hz_mean = 0 (no PIT on AArch64)
|
||||||
* - @c converged = 1 (Generic Timer is already calibrated by firmware)
|
* - @c converged = 1 (Generic Timer is already calibrated by firmware)
|
||||||
* - @c vm_mode = 1 (QEMU SBSA always uses virtualised Generic Timer)
|
* - @c vm_mode = real hypervisor-vs-hardware detection as of 2026-09-04
|
||||||
|
* (ACPI RSDP OEM ID check, running_under_hypervisor() above) -- the
|
||||||
|
* Generic Timer itself works identically either way, so this field's
|
||||||
|
* *timing* meaning here is purely informational, unlike amd64 where
|
||||||
|
* vm_mode gates a real calibration-path choice
|
||||||
* - @c trust = @c TIMER_TRUST_ABSOLUTE
|
* - @c trust = @c TIMER_TRUST_ABSOLUTE
|
||||||
*
|
*
|
||||||
* The record is exposed to @c kernel_main() and @c timer.h consumers for
|
* The record is exposed to @c kernel_main() and @c timer.h consumers for
|
||||||
|
|||||||
@@ -25,6 +25,37 @@
|
|||||||
#include "uefi.h"
|
#include "uefi.h"
|
||||||
#include "starkernel/fdt.h"
|
#include "starkernel/fdt.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* FABRIC-3.md §I.5, 2026-09-04: real hypervisor-vs-hardware detection.
|
||||||
|
* s_cal.vm_mode was hardcoded to 1 unconditionally below -- see
|
||||||
|
* aarch64/timer.c's own running_under_hypervisor() doc comment for why
|
||||||
|
* that's wrong to reuse as a general "are we in QEMU" signal elsewhere
|
||||||
|
* (contrib-capsule trust-tier enforcement, §I.5). RISC-V has no ACPI
|
||||||
|
* here (this file's own devicetree-only timebase-frequency discovery
|
||||||
|
* above is the proof) but does have a devicetree, already parsed for
|
||||||
|
* exactly one other property -- the root node's "compatible" property
|
||||||
|
* carries QEMU's own machine-model string ("qemu" appears in it for the
|
||||||
|
* virt board) on every QEMU riscv64 target; real hardware vendors set
|
||||||
|
* their own compatible strings, never this one. bytes_contain() is a
|
||||||
|
* tiny local substring search -- no strstr dependency assumed available
|
||||||
|
* in this translation unit. */
|
||||||
|
static int bytes_contain(const uint8_t *hay, uint32_t haylen, const char *needle) {
|
||||||
|
size_t nlen = strlen(needle);
|
||||||
|
if (nlen == 0 || haylen < nlen) return 0;
|
||||||
|
for (uint32_t i = 0; i + nlen <= haylen; i++) {
|
||||||
|
if (memcmp(hay + i, needle, nlen) == 0) return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int running_under_hypervisor(BootInfo *boot_info) {
|
||||||
|
if (!boot_info || !fdt_valid(boot_info->dtb)) return 0;
|
||||||
|
uint32_t len = 0;
|
||||||
|
const void *prop = fdt_find_prop(boot_info->dtb, "compatible", &len);
|
||||||
|
if (!prop) return 0;
|
||||||
|
return bytes_contain((const uint8_t *) prop, len, "qemu");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Read the RISC-V wall-clock counter (@c rdtime, CSR @c time 0xC01).
|
* @brief Read the RISC-V wall-clock counter (@c rdtime, CSR @c time 0xC01).
|
||||||
@@ -145,7 +176,7 @@ int timer_init(BootInfo *boot_info)
|
|||||||
s_cal.hpet_hz = 0;
|
s_cal.hpet_hz = 0;
|
||||||
s_cal.pit_hz_mean = 0;
|
s_cal.pit_hz_mean = 0;
|
||||||
s_cal.converged = 1;
|
s_cal.converged = 1;
|
||||||
s_cal.vm_mode = 1;
|
s_cal.vm_mode = running_under_hypervisor(boot_info) ? 1 : 0;
|
||||||
/* ABSOLUTE only when the rate came from firmware. On the fallback the
|
/* ABSOLUTE only when the rate came from firmware. On the fallback the
|
||||||
* counter is still monotonic and invariant, but its scaling to real time
|
* counter is still monotonic and invariant, but its scaling to real time
|
||||||
* is a guess, which is exactly the RELATIVE case. */
|
* is a guess, which is exactly the RELATIVE case. */
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
#include "starkernel/console.h"
|
#include "starkernel/console.h"
|
||||||
#include "starkernel/vm/stadium.h" /* item 4.1a -- stadium_grant_quota() */
|
#include "starkernel/vm/stadium.h" /* item 4.1a -- stadium_grant_quota() */
|
||||||
#include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-3.md §H.12 step 5 */
|
#include "starkernel/session.h" /* session_register()/session_set_pinned() -- FABRIC-3.md §H.12 step 5 */
|
||||||
|
#include "starkernel/timer.h" /* timer_calibration_record()->vm_mode -- FABRIC-3.md §I.5 CONTRIB trust tier */
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "platform_alloc.h"
|
#include "platform_alloc.h"
|
||||||
/* No LOG_LINE_MAX include-order constraint anymore: vm.h's own
|
/* No LOG_LINE_MAX include-order constraint anymore: vm.h's own
|
||||||
@@ -450,6 +451,25 @@ void capsule_vm_kill_all_nonmama(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FABRIC-3.md §I.5, 2026-09-04: contributor-capsule trust tier
|
||||||
|
* (QEMU-vs-real-hardware conditional enforcement, decided in
|
||||||
|
* conversation). CAPSULE_FLAG_CONTRIB capsules get the same WARN-only
|
||||||
|
* treatment as everything else under QEMU (timer_calibration_record()->
|
||||||
|
* vm_mode == 1) -- development/test is meant to run contrib capsules
|
||||||
|
* freely. On real hardware (vm_mode == 0), a contrib capsule additionally
|
||||||
|
* requires CAPSULE_SIG_OK -- MISSING/NO_ROOT_KEY, which stay WARN-only
|
||||||
|
* for every other capsule (no offline signing key on most machines,
|
||||||
|
* see capsule_birth_mama()'s own comment), are refused here specifically
|
||||||
|
* because a contributor's capsule has no other provenance to fall back
|
||||||
|
* on the way this project's own capsules do. Never touches the
|
||||||
|
* CAPSULE_SIG_INVALID refusal already in place for every capsule --
|
||||||
|
* additive, not a replacement. */
|
||||||
|
static int contrib_capsule_refused(uint32_t flags, CapsuleSigResult sr) {
|
||||||
|
if (!(flags & CAPSULE_FLAG_CONTRIB)) return 0;
|
||||||
|
if (timer_calibration_record()->vm_mode) return 0; /* QEMU: relaxed */
|
||||||
|
return sr != CAPSULE_SIG_OK; /* real hardware: must actually verify */
|
||||||
|
}
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* Mama Init
|
* Mama Init
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
@@ -569,6 +589,13 @@ CapsuleRunResult capsule_birth_baby(
|
|||||||
names[idx].name, capsule_sig_result_str(sr));
|
names[idx].name, capsule_sig_result_str(sr));
|
||||||
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
|
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
|
||||||
}
|
}
|
||||||
|
/* FABRIC-3.md §I.5: contrib trust tier -- see contrib_capsule_
|
||||||
|
* refused()'s own doc comment. */
|
||||||
|
if (contrib_capsule_refused(cap->flags, sr)) {
|
||||||
|
log_message(LOG_WARN, "capsule sig: %s: contrib capsule refused on real hardware (%s)",
|
||||||
|
names[idx].name, capsule_sig_result_str(sr));
|
||||||
|
return CAPSULE_RUN_ERR_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm_registry_live_count() >= stadium_max_vm_count()) {
|
if (vm_registry_live_count() >= stadium_max_vm_count()) {
|
||||||
@@ -759,6 +786,13 @@ CapsuleRunResult capsule_run_experiment(
|
|||||||
names[idx].name, capsule_sig_result_str(sr));
|
names[idx].name, capsule_sig_result_str(sr));
|
||||||
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
|
if (sr == CAPSULE_SIG_INVALID) return CAPSULE_RUN_ERR_INVALID;
|
||||||
}
|
}
|
||||||
|
/* FABRIC-3.md §I.5: contrib trust tier -- see contrib_capsule_
|
||||||
|
* refused()'s own doc comment. */
|
||||||
|
if (contrib_capsule_refused(cap->flags, sr)) {
|
||||||
|
log_message(LOG_WARN, "capsule sig: %s: contrib capsule refused on real hardware (%s)",
|
||||||
|
names[idx].name, capsule_sig_result_str(sr));
|
||||||
|
return CAPSULE_RUN_ERR_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t pre_dict_hash = vm_dict_hash_fn(mama_vm);
|
uint64_t pre_dict_hash = vm_dict_hash_fn(mama_vm);
|
||||||
|
|||||||
@@ -213,11 +213,16 @@ static void sign_capsule_bytes(const uint8_t *data, size_t len,
|
|||||||
#define FLAG_PRODUCTION 0x00000010
|
#define FLAG_PRODUCTION 0x00000010
|
||||||
#define FLAG_EXPERIMENT 0x00000020
|
#define FLAG_EXPERIMENT 0x00000020
|
||||||
#define FLAG_MAMA_INIT 0x00000040
|
#define FLAG_MAMA_INIT 0x00000040
|
||||||
|
#define FLAG_CONTRIB 0x00000080
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine flags from the colon-separated capsule name.
|
* Determine flags from the colon-separated capsule name.
|
||||||
*
|
*
|
||||||
* init.4th (bare) is Mama's canonical init — gets FLAG_MAMA_INIT only.
|
* init.4th (bare) is Mama's canonical init — gets FLAG_MAMA_INIT only.
|
||||||
|
* Anything under capsules/contrib/ (name starts with "contrib:") gets
|
||||||
|
* FLAG_CONTRIB in addition to the usual PRODUCTION|EXPERIMENT pair
|
||||||
|
* (FABRIC-3.md §I.5, 2026-09-04) — path-match, mirrors FLAG_MAMA_INIT's
|
||||||
|
* own exact-match pattern one line up, just prefix instead of exact.
|
||||||
* All other capsules carry both FLAG_PRODUCTION and FLAG_EXPERIMENT so
|
* All other capsules carry both FLAG_PRODUCTION and FLAG_EXPERIMENT so
|
||||||
* that birth eligibility is not gated on mode type (D2).
|
* that birth eligibility is not gated on mode type (D2).
|
||||||
*/
|
*/
|
||||||
@@ -228,6 +233,9 @@ static uint32_t flags_from_name(const char *name) {
|
|||||||
flags |= FLAG_MAMA_INIT;
|
flags |= FLAG_MAMA_INIT;
|
||||||
} else {
|
} else {
|
||||||
flags |= FLAG_PRODUCTION | FLAG_EXPERIMENT;
|
flags |= FLAG_PRODUCTION | FLAG_EXPERIMENT;
|
||||||
|
if (strncmp(name, "contrib:", 8) == 0) {
|
||||||
|
flags |= FLAG_CONTRIB;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
|
|||||||
Reference in New Issue
Block a user