xref: /aosp_15_r20/bootable/libbootloader/gbl/libboot/src/aarch64.rs (revision 5225e6b173e52d2efc6bcf950c27374fd72adabc)
1*5225e6b1SAndroid Build Coastguard Worker // Copyright 2023, The Android Open Source Project
2*5225e6b1SAndroid Build Coastguard Worker //
3*5225e6b1SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*5225e6b1SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*5225e6b1SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*5225e6b1SAndroid Build Coastguard Worker //
7*5225e6b1SAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*5225e6b1SAndroid Build Coastguard Worker //
9*5225e6b1SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*5225e6b1SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*5225e6b1SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*5225e6b1SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*5225e6b1SAndroid Build Coastguard Worker // limitations under the License.
14*5225e6b1SAndroid Build Coastguard Worker 
15*5225e6b1SAndroid Build Coastguard Worker //! Docs for booting on AArch64 is at:
16*5225e6b1SAndroid Build Coastguard Worker //!
17*5225e6b1SAndroid Build Coastguard Worker //!   https://www.kernel.org/doc/html/v5.11/arm64/booting.html
18*5225e6b1SAndroid Build Coastguard Worker 
19*5225e6b1SAndroid Build Coastguard Worker use core::arch::asm;
20*5225e6b1SAndroid Build Coastguard Worker use zbi::ZbiContainer;
21*5225e6b1SAndroid Build Coastguard Worker 
22*5225e6b1SAndroid Build Coastguard Worker /// ARM exception levels.
23*5225e6b1SAndroid Build Coastguard Worker #[allow(missing_docs)]
24*5225e6b1SAndroid Build Coastguard Worker #[derive(Debug, PartialEq)]
25*5225e6b1SAndroid Build Coastguard Worker pub enum ExceptionLevel {
26*5225e6b1SAndroid Build Coastguard Worker     EL0,
27*5225e6b1SAndroid Build Coastguard Worker     EL1,
28*5225e6b1SAndroid Build Coastguard Worker     EL2,
29*5225e6b1SAndroid Build Coastguard Worker     EL3,
30*5225e6b1SAndroid Build Coastguard Worker }
31*5225e6b1SAndroid Build Coastguard Worker 
32*5225e6b1SAndroid Build Coastguard Worker /// Gets the current EL;
current_el() -> ExceptionLevel33*5225e6b1SAndroid Build Coastguard Worker pub fn current_el() -> ExceptionLevel {
34*5225e6b1SAndroid Build Coastguard Worker     let mut el: u64;
35*5225e6b1SAndroid Build Coastguard Worker     // SAFETY: The assembly code only read current exception level.
36*5225e6b1SAndroid Build Coastguard Worker     unsafe {
37*5225e6b1SAndroid Build Coastguard Worker         asm!(
38*5225e6b1SAndroid Build Coastguard Worker             "mrs {el}, CurrentEL",
39*5225e6b1SAndroid Build Coastguard Worker             el = out(reg) el,
40*5225e6b1SAndroid Build Coastguard Worker         );
41*5225e6b1SAndroid Build Coastguard Worker     }
42*5225e6b1SAndroid Build Coastguard Worker     el = (el >> 2) & 3;
43*5225e6b1SAndroid Build Coastguard Worker     match el {
44*5225e6b1SAndroid Build Coastguard Worker         0 => ExceptionLevel::EL0,
45*5225e6b1SAndroid Build Coastguard Worker         1 => ExceptionLevel::EL1,
46*5225e6b1SAndroid Build Coastguard Worker         2 => ExceptionLevel::EL2,
47*5225e6b1SAndroid Build Coastguard Worker         3 => ExceptionLevel::EL3,
48*5225e6b1SAndroid Build Coastguard Worker         v => panic!("Unknown EL {v}"),
49*5225e6b1SAndroid Build Coastguard Worker     }
50*5225e6b1SAndroid Build Coastguard Worker }
51*5225e6b1SAndroid Build Coastguard Worker 
52*5225e6b1SAndroid Build Coastguard Worker extern "C" {
53*5225e6b1SAndroid Build Coastguard Worker     /// Clean and invalidate data cache by address range. The function is from ATF library.
flush_dcache_range(addr: usize, len: usize)54*5225e6b1SAndroid Build Coastguard Worker     fn flush_dcache_range(addr: usize, len: usize);
55*5225e6b1SAndroid Build Coastguard Worker }
56*5225e6b1SAndroid Build Coastguard Worker 
57*5225e6b1SAndroid Build Coastguard Worker /// Flush all data cache for the given buffer.
flush_dcache_buffer(buf: &[u8])58*5225e6b1SAndroid Build Coastguard Worker fn flush_dcache_buffer(buf: &[u8]) {
59*5225e6b1SAndroid Build Coastguard Worker     unsafe { flush_dcache_range(buf.as_ptr() as usize, buf.len()) }
60*5225e6b1SAndroid Build Coastguard Worker     // SAFETY: Assembly code for instruction synchronization.
61*5225e6b1SAndroid Build Coastguard Worker     unsafe { asm!("isb") };
62*5225e6b1SAndroid Build Coastguard Worker }
63*5225e6b1SAndroid Build Coastguard Worker 
64*5225e6b1SAndroid Build Coastguard Worker /// Disable cache, MMU and jump to the given kernel address with arguments.
65*5225e6b1SAndroid Build Coastguard Worker ///
66*5225e6b1SAndroid Build Coastguard Worker /// # Args
67*5225e6b1SAndroid Build Coastguard Worker ///
68*5225e6b1SAndroid Build Coastguard Worker /// * `addr`: Address to jump.
69*5225e6b1SAndroid Build Coastguard Worker /// * `arg[0-3]`: Arguments for the target jump address.
70*5225e6b1SAndroid Build Coastguard Worker ///
71*5225e6b1SAndroid Build Coastguard Worker /// # Safety
72*5225e6b1SAndroid Build Coastguard Worker ///
73*5225e6b1SAndroid Build Coastguard Worker /// * Caller must ensure that `addr` contains valid execution code.
74*5225e6b1SAndroid Build Coastguard Worker /// * Caller must ensure to flush any data cache for memory regions that contain data to be accessed
75*5225e6b1SAndroid Build Coastguard Worker ///   by the destination code, including the execution code itself at address `addr`
jump_kernel(addr: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize) -> !76*5225e6b1SAndroid Build Coastguard Worker unsafe fn jump_kernel(addr: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize) -> ! {
77*5225e6b1SAndroid Build Coastguard Worker     // TODO(b/334962949): Disable other stuffs such as interrupt, async abort, branch prediction etc.
78*5225e6b1SAndroid Build Coastguard Worker 
79*5225e6b1SAndroid Build Coastguard Worker     // After disabling MMU and cache, memory regions that have unflushed cache are stale and cannot
80*5225e6b1SAndroid Build Coastguard Worker     // be trusted, including stack memory. Therefore all needed data including local variables must
81*5225e6b1SAndroid Build Coastguard Worker     // be ensured to be loaded to registers first. `disable_cache_mmu_and_jump` only operates on
82*5225e6b1SAndroid Build Coastguard Worker     // registers and does not access stack or any other memory.
83*5225e6b1SAndroid Build Coastguard Worker     //
84*5225e6b1SAndroid Build Coastguard Worker     // SAFETY: By safety requirement of this function, `addr` contains valid execution code.
85*5225e6b1SAndroid Build Coastguard Worker     unsafe {
86*5225e6b1SAndroid Build Coastguard Worker         asm!(
87*5225e6b1SAndroid Build Coastguard Worker             "b disable_cache_mmu_and_jump",
88*5225e6b1SAndroid Build Coastguard Worker             in("x0") arg0,
89*5225e6b1SAndroid Build Coastguard Worker             in("x1") arg1,
90*5225e6b1SAndroid Build Coastguard Worker             in("x2") arg2,
91*5225e6b1SAndroid Build Coastguard Worker             in("x3") arg3,
92*5225e6b1SAndroid Build Coastguard Worker             in("x4") addr,
93*5225e6b1SAndroid Build Coastguard Worker         )
94*5225e6b1SAndroid Build Coastguard Worker     };
95*5225e6b1SAndroid Build Coastguard Worker     unreachable!();
96*5225e6b1SAndroid Build Coastguard Worker }
97*5225e6b1SAndroid Build Coastguard Worker 
98*5225e6b1SAndroid Build Coastguard Worker /// Boots a Linux kernel in mode EL2 or lower with the given FDT blob.
99*5225e6b1SAndroid Build Coastguard Worker ///
100*5225e6b1SAndroid Build Coastguard Worker /// # Safety
101*5225e6b1SAndroid Build Coastguard Worker ///
102*5225e6b1SAndroid Build Coastguard Worker /// Caller must ensure that `kernel` contains a valid Linux kernel.
jump_linux_el2_or_lower(kernel: &[u8], ramdisk: &[u8], fdt: &[u8]) -> !103*5225e6b1SAndroid Build Coastguard Worker pub unsafe fn jump_linux_el2_or_lower(kernel: &[u8], ramdisk: &[u8], fdt: &[u8]) -> ! {
104*5225e6b1SAndroid Build Coastguard Worker     assert_ne!(current_el(), ExceptionLevel::EL3);
105*5225e6b1SAndroid Build Coastguard Worker     // The following is sufficient to work for existing use cases such as Cuttlefish. But there are
106*5225e6b1SAndroid Build Coastguard Worker     // additional initializations listed
107*5225e6b1SAndroid Build Coastguard Worker     // https://www.kernel.org/doc/html/v5.11/arm64/booting.html that may need to be performed
108*5225e6b1SAndroid Build Coastguard Worker     // explicitly for other platforms.
109*5225e6b1SAndroid Build Coastguard Worker 
110*5225e6b1SAndroid Build Coastguard Worker     flush_dcache_buffer(kernel);
111*5225e6b1SAndroid Build Coastguard Worker     flush_dcache_buffer(ramdisk);
112*5225e6b1SAndroid Build Coastguard Worker     flush_dcache_buffer(fdt);
113*5225e6b1SAndroid Build Coastguard Worker     // SAFETY:
114*5225e6b1SAndroid Build Coastguard Worker     // * `kernel`, `ramdisk` and `fdt` have been flushed.
115*5225e6b1SAndroid Build Coastguard Worker     // * By requirement of this function, `kernel` is a valid kernel entry point.
116*5225e6b1SAndroid Build Coastguard Worker     unsafe { jump_kernel(kernel.as_ptr() as _, fdt.as_ptr() as _, 0, 0, 0) };
117*5225e6b1SAndroid Build Coastguard Worker }
118*5225e6b1SAndroid Build Coastguard Worker 
119*5225e6b1SAndroid Build Coastguard Worker /// Boots a ZBI kernel in mode EL2 or lower with the given ZBI blob.
120*5225e6b1SAndroid Build Coastguard Worker ///
121*5225e6b1SAndroid Build Coastguard Worker /// # Safety
122*5225e6b1SAndroid Build Coastguard Worker ///
123*5225e6b1SAndroid Build Coastguard Worker /// Caller must ensure that `zbi_kernel` contains a valid zircon kernel ZBI item.
jump_zircon_el2_or_lower(kernel: &[u8], zbi_item: &[u8]) -> !124*5225e6b1SAndroid Build Coastguard Worker pub unsafe fn jump_zircon_el2_or_lower(kernel: &[u8], zbi_item: &[u8]) -> ! {
125*5225e6b1SAndroid Build Coastguard Worker     assert_ne!(current_el(), ExceptionLevel::EL3);
126*5225e6b1SAndroid Build Coastguard Worker     let (entry, _) =
127*5225e6b1SAndroid Build Coastguard Worker         ZbiContainer::parse(kernel).unwrap().get_kernel_entry_and_reserved_memory_size().unwrap();
128*5225e6b1SAndroid Build Coastguard Worker     flush_dcache_buffer(kernel);
129*5225e6b1SAndroid Build Coastguard Worker     flush_dcache_buffer(zbi_item);
130*5225e6b1SAndroid Build Coastguard Worker     let addr = (kernel.as_ptr() as usize).checked_add(usize::try_from(entry).unwrap()).unwrap();
131*5225e6b1SAndroid Build Coastguard Worker     // SAFETY:
132*5225e6b1SAndroid Build Coastguard Worker     // * `zbi_kernel` and `zbi_item` have been flushed.
133*5225e6b1SAndroid Build Coastguard Worker     // * By requirement of this function, the computed `addr` is a valid kernel entry point.
134*5225e6b1SAndroid Build Coastguard Worker     unsafe { jump_kernel(addr, zbi_item.as_ptr() as _, 0, 0, 0) };
135*5225e6b1SAndroid Build Coastguard Worker }
136