blob: bffc14038332e10b155b30d473a3c3bca8edcc69 [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 Tosi5bbfca52022-10-21 12:14:35 +010051}
52
53main!(start);
54
55/// Entry point for pVM firmware.
56pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
57 // Limitations in this function:
58 // - can't access non-pvmfw memory (only statically-mapped memory)
59 // - can't access MMIO (therefore, no logging)
60
61 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
62 Ok(_) => jump_to_payload(fdt_address, payload_start),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000063 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010064 }
65
66 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
67}
68
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000069struct MemorySlices<'a> {
70 fdt: &'a mut libfdt::Fdt,
71 kernel: &'a [u8],
72 ramdisk: Option<&'a [u8]>,
73}
74
75impl<'a> MemorySlices<'a> {
76 fn new(
77 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000078 kernel: usize,
79 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000080 memory: &mut MemoryTracker,
81 ) -> Result<Self, RebootReason> {
82 // SAFETY - SIZE_2MB is non-zero.
83 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
84 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
85 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
86 // overwrite with the template DT and apply the DTBO.
87 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
88 error!("Failed to allocate the FDT range: {e}");
89 RebootReason::InternalError
90 })?;
91
92 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
93 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
94 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
95 error!("Failed to spawn the FDT wrapper: {e}");
96 RebootReason::InvalidFdt
97 })?;
98
99 debug!("Fdt passed validation!");
100
101 let memory_range = fdt
102 .memory()
103 .map_err(|e| {
104 error!("Failed to get /memory from the DT: {e}");
105 RebootReason::InvalidFdt
106 })?
107 .ok_or_else(|| {
108 error!("Node /memory was found empty");
109 RebootReason::InvalidFdt
110 })?
111 .next()
112 .ok_or_else(|| {
113 error!("Failed to read the memory size from the FDT");
114 RebootReason::InternalError
115 })?;
116
117 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
118
119 memory.shrink(&memory_range).map_err(|_| {
120 error!("Failed to use memory range value from DT: {memory_range:#x?}");
121 RebootReason::InvalidFdt
122 })?;
123
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000124 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
125 error!("Error while attempting to read the kernel range from the DT: {e}");
126 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000127 })?;
128
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000129 let kernel_range = if let Some(r) = kernel_range {
130 memory.alloc_range(&r).map_err(|e| {
131 error!("Failed to obtain the kernel range with DT range: {e}");
132 RebootReason::InternalError
133 })?
134 } else if cfg!(feature = "legacy") {
135 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
136
137 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
138 error!("Invalid kernel size: {kernel_size:#x}");
139 RebootReason::InvalidPayload
140 })?;
141
142 memory.alloc(kernel, kernel_size).map_err(|e| {
143 error!("Failed to obtain the kernel range with legacy range: {e}");
144 RebootReason::InternalError
145 })?
146 } else {
147 error!("Failed to locate the kernel from the DT");
148 return Err(RebootReason::InvalidPayload);
149 };
150
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000151 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
152 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000153 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000154
155 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
156 error!("An error occurred while locating the ramdisk in the device tree: {e}");
157 RebootReason::InternalError
158 })?;
159
160 let ramdisk = if let Some(r) = ramdisk_range {
161 debug!("Located ramdisk at {r:?}");
162 let r = memory.alloc_range(&r).map_err(|e| {
163 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.
183fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
184 // Limitations in this function:
185 // - only access MMIO once (and while) it has been mapped and configured
186 // - only perform logging once the logger has been initialized
187 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100188
189 // SAFETY - This function should and will only be called once, here.
190 unsafe { heap::init() };
191
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
197 mmio_guard::init().map_err(|e| {
198 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100199 RebootReason::InternalError
200 })?;
201
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100202 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
203 debug!("Failed to configure the UART: {e}");
204 RebootReason::InternalError
205 })?;
206
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100207 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
208 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000209 let appended_data = unsafe { get_appended_data_slice() };
210
211 // Up to this point, we were using the built-in static (from .rodata) page tables.
212
213 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
214 error!("Failed to set up the dynamic page tables: {e}");
215 RebootReason::InternalError
216 })?;
217
218 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
219 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
220 page_table.map_device(&uart_range).map_err(|e| {
221 error!("Failed to remap the UART as a dynamic page table entry: {e}");
222 RebootReason::InternalError
223 })?;
224
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100225 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
226 // linker script prevents it from overlapping with other objects.
227 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
228 error!("No valid configuration found");
229 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000230 })?;
231
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000232 let bcc_slice = appended.get_bcc_mut();
233 let bcc = Handover::new(bcc_slice).map_err(|e| {
234 error!("Invalid BCC Handover: {e:?}");
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100235 RebootReason::InvalidBcc
236 })?;
237
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000238 debug!("Activating dynamic page table...");
239 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
240 // aware of so activating it shouldn't have any visible effect.
241 unsafe { page_table.activate() };
242 debug!("... Success!");
243
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000244 let mut memory = MemoryTracker::new(page_table);
245 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
246
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100247 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000248 crate::main(slices.fdt, slices.kernel, slices.ramdisk, &bcc, &mut memory)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100249
250 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100251
Andrew Walbran19690632022-12-07 16:41:30 +0000252 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
253 memory.mmio_unmap_all().map_err(|e| {
254 error!("Failed to unshare MMIO ranges: {e}");
255 RebootReason::InternalError
256 })?;
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100257 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
258 error!("Failed to unshare the UART: {e}");
259 RebootReason::InternalError
260 })?;
261
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100262 Ok(())
263}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100264
265fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000266 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100267 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000268 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100269 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000270 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100271 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000272 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100273
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000274 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 +0100275
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000276 // Disable the exception vector, caches and page table and then jump to the payload at the
277 // given address, passing it the given FDT pointer.
278 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100279 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
280 unsafe {
281 asm!(
282 "msr sctlr_el1, {sctlr_el1_val}",
283 "isb",
284 "mov x1, xzr",
285 "mov x2, xzr",
286 "mov x3, xzr",
287 "mov x4, xzr",
288 "mov x5, xzr",
289 "mov x6, xzr",
290 "mov x7, xzr",
291 "mov x8, xzr",
292 "mov x9, xzr",
293 "mov x10, xzr",
294 "mov x11, xzr",
295 "mov x12, xzr",
296 "mov x13, xzr",
297 "mov x14, xzr",
298 "mov x15, xzr",
299 "mov x16, xzr",
300 "mov x17, xzr",
301 "mov x18, xzr",
302 "mov x19, xzr",
303 "mov x20, xzr",
304 "mov x21, xzr",
305 "mov x22, xzr",
306 "mov x23, xzr",
307 "mov x24, xzr",
308 "mov x25, xzr",
309 "mov x26, xzr",
310 "mov x27, xzr",
311 "mov x28, xzr",
312 "mov x29, xzr",
313 "msr ttbr0_el1, xzr",
314 "isb",
315 "dsb nsh",
316 "br x30",
317 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
318 in("x0") fdt_address,
319 in("x30") payload_start,
320 options(nomem, noreturn, nostack),
321 );
322 };
323}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100324
325unsafe fn get_appended_data_slice() -> &'static mut [u8] {
326 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
327 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
328 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
329
330 slice::from_raw_parts_mut(base as *mut u8, size)
331}
332
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100333enum AppendedPayload<'a> {
334 /// Configuration data.
335 Config(config::Config<'a>),
336 /// Deprecated raw BCC, as used in Android T.
337 LegacyBcc(&'a mut [u8]),
338}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100339
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100340impl<'a> AppendedPayload<'a> {
341 /// SAFETY - 'data' should respect the alignment of config::Header.
342 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
343 if Self::is_valid_config(data) {
344 Some(Self::Config(config::Config::new(data).unwrap()))
345 } else if cfg!(feature = "legacy") {
346 const BCC_SIZE: usize = helpers::SIZE_4KB;
347 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
348 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
349 } else {
350 None
351 }
352 }
353
354 unsafe fn is_valid_config(data: &mut [u8]) -> bool {
355 // This function is necessary to prevent the borrow checker from getting confused
356 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
357 let addr = data.as_ptr();
358 config::Config::new(data)
359 .map_err(|e| warn!("Invalid configuration data at {addr:?}: {e}"))
360 .is_ok()
361 }
362
363 #[allow(dead_code)] // TODO(b/232900974)
364 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
365 match self {
366 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
367 Self::LegacyBcc(_) => None,
368 }
369 }
370
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000371 fn get_bcc_mut(&mut self) -> &mut [u8] {
372 match self {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100373 Self::LegacyBcc(ref mut bcc) => bcc,
374 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000375 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100376 }
377}