blob: 631986322571cf0da0604f0583886e8628f746cb [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 Tosia1d3ea32022-11-01 15:05:11 +000021use crate::helpers;
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010022use crate::helpers::RangeExt as _;
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +000023use crate::memory::{self, MemoryTracker, MEMORY};
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +000024use crate::rand;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010025use core::arch::asm;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010026use core::mem::{drop, size_of};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000027use core::num::NonZeroUsize;
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010028use core::ops::Range;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010029use core::slice;
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070030use hyp::{get_hypervisor, HypervisorCap};
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010031use log::debug;
32use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000033use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010034use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010035use log::LevelFilter;
Alice Wangeacb7382023-06-05 12:53:54 +000036use vmbase::{
37 console, layout, logger, main,
38 memory::{SIZE_2MB, SIZE_4KB},
39 power::reboot,
40};
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);
64
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
Pierre-Clément Tosif03089c2023-05-15 17:33:39 +000071 // SAFETY - This function should and will only be called once, here.
72 unsafe { heap::init() };
73
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010074 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010075 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000076 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010077 }
78
79 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
80}
81
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000082struct MemorySlices<'a> {
83 fdt: &'a mut libfdt::Fdt,
84 kernel: &'a [u8],
85 ramdisk: Option<&'a [u8]>,
86}
87
88impl<'a> MemorySlices<'a> {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010089 fn new(fdt: usize, kernel: usize, kernel_size: usize) -> Result<Self, RebootReason> {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000090 // SAFETY - SIZE_2MB is non-zero.
Alice Wangeacb7382023-06-05 12:53:54 +000091 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(SIZE_2MB) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000092 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
93 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
94 // overwrite with the template DT and apply the DTBO.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010095 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, FDT_SIZE).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000096 error!("Failed to allocate the FDT range: {e}");
97 RebootReason::InternalError
98 })?;
99
100 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
101 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
102 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
103 error!("Failed to spawn the FDT wrapper: {e}");
104 RebootReason::InvalidFdt
105 })?;
106
Jiyong Park6a8789a2023-03-21 14:50:59 +0900107 let info = fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000108 debug!("Fdt passed validation!");
109
Jiyong Park6a8789a2023-03-21 14:50:59 +0900110 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000111 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100112 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
113 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000114 RebootReason::InvalidFdt
115 })?;
116
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000117 if get_hypervisor().has_cap(HypervisorCap::DYNAMIC_MEM_SHARE) {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100118 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool().map_err(|e| {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000119 error!("Failed to initialize dynamically shared pool: {e}");
120 RebootReason::InternalError
121 })?;
122 } else {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700123 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
124 error!("Pre-shared pool range not specified in swiotlb node");
125 RebootReason::InvalidFdt
126 })?;
127
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100128 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700129 error!("Failed to initialize pre-shared pool {e}");
130 RebootReason::InvalidFdt
131 })?;
132 }
133
Jiyong Park6a8789a2023-03-21 14:50:59 +0900134 let kernel_range = if let Some(r) = info.kernel_range {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100135 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000136 error!("Failed to obtain the kernel range with DT range: {e}");
137 RebootReason::InternalError
138 })?
139 } else if cfg!(feature = "legacy") {
140 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
141
142 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
143 error!("Invalid kernel size: {kernel_size:#x}");
144 RebootReason::InvalidPayload
145 })?;
146
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100147 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000148 error!("Failed to obtain the kernel range with legacy range: {e}");
149 RebootReason::InternalError
150 })?
151 } else {
152 error!("Failed to locate the kernel from the DT");
153 return Err(RebootReason::InvalidPayload);
154 };
155
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000156 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
157 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000158 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000159
Jiyong Park6a8789a2023-03-21 14:50:59 +0900160 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000161 debug!("Located ramdisk at {r:?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100162 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000163 error!("Failed to obtain the initrd range: {e}");
164 RebootReason::InvalidRamdisk
165 })?;
166
167 // SAFETY - The region was validated by memory to be in main memory, mapped, and
168 // not overlap.
169 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
170 } else {
171 info!("Couldn't locate the ramdisk from the device tree");
172 None
173 };
174
175 Ok(Self { fdt, kernel, ramdisk })
176 }
177}
178
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100179/// Sets up the environment for main() and wraps its result for start().
180///
181/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
182/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100183fn main_wrapper(
184 fdt: usize,
185 payload: usize,
186 payload_size: usize,
187) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100188 // Limitations in this function:
189 // - only access MMIO once (and while) it has been mapped and configured
190 // - only perform logging once the logger has been initialized
191 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100192
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100193 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100194
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100195 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
196 // builds that have tweaked the logger::init() call will actually attempt to log the message.
197
Alice Wang90e6f162023-04-17 13:49:45 +0000198 get_hypervisor().mmio_guard_init().map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100199 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100200 RebootReason::InternalError
201 })?;
202
Alice Wang90e6f162023-04-17 13:49:45 +0000203 get_hypervisor().mmio_guard_map(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100204 debug!("Failed to configure the UART: {e}");
205 RebootReason::InternalError
206 })?;
207
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100208 crypto::init();
209
Alice Wang807fa592023-06-02 09:54:43 +0000210 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000211 error!("Failed to set up the dynamic page tables: {e}");
212 RebootReason::InternalError
213 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100214
215 // SAFETY - We only get the appended payload from here, once. The region was statically mapped,
216 // then remapped by `init_page_table()`.
217 let appended_data = unsafe { get_appended_data_slice() };
218
219 let mut appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100220 error!("No valid configuration found");
221 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000222 })?;
223
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000224 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100225
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100226 // Up to this point, we were using the built-in static (from .rodata) page tables.
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100227 MEMORY.lock().replace(MemoryTracker::new(page_table));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100228
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100229 let slices = MemorySlices::new(fdt, payload, payload_size)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000230
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000231 rand::init().map_err(|e| {
232 error!("Failed to initialize rand: {e}");
233 RebootReason::InternalError
234 })?;
235
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100236 // This wrapper allows main() to be blissfully ignorant of platform details.
Jakob Vukalovicb99905d2023-04-20 15:46:02 +0100237 let next_bcc = crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, debug_policy)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100238
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100239 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
240 bcc_slice.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100241
Andrew Walbran19690632022-12-07 16:41:30 +0000242 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100243 MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000244 error!("Failed to unshare MMIO ranges: {e}");
245 RebootReason::InternalError
246 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000247 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
248 MEMORY.lock().as_mut().unwrap().unshare_all_memory();
Alice Wang90e6f162023-04-17 13:49:45 +0000249 get_hypervisor().mmio_guard_unmap(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100250 error!("Failed to unshare the UART: {e}");
251 RebootReason::InternalError
252 })?;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100253
254 // Drop MemoryTracker and deactivate page table.
255 drop(MEMORY.lock().take());
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100256
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100257 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100258}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100259
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100260fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
261 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000262 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100263 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000264 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100265 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000266 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100267 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000268 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100269
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000270 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 +0100271
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100272 let scratch = layout::scratch_range();
273
274 assert_ne!(scratch.len(), 0, "scratch memory is empty.");
275 assert_eq!(scratch.start % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
276 assert_eq!(scratch.end % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
277
278 assert!(bcc.is_within(&scratch));
279 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
280 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
281
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000282 let stack = memory::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100283
284 assert_ne!(stack.len(), 0, "stack region is empty.");
285 assert_eq!(stack.start % ASM_STP_ALIGN, 0, "Misaligned stack region.");
286 assert_eq!(stack.end % ASM_STP_ALIGN, 0, "Misaligned stack region.");
287
288 // 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 +0000289 // Disable the exception vector, caches and page table and then jump to the payload at the
290 // given address, passing it the given FDT pointer.
291 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100292 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
293 unsafe {
294 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100295 "cmp {scratch}, {bcc}",
296 "b.hs 1f",
297
298 // Zero .data & .bss until BCC.
299 "0: stp xzr, xzr, [{scratch}], 16",
300 "cmp {scratch}, {bcc}",
301 "b.lo 0b",
302
303 "1:",
304 // Skip BCC.
305 "mov {scratch}, {bcc_end}",
306 "cmp {scratch}, {scratch_end}",
307 "b.hs 1f",
308
309 // Keep zeroing .data & .bss.
310 "0: stp xzr, xzr, [{scratch}], 16",
311 "cmp {scratch}, {scratch_end}",
312 "b.lo 0b",
313
314 "1:",
315 // Flush d-cache over .data & .bss (including BCC).
316 "0: dc cvau, {cache_line}",
317 "add {cache_line}, {cache_line}, {dcache_line_size}",
318 "cmp {cache_line}, {scratch_end}",
319 "b.lo 0b",
320
321 "mov {cache_line}, {stack}",
322 // Zero stack region.
323 "0: stp xzr, xzr, [{stack}], 16",
324 "cmp {stack}, {stack_end}",
325 "b.lo 0b",
326
327 // Flush d-cache over stack region.
328 "0: dc cvau, {cache_line}",
329 "add {cache_line}, {cache_line}, {dcache_line_size}",
330 "cmp {cache_line}, {stack_end}",
331 "b.lo 0b",
332
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100333 "msr sctlr_el1, {sctlr_el1_val}",
334 "isb",
335 "mov x1, xzr",
336 "mov x2, xzr",
337 "mov x3, xzr",
338 "mov x4, xzr",
339 "mov x5, xzr",
340 "mov x6, xzr",
341 "mov x7, xzr",
342 "mov x8, xzr",
343 "mov x9, xzr",
344 "mov x10, xzr",
345 "mov x11, xzr",
346 "mov x12, xzr",
347 "mov x13, xzr",
348 "mov x14, xzr",
349 "mov x15, xzr",
350 "mov x16, xzr",
351 "mov x17, xzr",
352 "mov x18, xzr",
353 "mov x19, xzr",
354 "mov x20, xzr",
355 "mov x21, xzr",
356 "mov x22, xzr",
357 "mov x23, xzr",
358 "mov x24, xzr",
359 "mov x25, xzr",
360 "mov x26, xzr",
361 "mov x27, xzr",
362 "mov x28, xzr",
363 "mov x29, xzr",
364 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100365 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100366 "dsb nsh",
367 "br x30",
368 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100369 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
370 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
371 cache_line = in(reg) u64::try_from(scratch.start).unwrap(),
372 scratch = in(reg) u64::try_from(scratch.start).unwrap(),
373 scratch_end = in(reg) u64::try_from(scratch.end).unwrap(),
374 stack = in(reg) u64::try_from(stack.start).unwrap(),
375 stack_end = in(reg) u64::try_from(stack.end).unwrap(),
376 dcache_line_size = in(reg) u64::try_from(helpers::min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100377 in("x0") fdt_address,
378 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100379 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100380 );
381 };
382}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100383
Alan Stokesc3829f12023-06-02 15:02:23 +0100384/// # Safety
385///
386/// This must only be called once, since we are returning a mutable reference.
387/// The appended data region must be mapped.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100388unsafe fn get_appended_data_slice() -> &'static mut [u8] {
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000389 let range = memory::appended_payload_range();
Alan Stokesa0e42962023-04-14 17:59:50 +0100390 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
391 // objects.
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100392 unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100393}
394
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000395enum AppendedConfigType {
396 Valid,
397 Invalid,
398 NotFound,
399}
400
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100401enum AppendedPayload<'a> {
402 /// Configuration data.
403 Config(config::Config<'a>),
404 /// Deprecated raw BCC, as used in Android T.
405 LegacyBcc(&'a mut [u8]),
406}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100407
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100408impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100409 fn new(data: &'a mut [u8]) -> Option<Self> {
410 match Self::guess_config_type(data) {
Alan Stokesa0e42962023-04-14 17:59:50 +0100411 AppendedConfigType::Valid => {
Alan Stokesc3829f12023-06-02 15:02:23 +0100412 let config = config::Config::new(data);
Alan Stokesa0e42962023-04-14 17:59:50 +0100413 Some(Self::Config(config.unwrap()))
414 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000415 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
Alice Wangeacb7382023-06-05 12:53:54 +0000416 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000417 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
418 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
419 }
420 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100421 }
422 }
423
Alan Stokesc3829f12023-06-02 15:02:23 +0100424 fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100425 // This function is necessary to prevent the borrow checker from getting confused
426 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
427 let addr = data.as_ptr();
Alan Stokesa0e42962023-04-14 17:59:50 +0100428
Alan Stokesc3829f12023-06-02 15:02:23 +0100429 match config::Config::new(data) {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000430 Err(config::Error::InvalidMagic) => {
431 warn!("No configuration data found at {addr:?}");
432 AppendedConfigType::NotFound
433 }
434 Err(e) => {
435 error!("Invalid configuration data at {addr:?}: {e}");
436 AppendedConfigType::Invalid
437 }
438 Ok(_) => AppendedConfigType::Valid,
439 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100440 }
441
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000442 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100443 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000444 Self::Config(ref mut cfg) => cfg.get_entries(),
445 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000446 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100447 }
448}