blob: 19ba9c359d892df46d5d8df7214ed3629ae886bb [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;
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010020use crate::heap;
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000021use crate::memory::{self, MemoryTracker, MEMORY};
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +000022use crate::rand;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010023use core::arch::asm;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010024use core::mem::{drop, size_of};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000025use core::num::NonZeroUsize;
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010026use core::ops::Range;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010027use core::slice;
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070028use hyp::{get_hypervisor, HypervisorCap};
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010029use log::debug;
30use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000031use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010032use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010033use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000034use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000035use vmbase::{
36 console, layout, logger, main,
Alice Wang3fa9b802023-06-06 07:52:31 +000037 memory::{min_dcache_line_size, SIZE_2MB, 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);
63
64/// Entry point for pVM firmware.
65pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
66 // Limitations in this function:
67 // - can't access non-pvmfw memory (only statically-mapped memory)
68 // - can't access MMIO (therefore, no logging)
69
Pierre-Clément Tosif03089c2023-05-15 17:33:39 +000070 // SAFETY - This function should and will only be called once, here.
71 unsafe { heap::init() };
72
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010073 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010074 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000075 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010076 }
77
78 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
79}
80
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000081struct MemorySlices<'a> {
82 fdt: &'a mut libfdt::Fdt,
83 kernel: &'a [u8],
84 ramdisk: Option<&'a [u8]>,
85}
86
87impl<'a> MemorySlices<'a> {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010088 fn new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason> {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000089 // SAFETY - SIZE_2MB is non-zero.
Alice Wangeacb7382023-06-05 12:53:54 +000090 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(SIZE_2MB) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000091 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
92 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
93 // overwrite with the template DT and apply the DTBO.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010094 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, FDT_SIZE).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000095 error!("Failed to allocate the FDT range: {e}");
96 RebootReason::InternalError
97 })?;
98
99 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
100 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
101 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
102 error!("Failed to spawn the FDT wrapper: {e}");
103 RebootReason::InvalidFdt
104 })?;
105
Jiyong Park6a8789a2023-03-21 14:50:59 +0900106 let info = fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000107 debug!("Fdt passed validation!");
108
Jiyong Park6a8789a2023-03-21 14:50:59 +0900109 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000110 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100111 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
112 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000113 RebootReason::InvalidFdt
114 })?;
115
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000116 if get_hypervisor().has_cap(HypervisorCap::DYNAMIC_MEM_SHARE) {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100117 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool().map_err(|e| {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000118 error!("Failed to initialize dynamically shared pool: {e}");
119 RebootReason::InternalError
120 })?;
121 } else {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700122 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
123 error!("Pre-shared pool range not specified in swiotlb node");
124 RebootReason::InvalidFdt
125 })?;
126
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100127 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700128 error!("Failed to initialize pre-shared pool {e}");
129 RebootReason::InvalidFdt
130 })?;
131 }
132
Jiyong Park6a8789a2023-03-21 14:50:59 +0900133 let kernel_range = if let Some(r) = info.kernel_range {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100134 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000135 error!("Failed to obtain the kernel range with DT range: {e}");
136 RebootReason::InternalError
137 })?
138 } else if cfg!(feature = "legacy") {
139 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
140
141 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
142 error!("Invalid kernel size: {kernel_size:#x}");
143 RebootReason::InvalidPayload
144 })?;
145
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100146 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000147 error!("Failed to obtain the kernel range with legacy range: {e}");
148 RebootReason::InternalError
149 })?
150 } else {
151 error!("Failed to locate the kernel from the DT");
152 return Err(RebootReason::InvalidPayload);
153 };
154
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000155 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
156 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000157 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000158
Jiyong Park6a8789a2023-03-21 14:50:59 +0900159 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000160 debug!("Located ramdisk at {r:?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100161 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000162 error!("Failed to obtain the initrd range: {e}");
163 RebootReason::InvalidRamdisk
164 })?;
165
166 // SAFETY - The region was validated by memory to be in main memory, mapped, and
167 // not overlap.
168 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
169 } else {
170 info!("Couldn't locate the ramdisk from the device tree");
171 None
172 };
173
174 Ok(Self { fdt, kernel, ramdisk })
175 }
176}
177
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100178/// Sets up the environment for main() and wraps its result for start().
179///
180/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
181/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100182fn main_wrapper(
183 fdt: usize,
184 payload: usize,
185 payload_size: usize,
186) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100187 // Limitations in this function:
188 // - only access MMIO once (and while) it has been mapped and configured
189 // - only perform logging once the logger has been initialized
190 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100191
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100192 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100193
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100194 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
195 // builds that have tweaked the logger::init() call will actually attempt to log the message.
196
Alice Wang90e6f162023-04-17 13:49:45 +0000197 get_hypervisor().mmio_guard_init().map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100198 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100199 RebootReason::InternalError
200 })?;
201
Alice Wang90e6f162023-04-17 13:49:45 +0000202 get_hypervisor().mmio_guard_map(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100203 debug!("Failed to configure the UART: {e}");
204 RebootReason::InternalError
205 })?;
206
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100207 crypto::init();
208
Alice Wang807fa592023-06-02 09:54:43 +0000209 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000210 error!("Failed to set up the dynamic page tables: {e}");
211 RebootReason::InternalError
212 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100213
214 // SAFETY - We only get the appended payload from here, once. The region was statically mapped,
215 // then remapped by `init_page_table()`.
216 let appended_data = unsafe { get_appended_data_slice() };
217
218 let mut appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100219 error!("No valid configuration found");
220 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000221 })?;
222
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000223 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100224
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100225 // Up to this point, we were using the built-in static (from .rodata) page tables.
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100226 MEMORY.lock().replace(MemoryTracker::new(page_table));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100227
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100228 let slices = MemorySlices::new(fdt, payload, payload_size)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000229
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000230 rand::init().map_err(|e| {
231 error!("Failed to initialize rand: {e}");
232 RebootReason::InternalError
233 })?;
234
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100235 // This wrapper allows main() to be blissfully ignorant of platform details.
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100236 let next_bcc = crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100237
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100238 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
239 bcc_slice.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100240
Andrew Walbran19690632022-12-07 16:41:30 +0000241 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100242 MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000243 error!("Failed to unshare MMIO ranges: {e}");
244 RebootReason::InternalError
245 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000246 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
247 MEMORY.lock().as_mut().unwrap().unshare_all_memory();
Alice Wang90e6f162023-04-17 13:49:45 +0000248 get_hypervisor().mmio_guard_unmap(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100249 error!("Failed to unshare the UART: {e}");
250 RebootReason::InternalError
251 })?;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100252
253 // Drop MemoryTracker and deactivate page table.
254 drop(MEMORY.lock().take());
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100255
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100256 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100257}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100258
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100259fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
260 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000261 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100262 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000263 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100264 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000265 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100266 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000267 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100268
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000269 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 +0100270
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100271 let scratch = layout::scratch_range();
272
273 assert_ne!(scratch.len(), 0, "scratch memory is empty.");
274 assert_eq!(scratch.start % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
275 assert_eq!(scratch.end % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
276
277 assert!(bcc.is_within(&scratch));
278 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
279 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
280
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000281 let stack = memory::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100282
283 assert_ne!(stack.len(), 0, "stack region is empty.");
284 assert_eq!(stack.start % ASM_STP_ALIGN, 0, "Misaligned stack region.");
285 assert_eq!(stack.end % ASM_STP_ALIGN, 0, "Misaligned stack region.");
286
287 // 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 +0000288 // Disable the exception vector, caches and page table and then jump to the payload at the
289 // given address, passing it the given FDT pointer.
290 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100291 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
292 unsafe {
293 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100294 "cmp {scratch}, {bcc}",
295 "b.hs 1f",
296
297 // Zero .data & .bss until BCC.
298 "0: stp xzr, xzr, [{scratch}], 16",
299 "cmp {scratch}, {bcc}",
300 "b.lo 0b",
301
302 "1:",
303 // Skip BCC.
304 "mov {scratch}, {bcc_end}",
305 "cmp {scratch}, {scratch_end}",
306 "b.hs 1f",
307
308 // Keep zeroing .data & .bss.
309 "0: stp xzr, xzr, [{scratch}], 16",
310 "cmp {scratch}, {scratch_end}",
311 "b.lo 0b",
312
313 "1:",
314 // Flush d-cache over .data & .bss (including BCC).
315 "0: dc cvau, {cache_line}",
316 "add {cache_line}, {cache_line}, {dcache_line_size}",
317 "cmp {cache_line}, {scratch_end}",
318 "b.lo 0b",
319
320 "mov {cache_line}, {stack}",
321 // Zero stack region.
322 "0: stp xzr, xzr, [{stack}], 16",
323 "cmp {stack}, {stack_end}",
324 "b.lo 0b",
325
326 // Flush d-cache over stack region.
327 "0: dc cvau, {cache_line}",
328 "add {cache_line}, {cache_line}, {dcache_line_size}",
329 "cmp {cache_line}, {stack_end}",
330 "b.lo 0b",
331
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100332 "msr sctlr_el1, {sctlr_el1_val}",
333 "isb",
334 "mov x1, xzr",
335 "mov x2, xzr",
336 "mov x3, xzr",
337 "mov x4, xzr",
338 "mov x5, xzr",
339 "mov x6, xzr",
340 "mov x7, xzr",
341 "mov x8, xzr",
342 "mov x9, xzr",
343 "mov x10, xzr",
344 "mov x11, xzr",
345 "mov x12, xzr",
346 "mov x13, xzr",
347 "mov x14, xzr",
348 "mov x15, xzr",
349 "mov x16, xzr",
350 "mov x17, xzr",
351 "mov x18, xzr",
352 "mov x19, xzr",
353 "mov x20, xzr",
354 "mov x21, xzr",
355 "mov x22, xzr",
356 "mov x23, xzr",
357 "mov x24, xzr",
358 "mov x25, xzr",
359 "mov x26, xzr",
360 "mov x27, xzr",
361 "mov x28, xzr",
362 "mov x29, xzr",
363 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100364 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100365 "dsb nsh",
366 "br x30",
367 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100368 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
369 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
370 cache_line = in(reg) u64::try_from(scratch.start).unwrap(),
371 scratch = in(reg) u64::try_from(scratch.start).unwrap(),
372 scratch_end = in(reg) u64::try_from(scratch.end).unwrap(),
373 stack = in(reg) u64::try_from(stack.start).unwrap(),
374 stack_end = in(reg) u64::try_from(stack.end).unwrap(),
Alice Wang3fa9b802023-06-06 07:52:31 +0000375 dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100376 in("x0") fdt_address,
377 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100378 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100379 );
380 };
381}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100382
Alan Stokesc3829f12023-06-02 15:02:23 +0100383/// # Safety
384///
385/// This must only be called once, since we are returning a mutable reference.
386/// The appended data region must be mapped.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100387unsafe fn get_appended_data_slice() -> &'static mut [u8] {
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000388 let range = memory::appended_payload_range();
Alan Stokesa0e42962023-04-14 17:59:50 +0100389 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
390 // objects.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100391 unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100392}
393
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000394enum AppendedConfigType {
395 Valid,
396 Invalid,
397 NotFound,
398}
399
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100400enum AppendedPayload<'a> {
401 /// Configuration data.
402 Config(config::Config<'a>),
403 /// Deprecated raw BCC, as used in Android T.
404 LegacyBcc(&'a mut [u8]),
405}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100406
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100407impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100408 fn new(data: &'a mut [u8]) -> Option<Self> {
409 match Self::guess_config_type(data) {
Alan Stokesa0e42962023-04-14 17:59:50 +0100410 AppendedConfigType::Valid => {
Alan Stokesc3829f12023-06-02 15:02:23 +0100411 let config = config::Config::new(data);
Alan Stokesa0e42962023-04-14 17:59:50 +0100412 Some(Self::Config(config.unwrap()))
413 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000414 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
Alice Wangeacb7382023-06-05 12:53:54 +0000415 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000416 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
417 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
418 }
419 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100420 }
421 }
422
Alan Stokesc3829f12023-06-02 15:02:23 +0100423 fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100424 // This function is necessary to prevent the borrow checker from getting confused
425 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
426 let addr = data.as_ptr();
Alan Stokesa0e42962023-04-14 17:59:50 +0100427
Alan Stokesc3829f12023-06-02 15:02:23 +0100428 match config::Config::new(data) {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000429 Err(config::Error::InvalidMagic) => {
430 warn!("No configuration data found at {addr:?}");
431 AppendedConfigType::NotFound
432 }
433 Err(e) => {
434 error!("Invalid configuration data at {addr:?}: {e}");
435 AppendedConfigType::Invalid
436 }
437 Ok(_) => AppendedConfigType::Valid,
438 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100439 }
440
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000441 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100442 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000443 Self::Config(ref mut cfg) => cfg.get_entries(),
444 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000445 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100446 }
447}