kernel: discover bounded firmware platform resources

d88ae1a155a416b4e2052aa8ed816884ae65d499a7065c2db12f37676d78274c
Verified: make -C kernel check; hosted firmware cases and machine entry pass.
Alexis Sellier committed ago 1 parent 1f842c56
kernel/Makefile +7 -5
1 1
# Freestanding kernel and hosted mechanism checks.
2 2
EMU ?= $(or $(RAD_EMULATOR),emulator)
3 3
HOST_EMU ?= $(EMU)
4 4
COMPILER := ../bin/radiance.rv64.dev
5 5
COMPILE := $(HOST_EMU) -memory-size=385024 -data-size=348160 -stack-size=512 -run $(COMPILER)
6 -
MODULES :=
6 +
MODULES := core/fdt.rad core/platform.rad
7 +
CORE := -pkg core -mod core.rad $(addprefix -mod ,$(MODULES))
8 +
CHECK_MODULES := check/boot.rad check/fixture.rad
7 9
8 10
.PHONY: all check clean compiler-check
9 11
all: kernel.rv64
10 12
11 13
compiler-check:
12 14
13 15
$(COMPILER): compiler-check
14 16
	$(MAKE) -C .. RAD_EMULATOR=$(abspath $(shell command -v $(HOST_EMU)))
15 17
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 +
kernel.rv64: main.rad arch/entry.ras core.rad $(MODULES) $(COMPILER)
19 +
	$(COMPILE) $(CORE) -pkg kernel -start arch/entry.ras -mod main.rad -entry kernel -o $@
18 20
19 -
check.rv64: check.rad $(MODULES) $(COMPILER)
20 -
	$(COMPILE) -pkg kernel -mod check.rad $(addprefix -mod ,$(MODULES)) -o $@
21 +
check.rv64: check.rad core.rad $(MODULES) $(CHECK_MODULES) $(COMPILER)
22 +
	$(COMPILE) $(CORE) -pkg check -mod check.rad $(addprefix -mod ,$(CHECK_MODULES)) -entry check -o $@
21 23
22 24
check: all check.rv64
23 25
	$(HOST_EMU) -run check.rv64
24 26
	$(EMU) -machine -no-guard-stack -max-steps=1000000 -count-instructions -run kernel.rv64
25 27
kernel/NOTES.md +13 -2
1 1
# Kernel implementation decisions
2 2
3 3
The specification at https://radiant.computer/system/kernel takes precedence
4 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.
5 +
record the contracts established through step 2 of the 22-step plan.
6 6
7 7
## Source and trust boundary
8 8
9 9
- Kernel mechanisms use freestanding Radiance; RAS owns machine entry, register
10 10
  state, atomics, and MMIO. Hosted checks exercise the same mechanism modules.
20 20
  immutable; writable image state is an instance resource.
21 21
- The reference machine uses RAD0 and the sibling emulator, not an assumed
22 22
  QEMU `virt` memory map. Emulator execution is not evidence of real-hardware
23 23
  cache behavior or timing bounds. Report instruction counts, not host timings.
24 24
25 +
## Firmware discovery
26 +
27 +
- Accept version-17 FDT blobs up to 64 KiB, at most 256 nodes, and 32 nesting
28 +
  levels. Support one- and two-cell addresses, empty bus ranges, and explicit
29 +
  translations. Reject duplicate names/properties/phandles, overlapping sections,
30 +
  incomplete register tuples, and truncated inputs.
31 +
- Decode firmware reservations and /reserved-memory. MMIO is not allocatable RAM.
32 +
  CLINT, PLIC, and radiant,finish are machine resources, not user Device authority.
33 +
- Hardware hart identifiers fit the eight-entry build limit. CLINT banks and
34 +
  PLIC contexts come from interrupts-extended, not the hart identifier itself.
