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,
);
}
}