kernel: build a freestanding machine image

1f842c56a5d21c631f6b76e032cebae970268fdc4efa3dadc5173f6b294cdc4a
Verified: make -C kernel check with the machine-capable emulator; explicit boot finish and hosted checks pass.
Alexis Sellier committed ago 1 parent 9a9ad076
kernel/Makefile added +27 -0
1 +
# Freestanding kernel and hosted mechanism checks.
2 +
EMU ?= $(or $(RAD_EMULATOR),emulator)
3 +
HOST_EMU ?= $(EMU)
4 +
COMPILER := ../bin/radiance.rv64.dev
5 +
COMPILE := $(HOST_EMU) -memory-size=385024 -data-size=348160 -stack-size=512 -run $(COMPILER)
6 +
MODULES :=
7 +
8 +
.PHONY: all check clean compiler-check
9 +
all: kernel.rv64
10 +
11 +
compiler-check:
12 +
13 +
$(COMPILER): compiler-check
14 +
	$(MAKE) -C .. RAD_EMULATOR=$(abspath $(shell command -v $(HOST_EMU)))
15 +
16 +
kernel.rv64: main.rad arch/entry.ras $(MODULES) $(COMPILER)
17 +
	$(COMPILE) -pkg kernel -start arch/entry.ras -mod main.rad $(addprefix -mod ,$(MODULES)) -o $@
18 +
19 +
check.rv64: check.rad $(MODULES) $(COMPILER)
20 +
	$(COMPILE) -pkg kernel -mod check.rad $(addprefix -mod ,$(MODULES)) -o $@
21 +
22 +
check: all check.rv64
23 +
	$(HOST_EMU) -run check.rv64
24 +
	$(EMU) -machine -no-guard-stack -max-steps=1000000 -count-instructions -run kernel.rv64
25 +
26 +
clean:
27 +
	rm -f kernel.rv64 check.rv64
kernel/NOTES.md added +39 -0
1 +
# Kernel implementation decisions
2 +
3 +
The specification at https://radiant.computer/system/kernel takes precedence
4 +
for fixed call numbers, handle layout, rights, and object behavior. These notes
5 +
record the contracts established through step 1 of the 22-step plan.
6 +
7 +
## Source and trust boundary
8 +
9 +
- Kernel mechanisms use freestanding Radiance; RAS owns machine entry, register
10 +
  state, atomics, and MMIO. Hosted checks exercise the same mechanism modules.
11 +
  Make and shell provide build and verification plumbing.
12 +
- Use `Once` for explicit-consumption obligations and safe references for
13 +
  ordinary mutation. Raw addresses belong at platform and user-memory boundaries.
14 +
- Image loading is restricted to trusted binary RIL. Pointer-provenance and
15 +
  type-safety verification, including verifiable compiler output, are deferred.
16 +
  The compiler IL contains machine-word types and raw address operations; source
17 +
  compilation and structural decoding do not prove isolation from hostile code.
18 +
- All components that can change admitted executable bytes remain in the TCB,
19 +
  whether loading occurs inside or outside M-mode. Executable image code is
20 +
  immutable; writable image state is an instance resource.
21 +
- The reference machine uses RAD0 and the sibling emulator, not an assumed
22 +
  QEMU `virt` memory map. Emulator execution is not evidence of real-hardware
23 +
  cache behavior or timing bounds. Report instruction counts, not host timings.
24 +
25 +
## Validation
26 +
27 +
Use the current machine-capable sibling emulator. Set `RAD_EMULATOR`, pass
28 +
`EMU` to the kernel Make invocation, or put `emulator` on PATH. The kernel build
29 +
checks compiler dependencies. From the repository root, run:
30 +
31 +
```sh
32 +
make -C kernel check
33 +
```
34 +
35 +
The linked freestanding entry point and hosted runner must execute; an assertion failure must fail the check.
36 +
37 +
The entry probe uses explicit M-mode success/fault finish writes; secondary
38 +
harts idle. This checks machine entry, not user-domain execution. Finish writes
39 +
are a check protocol, not a domain-exit operation.
kernel/PLAN.md added +92 -0
1 +
# Kernel implementation plan
2 +
3 +
Specification: https://radiant.computer/system/kernel.
4 +
5 +
This is a 22-commit progression from a freestanding entry point to budgeted
6 +
multicore domain execution. Each step is a signed, buildable commit with the
7 +
mechanism and emulator checks available at that boundary. NOTES.md records
8 +
only established contracts; this plan describes the full dependency order.
9 +
10 +
## Implementation boundary
11 +
12 +
- Use Radiance for kernel mechanisms and RAS for architectural entry, register
13 +
  state, atomics, and MMIO. Make and shell provide build plumbing. User-space
