Skip to main content

regio/arm64/
mod.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5pub mod spec;
6
7use crate::{Accessible, IoHandle, LayoutOver, Register};
8
9use core::marker::PhantomData;
10
11/// Marker for an arm64 system register, expressing its encoding and access
12/// permissons.
13pub trait SystemRegisterSpec {
14    /// The register encoding's op0 parameter.
15    const OP0: u8;
16
17    /// The register encoding's op1 parameter.
18    const OP1: u8;
19
20    /// The register encoding's CRn parameter.
21    const CRN: u8;
22
23    /// The register encoding's CRm parameter.
24    const CRM: u8;
25
26    /// The register encoding's op2 parameter.
27    const OP2: u8;
28
29    /// The register's access permissions.
30    type Access: Accessible;
31
32    /// Whether the encoding is valid.
33    const VALID: () = {
34        assert!(Self::OP0 == 2 || Self::OP0 == 3, "op0 must be 2 or 3");
35        assert!(Self::OP1 <= 7, "op1 must be in between 0 and 7");
36        assert!(Self::CRN <= 15, "CRn must be in between 0 and 15");
37        assert!(Self::CRM <= 15, "CRm must be in between 0 and 15");
38        assert!(Self::OP2 <= 7, "op2 must be in between 0 and 7");
39    };
40}
41
42/// Tag for an arm64 system register, expressing its encoding and access
43/// permissons.
44pub struct SysRegSpec<
45    const OP0: u8,
46    const OP1: u8,
47    const CRN: u8,
48    const CRM: u8,
49    const OP2: u8,
50    Access: Accessible,
51>(PhantomData<Access>);
52
53impl<const OP0: u8, const OP1: u8, const CRN: u8, const CRM: u8, const OP2: u8, Access: Accessible>
54    SystemRegisterSpec for SysRegSpec<OP0, OP1, CRN, CRM, OP2, Access>
55{
56    const OP0: u8 = OP0;
57    const OP1: u8 = OP1;
58    const CRN: u8 = CRN;
59    const CRM: u8 = CRM;
60    const OP2: u8 = OP2;
61    type Access = Access;
62}
63
64/// Models an arm64 system register with a given layout.
65///
66/// It takes a system register 'spec' as a generic parameter, all of which have
67/// been stamped out in the `regio::arm64::spec` submodule with names equal to
68/// their official mnemonics.
69///
70/// Example usage:
71/// ```rust
72/// use regio::arm64::{SysReg, spec};
73///
74/// const TPIDR_EL0: SysReg<spec::TPIDR_EL0, u64> = SysReg::new();
75///
76///println!("TPIDR_EL0: {:#x}", TPIDR_EL0.read().get());
77/// ```
78pub type SysReg<Spec, Layout> =
79    Register<Layout, <Spec as SystemRegisterSpec>::Access, SysRegIo<Spec>>;
80
81impl<Spec, Layout> SysReg<Spec, Layout>
82where
83    Spec: SystemRegisterSpec,
84    Layout: LayoutOver<u64>,
85{
86    /// Constructs a new system register instance.
87    pub const fn new() -> Self {
88        // Safety: There is nothing unsafe about SysRegIo construction.
89        unsafe { Self::from_io(SysRegIo::new()) }
90    }
91}
92
93/// An I/O backend for arm64 system registers.
94pub struct SysRegIo<Spec: SystemRegisterSpec>(PhantomData<Spec>);
95
96impl<Spec: SystemRegisterSpec> SysRegIo<Spec> {
97    pub const fn new() -> Self {
98        // Associated constants are evaluated lazily, so force an evaluation
99        // now.
100        let _ = Spec::VALID;
101        Self(PhantomData)
102    }
103}
104
105impl<Spec: SystemRegisterSpec> IoHandle for SysRegIo<Spec> {
106    type Base = u64;
107}
108
109#[cfg(target_arch = "aarch64")]
110mod arm64_only {
111    use core::arch::asm;
112
113    use super::*;
114    use crate::{ReadHandle, Readable, Writable, WriteHandle};
115
116    impl<Spec: SystemRegisterSpec> ReadHandle for SysRegIo<Spec>
117    where
118        Spec::Access: Readable,
119    {
120        #[inline]
121        unsafe fn read_raw(&self) -> u64 {
122            let value: u64;
123            unsafe {
124                asm!(
125                    "mrs {value}, S{op0}_{op1}_C{crn}_C{crm}_{op2}",
126                    value = out(reg) value,
127                    op0 = const Spec::OP0,
128                    op1 = const Spec::OP1,
129                    crn = const Spec::CRN,
130                    crm = const Spec::CRM,
131                    op2 = const Spec::OP2,
132                    options(nomem, nostack, preserves_flags),
133                )
134            }
135            value
136        }
137    }
138
139    impl<Spec: SystemRegisterSpec> WriteHandle for SysRegIo<Spec>
140    where
141        Spec::Access: Writable,
142    {
143        #[inline]
144        unsafe fn write_raw(&self, value: u64) {
145            unsafe {
146                asm!(
147                    "msr S{op0}_{op1}_C{crn}_C{crm}_{op2}, {value}",
148                    value = in(reg) value,
149                    op0 = const Spec::OP0,
150                    op1 = const Spec::OP1,
151                    crn = const Spec::CRN,
152                    crm = const Spec::CRM,
153                    op2 = const Spec::OP2,
154                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
155                    options(nostack, preserves_flags),
156                )
157            }
158        }
159    }
160}
161
162#[cfg(all(test, target_arch = "aarch64"))]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn sysregs() {
168        const TPIDR_EL0: SysReg<spec::TPIDR_EL0, u64> = SysReg::new();
169
170        println!("TPIDR_EL0: {:#x}", TPIDR_EL0.read());
171    }
172}