1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Architecture-specific primitives for 64-bit Arm.

use core::arch::asm;

pub mod float;
pub mod interrupt;


#[macro_export]
#[doc(hidden)]
macro_rules! system_register_access {
    [$struct:ident, $register:literal $(, $setter_type:tt)?] => {
        impl $struct {
            /// Retrieve the current value of this register
            pub fn get() -> Self {
                let mut value = Self(0);
                unsafe {
                    core::arch::asm!(
                        concat!("mrs {}, ", $register),
                        out(reg) value.0,
                    );
                }
                value
            }

            system_register_access!(@setter $register $($setter_type)?);
        }
    };

    [@setter $register:literal readonly] => { };

    [@setter $register:literal] => {
        /// Update the register to the given value.
        pub fn set(value: Self) {
            unsafe {
                core::arch::asm!(
                    concat!("msr ", $register, ", {}"),
                    in(reg) value.0,
                );
            }
        }
    };

    [@setter $register:literal unsafe] => {
        /// Update the register to the given value.
        ///
        /// # Safety
        /// Altering certain system flags can have dramatic effects on the execution
        /// of this and other programs, including memory safety.
        pub unsafe fn set(value: Self) {
            core::arch::asm!(
                concat!("msr ", $register, ", {}"),
                in(reg) value.0,
            );
        }
    };
}


/// `EL`: Defines the privilege level of executing code. Higher values have more
/// privileges.
#[repr(u8)]
pub enum ExceptionLevel {
    /// `EL0`, used for unprivileged user code.
    Zero,
    /// `EL1`, used for OS kernel code.
    One,
    /// `EL2`, used for hypervisors in virtualized systems.
    Two,
    /// `EL3`, used for the secure manager.
    Three,
}

impl ExceptionLevel {
    /// Get the current exception level. Only accessible from EL1 or higher.
    pub fn get() -> Self {
        let mut raw_value: u64;
        unsafe {
            asm!(
                "mrs {}, CurrentEL",
                out(reg) raw_value,
            );
        }
        Self::from((raw_value >> 2) as u8)
    }
}

impl From<u8> for ExceptionLevel {
    fn from(value: u8) -> Self {
        // SAFETY: When truncating to two bits, the defined enum values are exhaustive
        unsafe { core::mem::transmute(value & 0b11) }
    }
}


/// Controls which stack pointer register is used when executing code at EL1 or higher.
pub enum StackPointerSelect {
    /// `T` (thread) mode: use the stack pointer register from EL0.
    Level0,
    /// `H` (handler) mode: use the stack pointer register for the current exception
    /// level.
    CurrentLevel,
}

impl StackPointerSelect {
    /// Get the value of the flag for the current exception level. Only accessible from
    /// EL1 or higher.
    pub fn get() -> Self {
        let mut raw_value: u64;
        unsafe {
            asm!(
                "mrs {}, SPSel",
                out(reg) raw_value,
            );
        }
        if raw_value & 1 == 0 {
            Self::Level0
        } else {
            Self::CurrentLevel
        }
    }

    /// Update the value of the flag for the current exception level.
    ///
    /// # Safety
    /// This can alter the current stack pointer, which can have dramatic effects on the
    /// execution of the current function.
    #[inline(always)]
    pub unsafe fn set(value: Self) {
        asm!(
            "msr SPSel, {}",
            in(reg) value as u64,
        );
    }
}