blob: 4f309022b87a9ce7554c9a25556504df1cffbe6f [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;
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010019use crate::heap;
Pierre-Clément Tosia1d3ea32022-11-01 15:05:11 +000020use crate::helpers;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000021use crate::memory::MemoryTracker;
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010022use crate::mmio_guard;
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000023use crate::mmu;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010024use core::arch::asm;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000025use core::num::NonZeroUsize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010026use core::slice;
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +000027use dice::bcc::Handover;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010028use log::debug;
29use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000030use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010031use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010032use log::LevelFilter;
33use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010034
35#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000036pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010037 /// A malformed BCC was received.
38 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010039 /// An invalid configuration was appended to pvmfw.
40 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010041 /// An unexpected internal error happened.
42 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000043 /// The provided FDT was invalid.
44 InvalidFdt,
45 /// The provided payload was invalid.
46 InvalidPayload,
47 /// The provided ramdisk was invalid.
48 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000049 /// Failed to verify the payload.
50 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000051 /// DICE layering process failed.
52 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010053}
54
55main!(start);
56
57/// Entry point for pVM firmware.
58pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
59 // Limitations in this function:
60 // - can't access non-pvmfw memory (only statically-mapped memory)
61 // - can't access MMIO (therefore, no logging)
62
63 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
64 Ok(_) => jump_to_payload(fdt_address, payload_start),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000065 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010066 }
67
68 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
69}
70
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000071struct MemorySlices<'a> {
72 fdt: &'a mut libfdt::Fdt,
73 kernel: &'a [u8],
74 ramdisk: Option<&'a [u8]>,
75}
76
77impl<'a> MemorySlices<'a> {
78 fn new(
79 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000080 kernel: usize,
81 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000082 memory: &mut MemoryTracker,
83 ) -> Result<Self, RebootReason> {
84 // SAFETY - SIZE_2MB is non-zero.
85 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
86 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
87 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
88 // overwrite with the template DT and apply the DTBO.
89 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
90 error!("Failed to allocate the FDT range: {e}");
91 RebootReason::InternalError
92 })?;
93
94 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
95 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
96 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
97 error!("Failed to spawn the FDT wrapper: {e}");
98 RebootReason::InvalidFdt
99 })?;
100
101 debug!("Fdt passed validation!");
102
103 let memory_range = fdt
104 .memory()
105 .map_err(|e| {
106 error!("Failed to get /memory from the DT: {e}");
107 RebootReason::InvalidFdt
108 })?
109 .ok_or_else(|| {
110 error!("Node /memory was found empty");
111 RebootReason::InvalidFdt
112 })?
113 .next()
114 .ok_or_else(|| {
115 error!("Failed to read the memory size from the FDT");
116 RebootReason::InternalError
117 })?;
118
119 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
120
121 memory.shrink(&memory_range).map_err(|_| {
122 error!("Failed to use memory range value from DT: {memory_range:#x?}");
123 RebootReason::InvalidFdt
124 })?;
125
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000126 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
127 error!("Error while attempting to read the kernel range from the DT: {e}");
128 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000129 })?;
130
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000131 let kernel_range = if let Some(r) = kernel_range {
132 memory.alloc_range(&r).map_err(|e| {
133 error!("Failed to obtain the kernel range with DT range: {e}");
134 RebootReason::InternalError
135 })?
136 } else if cfg!(feature = "legacy") {
137 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
138
139 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
140 error!("Invalid kernel size: {kernel_size:#x}");
141 RebootReason::InvalidPayload
142 })?;
143
144 memory.alloc(kernel, kernel_size).map_err(|e| {
145 error!("Failed to obtain the kernel range with legacy range: {e}");
146 RebootReason::InternalError
147 })?
148 } else {
149 error!("Failed to locate the kernel from the DT");
150 return Err(RebootReason::InvalidPayload);
151 };
152
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000153 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
154 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000155 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000156
157 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
158 error!("An error occurred while locating the ramdisk in the device tree: {e}");
159 RebootReason::InternalError
160 })?;
161
162 let ramdisk = if let Some(r) = ramdisk_range {
163 debug!("Located ramdisk at {r:?}");
164 let r = memory.alloc_range(&r).map_err(|e| {
165 error!("Failed to obtain the initrd range: {e}");
166 RebootReason::InvalidRamdisk
167 })?;
168
169 // SAFETY - The region was validated by memory to be in main memory, mapped, and
170 // not overlap.
171 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
172 } else {
173 info!("Couldn't locate the ramdisk from the device tree");
174 None
175 };
176
177 Ok(Self { fdt, kernel, ramdisk })
178 }
179}
180
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000181/// Applies the debug policy device tree overlay to the pVM DT.
182///
183/// # Safety
184///
185/// When an error is returned by this function, the input `Fdt` should be discarded as it may have
186/// have been partially corrupted during the overlay application process.
187unsafe fn apply_debug_policy(
188 fdt: &mut libfdt::Fdt,
189 debug_policy: &mut [u8],
190) -> Result<(), RebootReason> {
191 let overlay = libfdt::Fdt::from_mut_slice(debug_policy).map_err(|e| {
192 error!("Failed to load the debug policy overlay: {e}");
193 RebootReason::InvalidConfig
194 })?;
195
196 fdt.unpack().map_err(|e| {
197 error!("Failed to unpack DT for debug policy: {e}");
198 RebootReason::InternalError
199 })?;
200
201 let fdt = fdt.apply_overlay(overlay).map_err(|e| {
202 error!("Failed to apply the debug policy overlay: {e}");
203 RebootReason::InvalidConfig
204 })?;
205
206 fdt.pack().map_err(|e| {
207 error!("Failed to re-pack DT after debug policy: {e}");
208 RebootReason::InternalError
209 })
210}
211
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100212/// Sets up the environment for main() and wraps its result for start().
213///
214/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
215/// the assumption that its environment has been properly configured.
216fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
217 // Limitations in this function:
218 // - only access MMIO once (and while) it has been mapped and configured
219 // - only perform logging once the logger has been initialized
220 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100221
222 // SAFETY - This function should and will only be called once, here.
223 unsafe { heap::init() };
224
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100225 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100226
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100227 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
228 // builds that have tweaked the logger::init() call will actually attempt to log the message.
229
230 mmio_guard::init().map_err(|e| {
231 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100232 RebootReason::InternalError
233 })?;
234
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100235 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
236 debug!("Failed to configure the UART: {e}");
237 RebootReason::InternalError
238 })?;
239
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100240 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
241 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000242 let appended_data = unsafe { get_appended_data_slice() };
243
244 // Up to this point, we were using the built-in static (from .rodata) page tables.
245
246 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
247 error!("Failed to set up the dynamic page tables: {e}");
248 RebootReason::InternalError
249 })?;
250
251 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
252 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
253 page_table.map_device(&uart_range).map_err(|e| {
254 error!("Failed to remap the UART as a dynamic page table entry: {e}");
255 RebootReason::InternalError
256 })?;
257
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100258 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
259 // linker script prevents it from overlapping with other objects.
260 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
261 error!("No valid configuration found");
262 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000263 })?;
264
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000265 let bcc_slice = appended.get_bcc_mut();
266 let bcc = Handover::new(bcc_slice).map_err(|e| {
267 error!("Invalid BCC Handover: {e:?}");
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100268 RebootReason::InvalidBcc
269 })?;
270
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000271 debug!("Activating dynamic page table...");
272 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
273 // aware of so activating it shouldn't have any visible effect.
274 unsafe { page_table.activate() };
275 debug!("... Success!");
276
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000277 let mut memory = MemoryTracker::new(page_table);
278 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
279
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100280 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000281 crate::main(slices.fdt, slices.kernel, slices.ramdisk, &bcc, &mut memory)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100282
Pierre-Clément Tosi8383c542022-11-01 14:07:29 +0000283 helpers::flushed_zeroize(bcc_slice);
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000284 helpers::flush(slices.fdt.as_slice());
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100285
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000286 if let Some(debug_policy) = appended.get_debug_policy() {
287 // SAFETY - As we `?` the result, there is no risk of re-using a bad `slices.fdt`.
288 unsafe { apply_debug_policy(slices.fdt, debug_policy) }?;
289 }
290
Andrew Walbran19690632022-12-07 16:41:30 +0000291 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
292 memory.mmio_unmap_all().map_err(|e| {
293 error!("Failed to unshare MMIO ranges: {e}");
294 RebootReason::InternalError
295 })?;
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100296 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
297 error!("Failed to unshare the UART: {e}");
298 RebootReason::InternalError
299 })?;
300
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100301 Ok(())
302}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100303
304fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000305 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100306 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000307 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100308 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000309 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100310 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000311 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100312
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000313 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 +0100314
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000315 // Disable the exception vector, caches and page table and then jump to the payload at the
316 // given address, passing it the given FDT pointer.
317 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100318 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
319 unsafe {
320 asm!(
321 "msr sctlr_el1, {sctlr_el1_val}",
322 "isb",
323 "mov x1, xzr",
324 "mov x2, xzr",
325 "mov x3, xzr",
326 "mov x4, xzr",
327 "mov x5, xzr",
328 "mov x6, xzr",
329 "mov x7, xzr",
330 "mov x8, xzr",
331 "mov x9, xzr",
332 "mov x10, xzr",
333 "mov x11, xzr",
334 "mov x12, xzr",
335 "mov x13, xzr",
336 "mov x14, xzr",
337 "mov x15, xzr",
338 "mov x16, xzr",
339 "mov x17, xzr",
340 "mov x18, xzr",
341 "mov x19, xzr",
342 "mov x20, xzr",
343 "mov x21, xzr",
344 "mov x22, xzr",
345 "mov x23, xzr",
346 "mov x24, xzr",
347 "mov x25, xzr",
348 "mov x26, xzr",
349 "mov x27, xzr",
350 "mov x28, xzr",
351 "mov x29, xzr",
352 "msr ttbr0_el1, xzr",
353 "isb",
354 "dsb nsh",
355 "br x30",
356 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
357 in("x0") fdt_address,
358 in("x30") payload_start,
359 options(nomem, noreturn, nostack),
360 );
361 };
362}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100363
364unsafe fn get_appended_data_slice() -> &'static mut [u8] {
365 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
366 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
367 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
368
369 slice::from_raw_parts_mut(base as *mut u8, size)
370}
371
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000372enum AppendedConfigType {
373 Valid,
374 Invalid,
375 NotFound,
376}
377
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100378enum AppendedPayload<'a> {
379 /// Configuration data.
380 Config(config::Config<'a>),
381 /// Deprecated raw BCC, as used in Android T.
382 LegacyBcc(&'a mut [u8]),
383}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100384
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100385impl<'a> AppendedPayload<'a> {
386 /// SAFETY - 'data' should respect the alignment of config::Header.
387 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000388 match Self::guess_config_type(data) {
389 AppendedConfigType::Valid => Some(Self::Config(config::Config::new(data).unwrap())),
390 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
391 const BCC_SIZE: usize = helpers::SIZE_4KB;
392 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
393 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
394 }
395 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100396 }
397 }
398
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000399 unsafe fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100400 // This function is necessary to prevent the borrow checker from getting confused
401 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
402 let addr = data.as_ptr();
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000403 match config::Config::new(data) {
404 Err(config::Error::InvalidMagic) => {
405 warn!("No configuration data found at {addr:?}");
406 AppendedConfigType::NotFound
407 }
408 Err(e) => {
409 error!("Invalid configuration data at {addr:?}: {e}");
410 AppendedConfigType::Invalid
411 }
412 Ok(_) => AppendedConfigType::Valid,
413 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100414 }
415
416 #[allow(dead_code)] // TODO(b/232900974)
417 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
418 match self {
419 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
420 Self::LegacyBcc(_) => None,
421 }
422 }
423
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000424 fn get_bcc_mut(&mut self) -> &mut [u8] {
425 match self {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100426 Self::LegacyBcc(ref mut bcc) => bcc,
427 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000428 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100429 }
430}