14 +
  scheduling and application policy are not kernel mechanisms.
15 +
- Load only trusted binary RIL through the Radiance compiler and shared RV64
16 +
  backend. Pointer-provenance and type-safety verification, including verifiable
17 +
  compiler output, are deferred from the outset. Encoding and structural checks
18 +
  do not provide isolation from hostile programs. Components able to change
19 +
  executable bytes remain in the TCB.
20 +
- Keep modules single-purpose, below 1,000 lines with a 500-line target. Keep
21 +
  kernel source below 10,000 non-comment lines. Do not duplicate native lowering.
22 +
- Compiler prerequisites occupy explicit dependency steps. No successful no-op
23 +
  stands in for an operation, and no test claims a later stage's behavior.
24 +
25 +
## Commit sequence
26 +
27 +
1. **Freestanding machine image.** Build the Radiance/RAS entry point, failure path, linked RAD0 image, and hosted mechanism-check runner. Execute the machine entry point.
28 +
29 +
2. **Bounded platform discovery.** Decode version-17 FDT data into RAM, reservations, hart topology, CLINT, PLIC, and device regions. Reject malformed and truncated descriptions.
30 +
31 +
3. **Physical frame ownership.** Allocate and clear contiguous frames, exclude reserved memory, and enforce exact range and exhaustion checks.
32 +
33 +
4. **Typed capability identities.** Define packed handles, object kinds, rights, slot generations, and retirement. Reject fabricated, stale, wrong-kind, and reserved-bit handles.
34 +
35 +
5. **Domains and startup authority.** Create bounded Pending domains with private handle tables and Events state. Establish non-ambient self authority and terminal-event reservations.
36 +
37 +
6. **Transactional capability operations.** Grant with attenuation, transfer atomically, and drop handles without inventing authority. Enforce Events locality and Interrupt exclusivity.
38 +
39 +
7. **Aligned aggregate code generation.** Preserve proven alignment in aggregate copies and exact-width register returns. Exercise machine boot and the compiler regression without changing layout.
40 +
41 +
8. **Persistent Page lifetimes.** Connect Page allocation and splitting to authority, object claims, and recipient-domain lifetime pins. Reclaim only after every claim and pin is gone.
42 +
43 +
9. **Ordered event publication.** Publish complete shared-ring entries with acquire/release ordering, validate consumer progress, reserve terminal delivery, and coalesce IRQ notifications.
44 +
45 +
10. **Architectural context boundary.** Save complete user register state on private machine frames, enter U-mode, return direct-call results, classify faults, and wake M-mode idle.
46 +
47 +
11. **Defining-module layout resolution.** Prepare a nominal type’s defining imports before resolving its layout. Reject by-value cycles and retain pointer recursion.
48 +
49 +
12. **Finite hart-bound budgets.** Implement conserved tick quantities, budget splitting and binding, incarnation-qualified context identities, exact-use accounting, and CLINT preemption.
50 +
51 +
13. **Waits and one-shot timeouts.** Retain accepted timeouts under event backpressure, validate waits, authorize wakeups, and choose timer deadlines without granting execution budget.
52 +
53 +
14. **Devices and external interrupts.** Validate bounded MMIO access and route exclusive interrupt ownership through masked PLIC claims, consumption, draining, and transfer.
54 +
55 +
15. **Bounded assembler storage and fences.** Size linking tables from name tokens and encode memory/I/O and instruction-fetch fences. Exercise assembler and compiler regressions.
56 +
57 +
16. **Binary RIL codec and standalone compilation.** Encode complete IL graphs in RIL0, validate binary structure, and compile decoded inputs through the existing RV64 backend. Keep provenance and type-safety verification deferred.
58 +
59 +
17. **Shared image catalogs and native execution.** Link ordered, namespaced image catalogs through the shared RV64 backend. Add typed user primitives, seven source-compiled binary images, private native state, and machine trap execution. Make and shell provide build plumbing.
60 +
61 +
18. **Complete image instances and contexts.** Create owned private image instances and activate complete argument/stack ranges. Add independent contexts, the instance workload, and checks for shared state within a domain and isolation between trusted instances.
62 +
63 +
19. **Lifecycle and reclamation.** Implement exit, faults, administrative destruction, creation-ancestry cascades, reparenting, and deferred reclamation after context quiescence. Preserve terminal reports and surviving Page recipients.
64 +
65 +
20. **Complete direct-call dispatch.** Validate every direct call and metadata query, including the budget/context additions and Delegate. Exercise authority, error returns, effects, and query error validation in hosted checks.
66 +
67 +
21. **Hart startup and bounded mailboxes.** Publish shared boot state, start private per-hart machine stacks, serialize kernel mutation with linear lock ownership, and exchange bounded requests through CLINT software interrupts. Exercise 1, 2, and 8 harts.
68 +
69 +
22. **Multicore runtime and complete boot.** Connect budgeted architectural owners, call effects, local continuations, remote admission, reserved Stop requests, and acknowledged teardown. Bootstrap real root authority and run control workloads on 1, 2, and 8 harts, including continued machine operation after root exit.
70 +
71 +
## Validation
72 +
73 +
From the repository root, set `RAD_EMULATOR` to the sibling emulator executable
74 +
and run `make -C kernel check` at every step. Compiler/backend steps also run
75 +
`make std-test bin-test` and their focused compiler or machine regressions.
76 +
NOTES.md identifies each step's observable checks and emulator prerequisites.
77 +
78 +
- Exercise the actual changed machine path when a step adds architectural
79 +
  behavior. Assertion failure must fail the check. Independent emulator changes
