1#![no_std]
8#![deny(clippy::undocumented_unsafe_blocks)]
9#![deny(unsafe_op_in_unsafe_fn)]
10
11#[cfg(any(
12 all(feature = "el1", feature = "el2"),
13 all(feature = "el1", feature = "el3"),
14 all(feature = "el2", feature = "el3"),
15))]
16compile_error!("Only one `el` feature may be enabled at once.");
17
18mod entry;
19#[cfg(feature = "exceptions")]
20mod exceptions;
21#[cfg(feature = "initial-pagetable")]
22mod pagetable;
23
24#[cfg(feature = "initial-pagetable")]
25#[doc(hidden)]
26pub mod __private {
27 pub use crate::pagetable::{__enable_mmu_el1, __enable_mmu_el2, __enable_mmu_el3};
28}
29
30#[cfg(any(feature = "exceptions", feature = "psci"))]
31use core::arch::asm;
32#[cfg(not(feature = "initial-pagetable"))]
33use core::arch::naked_asm;
34use core::mem::ManuallyDrop;
35pub use entry::secondary_entry;
36#[cfg(feature = "exceptions")]
37pub use exceptions::{ExceptionHandlers, RegisterState, RegisterStateRef};
38#[cfg(all(feature = "initial-pagetable", feature = "el1"))]
39pub use pagetable::DEFAULT_TCR_EL1 as DEFAULT_TCR;
40#[cfg(all(feature = "initial-pagetable", feature = "el2"))]
41pub use pagetable::DEFAULT_TCR_EL2 as DEFAULT_TCR;
42#[cfg(all(feature = "initial-pagetable", feature = "el3"))]
43pub use pagetable::DEFAULT_TCR_EL3 as DEFAULT_TCR;
44#[cfg(feature = "initial-pagetable")]
45pub use pagetable::{
46 DEFAULT_MAIR, DEFAULT_SCTLR, DEFAULT_TCR_EL1, DEFAULT_TCR_EL2, DEFAULT_TCR_EL3,
47 InitialPagetable,
48};
49
50#[cfg(not(feature = "initial-pagetable"))]
57#[unsafe(naked)]
58#[unsafe(link_section = ".init")]
59#[unsafe(export_name = "enable_mmu")]
60pub unsafe extern "C" fn enable_mmu() {
61 naked_asm!("ret")
62}
63
64#[cfg(feature = "initial-pagetable")]
65unsafe extern "C" {
66 pub unsafe fn enable_mmu();
76}
77
78pub extern "C" fn set_exception_vector() {
83 #[cfg(all(feature = "el1", feature = "exceptions"))]
85 unsafe {
86 asm!(
87 "adr x9, vector_table_el1",
88 "msr vbar_el1, x9",
89 options(nomem, nostack),
90 out("x9") _,
91 );
92 }
93 #[cfg(all(feature = "el2", feature = "exceptions"))]
95 unsafe {
96 asm!(
97 "adr x9, vector_table_el2",
98 "msr vbar_el2, x9",
99 options(nomem, nostack),
100 out("x9") _,
101 );
102 }
103 #[cfg(all(feature = "el3", feature = "exceptions"))]
105 unsafe {
106 asm!(
107 "adr x9, vector_table_el3",
108 "msr vbar_el3, x9",
109 options(nomem, nostack),
110 out("x9") _,
111 );
112 }
113 #[cfg(all(
114 feature = "exceptions",
115 not(any(feature = "el1", feature = "el2", feature = "el3"))
116 ))]
117 {
118 let current_el: u64;
119 unsafe {
121 asm!(
122 "mrs {current_el}, CurrentEL",
123 options(nomem, nostack, preserves_flags),
124 current_el = out(reg) current_el,
125 );
126 }
127 match (current_el >> 2) & 0b11 {
128 1 => unsafe {
130 asm!(
131 "adr x9, vector_table_el1",
132 "msr vbar_el1, x9",
133 options(nomem, nostack, preserves_flags),
134 out("x9") _,
135 );
136 },
137 2 => unsafe {
139 asm!(
140 "adr x9, vector_table_el2",
141 "msr vbar_el2, x9",
142 options(nomem, nostack, preserves_flags),
143 out("x9") _,
144 );
145 },
146 3 => unsafe {
148 asm!(
149 "adr x9, vector_table_el3",
150 "msr vbar_el3, x9",
151 options(nomem, nostack, preserves_flags),
152 out("x9") _,
153 );
154 },
155 _ => {
156 panic!("Unexpected EL");
157 }
158 }
159 }
160}
161
162extern "C" fn rust_entry(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> ! {
163 set_exception_vector();
164 __main(arg0, arg1, arg2, arg3)
165}
166
167unsafe extern "Rust" {
168 safe fn __main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> !;
170}
171
172#[macro_export]
189macro_rules! entry {
190 ($name:path) => {
191 entry!($name, 40);
192 };
193 ($name:path, $boot_stack_pages:expr) => {
194 #[unsafe(export_name = "boot_stack")]
195 #[unsafe(link_section = ".stack.boot_stack")]
196 static mut __BOOT_STACK: $crate::Stack<$boot_stack_pages> = $crate::Stack::new();
197
198 #[unsafe(export_name = "__main")]
200 fn __main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> ! {
201 $name(arg0, arg1, arg2, arg3)
203 }
204 };
205}
206
207#[repr(C, align(4096))]
211pub struct Stack<const NUM_PAGES: usize>([StackPage; NUM_PAGES]);
212
213impl<const NUM_PAGES: usize> Stack<NUM_PAGES> {
214 pub const fn new() -> Self {
216 Self([const { StackPage::new() }; NUM_PAGES])
217 }
218}
219
220impl<const NUM_PAGES: usize> Default for Stack<NUM_PAGES> {
221 fn default() -> Self {
222 Self::new()
223 }
224}
225
226#[repr(C, align(4096))]
227struct StackPage([u8; 4096]);
228
229impl StackPage {
230 const fn new() -> Self {
231 Self([0; 4096])
232 }
233}
234
235#[repr(C)]
236pub(crate) struct StartCoreStack<F> {
237 entry_ptr: *mut ManuallyDrop<F>,
238 trampoline_ptr: unsafe extern "C" fn(&mut ManuallyDrop<F>) -> !,
239}
240
241#[cfg(feature = "psci")]
242pub unsafe fn start_core<C: smccc::Call, F: FnOnce() + Send + 'static, const N: usize>(
262 mpidr: u64,
263 stack: *mut Stack<N>,
264 rust_entry: F,
265) -> Result<(), smccc::psci::Error> {
266 const {
267 assert!(
268 size_of::<StartCoreStack<F>>()
269 + 2 * size_of::<F>()
270 + 2 * align_of::<F>()
271 + 1024 <= size_of::<Stack<N>>(),
273 "the `rust_entry` closure is too big to fit in the core stack"
274 );
275 }
276
277 let rust_entry = ManuallyDrop::new(rust_entry);
278
279 let stack_start = stack.cast::<u8>();
280 let align_offfset = stack_start.align_offset(align_of::<F>());
281 let entry_ptr = stack_start
282 .wrapping_add(align_offfset)
283 .cast::<ManuallyDrop<F>>();
284
285 assert!(stack.is_aligned());
286 let stack_end = stack.wrapping_add(1);
288 let params = stack_end.cast::<StartCoreStack<F>>().wrapping_sub(1);
289
290 unsafe {
293 entry_ptr.write(rust_entry);
294 *params = StartCoreStack {
295 entry_ptr,
296 trampoline_ptr: trampoline::<F>,
297 };
298 };
299
300 dsb_st();
302
303 smccc::psci::cpu_on::<C>(
304 mpidr,
305 secondary_entry as usize as _,
306 stack_end as usize as _,
307 )
308}
309
310#[cfg(feature = "psci")]
311unsafe extern "C" fn trampoline<F: FnOnce() + Send + 'static>(entry: &mut ManuallyDrop<F>) -> ! {
320 let entry = unsafe { ManuallyDrop::take(entry) };
323 entry();
324
325 panic!("rust_entry function passed to start_core should never return");
326}
327
328#[cfg(feature = "psci")]
330fn dsb_st() {
331 unsafe {
333 asm!("dsb st", options(nostack));
334 }
335}