blob: 945ad6ad397a5da92e695820dc2a40ce9a56a19b [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;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010020use core::arch::asm;
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +010021use core::mem::{drop, size_of};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000022use core::num::NonZeroUsize;
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010023use core::ops::Range;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010024use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010025use log::debug;
26use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000027use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010028use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010029use log::LevelFilter;
Alice Wang4be4dd02023-06-07 07:50:40 +000030use vmbase::util::RangeExt as _;
Alice Wangeacb7382023-06-05 12:53:54 +000031use vmbase::{
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010032 configure_heap, console_writeln,
Pierre-Clément Tosia9b345f2024-04-27 01:01:42 +010033 hyp::{get_mem_sharer, get_mmio_guard},
Pierre-Clément Tosi38a36212024-06-06 11:30:39 +010034 layout::{self, crosvm, UART_PAGE_ADDR},
Pierre-Clément Tosid3305482023-06-29 15:03:48 +000035 main,
Pierre-Clément Tosif3681e82023-06-22 11:38:22 +000036 memory::{min_dcache_line_size, MemoryTracker, MEMORY, SIZE_128KB, SIZE_4KB},
Alice Wangeacb7382023-06-05 12:53:54 +000037 power::reboot,
38};
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010039use zeroize::Zeroize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010040
41#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000042pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010043 /// A malformed BCC was received.
44 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010045 /// An invalid configuration was appended to pvmfw.
46 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010047 /// An unexpected internal error happened.
48 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000049 /// The provided FDT was invalid.
50 InvalidFdt,
51 /// The provided payload was invalid.
52 InvalidPayload,
53 /// The provided ramdisk was invalid.
54 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000055 /// Failed to verify the payload.
56 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000057 /// DICE layering process failed.
58 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010059}
60
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010061impl RebootReason {
62 pub fn as_avf_reboot_string(&self) -> &'static str {
63 match self {
64 Self::InvalidBcc => "PVM_FIRMWARE_INVALID_BCC",
65 Self::InvalidConfig => "PVM_FIRMWARE_INVALID_CONFIG_DATA",
66 Self::InternalError => "PVM_FIRMWARE_INTERNAL_ERROR",
67 Self::InvalidFdt => "PVM_FIRMWARE_INVALID_FDT",
68 Self::InvalidPayload => "PVM_FIRMWARE_INVALID_PAYLOAD",
69 Self::InvalidRamdisk => "PVM_FIRMWARE_INVALID_RAMDISK",
70 Self::PayloadVerificationError => "PVM_FIRMWARE_PAYLOAD_VERIFICATION_FAILED",
71 Self::SecretDerivationError => "PVM_FIRMWARE_SECRET_DERIVATION_FAILED",
72 }
73 }
74}
75
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010076main!(start);
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000077configure_heap!(SIZE_128KB);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010078
79/// Entry point for pVM firmware.
80pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
81 // Limitations in this function:
82 // - can't access non-pvmfw memory (only statically-mapped memory)
Pierre-Clément Tosi8e92d1a2024-06-18 16:15:14 +010083 // - can't access MMIO (except the console, already configured by vmbase)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010084
85 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +010086 Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010087 Err(e) => {
88 const REBOOT_REASON_CONSOLE: usize = 1;
89 console_writeln!(REBOOT_REASON_CONSOLE, "{}", e.as_avf_reboot_string());
90 reboot()
91 }
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010092 }
93
94 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
95}
96
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000097struct MemorySlices<'a> {
98 fdt: &'a mut libfdt::Fdt,
99 kernel: &'a [u8],
100 ramdisk: Option<&'a [u8]>,
101}
102
103impl<'a> MemorySlices<'a> {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900104 fn new(
105 fdt: usize,
106 kernel: usize,
107 kernel_size: usize,
108 vm_dtbo: Option<&mut [u8]>,
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900109 vm_ref_dt: Option<&[u8]>,
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900110 ) -> Result<Self, RebootReason> {
Pierre-Clément Tosid88fd2d2023-07-07 15:54:33 +0000111 let fdt_size = NonZeroUsize::new(crosvm::FDT_MAX_SIZE).unwrap();
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000112 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
113 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
114 // overwrite with the template DT and apply the DTBO.
Pierre-Clément Tosid88fd2d2023-07-07 15:54:33 +0000115 let range = MEMORY.lock().as_mut().unwrap().alloc_mut(fdt, fdt_size).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000116 error!("Failed to allocate the FDT range: {e}");
117 RebootReason::InternalError
118 })?;
119
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100120 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000121 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900122
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900123 let info = fdt::sanitize_device_tree(fdt, vm_dtbo, vm_ref_dt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000124 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
Jaewan Kimc6e023b2023-10-12 15:11:05 +0900125 error!("Failed to load sanitized FDT: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000126 RebootReason::InvalidFdt
127 })?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000128 debug!("Fdt passed validation!");
129
Jiyong Park6a8789a2023-03-21 14:50:59 +0900130 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000131 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100132 MEMORY.lock().as_mut().unwrap().shrink(&memory_range).map_err(|e| {
133 error!("Failed to use memory range value from DT: {memory_range:#x?}: {e}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000134 RebootReason::InvalidFdt
135 })?;
136
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000137 if let Some(mem_sharer) = get_mem_sharer() {
138 let granule = mem_sharer.granule().map_err(|e| {
Alice Wangb6d2c642023-06-13 13:07:06 +0000139 error!("Failed to get memory protection granule: {e}");
140 RebootReason::InternalError
141 })?;
142 MEMORY.lock().as_mut().unwrap().init_dynamic_shared_pool(granule).map_err(|e| {
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000143 error!("Failed to initialize dynamically shared pool: {e}");
144 RebootReason::InternalError
145 })?;
146 } else {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700147 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
148 error!("Pre-shared pool range not specified in swiotlb node");
149 RebootReason::InvalidFdt
150 })?;
151
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100152 MEMORY.lock().as_mut().unwrap().init_static_shared_pool(range).map_err(|e| {
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700153 error!("Failed to initialize pre-shared pool {e}");
154 RebootReason::InvalidFdt
155 })?;
156 }
157
Jiyong Park6a8789a2023-03-21 14:50:59 +0900158 let kernel_range = if let Some(r) = info.kernel_range {
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100159 MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000160 error!("Failed to obtain the kernel range with DT range: {e}");
161 RebootReason::InternalError
162 })?
163 } else if cfg!(feature = "legacy") {
164 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
165
166 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
167 error!("Invalid kernel size: {kernel_size:#x}");
168 RebootReason::InvalidPayload
169 })?;
170
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100171 MEMORY.lock().as_mut().unwrap().alloc(kernel, kernel_size).map_err(|e| {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000172 error!("Failed to obtain the kernel range with legacy range: {e}");
173 RebootReason::InternalError
174 })?
175 } else {
176 error!("Failed to locate the kernel from the DT");
177 return Err(RebootReason::InvalidPayload);
178 };
179
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100180 let kernel = kernel_range.start as *const u8;
181 // SAFETY: The tracker validated the range to be in main memory, mapped, and not overlap.
182 let kernel = unsafe { slice::from_raw_parts(kernel, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000183
Jiyong Park6a8789a2023-03-21 14:50:59 +0900184 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000185 debug!("Located ramdisk at {r:?}");
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100186 let r = MEMORY.lock().as_mut().unwrap().alloc_range(&r).map_err(|e| {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000187 error!("Failed to obtain the initrd range: {e}");
188 RebootReason::InvalidRamdisk
189 })?;
190
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100191 // SAFETY: The region was validated by memory to be in main memory, mapped, and
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000192 // not overlap.
193 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
194 } else {
195 info!("Couldn't locate the ramdisk from the device tree");
196 None
197 };
198
199 Ok(Self { fdt, kernel, ramdisk })
200 }
201}
202
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100203/// Sets up the environment for main() and wraps its result for start().
204///
205/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
206/// the assumption that its environment has been properly configured.
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100207fn main_wrapper(
208 fdt: usize,
209 payload: usize,
210 payload_size: usize,
211) -> Result<(usize, Range<usize>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100212 // Limitations in this function:
213 // - only access MMIO once (and while) it has been mapped and configured
214 // - only perform logging once the logger has been initialized
215 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100216
Pierre-Clément Tosid3305482023-06-29 15:03:48 +0000217 log::set_max_level(LevelFilter::Info);
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100218
Alice Wang807fa592023-06-02 09:54:43 +0000219 let page_table = memory::init_page_table().map_err(|e| {
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000220 error!("Failed to set up the dynamic page tables: {e}");
221 RebootReason::InternalError
222 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100223
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100224 // SAFETY: We only get the appended payload from here, once. The region was statically mapped,
Alan Stokesc3829f12023-06-02 15:02:23 +0100225 // then remapped by `init_page_table()`.
226 let appended_data = unsafe { get_appended_data_slice() };
227
Alan Stokes65618332023-12-15 14:09:25 +0000228 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100229 error!("No valid configuration found");
230 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000231 })?;
232
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000233 let config_entries = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100234
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100235 // Up to this point, we were using the built-in static (from .rodata) page tables.
Alice Wang4c70d142023-06-06 11:52:33 +0000236 MEMORY.lock().replace(MemoryTracker::new(
237 page_table,
Alice Wang63f4c9e2023-06-12 09:36:43 +0000238 crosvm::MEM_START..layout::MAX_VIRT_ADDR,
Alice Wang89d29592023-06-12 09:41:29 +0000239 crosvm::MMIO_RANGE,
Alice Wang5bb79502023-06-12 09:25:07 +0000240 Some(memory::appended_payload_range()),
Alice Wang4c70d142023-06-06 11:52:33 +0000241 ));
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100242
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900243 let slices = MemorySlices::new(
244 fdt,
245 payload,
246 payload_size,
247 config_entries.vm_dtbo,
Seungjae Yoof0af81d2024-01-17 13:48:36 +0900248 config_entries.vm_ref_dt,
Seungjae Yoo013f4c42024-01-02 13:04:19 +0900249 )?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000250
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100251 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100252 let (next_bcc, debuggable_payload) = crate::main(
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000253 slices.fdt,
254 slices.kernel,
255 slices.ramdisk,
256 config_entries.bcc,
257 config_entries.debug_policy,
258 )?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100259
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100260 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000261 config_entries.bcc.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100262
Andrew Walbran19690632022-12-07 16:41:30 +0000263 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Pierre-Clément Tosi6b867532024-04-29 02:29:42 +0100264 MEMORY.lock().as_mut().unwrap().unshare_all_mmio().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000265 error!("Failed to unshare MMIO ranges: {e}");
266 RebootReason::InternalError
267 })?;
Pierre-Clément Tosif19c0e62023-05-02 13:56:58 +0000268 // Call unshare_all_memory here (instead of relying on the dtor) while UART is still mapped.
269 MEMORY.lock().as_mut().unwrap().unshare_all_memory();
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100270
Pierre-Clément Tosid643cfe2023-06-29 09:30:51 +0000271 if let Some(mmio_guard) = get_mmio_guard() {
Nikita Ioffeb4268b32024-09-03 10:23:14 +0000272 if cfg!(debuggable_vms_improvements) && debuggable_payload {
273 // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
274 } else {
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100275 mmio_guard.unmap(UART_PAGE_ADDR).map_err(|e| {
276 error!("Failed to unshare the UART: {e}");
277 RebootReason::InternalError
278 })?;
279 }
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000280 }
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100281
282 // Drop MemoryTracker and deactivate page table.
283 drop(MEMORY.lock().take());
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100284
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100285 Ok((slices.kernel.as_ptr() as usize, next_bcc))
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100286}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100287
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100288fn jump_to_payload(fdt_address: u64, payload_start: u64, bcc: Range<usize>) -> ! {
289 const ASM_STP_ALIGN: usize = size_of::<u64>() * 2;
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000290 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100291 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000292 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100293 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000294 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100295 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000296 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100297
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000298 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 +0100299
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100300 let scratch = layout::scratch_range();
301
Alice Wanga3931aa2023-07-05 12:52:09 +0000302 assert_ne!(scratch.end - scratch.start, 0, "scratch memory is empty.");
303 assert_eq!(scratch.start.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
304 assert_eq!(scratch.end.0 % ASM_STP_ALIGN, 0, "scratch memory is misaligned.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100305
Alice Wanga3931aa2023-07-05 12:52:09 +0000306 assert!(bcc.is_within(&(scratch.start.0..scratch.end.0)));
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100307 assert_eq!(bcc.start % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
308 assert_eq!(bcc.end % ASM_STP_ALIGN, 0, "Misaligned guest BCC.");
309
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000310 let stack = memory::stack_range();
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100311
Alice Wanga3931aa2023-07-05 12:52:09 +0000312 assert_ne!(stack.end - stack.start, 0, "stack region is empty.");
313 assert_eq!(stack.start.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
314 assert_eq!(stack.end.0 % ASM_STP_ALIGN, 0, "Misaligned stack region.");
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100315
316 // 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 +0000317 // Disable the exception vector, caches and page table and then jump to the payload at the
318 // given address, passing it the given FDT pointer.
319 //
Andrew Walbran20bb4e42023-07-07 13:55:55 +0100320 // 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 +0100321 unsafe {
322 asm!(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100323 "cmp {scratch}, {bcc}",
324 "b.hs 1f",
325
326 // Zero .data & .bss until BCC.
327 "0: stp xzr, xzr, [{scratch}], 16",
328 "cmp {scratch}, {bcc}",
329 "b.lo 0b",
330
331 "1:",
332 // Skip BCC.
333 "mov {scratch}, {bcc_end}",
334 "cmp {scratch}, {scratch_end}",
335 "b.hs 1f",
336
337 // Keep zeroing .data & .bss.
338 "0: stp xzr, xzr, [{scratch}], 16",
339 "cmp {scratch}, {scratch_end}",
340 "b.lo 0b",
341
342 "1:",
343 // Flush d-cache over .data & .bss (including BCC).
344 "0: dc cvau, {cache_line}",
345 "add {cache_line}, {cache_line}, {dcache_line_size}",
346 "cmp {cache_line}, {scratch_end}",
347 "b.lo 0b",
348
349 "mov {cache_line}, {stack}",
350 // Zero stack region.
351 "0: stp xzr, xzr, [{stack}], 16",
352 "cmp {stack}, {stack_end}",
353 "b.lo 0b",
354
355 // Flush d-cache over stack region.
356 "0: dc cvau, {cache_line}",
357 "add {cache_line}, {cache_line}, {dcache_line_size}",
358 "cmp {cache_line}, {stack_end}",
359 "b.lo 0b",
360
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100361 "msr sctlr_el1, {sctlr_el1_val}",
362 "isb",
363 "mov x1, xzr",
364 "mov x2, xzr",
365 "mov x3, xzr",
366 "mov x4, xzr",
367 "mov x5, xzr",
368 "mov x6, xzr",
369 "mov x7, xzr",
370 "mov x8, xzr",
371 "mov x9, xzr",
372 "mov x10, xzr",
373 "mov x11, xzr",
374 "mov x12, xzr",
375 "mov x13, xzr",
376 "mov x14, xzr",
377 "mov x15, xzr",
378 "mov x16, xzr",
379 "mov x17, xzr",
380 "mov x18, xzr",
381 "mov x19, xzr",
382 "mov x20, xzr",
383 "mov x21, xzr",
384 "mov x22, xzr",
385 "mov x23, xzr",
386 "mov x24, xzr",
387 "mov x25, xzr",
388 "mov x26, xzr",
389 "mov x27, xzr",
390 "mov x28, xzr",
391 "mov x29, xzr",
392 "msr ttbr0_el1, xzr",
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100393 // Ensure that CMOs have completed before entering payload.
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100394 "dsb nsh",
395 "br x30",
396 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100397 bcc = in(reg) u64::try_from(bcc.start).unwrap(),
398 bcc_end = in(reg) u64::try_from(bcc.end).unwrap(),
Alice Wanga3931aa2023-07-05 12:52:09 +0000399 cache_line = in(reg) u64::try_from(scratch.start.0).unwrap(),
400 scratch = in(reg) u64::try_from(scratch.start.0).unwrap(),
401 scratch_end = in(reg) u64::try_from(scratch.end.0).unwrap(),
402 stack = in(reg) u64::try_from(stack.start.0).unwrap(),
403 stack_end = in(reg) u64::try_from(stack.end.0).unwrap(),
Alice Wang3fa9b802023-06-06 07:52:31 +0000404 dcache_line_size = in(reg) u64::try_from(min_dcache_line_size()).unwrap(),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100405 in("x0") fdt_address,
406 in("x30") payload_start,
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100407 options(noreturn),
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100408 );
409 };
410}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100411
Alan Stokesc3829f12023-06-02 15:02:23 +0100412/// # Safety
413///
414/// This must only be called once, since we are returning a mutable reference.
415/// The appended data region must be mapped.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100416unsafe fn get_appended_data_slice() -> &'static mut [u8] {
Pierre-Clément Tosiad1fc752023-05-31 16:56:56 +0000417 let range = memory::appended_payload_range();
Alan Stokesa0e42962023-04-14 17:59:50 +0100418 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
419 // objects.
Alice Wanga3931aa2023-07-05 12:52:09 +0000420 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 +0100421}
422
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100423enum AppendedPayload<'a> {
424 /// Configuration data.
425 Config(config::Config<'a>),
426 /// Deprecated raw BCC, as used in Android T.
427 LegacyBcc(&'a mut [u8]),
428}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100429
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100430impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100431 fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100432 // The borrow checker gets confused about the ownership of data (see inline comments) so we
433 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
434 // in v1.77) in https://users.rust-lang.org/t/78467.
435 let data_ptr = data as *mut [u8];
436
437 // Config::new() borrows data as mutable ...
438 match config::Config::new(data) {
439 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
440 Ok(valid) => Some(Self::Config(valid)),
441 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
442 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
443 // ... the borrow checker still complains about a second mutable ref without this.
444 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
445 let data: &'a mut _ = unsafe { &mut *data_ptr };
446
Alice Wangeacb7382023-06-05 12:53:54 +0000447 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000448 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
449 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
450 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000451 Err(e) => {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100452 error!("Invalid configuration data at {data_ptr:?}: {e}");
453 None
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000454 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000455 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100456 }
457
Alan Stokes65618332023-12-15 14:09:25 +0000458 fn get_entries(self) -> config::Entries<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100459 match self {
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000460 Self::Config(cfg) => cfg.get_entries(),
461 Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000462 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100463 }
464}