35 +
25 36
## Validation
26 37
27 38
Use the current machine-capable sibling emulator. Set `RAD_EMULATOR`, pass
28 39
`EMU` to the kernel Make invocation, or put `emulator` on PATH. The kernel build
29 40
checks compiler dependencies. From the repository root, run:
30 41
31 42
```sh
32 43
make -C kernel check
33 44
```
34 45
35 -
The linked freestanding entry point and hosted runner must execute; an assertion failure must fail the check.
46 +
Exercise bounded FDT topology discovery, malformed sections, every truncated prefix, reservations, and disabled CPU nodes.
36 47
37 48
The entry probe uses explicit M-mode success/fault finish writes; secondary
38 49
harts idle. This checks machine entry, not user-domain execution. Finish writes
39 50
are a check protocol, not a domain-exit operation.
kernel/check.rad +4 -0
1 1
//! Hosted entry for kernel mechanism checks.
2 2
3 +
mod boot;
4 +
export mod fixture;
5 +
3 6
/// Run the available kernel mechanism checks.
4 7
@default fn main() -> u32 {
8 +
    boot::run();
5 9
    return 0;
6 10
}
kernel/check/boot.rad added +66 -0
1 +
//! Device-tree parsing and physical-platform behavior checks.
2 +
3 +
use core::fdt;
4 +
use core::platform;
5 +
use check::fixture;
6 +
7 +
/// Set a big-endian word in a test-owned buffer.
8 +
fn put(bytes: *mut [u8], offset: u32, value: u32) {
9 +
    set bytes[offset] = (value >> 24) as u8;
10 +
    set bytes[offset + 1] = (value >> 16) as u8;
11 +
    set bytes[offset + 2] = (value >> 8) as u8;
12 +
    set bytes[offset + 3] = value as u8;
13 +
}
14 +
15 +
/// Require malformed input to fail without exposing partially decoded state.
16 +
fn reject(bytes: *[u8]) {
17 +
    let mut tree: fdt::Tree = undefined;
18 +
    try fdt::decode(bytes, &mut tree) catch { return; };
19 +
    panic "reject: malformed FDT accepted";
20 +
}
21 +
22 +
/// Check platform resources and reject truncated or overlapping sections.
23 +
export fn run() {
24 +
    let bytes = &fixture::BYTES[..];
25 +
    let mut tree: fdt::Tree = undefined;
26 +
    try! fdt::decode(bytes, &mut tree);
27 +
    let mut machine: platform::Platform = undefined;
28 +
    try! platform::discover(&tree, &mut machine);
29 +
    assert machine.memoryCount == 1;
30 +
    assert machine.memory[0].base == 0;
31 +
    assert machine.memory[0].size == 0x8000000;
32 +
    assert machine.reserved[0].size == 0x1000000;
33 +
    assert machine.hartCount == 2;
34 +
    assert machine.harts[1].id == 1;
35 +
    assert machine.harts[1].clintIndex == 1;
36 +
    assert machine.harts[1].plicContext == 1;
37 +
    assert machine.clint.base == 0x2000000;
38 +
    assert machine.plic.base == 0xc000000;
39 +
    assert machine.devices[0].region.base == 0x10000000;
40 +
    assert machine.devices[0].irq == 10;
41 +
    for n in 0..bytes.len { reject(&bytes[..n]); }
42 +
43 +
    let mut corrupt = fixture::BYTES;
44 +
    put(&mut corrupt[..], 8, 0xfffffff0);
45 +
    reject(&corrupt[..]);
46 +
    set corrupt = fixture::BYTES;
47 +
    put(&mut corrupt[..], 12, 72);
48 +
    reject(&corrupt[..]);
49 +
    set corrupt = fixture::BYTES;
50 +
    put(&mut corrupt[..], 16, 72);
51 +
    reject(&corrupt[..]);
52 +
    set corrupt = fixture::BYTES;
53 +
    put(&mut corrupt[..], 72, 2);
54 +
    reject(&corrupt[..]);
55 +
56 +
    // A disabled hart must not receive a route in the online set.
57 +
    try! fdt::decode(bytes, &mut tree);
58 +
    for i in 0..tree.count {
59 +
        if fdt::equal(tree.nodes[i].name, "cpu@1") {
60 +
            set tree.nodes[i].status = "disabled\0";
61 +
        }
62 +
    }
63 +
    try! platform::discover(&tree, &mut machine);
64 +
    assert machine.hartCount == 1;
65 +
    assert machine.harts[0].id == 0;
66 +
}
kernel/check/fixture.rad added +76 -0
1 +
//! Encoded two-hart platform used by device-tree behavior checks.
2 +
3 +
/// A complete version-17 FDT with RAM, CLINT, PLIC, and UART.
4 +
export constant BYTES: [u8; 1136] = [
5 +
    0xd0, 0x0d, 0xfe, 0xed, 0x00, 0x00, 0x04, 0x70, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x03, 0xcc,
6 +
    0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
7 +
    0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
8 +
    0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
9 +
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
10 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
11 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x02,
12 +
    0x00, 0x00, 0x00, 0x01, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00,
13 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1b, 0x6d, 0x65, 0x6d, 0x6f,
14 +
    0x72, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x27,
15 +
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
16 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x63, 0x70, 0x75, 0x73, 0x00, 0x00, 0x00, 0x00,
17 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
18 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
19 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x0f, 0x42, 0x40,
20 +
    0x00, 0x00, 0x00, 0x01, 0x63, 0x70, 0x75, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
21 +
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x1b, 0x63, 0x70, 0x75, 0x00, 0x00, 0x00, 0x00, 0x03,
22 +
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
23 +
    0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f,
24 +
    0x6c, 0x6c, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0f,
25 +
    0x00, 0x00, 0x00, 0x3e, 0x72, 0x69, 0x73, 0x63, 0x76, 0x2c, 0x63, 0x70, 0x75, 0x2d, 0x69, 0x6e,
26 +
    0x74, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x49,
27 +
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x51,
28 +
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
29 +
    0x63, 0x70, 0x75, 0x40, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
30 +
    0x00, 0x00, 0x00, 0x1b, 0x63, 0x70, 0x75, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
31 +
    0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x69, 0x6e, 0x74, 0x65,
32 +
    0x72, 0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72,
33 +
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3e,
34 +
    0x72, 0x69, 0x73, 0x63, 0x76, 0x2c, 0x63, 0x70, 0x75, 0x2d, 0x69, 0x6e, 0x74, 0x63, 0x00, 0x00,
35 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x02,
36 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x01,
37 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
38 +
    0x73, 0x6f, 0x63, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
39 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0f,
40 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62,
41 +
    0x00, 0x00, 0x00, 0x01, 0x63, 0x6c, 0x69, 0x6e, 0x74, 0x40, 0x32, 0x30, 0x30, 0x30, 0x30, 0x30,
42 +
    0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3e,
43 +
    0x72, 0x69, 0x73, 0x63, 0x76, 0x2c, 0x63, 0x6c, 0x69, 0x6e, 0x74, 0x30, 0x00, 0x00, 0x00, 0x00,
44 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
45 +
    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
46 +
    0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x69, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03,
47 +
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
48 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
49 +
    0x70, 0x6c, 0x69, 0x63, 0x40, 0x63, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00,
50 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3e, 0x72, 0x69, 0x73, 0x63,
51 +
    0x76, 0x2c, 0x70, 0x6c, 0x69, 0x63, 0x30, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10,
52 +
    0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 +
    0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x49,
54 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x51,
55 +
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x7d,
56 +
    0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x69,
57 +
    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0b,
58 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x75, 0x61, 0x72, 0x74, 0x40, 0x31, 0x30, 0x30,
59 +
    0x30, 0x30, 0x30, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x09,
60 +
    0x00, 0x00, 0x00, 0x3e, 0x6e, 0x73, 0x31, 0x36, 0x35, 0x35, 0x30, 0x61, 0x00, 0x00, 0x00, 0x00,
61 +
    0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00,
62 +
    0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x03,
63 +
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03,
64 +
    0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x99, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x02,
65 +
    0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09, 0x23, 0x61, 0x64, 0x64,
66 +
    0x72, 0x65, 0x73, 0x73, 0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x23, 0x73, 0x69, 0x7a, 0x65,
67 +
    0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x74, 0x79,
68 +
    0x70, 0x65, 0x00, 0x72, 0x65, 0x67, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x62, 0x61, 0x73, 0x65, 0x2d,
69 +
    0x66, 0x72, 0x65, 0x71, 0x75, 0x65, 0x6e, 0x63, 0x79, 0x00, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74,
70 +
    0x69, 0x62, 0x6c, 0x65, 0x00, 0x70, 0x68, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x00, 0x23, 0x69, 0x6e,
71 +
    0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2d, 0x63, 0x65, 0x6c, 0x6c, 0x73, 0x00, 0x72, 0x61,
72 +
    0x6e, 0x67, 0x65, 0x73, 0x00, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x73, 0x2d,
73 +
    0x65, 0x78, 0x74, 0x65, 0x6e, 0x64, 0x65, 0x64, 0x00, 0x72, 0x69, 0x73, 0x63, 0x76, 0x2c, 0x6e,
74 +
    0x64, 0x65, 0x76, 0x00, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x2d, 0x70, 0x61,
75 +
    0x72, 0x65, 0x6e, 0x74, 0x00, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x72, 0x75, 0x70, 0x74, 0x73, 0x00,
76 +
];
kernel/core.rad added +4 -0
1 +
//! Safe kernel mechanisms shared by machine execution and hosted checks.
2 +
3 +
export mod fdt;
4 +
export mod platform;
kernel/core/fdt.rad added +323 -0
1 +
//! Bounded decoding of flattened device trees, version 17.
2 +
3 +
/// Maximum nodes accepted from platform firmware.
4 +
export constant MAX_NODES: u32 = 256;
5 +
/// Maximum nesting depth accepted from platform firmware.
6 +
constant MAX_DEPTH: u32 = 32;
7 +
/// Maximum firmware reservation entries.
8 +
export constant MAX_RESERVED: u32 = 64;
9 +
/// Maximum complete device-tree size accepted at boot.
10 +
export constant MAX_BYTES: u32 = 65536;
11 +
/// Maximum properties retained across the active parser stack.
12 +
constant MAX_PROPERTIES: u32 = 256;
13 +
/// Parent index of the root node.
14 +
export constant NO_PARENT: u32 = 0xffffffff;
15 +
/// Device tree header magic.
16 +
constant MAGIC: u32 = 0xd00dfeed;
17 +
18 +
/// Invalid firmware input or exhausted decoder storage.
19 +
export union Error: Copy {
20 +
    /// Input violates the flattened tree format or supported cell widths.
21 +
    Invalid,
22 +
    /// Input exceeds a fixed decoder limit.
23 +
    Exhausted,
24 +
}
25 +
26 +
/// One physical half-open address range.
27 +
export record Range: Copy {
28 +
    /// Physical start address.
29 +
    base: u64,
30 +
    /// Number of bytes in the range.
31 +
    size: u64,
32 +
}
33 +
34 +
/// Properties used to discover physical resources.
35 +
export record Node: Copy {
36 +
    /// Node name without its terminating zero byte.
37 +
    name: *[u8],
38 +
    /// Parent node index, or NO_PARENT for the root.
39 +
    parent: u32,
40 +
    /// Number of address cells used by children.
41 +
    addressCells: u32,
42 +
    /// Number of size cells used by children.
43 +
    sizeCells: u32,
44 +
    /// Physical register tuples encoded with parent cell widths.
45 +
    reg: *[u8],
46 +
    /// Bus address translations.
47 +
    ranges: ?*[u8],
48 +
    /// Zero-terminated compatible strings.
49 +
    compatible: *[u8],
50 +
    /// Device type string.
51 +
    deviceType: *[u8],
52 +
    /// Status string; empty means enabled.
53 +
    status: *[u8],
54 +
    /// Interrupt-controller identifier.
55 +
    phandle: u32,
56 +
    /// Interrupt-parent identifier; zero inherits from the parent.
57 +
    interruptParent: u32,
58 +
    /// Number of cells in interrupt specifiers.
59 +
    interruptCells: u32,
60 +
    /// Interrupt source specifiers.
61 +
    interrupts: *[u8],
62 +
    /// Interrupt specifiers with controller identifiers.
63 +
    extended: *[u8],
64 +
    /// Number of PLIC interrupt sources.
65 +
    sources: u32,
66 +
    /// Timer ticks per second for the CPU bus.
67 +
    frequency: u32,
68 +
}
69 +
70 +
/// Decoded nodes and firmware reservations borrowed from the input.
71 +
export record Tree: Copy {
72 +
    /// Nodes in structure-block order.
73 +
    nodes: [Node; MAX_NODES],
74 +
    /// Number of initialized node entries.
75 +
    count: u32,
76 +
    /// Physical reservations from the header reservation map.
77 +
    reserved: [Range; MAX_RESERVED],
78 +
    /// Number of initialized reservation entries.
79 +
    reservedCount: u32,
80 +
    /// Total byte size of the firmware blob.
81 +
    size: u32,
82 +
}
83 +
84 +
/// Read a big-endian word after checking the complete range.
85 +
export fn word(bytes: *[u8], offset: u32) -> u32 throws (Error) {
86 +
    if offset > bytes.len or bytes.len - offset < 4 {
87 +
        throw Error::Invalid;
88 +
    }
89 +
    return (bytes[offset] as u32 << 24) | (bytes[offset + 1] as u32 << 16)
90 +
        | (bytes[offset + 2] as u32 << 8) | bytes[offset + 3] as u32;
91 +
}
92 +
93 +
/// Read a one-cell or two-cell integer.
94 +
export fn cells(bytes: *[u8], offset: u32, count: u32) -> u64 throws (Error) {
95 +
    if count == 0 { return 0; }
96 +
    if count == 1 { return try word(bytes, offset) as u64; }
97 +
    if count <> 2 { throw Error::Invalid; }
98 +
    let high = try word(bytes, offset);
99 +
    let low = try word(bytes, offset + 4);
100 +
    return (high as u64 << 32) | low as u64;
101 +
}
102 +
103 +
/// Compare byte strings without reading beyond either slice.
104 +
export fn equal(a: *[u8], b: *[u8]) -> bool {
105 +
    if a.len <> b.len { return false; }
106 +
    for byte, i in a {
107 +
        if byte <> b[i] { return false; }
108 +
    }
109 +
    return true;
110 +
}
111 +
112 +
/// Return a bounded zero-terminated string without its terminator.
113 +
fn string(bytes: *[u8], offset: u32) -> *[u8] throws (Error) {
114 +
    if offset >= bytes.len { throw Error::Invalid; }
115 +
    let mut end = offset;
116 +
    while bytes[end] <> 0 {
117 +
        set end += 1;
118 +
        if end == bytes.len { throw Error::Invalid; }
119 +
    }
120 +
    return &bytes[offset..end];
121 +
}
122 +
123 +
/// Check for an exact item in a sequence of zero-terminated strings.
124 +
export fn contains(bytes: *[u8], item: *[u8]) -> bool {
125 +
    let mut offset: u32 = 0;
126 +
    while offset < bytes.len {
127 +
        let value = try string(bytes, offset) catch { return false; };
128 +
        if equal(value, item) { return true; }
129 +
        set offset += value.len + 1;
130 +
    }
131 +
    return false;
132 +
}
133 +
134 +
/// Test whether firmware enables a node and all its parents.
135 +
export fn enabled(tree: *Tree, index: u32) -> bool {
136 +
    let mut current = index;
137 +
    while current <> NO_PARENT {
138 +
        let node = &tree.nodes[current];
139 +
        if node.status.len <> 0 and not equal(node.status, "okay\0")
140 +
            and not equal(node.status, "ok\0") {
141 +
            return false;
142 +
        }
143 +
        set current = node.parent;
144 +
    }
145 +
    return true;
146 +
}
147 +
148 +
/// Check a section range against the complete input.
149 +
fn section(bytes: *[u8], offset: u32, size: u32) -> *[u8] throws (Error) {
150 +
    if offset > bytes.len or size > bytes.len - offset { throw Error::Invalid; }
151 +
    return &bytes[offset..offset + size];
152 +
}
153 +
154 +
/// Store one relevant property after validating its representation.
155 +
fn property(node: *mut Node, name: *[u8], value: *[u8]) throws (Error) {
156 +
    if equal(name, "#address-cells") {
157 +
        if value.len <> 4 { throw Error::Invalid; }
158 +
        set node.addressCells = try word(value, 0);
159 +
        if node.addressCells == 0 or node.addressCells > 2 { throw Error::Invalid; }
160 +
    } else if equal(name, "#size-cells") {
161 +
        if value.len <> 4 { throw Error::Invalid; }
162 +
        set node.sizeCells = try word(value, 0);
163 +
        if node.sizeCells > 2 { throw Error::Invalid; }
164 +
    } else if equal(name, "reg") {
165 +
        set node.reg = value;
166 +
    } else if equal(name, "ranges") {
167 +
        set node.ranges = value;
168 +
    } else if equal(name, "compatible") {
169 +
        if value.len == 0 or value[value.len - 1] <> 0 { throw Error::Invalid; }
170 +
        set node.compatible = value;
171 +
    } else if equal(name, "device_type") {
172 +
        set node.deviceType = value;
173 +
    } else if equal(name, "status") {
174 +
        set node.status = value;
175 +
    } else if equal(name, "interrupts") {
176 +
        set node.interrupts = value;
177 +
    } else if equal(name, "interrupts-extended") {
178 +
        set node.extended = value;
179 +
    } else {
180 +
        let scalar = equal(name, "phandle") or equal(name, "linux,phandle")
181 +
            or equal(name, "interrupt-parent") or equal(name, "#interrupt-cells")
182 +
            or equal(name, "riscv,ndev") or equal(name, "timebase-frequency");
183 +
        if scalar {
184 +
            if value.len <> 4 { throw Error::Invalid; }
185 +
            let number = try word(value, 0);
186 +
            if equal(name, "phandle") or equal(name, "linux,phandle") {
187 +
                if number == 0 or number == 0xffffffff { throw Error::Invalid; }
188 +
                if node.phandle <> 0 and node.phandle <> number { throw Error::Invalid; }
189 +
                set node.phandle = number;
190 +
            } else if equal(name, "interrupt-parent") {
191 +
                set node.interruptParent = number;
192 +
            } else if equal(name, "#interrupt-cells") {
193 +
                set node.interruptCells = number;
194 +
            } else if equal(name, "riscv,ndev") {
195 +
                set node.sources = number;
196 +
            } else {
197 +
                set node.frequency = number;
198 +
            }
199 +
        }
200 +
    }
201 +
}
202 +
203 +
/// Decode a complete FDT into caller-owned fixed storage.
204 +
export fn decode(bytes: *[u8], tree: *mut Tree) throws (Error) {
205 +
    if bytes.len < 40 or try word(bytes, 0) <> MAGIC { throw Error::Invalid; }
206 +
    let size = try word(bytes, 4);
207 +
    if size < 40 or size > bytes.len or size > MAX_BYTES { throw Error::Invalid; }
208 +
    let blob = &bytes[..size];
209 +
    let structureOffset = try word(blob, 8);
210 +
    let stringsOffset = try word(blob, 12);
211 +
    let reserveOffset = try word(blob, 16);
212 +
    if try word(blob, 20) <> 17 or try word(blob, 24) > 17 {
213 +
        throw Error::Invalid;
214 +
    }
215 +
    let stringsSize = try word(blob, 32);
216 +
    let structureSize = try word(blob, 36);
217 +
    if structureOffset < 40 or stringsOffset < 40 or reserveOffset < 40
218 +
        or (structureOffset & 3) <> 0 or (reserveOffset & 7) <> 0 {
219 +
        throw Error::Invalid;
220 +
    }
221 +
    let structure = try section(blob, structureOffset, structureSize);
222 +
    let strings = try section(blob, stringsOffset, stringsSize);
223 +
    if structureOffset < stringsOffset + stringsSize
224 +
        and stringsOffset < structureOffset + structureSize { throw Error::Invalid; }
225 +
    set tree.count = 0;
226 +
    set tree.reservedCount = 0;
227 +
    set tree.size = size;
228 +
    let mut pos = reserveOffset;
229 +
    loop {
230 +
        let entry = try section(blob, pos, 16);
231 +
        let base = try cells(entry, 0, 2);
232 +
        let length = try cells(entry, 8, 2);
233 +
        set pos += 16;
234 +
        if base == 0 and length == 0 { break; }
235 +
        if length == 0 or base > 0xffffffffffffffff - length { throw Error::Invalid; }
236 +
        if tree.reservedCount == MAX_RESERVED { throw Error::Exhausted; }
237 +
        set tree.reserved[tree.reservedCount] = Range { base, size: length };
238 +
        set tree.reservedCount += 1;
239 +
    }
240 +
    if reserveOffset < structureOffset + structureSize and structureOffset < pos
241 +
        or reserveOffset < stringsOffset + stringsSize and stringsOffset < pos {
242 +
        throw Error::Invalid;
243 +
    }
244 +
    let mut stack: [u32; MAX_DEPTH] = undefined;
245 +
    let mut depth: u32 = 0;
246 +
    let mut childSeen: [bool; MAX_DEPTH] = undefined;
247 +
    let mut propertyNames: [*[u8]; MAX_PROPERTIES] = undefined;
248 +
    let mut propertyStart: [u32; MAX_DEPTH] = undefined;
249 +
    let mut propertyCount: u32 = 0;
250 +
    set pos = 0;
251 +
    loop {
252 +
        let token = try word(structure, pos);
253 +
        set pos += 4;
254 +
        if token == 1 {
255 +
            if depth == MAX_DEPTH or tree.count == MAX_NODES { throw Error::Exhausted; }
256 +
            if depth == 0 and tree.count <> 0 { throw Error::Invalid; }
257 +
            let name = try string(structure, pos);
258 +
            if tree.count == 0 and name.len <> 0 { throw Error::Invalid; }
259 +
            let padded = (name.len + 4) & ~3;
260 +
            let _nameBytes = try section(structure, pos, padded);
261 +
            set pos += padded;
262 +
            let mut parent = NO_PARENT;
263 +
            if depth <> 0 {
264 +
                set parent = stack[depth - 1];
265 +
                set childSeen[depth - 1] = true;
266 +
            }
267 +
            for i in 0..tree.count {
268 +
                if tree.nodes[i].parent == parent and equal(tree.nodes[i].name, name) {
269 +
                    throw Error::Invalid;
270 +
                }
271 +
            }
272 +
            set tree.nodes[tree.count] = Node {
273 +
                name, parent, addressCells: 2, sizeCells: 1,
274 +
                reg: "", ranges: nil, compatible: "", deviceType: "", status: "",
275 +
                phandle: 0, interruptParent: 0, interruptCells: 0,
276 +
                interrupts: "", extended: "", sources: 0, frequency: 0,
277 +
            };
278 +
            set stack[depth] = tree.count;
279 +
            set childSeen[depth] = false;
280 +
            set propertyStart[depth] = propertyCount;
281 +
            set depth += 1;
282 +
            set tree.count += 1;
283 +
        } else if token == 2 {
284 +
            if depth == 0 { throw Error::Invalid; }
285 +
            set depth -= 1;
286 +
            set propertyCount = propertyStart[depth];
287 +
        } else if token == 3 {
288 +
            if depth == 0 or childSeen[depth - 1] { throw Error::Invalid; }
289 +
            let length = try word(structure, pos);
290 +
            let nameOffset = try word(structure, pos + 4);
291 +
            set pos += 8;
292 +
            let name = try string(strings, nameOffset);
293 +
            if name.len == 0 { throw Error::Invalid; }
294 +
            for i in propertyStart[depth - 1]..propertyCount {
295 +
                if equal(propertyNames[i], name) { throw Error::Invalid; }
296 +
            }
297 +
            if propertyCount == MAX_PROPERTIES { throw Error::Exhausted; }
298 +
            set propertyNames[propertyCount] = name;
299 +
            set propertyCount += 1;
300 +
            let value = try section(structure, pos, length);
301 +
            if length > 0xfffffffc { throw Error::Invalid; }
302 +
            let padded = (length + 3) & ~3;
303 +
            let _valueBytes = try section(structure, pos, padded);
304 +
            set pos += padded;
305 +
            try property(&mut tree.nodes[stack[depth - 1]], name, value);
306 +
        } else if token == 4 {
307 +
            // NOP tokens can occur between structure entries.
308 +
        } else if token == 9 {
309 +
            if depth <> 0 or tree.count == 0 or pos <> structure.len { throw Error::Invalid; }
310 +
            for i in 0..tree.count {
311 +
                let id = tree.nodes[i].phandle;
312 +
                if id <> 0 {
313 +
                    for j in 0..i {
314 +
                        if tree.nodes[j].phandle == id { throw Error::Invalid; }
315 +
                    }
316 +
                }
317 +
            }
318 +
            return;
319 +
        } else {
320 +
            throw Error::Invalid;
321 +
        }
322 +
    }
323 +
}
kernel/core/platform.rad added +276 -0
1 +
//! Physical resources and interrupt topology from a validated device tree.
2 +
3 +
use core::fdt;
4 +
5 +
/// Maximum online harts in this build.
6 +
export constant MAX_HARTS: u32 = 8;
7 +
/// Maximum disjoint RAM regions in the boot description.
8 +
export constant MAX_MEMORY: u32 = 16;
9 +
/// Maximum user-visible MMIO regions.
10 +
export constant MAX_DEVICES: u32 = 128;
11 +
/// Maximum PLIC source identifier, including unused source zero.
12 +
export constant MAX_IRQS: u32 = 128;
13 +
14 +
/// One enabled hart and its machine interrupt-controller routes.
15 +
export record Hart: Copy {
16 +
    /// Hardware hart identifier.
17 +
    id: u32,
18 +
    /// Local CPU interrupt-controller phandle.
19 +
    controller: u32,
20 +
    /// CLINT register bank index.
21 +
    clintIndex: u32,
22 +
    /// PLIC machine-context index.
23 +
    plicContext: u32,
24 +
}
25 +
26 +
/// One user-visible device region.
27 +
export record Device: Copy {
28 +
    /// Physical register window.
29 +
    region: fdt::Range,
30 +
    /// PLIC source, or zero for a device without interrupts.
31 +
    irq: u32,
32 +
}
33 +
34 +
/// Fixed storage for all discovered platform resources.
35 +
export record Platform: Copy {
36 +
    /// Physical RAM banks.
37 +
    memory: [fdt::Range; MAX_MEMORY],
38 +
    /// Number of initialized RAM banks.
39 +
    memoryCount: u32,
40 +
    /// Firmware reservations.
41 +
    reserved: [fdt::Range; fdt::MAX_RESERVED],
42 +
    /// Number of initialized reservations.
43 +
    reservedCount: u32,
44 +
    /// Online harts in device-tree order.
45 +
    harts: [Hart; MAX_HARTS],
46 +
    /// Number of online harts.
47 +
    hartCount: u32,
48 +
    /// User-visible devices in device-tree order.
49 +
    devices: [Device; MAX_DEVICES],
50 +
    /// Number of initialized device regions.
51 +
    deviceCount: u32,
52 +
    /// CLINT register window.
53 +
    clint: fdt::Range,
54 +
    /// PLIC register window.
55 +
    plic: fdt::Range,
56 +
    /// Number of usable PLIC sources.
57 +
    irqCount: u32,
58 +
    /// Monotonic timer frequency in ticks per second.
59 +
    frequency: u32,
60 +
}
61 +
62 +
/// Translate a range through its parent bus mappings.
63 +
fn translate(tree: *fdt::Tree, parent: u32, range: fdt::Range) -> fdt::Range throws (fdt::Error) {
64 +
    let mut result = range;
65 +
    let mut bus = parent;
66 +
    while tree.nodes[bus].parent <> fdt::NO_PARENT {
67 +
        let node = &tree.nodes[bus];
68 +
        let ancestor = &tree.nodes[node.parent];
69 +
        let ranges = node.ranges else { throw fdt::Error::Invalid; };
70 +
        if ranges.len <> 0 {
71 +
            let tupleSize = (node.addressCells + ancestor.addressCells + node.sizeCells) * 4;
72 +
            if tupleSize == 0 or ranges.len % tupleSize <> 0 { throw fdt::Error::Invalid; }
73 +
            let mut found = false;
74 +
            let mut offset: u32 = 0;
75 +
            while offset < ranges.len {
76 +
                let child = try fdt::cells(ranges, offset, node.addressCells);
77 +
                let target = try fdt::cells(ranges, offset + node.addressCells * 4, ancestor.addressCells);
78 +
                let size = try fdt::cells(ranges, offset + (node.addressCells + ancestor.addressCells) * 4, node.sizeCells);
79 +
                if result.base >= child and result.base - child <= size
80 +
                    and result.size <= size - (result.base - child) {
81 +
                    let delta = result.base - child;
82 +
                    if target > 0xffffffffffffffff - delta
83 +
                        or result.size > 0xffffffffffffffff - (target + delta) {
84 +
                        throw fdt::Error::Invalid;
85 +
                    }
86 +
                    set result.base = target + delta;
87 +
                    set found = true;
88 +
                    break;
89 +
                }
90 +
                set offset += tupleSize;
91 +
            }
92 +
            if not found { throw fdt::Error::Invalid; }
93 +
        }
94 +
        set bus = node.parent;
95 +
    }
96 +
    return result;
97 +
}
98 +
99 +
/// Decode and translate one register tuple.
100 +
fn region(tree: *fdt::Tree, index: u32, offset: u32) -> fdt::Range throws (fdt::Error) {
101 +
    let node = &tree.nodes[index];
102 +
    if node.parent == fdt::NO_PARENT { throw fdt::Error::Invalid; }
103 +
    let parent = &tree.nodes[node.parent];
104 +
    let base = try fdt::cells(node.reg, offset, parent.addressCells);
105 +
    let size = try fdt::cells(node.reg, offset + parent.addressCells * 4, parent.sizeCells);
106 +
    if size == 0 or base > 0xffffffffffffffff - size { throw fdt::Error::Invalid; }
107 +
    return try translate(tree, node.parent, fdt::Range { base, size });
108 +
}
109 +
110 +
/// Resolve CPU-local interrupt routing for CLINT and PLIC.
111 +
fn routes(tree: *fdt::Tree, node: *fdt::Node, platform: *mut Platform, plic: bool) throws (fdt::Error) {
112 +
    if node.extended.len == 0 or node.extended.len % 8 <> 0 { throw fdt::Error::Invalid; }
113 +
    let mut offset: u32 = 0;
114 +
    while offset < node.extended.len {
115 +
        let controller = try fdt::word(node.extended, offset);
116 +
        let cause = try fdt::word(node.extended, offset + 4);
117 +
        let mut known = false;
118 +
        for i in 0..tree.count {
119 +
            if tree.nodes[i].phandle == controller and tree.nodes[i].interruptCells == 1 {
120 +
                set known = true;
121 +
            }
122 +
        }
123 +
        if not known { throw fdt::Error::Invalid; }
124 +
        for i in 0..platform.hartCount {
125 +
            let hart = &mut platform.harts[i];
126 +
            if hart.controller == controller {
127 +
                if plic and cause == 11 {
128 +
                    if hart.plicContext <> fdt::NO_PARENT { throw fdt::Error::Invalid; }
129 +
                    set hart.plicContext = offset / 8;
130 +
                } else if not plic and cause == 3 {
131 +
                    if hart.clintIndex <> fdt::NO_PARENT { throw fdt::Error::Invalid; }
132 +
                    if offset + 16 > node.extended.len
133 +
                        or try fdt::word(node.extended, offset + 8) <> controller
134 +
                        or try fdt::word(node.extended, offset + 12) <> 7 {
135 +
                        throw fdt::Error::Invalid;
136 +
                    }
137 +
                    set hart.clintIndex = offset / 16;
138 +
                }
139 +
            }
140 +
        }
141 +
        set offset += 8;
142 +
    }
143 +
}
144 +
145 +
/// Discover enabled RAM, harts, devices, and their interrupt routes.
146 +
export fn discover(tree: *fdt::Tree, platform: *mut Platform) throws (fdt::Error) {
147 +
    set platform.memoryCount = 0;
148 +
    set platform.hartCount = 0;
149 +
    set platform.deviceCount = 0;
150 +
    set platform.reservedCount = tree.reservedCount;
151 +
    set platform.frequency = 0;
152 +
    set platform.clint = fdt::Range { base: 0, size: 0 };
153 +
    set platform.plic = fdt::Range { base: 0, size: 0 };
154 +
    set platform.irqCount = 0;
155 +
    for i in 0..tree.reservedCount { set platform.reserved[i] = tree.reserved[i]; }
156 +
    let mut clintNode = fdt::NO_PARENT;
157 +
    let mut plicNode = fdt::NO_PARENT;
158 +
    for i in 0..tree.count {
159 +
        let node = &tree.nodes[i];
160 +
        if not fdt::enabled(tree, i) { continue; }
161 +
        if fdt::equal(node.name, "cpus") {
162 +
            set platform.frequency = node.frequency;
163 +
        }
164 +
        if fdt::equal(node.deviceType, "cpu\0") {
165 +
            if platform.hartCount == MAX_HARTS or node.parent == fdt::NO_PARENT {
166 +
                throw fdt::Error::Exhausted;
167 +
            }
168 +
            let cells = tree.nodes[node.parent].addressCells;
169 +
            if node.reg.len <> cells * 4 { throw fdt::Error::Invalid; }
170 +
            let id = try fdt::cells(node.reg, 0, cells);
171 +
            if id >= MAX_HARTS as u64 { throw fdt::Error::Invalid; }
172 +
            for j in 0..platform.hartCount {
173 +
                if platform.harts[j].id == id as u32 { throw fdt::Error::Invalid; }
174 +
            }
175 +
            let mut controller: u32 = 0;
176 +
            for j in 0..tree.count {
177 +
                let child = &tree.nodes[j];
178 +
                if child.parent == i and fdt::contains(child.compatible, "riscv,cpu-intc") {
179 +
                    if controller <> 0 or child.phandle == 0 or child.interruptCells <> 1 {
180 +
                        throw fdt::Error::Invalid;
181 +
                    }
182 +
                    set controller = child.phandle;
183 +
                }
184 +
            }
185 +
            if controller == 0 { throw fdt::Error::Invalid; }
186 +
            set platform.harts[platform.hartCount] = Hart {
187 +
                id: id as u32, controller, clintIndex: fdt::NO_PARENT, plicContext: fdt::NO_PARENT,
188 +
            };
189 +
            set platform.hartCount += 1;
190 +
            continue;
191 +
        }
192 +
        if node.reg.len == 0 { continue; }
193 +
        if node.parent == fdt::NO_PARENT { throw fdt::Error::Invalid; }
194 +
        let parent = &tree.nodes[node.parent];
195 +
        if parent.sizeCells == 0 { continue; }
196 +
        let tupleSize = (parent.addressCells + parent.sizeCells) * 4;
197 +
        if node.reg.len % tupleSize <> 0 { throw fdt::Error::Invalid; }
198 +
        let memory = fdt::equal(node.deviceType, "memory\0");
199 +
        let reserved = fdt::equal(parent.name, "reserved-memory");
200 +
        let clint = fdt::contains(node.compatible, "riscv,clint0");
201 +
        let plic = fdt::contains(node.compatible, "riscv,plic0")
202 +
            or fdt::contains(node.compatible, "sifive,plic-1.0.0");
203 +
        let mut offset: u32 = 0;
204 +
        while offset < node.reg.len {
205 +
            let range = try region(tree, i, offset);
206 +
            if not memory {
207 +
                if platform.reservedCount == fdt::MAX_RESERVED { throw fdt::Error::Exhausted; }
208 +
                set platform.reserved[platform.reservedCount] = range;
209 +
                set platform.reservedCount += 1;
210 +
            }
211 +
            if memory {
212 +
                if platform.memoryCount == MAX_MEMORY { throw fdt::Error::Exhausted; }
213 +
                for j in 0..platform.memoryCount {
214 +
                    let other = platform.memory[j];
215 +
                    if range.base < other.base + other.size and other.base < range.base + range.size {
216 +
                        throw fdt::Error::Invalid;
217 +
                    }
218 +
                }
219 +
                set platform.memory[platform.memoryCount] = range;
220 +
                set platform.memoryCount += 1;
221 +
            } else if clint {
222 +
                if clintNode <> fdt::NO_PARENT or range.size < 0xc000 { throw fdt::Error::Invalid; }
223 +
                set platform.clint = range;
224 +
                set clintNode = i;
225 +
            } else if plic {
226 +
                if plicNode <> fdt::NO_PARENT or range.size < 0x200008
227 +
                    or node.sources == 0 or node.sources >= MAX_IRQS { throw fdt::Error::Invalid; }
228 +
                set platform.plic = range;
229 +
                set platform.irqCount = node.sources;
230 +
                set plicNode = i;
231 +
            } else if not reserved and node.compatible.len <> 0
232 +
                and not fdt::contains(node.compatible, "radiant,finish") {
233 +
                // User-visible devices exclude machine interrupt controllers.
234 +
                if platform.deviceCount == MAX_DEVICES { throw fdt::Error::Exhausted; }
235 +
                let mut irq: u32 = 0;
236 +
                if node.interrupts.len <> 0 {
237 +
                    if node.interrupts.len <> 4 { throw fdt::Error::Invalid; }
238 +
                    set irq = try fdt::word(node.interrupts, 0);
239 +
                }
240 +
                set platform.devices[platform.deviceCount] = Device { region: range, irq };
241 +
                set platform.deviceCount += 1;
242 +
            }
243 +
            set offset += tupleSize;
244 +
        }
245 +
    }
246 +
    if platform.memoryCount == 0 or platform.hartCount == 0 or platform.frequency == 0
247 +
        or clintNode == fdt::NO_PARENT or plicNode == fdt::NO_PARENT {
248 +
        throw fdt::Error::Invalid;
249 +
    }
250 +
    try routes(tree, &tree.nodes[clintNode], platform, false);
251 +
    try routes(tree, &tree.nodes[plicNode], platform, true);
252 +
    for i in 0..platform.hartCount {
253 +
        let hart = &platform.harts[i];
254 +
        if hart.clintIndex == fdt::NO_PARENT or hart.plicContext == fdt::NO_PARENT
255 +
            or 0x4008 + hart.clintIndex as u64 * 8 > platform.clint.size
256 +
            or 0x200008 + hart.plicContext as u64 * 0x1000 > platform.plic.size {
257 +
            throw fdt::Error::Invalid;
258 +
        }
259 +
    }
260 +
    for i in 0..platform.deviceCount {
261 +
        if platform.devices[i].irq > platform.irqCount { throw fdt::Error::Invalid; }
262 +
    }
263 +
    for i in 0..tree.count {
264 +
        let node = &tree.nodes[i];
265 +
        if not fdt::enabled(tree, i) or node.interrupts.len == 0 { continue; }
266 +
        let mut current = i;
267 +
        let mut controller: u32 = 0;
268 +
        while current <> fdt::NO_PARENT and controller == 0 {
269 +
            set controller = tree.nodes[current].interruptParent;
270 +
            set current = tree.nodes[current].parent;
271 +
        }
272 +
        if controller == 0 or controller <> tree.nodes[plicNode].phandle {
273 +
            throw fdt::Error::Invalid;
274 +
        }
275 +
    }
276 +
}
kernel/main.rad +1 -0
1 1
//! Freestanding machine initialization.
2 2
3 +
3 4
/// Maximum online harts in this kernel build.
4 5
constant MAX_HARTS: u64 = 8;
5 6
6 7
/// Validate the entry hart before machine initialization.
7 8
@default fn main(hart: u64) -> u32 {