aarch64_rt/entry.rs
1// Copyright 2025 The aarch64-rt Authors.
2// This project is dual-licensed under Apache 2.0 and MIT terms.
3// See LICENSE-APACHE and LICENSE-MIT for details.
4
5//! Entrypoint code
6
7use core::{arch::naked_asm, mem::offset_of};
8
9use crate::StartCoreStack;
10
11/// This is a generic entry point for an image. It carries out the operations required to prepare the
12/// loaded image to be run. Specifically, it zeroes the bss section using registers x25 and above,
13/// prepares the stack, enables floating point, and sets up the exception vector. It preserves x0-x3
14/// for the Rust entry point, as these may contain boot parameters.
15///
16/// # Safety
17///
18/// This function is marked unsafe because it should never be called by anyone. The linker is
19/// responsible for setting it as the entry function.
20#[unsafe(naked)]
21#[unsafe(link_section = ".init.entry")]
22#[unsafe(export_name = "entry")]
23unsafe extern "C" fn entry() -> ! {
24 naked_asm!(
25 ".macro adr_l, reg:req, sym:req",
26 r"adrp \reg, \sym",
27 r"add \reg, \reg, :lo12:\sym",
28 ".endm",
29 "bl enable_mmu",
30 // Disable trapping floating point access in EL1.
31 "mrs x30, cpacr_el1",
32 "orr x30, x30, #(0x3 << 20)",
33 "msr cpacr_el1, x30",
34 "isb",
35 // Zero out the bss section.
36 "adr_l x29, bss_begin",
37 "adr_l x30, bss_end",
38 "0:",
39 "cmp x29, x30",
40 "b.hs 1f",
41 "stp xzr, xzr, [x29], #16",
42 "b 0b",
43 "1:",
44 // Prepare the stack.
45 "adr_l x30, boot_stack_end",
46 "mov sp, x30",
47 // Call into Rust code.
48 "b {rust_entry}",
49 rust_entry = sym crate::rust_entry,
50 )
51}
52
53/// An assembly entry point for secondary cores.
54///
55/// It will enable the MMU, disable trapping of floating point instructions, initialise the
56/// stack pointer to `stack_end` and then jump to the trampoline function pointer at the bottom
57/// of the stack with the closure pointer second on the stack as a parameter.
58///
59/// # Safety
60///
61/// This requires that an initial stack pointer value be passed in `x0`, and the stack must contain
62/// the address of a Rust entry point to jump to and a parameter value to pass to it.
63#[unsafe(naked)]
64pub unsafe extern "C" fn secondary_entry(stack_end: *mut u64) -> ! {
65 naked_asm!(
66 "bl enable_mmu",
67 // Disable trapping floating point access in EL1.
68 "mrs x30, cpacr_el1",
69 "orr x30, x30, #(0x3 << 20)",
70 "msr cpacr_el1, x30",
71 "isb",
72 // Set the stack pointer which was passed.
73 "mov sp, x0",
74 // Load the closure address into x19 and the trampoline address into x20.
75 // This is loaded from StartCoreStack.
76 "ldr x19, [sp, #{entry_ptr_offset}]",
77 "ldr x20, [sp, #{trampoline_ptr_offset}]",
78 // Set the exception vector.
79 "bl {set_exception_vector}",
80 // Pass the entry point (closure) address to the trampoline function.
81 "mov x0, x19",
82 // Call into Rust trampoline.
83 "br x20",
84 entry_ptr_offset = const offset_of!(StartCoreStack<()>, entry_ptr) as isize
85 - size_of::<StartCoreStack<()>>() as isize,
86 trampoline_ptr_offset = const offset_of!(StartCoreStack<()>, trampoline_ptr) as isize
87 - size_of::<StartCoreStack<()>>() as isize,
88 set_exception_vector = sym crate::set_exception_vector,
89 )
90}
91
92/// An assembly entry point for warm boot (e.g. resume from suspend).
93///
94/// It will enable the MMU, disable trapping of floating point instructions, set up the exception
95/// vector, set the stack pointer to `context.stack_ptr`, and then jump to
96/// `context.entry(context.arg)`.
97///
98/// The function expects to be passed a pointer to a `SuspendContext` instance that will be valid
99/// after resuming from suspend. It should therefore be a static, allocated on the heap, or on the
100/// stack of the resuming core, to avoid being deallocated before resuming.
101///
102/// This is a low-level function that should be used as the entry point when manually calling the
103/// `CPU_SUSPEND` PSCI call. It deliberately doesn't store any data itself so that the caller has
104/// maximum flexibility over things such as where the `SuspendContext` is stored. If you need to
105/// restore any state (such as the registers), or you want to emulate returning from a function
106/// after suspending the core, you need to implement this functionality yourself in the `entry`
107/// function of the `SuspendContext`.
108///
109/// # Safety
110///
111/// The caller must ensure that the `SuspendContext` instance passed to the function will be valid
112/// and safe to read when the core resumes, at least until the first call to any Rust function. The
113/// best way to do this is to put it on the stack of the core which is resuming, as the stack won't
114/// otherwise be used until after the `SuspendContext` has been read.
115///
116/// `context.stack_ptr` must be a valid stack pointer to use for the resuming core. Depending on how
117/// you want to handle resuming this could either be the bottom of the stack (if you want to treat
118/// resuming like `CPU_ON`) or the top (if `context.entry` will restore register state and return
119/// from the point where the suspend happened).
120#[unsafe(naked)]
121pub unsafe extern "C" fn warm_boot_entry(context: *const SuspendContext) -> ! {
122 naked_asm!(
123 "bl enable_mmu",
124 // Disable trapping floating point access in EL1.
125 "mrs x30, cpacr_el1",
126 "orr x30, x30, #(0x3 << 20)",
127 "msr cpacr_el1, x30",
128 "isb",
129 // Load stack pointer, entry point and arg from SuspendContext. This may be on the stack,
130 // so needs to happen before we set the stack pointer and call functions which may use the
131 // stack.
132 "ldr x19, [x0, #{stack_ptr_offset}]",
133 "ldr x20, [x0, #{entry_offset}]",
134 "ldr x21, [x0, #{arg_offset}]",
135 // Set the stack pointer which was passed.
136 "mov sp, x19",
137 // Set the exception vector. This may use the stack and caller-saved registers.
138 "bl {set_exception_vector}",
139 // Jump to entry point (x20) with arg (x0).
140 "mov x0, x21",
141 "br x20",
142 set_exception_vector = sym crate::set_exception_vector,
143 stack_ptr_offset = const offset_of!(SuspendContext, stack_ptr),
144 entry_offset = const offset_of!(SuspendContext, entry),
145 arg_offset = const offset_of!(SuspendContext, arg),
146 )
147}
148
149/// Data used by [`warm_boot_entry`] to restore the CPU state after resuming.
150#[derive(Debug, Copy, Clone)]
151#[repr(C)]
152pub struct SuspendContext {
153 /// Value to which to set the stack pointer before calling `entry`.
154 pub stack_ptr: *mut u64,
155 /// Entry point to call after resuming.
156 pub entry: extern "C" fn(u64) -> !,
157 /// Parameter to pass to `entry`.
158 pub arg: u64,
159}