blob: 772797099d1232632fcf3d67b8defd1b01ec15c6 [file] [log] [blame]
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +01001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Low-level entry and exit points of pvmfw.
16
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010017use crate::config;
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +010018use crate::crypto;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000019use crate::fdt;
Alice Wang93ee98a2023-06-08 08:20:39 +000020use crate::memory;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010021use core::arch::asm;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010022use core::mem::{drop, size_of};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000023use core::num::NonZeroUsize;
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010024use core::ops::Range;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010025use core::slice;
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +000026use hyp::{get_mem_sharer, get_mmio_guard};
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010027use log::debug;
28use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000029use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010030use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010031use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000032use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000033use vmbase::{
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000034 configure_heap, console,
Alice Wang4c70d142023-06-06 11:52:33 +000035 layout::{self, crosvm},
36 logger, main,
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000037 memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
Alice Wangeacb7382023-06-05 12:53:54 +000038 power::reboot,
Pierre-Clément Tosi3e3d7332023-06-22 10:44:29 +000039 rand,
Alice Wangeacb7382023-06-05 12:53:54 +000040};
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010041use zeroize::Zeroize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010042
43#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000044pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010045 /// A malformed BCC was received.
46 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010047 /// An invalid configuration was appended to pvmfw.
48 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010049 /// An unexpected internal error happened.
50 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000051 /// The provided FDT was invalid.
52 InvalidFdt,
53 /// The provided payload was invalid.
54 InvalidPayload,
55 /// The provided ramdisk was invalid.
56 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000057 /// Failed to verify the payload.
58 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000059 /// DICE layering process failed.
60 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010061}
62
63main!(start);
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000064configure_heap!(SIZE_128KB);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010065
66/// Entry point for pVM firmware.
67pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
68 // Limitations in this function:
69 // - can't access non-pvmfw memory (only statically-mapped memory)
70 // - can't access MMIO (therefore, no logging)
71
72 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010073 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000074 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010075 }
76
77 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
78}
79
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000080struct MemorySlices<'a> {
81 fdt: &'a mut libfdt::Fdt,
82 kernel: &'a [u8],
83 ramdisk: Option<&'a [u8]>,
84}
85
86impl<'a> MemorySlices<'a> {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010087 fn new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason> {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000088 // SAFETY - SIZE_2MB is non-zero.
Alice Wange9ae5c42023-06-12 11:14:13 +000089 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(crosvm::FDT_MAX_SIZE) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000090 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
91 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
92 // overwrite with the template DT and apply the DTBO.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010093 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, FDT_SIZE).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000094 error!("Failed to allocate the FDT range: {e}");
95 RebootReason::InternalError
96 })?;
97
98 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
99 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
100 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
101 error!("Failed to spawn the FDT wrapper: {e}");
102 RebootReason::InvalidFdt
103 })?;
104
Jiyong Park6a8789a2023-03-21 14:50:59 +0900105 let info = fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000106 debug!("Fdt passed validation!");
107
Jiyong Park6a8789a2023-03-21 14:50:59 +0900108 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000109 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100110 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
111 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000112 RebootReason::InvalidFdt
113 })?;
114
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000115 if let Some(mem_sharer) = get_mem_sharer() {
116 let granule = mem_sharer.granule().map_err(|e| {
Alice Wangb6d2c642023-06-13 13:07:06 +0000117 error!("Failed to get memory protection granule: {e}");
118 RebootReason::InternalError
119 })?;
120 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).map_err(|e| {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000121 error!("Failed to initialize dynamically shared pool: {e}");
122 RebootReason::InternalError
123 })?;
124 } else {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700125 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
126 error!("Pre-shared pool range not specified in swiotlb node");
127 RebootReason::InvalidFdt
128 })?;
129
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100130 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700131 error!("Failed to initialize pre-shared pool {e}");
132 RebootReason::InvalidFdt
133 })?;
134 }
135
Jiyong Park6a8789a2023-03-21 14:50:59 +0900136 let kernel_range = if let Some(r) = info.kernel_range {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100137 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000138 error!("Failed to obtain the kernel range with DT range: {e}");
139 RebootReason::InternalError
140 })?
141 } else if cfg!(feature = "legacy") {
142 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
143
144 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
145 error!("Invalid kernel size: {kernel_size:#x}");
146 RebootReason::InvalidPayload
147 })?;
148
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100149 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000150 error!("Failed to obtain the kernel range with legacy range: {e}");
151 RebootReason::InternalError
152 })?
153 } else {
154 error!("Failed to locate the kernel from the DT");
155 return Err(RebootReason::InvalidPayload);
156 };
157
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000158 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
159 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000160 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000161
Jiyong Park6a8789a2023-03-21 14:50:59 +0900162 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000163 debug!("Located ramdisk at {r:?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100164 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000165 error!("Failed to obtain the initrd range: {e}");
166 RebootReason::InvalidRamdisk
167 })?;
168
169 // SAFETY - The region was validated by memory to be in main memory, mapped, and
170 // not overlap.
171 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
172 } else {
173 info!("Couldn't locate the ramdisk from the device tree");
174 None
175 };
176
177 Ok(Self { fdt, kernel, ramdisk })
178 }
179}
180
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100181/// Sets up the environment for main() and wraps its result for start().
182///
183/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
184/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100185fn main_wrapper(
186 fdt: usize,
187 payload: usize,
188 payload_size: usize,
189) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100190 // Limitations in this function:
191 // - only access MMIO once (and while) it has been mapped and configured
192 // - only perform logging once the logger has been initialized
193 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100194
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100195 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100196
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100197 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
198 // builds that have tweaked the logger::init() call will actually attempt to log the message.
199
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000200 if let Some(mmio_guard) = get_mmio_guard() {
201 mmio_guard.init().map_err(|e| {
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000202 debug!("{e}");
203 RebootReason::InternalError
204 })?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100205
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000206 mmio_guard.map(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000207 debug!("Failed to configure the UART: {e}");
208 RebootReason::InternalError
209 })?;
210 }
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100211
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100212 crypto::init();
213
Alice Wang807fa592023-06-02 09:54:43 +0000214 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000215 error!("Failed to set up the dynamic page tables: {e}");
216 RebootReason::InternalError
217 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100218
219 // SAFETY - We only get the appended payload from here, once. The region was statically mapped,
220 // then remapped by `init_page_table()`.
221 let appended_data = unsafe { get_appended_data_slice() };
222
223 let mut appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100224 error!("No valid configuration found");
225 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000226 })?;
227
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000228 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100229
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100230 // Up to this point, we were using the built-in static (from .rodata) page tables.
Alice Wang4c70d142023-06-06 11:52:33 +0000231 MEMORY.lock().replace(MemoryTracker::new(
232 page_table,
Alice Wang63f4c9e2023-06-12 09:36:43 +0000233 crosvm::MEM_START..layout::MAX_VIRT_ADDR,
Alice Wang89d29592023-06-12 09:41:29 +0000234 crosvm::MMIO_RANGE,
Alice Wang5bb79502023-06-12 09:25:07 +0000235 Some(memory::appended_payload_range()),
Alice Wang4c70d142023-06-06 11:52:33 +0000236 ));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100237
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100238 let slices = MemorySlices::new(fdt, payload, payload_size)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000239
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000240 rand::init().map_err(|e| {
241 error!("Failed to initialize rand: {e}");
242 RebootReason::InternalError
243 })?;
244
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100245 // This wrapper allows main() to be blissfully ignorant of platform details.
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100246 let next_bcc = crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100247
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100248 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
249 bcc_slice.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100250
Andrew Walbran19690632022-12-07 16:41:30 +0000251 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100252 MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000253 error!("Failed to unshare MMIO ranges: {e}");
254 RebootReason::InternalError
255 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000256 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
257 MEMORY.lock().as_mut().unwrap().unshare_all_memory();
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000258 if let Some(mmio_guard) = get_mmio_guard() {
259 mmio_guard.unmap(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000260 error!("Failed to unshare the UART: {e}");
261 RebootReason::InternalError
262 })?;
263 }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100264
265 // Drop MemoryTracker and deactivate page table.
266 drop(MEMORY.lock().take());
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100267
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100268 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100269}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100270
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100271fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
272 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000273 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100274 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000275 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100276 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000277 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100278 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000279 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100280
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000281 const SCTLR_EL1_VAL: u64 = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100282
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100283 let scratch = layout::scratch_range();
284
Alice Wanga3931aa2023-07-05 12:52:09 +0000285 assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
286 assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
287 assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100288
Alice Wanga3931aa2023-07-05 12:52:09 +0000289 assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100290 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
291 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
292
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000293 let stack = memory::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100294
Alice Wanga3931aa2023-07-05 12:52:09 +0000295 assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
296 assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
297 assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100298
299 // Zero all memory that could hold secrets and that can't be safely written to from Rust.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000300 // Disable the exception vector, caches and page table and then jump to the payload at the
301 // given address, passing it the given FDT pointer.
302 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100303 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
304 unsafe {
305 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100306 "cmp {scratch}, {bcc}",
307 "b.hs 1f",
308
309 // Zero .data & .bss until BCC.
310 "0: stp xzr, xzr, [{scratch}], 16",
311 "cmp {scratch}, {bcc}",
312 "b.lo 0b",
313
314 "1:",
315 // Skip BCC.
316 "mov {scratch}, {bcc_end}",
317 "cmp {scratch}, {scratch_end}",
318 "b.hs 1f",
319
320 // Keep zeroing .data & .bss.
321 "0: stp xzr, xzr, [{scratch}], 16",
322 "cmp {scratch}, {scratch_end}",
323 "b.lo 0b",
324
325 "1:",
326 // Flush d-cache over .data & .bss (including BCC).
327 "0: dc cvau, {cache_line}",
328 "add {cache_line}, {cache_line}, {dcache_line_size}",
329 "cmp {cache_line}, {scratch_end}",
330 "b.lo 0b",
331
332 "mov {cache_line}, {stack}",
333 // Zero stack region.
334 "0: stp xzr, xzr, [{stack}], 16",
335 "cmp {stack}, {stack_end}",
336 "b.lo 0b",
337
338 // Flush d-cache over stack region.
339 "0: dc cvau, {cache_line}",
340 "add {cache_line}, {cache_line}, {dcache_line_size}",
341 "cmp {cache_line}, {stack_end}",
342 "b.lo 0b",
343
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100344 "msr sctlr_el1, {sctlr_el1_val}",
345 "isb",
346 "mov x1, xzr",
347 "mov x2, xzr",
348 "mov x3, xzr",
349 "mov x4, xzr",
350 "mov x5, xzr",
351 "mov x6, xzr",
352 "mov x7, xzr",
353 "mov x8, xzr",
354 "mov x9, xzr",
355 "mov x10, xzr",
356 "mov x11, xzr",
357 "mov x12, xzr",
358 "mov x13, xzr",
359 "mov x14, xzr",
360 "mov x15, xzr",
361 "mov x16, xzr",
362 "mov x17, xzr",
363 "mov x18, xzr",
364 "mov x19, xzr",
365 "mov x20, xzr",
366 "mov x21, xzr",
367 "mov x22, xzr",
368 "mov x23, xzr",
369 "mov x24, xzr",
370 "mov x25, xzr",
371 "mov x26, xzr",
372 "mov x27, xzr",
373 "mov x28, xzr",
374 "mov x29, xzr",
375 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100376 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100377 "dsb nsh",
378 "br x30",
379 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100380 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
381 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
Alice Wanga3931aa2023-07-05 12:52:09 +0000382 cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
383 scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
384 scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
385 stack = in(reg) u64::try_from(stack.start.0).unwrap(),
386 stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
Alice Wang3fa9b802023-06-06 07:52:31 +0000387 dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100388 in("x0") fdt_address,
389 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100390 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100391 );
392 };
393}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100394
Alan Stokesc3829f12023-06-02 15:02:23 +0100395/// # Safety
396///
397/// This must only be called once, since we are returning a mutable reference.
398/// The appended data region must be mapped.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100399unsafe fn get_appended_data_slice() -> &'static mut [u8] {
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000400 let range = memory::appended_payload_range();
Alan Stokesa0e42962023-04-14 17:59:50 +0100401 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
402 // objects.
Alice Wanga3931aa2023-07-05 12:52:09 +0000403 unsafe { slice::from_raw_parts_mut(range.start.0 as *mut u8, range.end - range.start) }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100404}
405
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000406enum AppendedConfigType {
407 Valid,
408 Invalid,
409 NotFound,
410}
411
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100412enum AppendedPayload<'a> {
413 /// Configuration data.
414 Config(config::Config<'a>),
415 /// Deprecated raw BCC, as used in Android T.
416 LegacyBcc(&'a mut [u8]),
417}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100418
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100419impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100420 fn new(data: &'a mut [u8]) -> Option<Self> {
421 match Self::guess_config_type(data) {
Alan Stokesa0e42962023-04-14 17:59:50 +0100422 AppendedConfigType::Valid => {
Alan Stokesc3829f12023-06-02 15:02:23 +0100423 let config = config::Config::new(data);
Alan Stokesa0e42962023-04-14 17:59:50 +0100424 Some(Self::Config(config.unwrap()))
425 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000426 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
Alice Wangeacb7382023-06-05 12:53:54 +0000427 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000428 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
429 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
430 }
431 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100432 }
433 }
434
Alan Stokesc3829f12023-06-02 15:02:23 +0100435 fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100436 // This function is necessary to prevent the borrow checker from getting confused
437 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
438 let addr = data.as_ptr();
Alan Stokesa0e42962023-04-14 17:59:50 +0100439
Alan Stokesc3829f12023-06-02 15:02:23 +0100440 match config::Config::new(data) {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000441 Err(config::Error::InvalidMagic) => {
442 warn!("No configuration data found at {addr:?}");
443 AppendedConfigType::NotFound
444 }
445 Err(e) => {
446 error!("Invalid configuration data at {addr:?}: {e}");
447 AppendedConfigType::Invalid
448 }
449 Ok(_) => AppendedConfigType::Valid,
450 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100451 }
452
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000453 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100454 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000455 Self::Config(ref mut cfg) => cfg.get_entries(),
456 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000457 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100458 }
459}