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
93impl<Spec, Layout> Default for SysReg<Spec, Layout>
94where
95    Spec: SystemRegisterSpec,
96    Layout: LayoutOver<u64>,
97{
98    fn default() -> Self {
99        Self::new()
100    }
101}
102
103/// An I/O backend for arm64 system registers.
104pub struct SysRegIo<Spec: SystemRegisterSpec>(PhantomData<Spec>);
105
106impl<Spec: SystemRegisterSpec> SysRegIo<Spec> {
107    pub const fn new() -> Self {
108        // Associated constants are evaluated lazily, so force an evaluation
109        // now.
110        const { Spec::VALID };
111        Self(PhantomData)
112    }
113}
114
115impl<Spec: SystemRegisterSpec> Default for SysRegIo<Spec> {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121impl<Spec: SystemRegisterSpec> IoHandle for SysRegIo<Spec> {
122    type Base = u64;
123}
124
125#[cfg(target_arch = "aarch64")]
126mod arm64_only {
127    use core::arch::asm;
128
129    use super::*;
130    use crate::{ReadHandle, Readable, Writable, WriteHandle};
131
132    impl<Spec: SystemRegisterSpec> ReadHandle for SysRegIo<Spec>
133    where
134        Spec::Access: Readable,
135    {
136        #[inline]
137        unsafe fn read_raw(&self) -> u64 {
138            let value: u64;
139            unsafe {
140                asm!(
141                    "mrs {value}, S{op0}_{op1}_C{crn}_C{crm}_{op2}",
142                    value = out(reg) value,
143                    op0 = const Spec::OP0,
144                    op1 = const Spec::OP1,
145                    crn = const Spec::CRN,
146                    crm = const Spec::CRM,
147                    op2 = const Spec::OP2,
148                    options(nomem, nostack, preserves_flags),
149                )
150            }
151            value
152        }
153    }
154
155    impl<Spec: SystemRegisterSpec> WriteHandle for SysRegIo<Spec>
156    where
157        Spec::Access: Writable,
158    {
159        #[inline]
160        unsafe fn write_raw(&self, value: u64) {
161            unsafe {
162                asm!(
163                    "msr S{op0}_{op1}_C{crn}_C{crm}_{op2}, {value}",
164                    value = in(reg) value,
165                    op0 = const Spec::OP0,
166                    op1 = const Spec::OP1,
167                    crn = const Spec::CRN,
168                    crm = const Spec::CRM,
169                    op2 = const Spec::OP2,
170                    // TODO(https://fxbug.dev/525077555): Revisit using nomem here.
171                    options(nostack, preserves_flags),
172                )
173            }
174        }
175    }
176}
177
178#[cfg(all(test, target_arch = "aarch64"))]
179mod tests {
180    use super::*;
181
182    #[test]
183    fn sysregs() {
184        const TPIDR_EL0: SysReg<spec::TPIDR_EL0, u64> = SysReg::new();
185
186        println!("TPIDR_EL0: {:#x}", TPIDR_EL0.read());
187    }
188}