1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! The RISC-V `virt` board: the devices a hart needs to boot an operating
//! system.
//!
//! `ROADMAP.md` §6 picks RISC-V as the first architecture to boot real system
//! software, and `docs/platforms/riscv-virt.md` picks this board because it is
//! the smallest credible thing that does: a hart, a timer, an interrupt
//! controller, a serial port, and virtio for storage. No PCI, no ACPI, no
//! legacy — and every part of it specified in a document anybody can download.
//!
//! | Module | Covers |
//! | --- | --- |
//! | [`clint`] | `mtime`, per-hart `mtimecmp`, software interrupts |
//! | [`plic`] | the platform-level interrupt controller: priority, enable, claim |
//! | [`uart`] | a 16550 on the [`chardev`](crate::host::chardev) seam |
//! | [`syscon`] | the system controller a guest powers itself off through |
//! | [`virtio`] | the virtio-MMIO transport, plus block and entropy devices |
//! | [`fdt`] | the flattened device tree *format* |
//! | [`dt`] | the device tree *generator*, which walks the realized machine |
//! | [`boot`] | the reset vector, and where the generated tree lands |
//! | [`loader`] | putting a firmware or kernel image into guest memory |
//!
//! # The board
//!
//! `machines/riscv-virt.machine` puts it together. The layout below is the
//! conventional one for a RISC-V `virt` board, and it is conventional rather
//! than required: the guest learns every address from the device tree we
//! generate, so nothing outside this repository fixes these numbers. They are
//! the familiar ones because familiar is easier to debug.
//!
//! ```text
//! 0x0000_1000 boot ROM: the reset vector, then the generated DTB
//! 0x0010_0000 system controller (poweroff, reboot)
//! 0x0200_0000 CLINT
//! 0x0c00_0000 PLIC
//! 0x1000_0000 16550 UART
//! 0x1000_1000 virtio-mmio, one 4 KiB window each
//! 0x2000_0000 NOR flash bank 0: the firmware
//! 0x2200_0000 NOR flash bank 1: the UEFI variable store
//! 0x8000_0000 DRAM
//! ```
//!
//! The two flash banks are [`crate::dev::flash::cfi`] rather than anything in
//! this module: a CFI part is not a RISC-V device, and the board simply maps
//! two of them. What *is* board-specific is that they appear in the generated
//! tree as `cfi-flash` nodes, which is how a UEFI build finds its variable
//! store.
//!
//! # How far it boots
//!
//! Measured, not asserted (`ROADMAP.md` §0), and the runners are in
//! [`tests`](self#modules)'s sibling `tests.rs`:
//!
//! * **A bare-metal program** written straight to `0x80000000` prints to the
//! console, takes a CLINT timer interrupt at `mtvec`, receives a keystroke
//! through the UART, the PLIC and `meip`, and stops the machine through the
//! system controller. No firmware, no operating system, no fetched fixture.
//! * **OpenSBI** (`fw_jump.bin`, BSD-2-Clause, fetched by
//! `scripts/fetch-testdata.sh opensbi`) runs to completion on the generated
//! device tree: it finds the timer as `aclint-mtimer @ 10000000Hz`, the
//! console as `uart8250`, both `syscon-poweroff` and `syscon-reboot`, builds
//! its domain from our memory map, and jumps to `0x80200000` in S-mode.
//! * **EDK2/UEFI** (`OvmfPkg/RiscVVirt`, BSD-2-Clause-Patent) boots out of the
//! two NOR banks to `UEFI Interactive Shell v2.2` at a `Shell>` prompt:
//! `gEfiVariableWriteArchProtocolGuid` installs against the flash, the DXE
//! dispatcher completes, and BDS enumerates its boot options. A variable
//! written in one run is in the store the next — the append-only variable log
//! continues where the previous run left it rather than restarting, which is
//! only possible on a part where a program clears bits and an erase costs a
//! block. `docs/platforms/riscv-virt.md` has the numbers.
//! * **Linux** (a 6.12 `riscv64` Image behind OpenSBI) enters, parses the tree
//! — `Hardware name: rsemu riscv-virt (DT)` — sets up memory, the RISC-V
//! INTC, the SBI IPI and timer extensions and a 10 MHz clocksource, and
//! prints its whole early log. It then live-locks in the timer path, and the
//! reason is known and is not on this side of the boundary: the hart's `time`
//! CSR is a field nothing advances, so `rdtime` reads zero and every deadline
//! the kernel computes is already in the past. [`clint`] documents the gap
//! and supplies the half of the fix that belongs to a CLINT.
//!
//! # Provenance
//!
//! Written from the RISC-V Privileged Architecture (CC-BY-4.0), the RISC-V PLIC
//! specification, the ACLINT specification, the National Semiconductor PC16550D
//! data sheet, the OASIS VIRTIO 1.2 specification and the Devicetree
//! Specification. Each module names the sections it used. No emulator source of
//! any licence was consulted, and in particular no virtio *driver* — which
//! `ROADMAP.md` §1 calls out as the most common way the rule gets broken.
// The board-level tests need a hart and the machine layer to run on, so they
// come with `machine-riscv-virt` rather than with the devices alone.
pub use BootRom;
pub use Clint;
pub use Loader;
pub use Plic;
pub use Syscon;
pub use Uart16550;
/// Add every board class to a registry.
///
/// # Errors
///
/// [`Error::Config`](crate::core::Error::Config) if a name is already claimed.
/// Bind every board class into the machine graph.
///
/// # Errors
///
/// As [`register`].
/// Every board class's validator schema.