squib_arch/regs.rs
1//! aarch64 register set + initial-register helper.
2//!
3//! `set_boot_regs` writes the four registers that bring vCPU 0 to the kernel entry point.
4//! Per [13-arch-and-boot.md § 8](../../../specs/13-arch-and-boot.md#8-vcpu-initial-registers):
5//!
6//! ```text
7//! PC = kernel_load_addr
8//! X0 = fdt_addr
9//! X1..X3 = 0
10//! PSTATE = 0x3C5 // EL1h, DAIF masked, M[3:0] = EL1h, F=I=A=D=1
11//! ```
12//!
13//! Secondary vCPUs come up via PSCI `CPU_ON` with the same triple; the dispatcher in
14//! `psci` produces the inputs and the host backend (`squib-hv`) calls `set_boot_regs` on
15//! the target vCPU.
16
17/// PSTATE value the boot registers initialise — EL1h with DAIF masked.
18///
19/// Bit layout (Arm ARM § C5.2.18):
20/// - `M[3:0] = 0b0101` (EL1h)
21/// - `F = 1, I = 1, A = 1, D = 1` (all interrupts/aborts masked initially)
22pub const BOOT_PSTATE: u64 = 0x3C5;
23
24/// Reg enum — the registers the trait surface exposes get/set on.
25///
26/// Mirrors the spec in [11-runtime-core.md § 2](../../../specs/11-runtime-core.md#2-interface):
27/// X0..X30, SP_EL1, PC, PSTATE.
28#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
29#[non_exhaustive]
30pub enum Reg {
31 /// General-purpose register `Xn` where `n ∈ 0..=30`.
32 X(u8),
33 /// Stack pointer at EL1.
34 SpEl1,
35 /// Program counter.
36 Pc,
37 /// Processor state register.
38 PState,
39}
40
41impl Reg {
42 /// Build [`Reg::X`] with index validation.
43 ///
44 /// # Errors
45 /// Returns `None` for indices ≥ 31.
46 #[must_use]
47 pub const fn x(index: u8) -> Option<Self> {
48 if index <= 30 {
49 Some(Self::X(index))
50 } else {
51 None
52 }
53 }
54}
55
56/// The four register values written at boot for vCPU 0 (and via PSCI `CPU_ON` for
57/// secondaries).
58#[derive(Debug, Clone, Copy, Eq, PartialEq)]
59pub struct BootRegs {
60 /// Kernel entry point (PC).
61 pub kernel_load_addr: u64,
62 /// FDT base address (placed in X0 per `arm64/booting.rst`).
63 pub fdt_addr: u64,
64 /// PSTATE — defaults to [`BOOT_PSTATE`].
65 pub pstate: u64,
66}
67
68impl BootRegs {
69 /// Build a [`BootRegs`] for the boot vCPU.
70 #[must_use]
71 pub const fn new(kernel_load_addr: u64, fdt_addr: u64) -> Self {
72 Self {
73 kernel_load_addr,
74 fdt_addr,
75 pstate: BOOT_PSTATE,
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn boot_pstate_matches_spec() {
86 assert_eq!(BOOT_PSTATE, 0x3C5);
87 }
88
89 #[test]
90 fn x_constructor_accepts_valid_indices() {
91 for i in 0..=30u8 {
92 assert!(matches!(Reg::x(i), Some(Reg::X(_))));
93 }
94 }
95
96 #[test]
97 fn x_constructor_rejects_out_of_range() {
98 assert!(Reg::x(31).is_none());
99 assert!(Reg::x(255).is_none());
100 }
101
102 #[test]
103 fn boot_regs_default_pstate() {
104 let regs = BootRegs::new(0x8020_0000, 0xBFE0_0000);
105 assert_eq!(regs.pstate, BOOT_PSTATE);
106 assert_eq!(regs.kernel_load_addr, 0x8020_0000);
107 assert_eq!(regs.fdt_addr, 0xBFE0_0000);
108 }
109}