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"))]
51#[unsafe(naked)]
52#[unsafe(link_section = ".init")]
53#[unsafe(export_name = "enable_mmu")]
54extern "C" fn enable_mmu() {
55 naked_asm!("ret")
56}
57
58extern "C" fn set_exception_vector() {
61 #[cfg(all(feature = "el1", feature = "exceptions"))]
63 unsafe {
64 asm!(
65 "adr x9, vector_table_el1",
66 "msr vbar_el1, x9",
67 options(nomem, nostack),
68 out("x9") _,
69 );
70 }
71 #[cfg(all(feature = "el2", feature = "exceptions"))]
73 unsafe {
74 asm!(
75 "adr x9, vector_table_el2",
76 "msr vbar_el2, x9",
77 options(nomem, nostack),
78 out("x9") _,
79 );
80 }
81 #[cfg(all(feature = "el3", feature = "exceptions"))]
83 unsafe {
84 asm!(
85 "adr x9, vector_table_el3",
86 "msr vbar_el3, x9",
87 options(nomem, nostack),
88 out("x9") _,
89 );
90 }
91 #[cfg(all(
92 feature = "exceptions",
93 not(any(feature = "el1", feature = "el2", feature = "el3"))
94 ))]
95 {
96 let current_el: u64;
97 unsafe {
99 asm!(
100 "mrs {current_el}, CurrentEL",
101 options(nomem, nostack, preserves_flags),
102 current_el = out(reg) current_el,
103 );
104 }
105 match (current_el >> 2) & 0b11 {
106 1 => unsafe {
108 asm!(
109 "adr x9, vector_table_el1",
110 "msr vbar_el1, x9",
111 options(nomem, nostack, preserves_flags),
112 out("x9") _,
113 );
114 },
115 2 => unsafe {
117 asm!(
118 "adr x9, vector_table_el2",
119 "msr vbar_el2, x9",
120 options(nomem, nostack, preserves_flags),
121 out("x9") _,
122 );
123 },
124 3 => unsafe {
126 asm!(
127 "adr x9, vector_table_el3",
128 "msr vbar_el3, x9",
129 options(nomem, nostack, preserves_flags),
130 out("x9") _,
131 );
132 },
133 _ => {
134 panic!("Unexpected EL");
135 }
136 }
137 }
138}
139
140extern "C" fn rust_entry(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> ! {
141 set_exception_vector();
142 __main(arg0, arg1, arg2, arg3)
143}
144
145unsafe extern "Rust" {
146 safe fn __main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> !;
148}
149
150#[macro_export]
167macro_rules! entry {
168 ($name:path) => {
169 entry!($name, 40);
170 };
171 ($name:path, $boot_stack_pages:expr) => {
172 #[unsafe(export_name = "boot_stack")]
173 #[unsafe(link_section = ".stack.boot_stack")]
174 static mut __BOOT_STACK: $crate::Stack<$boot_stack_pages> = $crate::Stack::new();
175
176 #[unsafe(export_name = "__main")]
178 fn __main(arg0: u64, arg1: u64, arg2: u64, arg3: u64) -> ! {
179 $name(arg0, arg1, arg2, arg3)
181 }
182 };
183}
184
185#[repr(C, align(4096))]
189pub struct Stack<const NUM_PAGES: usize>([StackPage; NUM_PAGES]);
190
191impl<const NUM_PAGES: usize> Stack<NUM_PAGES> {
192 pub const fn new() -> Self {
194 Self([const { StackPage::new() }; NUM_PAGES])
195 }
196}
197
198impl<const NUM_PAGES: usize> Default for Stack<NUM_PAGES> {
199 fn default() -> Self {
200 Self::new()
201 }
202}
203
204#[repr(C, align(4096))]
205struct StackPage([u8; 4096]);
206
207impl StackPage {
208 const fn new() -> Self {
209 Self([0; 4096])
210 }
211}
212
213#[repr(C)]
214pub(crate) struct StartCoreStack<F> {
215 entry_ptr: *mut ManuallyDrop<F>,
216 trampoline_ptr: unsafe extern "C" fn(&mut ManuallyDrop<F>) -> !,
217}
218
219#[cfg(feature = "psci")]
220pub unsafe fn start_core<C: smccc::Call, F: FnOnce() + Send + 'static, const N: usize>(
240 mpidr: u64,
241 stack: *mut Stack<N>,
242 rust_entry: F,
243) -> Result<(), smccc::psci::Error> {
244 const {
245 assert!(
246 size_of::<StartCoreStack<F>>()
247 + 2 * size_of::<F>()
248 + 2 * align_of::<F>()
249 + 1024 <= size_of::<Stack<N>>(),
251 "the `rust_entry` closure is too big to fit in the core stack"
252 );
253 }
254
255 let rust_entry = ManuallyDrop::new(rust_entry);
256
257 let stack_start = stack.cast::<u8>();
258 let align_offfset = stack_start.align_offset(align_of::<F>());
259 let entry_ptr = stack_start
260 .wrapping_add(align_offfset)
261 .cast::<ManuallyDrop<F>>();
262
263 assert!(stack.is_aligned());
264 let stack_end = stack.wrapping_add(1);
266 let params = stack_end.cast::<StartCoreStack<F>>().wrapping_sub(1);
267
268 unsafe {
271 entry_ptr.write(rust_entry);
272 *params = StartCoreStack {
273 entry_ptr,
274 trampoline_ptr: trampoline::<F>,
275 };
276 };
277
278 dsb_st();
280
281 smccc::psci::cpu_on::<C>(
282 mpidr,
283 secondary_entry as usize as _,
284 stack_end as usize as _,
285 )
286}
287
288#[cfg(feature = "psci")]
289unsafe extern "C" fn trampoline<F: FnOnce() + Send + 'static>(entry: &mut ManuallyDrop<F>) -> ! {
298 let entry = unsafe { ManuallyDrop::take(entry) };
301 entry();
302
303 panic!("rust_entry function passed to start_core should never return");
304}
305
306#[cfg(feature = "psci")]
308fn dsb_st() {
309 unsafe {
311 asm!("dsb st", options(nostack));
312 }
313}