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}