80 +
  are signed in the sibling repository; record required revisions in NOTES.md.
81 +
- Test authority, resource bounds, state transitions, ABI results, and concurrency
82 +
  invariants, not source spelling or wiring. Decode malformed binary encodings
83 +
  and exercise resource exhaustion, event backpressure, and finite budgets.
84 +
- Final checks cover all direct calls, native image state, 1/2/8-hart control
85 +
  workloads, acknowledged remote destruction, and root exit without shutdown.
86 +
  Check final module sizes and the signed, buildable 22-step sequence.
87 +
- The emulator supplies shared RAM and independent hart architectural state;
88 +
  machine U-mode/M-mode images cannot escape through hosted syscalls. Deterministic
89 +
  interleaving tests are not measurements of hardware cache behavior or timing.
90 +
  Instruction counts may describe a specific build, never establish isolation.
91 +
- The trusted binary-input requirement and deferred provenance/type-safety
92 +
  verification remain explicit in every validation claim.
kernel/arch/entry.ras added +20 -0
1 +
// Machine entry with a boot-time trap sink.
2 +
.text;
3 +
	la     %t0    @bootFault;
4 +
	csrw   mtvec  %t0;
5 +
	csrr   %a0    mhartid;
6 +
	bnez   %a0    @bootIdle;
7 +
	call   @"::default";
8 +
	li     %t1    0x5555;
9 +
	j      @bootFinish;
10 +
@bootFault
11 +
	csrr   %t1    mcause;
12 +
	slli   %t1    %t1    16;
13 +
	li     %t0    0x3333;
14 +
	or     %t1    %t1    %t0;
15 +
@bootFinish
16 +
	li     %t0    0x10001000;
17 +
	sw     %t1    0(%t0);
18 +
@bootIdle
19 +
	wfi;
20 +
	j      @bootIdle;
kernel/check.rad added +6 -0
1 +
//! Hosted entry for kernel mechanism checks.
2 +
3 +
/// Run the available kernel mechanism checks.
4 +
@default fn main() -> u32 {
5 +
    return 0;
6 +
}
kernel/main.rad added +10 -0
1 +
//! Freestanding machine initialization.
2 +
3 +
/// Maximum online harts in this kernel build.
4 +
constant MAX_HARTS: u64 = 8;
5 +
6 +
/// Validate the entry hart before machine initialization.
7 +
@default fn main(hart: u64) -> u32 {
8 +
    assert hart < MAX_HARTS;
9 +
    return 0;
10 +
}