blob: cc6e302f193ba846ea4921c9868d28f283dd4a7e [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 Tosia0934c12022-11-25 20:54:11 +000018use crate::fdt;
Alice Wang93ee98a2023-06-08 08:20:39 +000019use crate::memory;
Maurice Lam0322b8c2023-12-18 22:13:48 +000020use bssl_sys::CRYPTO_library_init;
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 Tosie8726e42022-10-17 13:35:27 +010026use log::debug;
27use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000028use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010029use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010030use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000031use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000032use vmbase::{
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010033 configure_heap,
Pierre-Clément Tosia9b345f2024-04-27 01:01:42 +010034 hyp::{get_mem_sharer, get_mmio_guard},
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010035 layout::{self, crosvm, UART_PAGE_ADDR},
Pierre-Clément Tosid3305482023-06-29 15:03:48 +000036 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,
39};
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010040use zeroize::Zeroize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010041
42#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000043pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010044 /// A malformed BCC was received.
45 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010046 /// An invalid configuration was appended to pvmfw.
47 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010048 /// An unexpected internal error happened.
49 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000050 /// The provided FDT was invalid.
51 InvalidFdt,
52 /// The provided payload was invalid.
53 InvalidPayload,
54 /// The provided ramdisk was invalid.
55 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000056 /// Failed to verify the payload.
57 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000058 /// DICE layering process failed.
59 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010060}
61
62main!(start);
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000063configure_heap!(SIZE_128KB);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010064
65/// Entry point for pVM firmware.
66pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
67 // Limitations in this function:
68 // - can't access non-pvmfw memory (only statically-mapped memory)
69 // - can't access MMIO (therefore, no logging)
70
71 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010072 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000073 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010074 }
75
76 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
77}
78
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000079struct MemorySlices<'a> {
80 fdt: &'a mut libfdt::Fdt,
81 kernel: &'a [u8],
82 ramdisk: Option<&'a [u8]>,
83}
84
85impl<'a> MemorySlices<'a> {
Jaewan Kimc6e023b2023-10-12 15:11:05 +090086 fn new(
87 fdt: usize,
88 kernel: usize,
89 kernel_size: usize,
90 vm_dtbo: Option<&mut [u8]>,
Seungjae Yoof0af81d2024-01-17 13:48:36 +090091 vm_ref_dt: Option<&[u8]>,
Jaewan Kimc6e023b2023-10-12 15:11:05 +090092 ) -> Result<Self, RebootReason> {
Pierre-Clément Tosid88fd2d2023-07-07 15:54:33 +000093 let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000094 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
95 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
96 // overwrite with the template DT and apply the DTBO.
Pierre-Clément Tosid88fd2d2023-07-07 15:54:33 +000097 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, fdt_size).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000098 error!("Failed to allocate the FDT range: {e}");
99 RebootReason::InternalError
100 })?;
101
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100102 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000103 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900104
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900105 let info = fdt::sanitize_device_tree(fdt, vm_dtbo, vm_ref_dt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000106 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900107 error!("Failed to load sanitized FDT: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000108 RebootReason::InvalidFdt
109 })?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000110 debug!("Fdt passed validation!");
111
Jiyong Park6a8789a2023-03-21 14:50:59 +0900112 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000113 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100114 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
115 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000116 RebootReason::InvalidFdt
117 })?;
118
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000119 if let Some(mem_sharer) = get_mem_sharer() {
120 let granule = mem_sharer.granule().map_err(|e| {
Alice Wangb6d2c642023-06-13 13:07:06 +0000121 error!("Failed to get memory protection granule: {e}");
122 RebootReason::InternalError
123 })?;
124 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).map_err(|e| {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000125 error!("Failed to initialize dynamically shared pool: {e}");
126 RebootReason::InternalError
127 })?;
128 } else {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700129 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
130 error!("Pre-shared pool range not specified in swiotlb node");
131 RebootReason::InvalidFdt
132 })?;
133
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100134 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700135 error!("Failed to initialize pre-shared pool {e}");
136 RebootReason::InvalidFdt
137 })?;
138 }
139
Jiyong Park6a8789a2023-03-21 14:50:59 +0900140 let kernel_range = if let Some(r) = info.kernel_range {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100141 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000142 error!("Failed to obtain the kernel range with DT range: {e}");
143 RebootReason::InternalError
144 })?
145 } else if cfg!(feature = "legacy") {
146 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
147
148 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
149 error!("Invalid kernel size: {kernel_size:#x}");
150 RebootReason::InvalidPayload
151 })?;
152
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100153 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000154 error!("Failed to obtain the kernel range with legacy range: {e}");
155 RebootReason::InternalError
156 })?
157 } else {
158 error!("Failed to locate the kernel from the DT");
159 return Err(RebootReason::InvalidPayload);
160 };
161
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100162 let kernel = kernel_range.start as *const u8;
163 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
164 let kernel = unsafe { slice::from_raw_parts(kernel, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000165
Jiyong Park6a8789a2023-03-21 14:50:59 +0900166 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000167 debug!("Located ramdisk at {r:?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100168 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000169 error!("Failed to obtain the initrd range: {e}");
170 RebootReason::InvalidRamdisk
171 })?;
172
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100173 // SAFETY: The region was validated by memory to be in main memory, mapped, and
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000174 // not overlap.
175 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
176 } else {
177 info!("Couldn't locate the ramdisk from the device tree");
178 None
179 };
180
181 Ok(Self { fdt, kernel, ramdisk })
182 }
183}
184
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100185/// Sets up the environment for main() and wraps its result for start().
186///
187/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
188/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100189fn main_wrapper(
190 fdt: usize,
191 payload: usize,
192 payload_size: usize,
193) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100194 // Limitations in this function:
195 // - only access MMIO once (and while) it has been mapped and configured
196 // - only perform logging once the logger has been initialized
197 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100198
Pierre-Clément Tosid3305482023-06-29 15:03:48 +0000199 log::set_max_level(LevelFilter::Info);
Alice Wangee07f722023-10-03 15:20:17 +0000200 // TODO(https://crbug.com/boringssl/35): Remove this init when BoringSSL can handle this
201 // internally.
202 // SAFETY: Configures the internal state of the library - may be called multiple times.
203 unsafe {
204 CRYPTO_library_init();
205 }
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100206
Alice Wang807fa592023-06-02 09:54:43 +0000207 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000208 error!("Failed to set up the dynamic page tables: {e}");
209 RebootReason::InternalError
210 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100211
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100212 // SAFETY: We only get the appended payload from here, once. The region was statically mapped,
Alan Stokesc3829f12023-06-02 15:02:23 +0100213 // then remapped by `init_page_table()`.
214 let appended_data = unsafe { get_appended_data_slice() };
215
Alan Stokes65618332023-12-15 14:09:25 +0000216 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100217 error!("No valid configuration found");
218 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000219 })?;
220
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000221 let config_entries = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100222
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100223 // Up to this point, we were using the built-in static (from .rodata) page tables.
Alice Wang4c70d142023-06-06 11:52:33 +0000224 MEMORY.lock().replace(MemoryTracker::new(
225 page_table,
Alice Wang63f4c9e2023-06-12 09:36:43 +0000226 crosvm::MEM_START..layout::MAX_VIRT_ADDR,
Alice Wang89d29592023-06-12 09:41:29 +0000227 crosvm::MMIO_RANGE,
Alice Wang5bb79502023-06-12 09:25:07 +0000228 Some(memory::appended_payload_range()),
Alice Wang4c70d142023-06-06 11:52:33 +0000229 ));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100230
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900231 let slices = MemorySlices::new(
232 fdt,
233 payload,
234 payload_size,
235 config_entries.vm_dtbo,
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900236 config_entries.vm_ref_dt,
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900237 )?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000238
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100239 // This wrapper allows main() to be blissfully ignorant of platform details.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000240 let next_bcc = crate::main(
241 slices.fdt,
242 slices.kernel,
243 slices.ramdisk,
244 config_entries.bcc,
245 config_entries.debug_policy,
246 )?;
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.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000249 config_entries.bcc.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");
Pierre-Clément Tosi6b867532024-04-29 02:29:42 +0100252 MEMORY.lock().as_mut().unwrap().unshare_all_mmio().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() {
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +0100259 mmio_guard.unmap(UART_PAGE_ADDR).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 //
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100303 // SAFETY: We're exiting pvmfw by passing the register values we need to a noreturn asm!().
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100304 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 Tosi20b60962022-10-17 13:35:27 +0100406enum AppendedPayload<'a> {
407 /// Configuration data.
408 Config(config::Config<'a>),
409 /// Deprecated raw BCC, as used in Android T.
410 LegacyBcc(&'a mut [u8]),
411}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100412
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100413impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100414 fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100415 // The borrow checker gets confused about the ownership of data (see inline comments) so we
416 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
417 // in v1.77) in https://users.rust-lang.org/t/78467.
418 let data_ptr = data as *mut [u8];
419
420 // Config::new() borrows data as mutable ...
421 match config::Config::new(data) {
422 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
423 Ok(valid) => Some(Self::Config(valid)),
424 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
425 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
426 // ... the borrow checker still complains about a second mutable ref without this.
427 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
428 let data: &'a mut _ = unsafe { &mut *data_ptr };
429
Alice Wangeacb7382023-06-05 12:53:54 +0000430 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000431 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
432 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
433 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000434 Err(e) => {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100435 error!("Invalid configuration data at {data_ptr:?}: {e}");
436 None
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000437 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000438 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100439 }
440
Alan Stokes65618332023-12-15 14:09:25 +0000441 fn get_entries(self) -> config::Entries<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100442 match self {
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000443 Self::Config(cfg) => cfg.get_entries(),
444 Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000445 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100446 }
447}