Skip to main content

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}