tartan_arch/
aarch64.rs

1//! Architecture-specific primitives for 64-bit Arm.
2
3use core::arch::asm;
4
5pub mod float;
6pub mod interrupt;
7
8
9#[macro_export]
10#[doc(hidden)]
11macro_rules! system_register_access {
12    [$struct:ident, $register:literal $(, $setter_type:tt)?] => {
13        impl $struct {
14            /// Retrieve the current value of this register
15            pub fn get() -> Self {
16                let mut value = Self(0);
17                unsafe {
18                    core::arch::asm!(
19                        concat!("mrs {}, ", $register),
20                        out(reg) value.0,
21                    );
22                }
23                value
24            }
25
26            system_register_access!(@setter $register $($setter_type)?);
27        }
28    };
29
30    [@setter $register:literal readonly] => { };
31
32    [@setter $register:literal] => {
33        /// Update the register to the given value.
34        pub fn set(value: Self) {
35            unsafe {
36                core::arch::asm!(
37                    concat!("msr ", $register, ", {}"),
38                    in(reg) value.0,
39                );
40            }
41        }
42    };
43
44    [@setter $register:literal unsafe] => {
45        /// Update the register to the given value.
46        ///
47        /// # Safety
48        /// Altering certain system flags can have dramatic effects on the execution
49        /// of this and other programs, including memory safety.
50        pub unsafe fn set(value: Self) {
51            core::arch::asm!(
52                concat!("msr ", $register, ", {}"),
53                in(reg) value.0,
54            );
55        }
56    };
57}
58
59
60/// `EL`: Defines the privilege level of executing code. Higher values have more
61/// privileges.
62#[repr(u8)]
63pub enum ExceptionLevel {
64    /// `EL0`, used for unprivileged user code.
65    Zero,
66    /// `EL1`, used for OS kernel code.
67    One,
68    /// `EL2`, used for hypervisors in virtualized systems.
69    Two,
70    /// `EL3`, used for the secure manager.
71    Three,
72}
73
74impl ExceptionLevel {
75    /// Get the current exception level. Only accessible from EL1 or higher.
76    pub fn get() -> Self {
77        let mut raw_value: u64;
78        unsafe {
79            asm!(
80                "mrs {}, CurrentEL",
81                out(reg) raw_value,
82            );
83        }
84        Self::from((raw_value >> 2) as u8)
85    }
86}
87
88impl From<u8> for ExceptionLevel {
89    fn from(value: u8) -> Self {
90        // SAFETY: When truncating to two bits, the defined enum values are exhaustive
91        unsafe { core::mem::transmute(value & 0b11) }
92    }
93}
94
95
96/// Controls which stack pointer register is used when executing code at EL1 or higher.
97pub enum StackPointerSelect {
98    /// `T` (thread) mode: use the stack pointer register from EL0.
99    Level0,
100    /// `H` (handler) mode: use the stack pointer register for the current exception
101    /// level.
102    CurrentLevel,
103}
104
105impl StackPointerSelect {
106    /// Get the value of the flag for the current exception level. Only accessible from
107    /// EL1 or higher.
108    pub fn get() -> Self {
109        let mut raw_value: u64;
110        unsafe {
111            asm!(
112                "mrs {}, SPSel",
113                out(reg) raw_value,
114            );
115        }
116        if raw_value & 1 == 0 {
117            Self::Level0
118        } else {
119            Self::CurrentLevel
120        }
121    }
122
123    /// Update the value of the flag for the current exception level.
124    ///
125    /// # Safety
126    /// This can alter the current stack pointer, which can have dramatic effects on the
127    /// execution of the current function.
128    #[inline(always)]
129    pub unsafe fn set(value: Self) {
130        asm!(
131            "msr SPSel, {}",
132            in(reg) value as u64,
133        );
134    }
135}