blob: ddde50adcd6e3c6e0f21d40f013026682c174302 [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;
Jaewan Kimba8929b2023-01-13 11:13:29 +090018use crate::debug_policy::{handle_debug_policy, DebugPolicyError};
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 Tosia0934c12022-11-25 20:54:11 +000022use crate::memory::MemoryTracker;
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010023use crate::mmio_guard;
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000024use crate::mmu;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010025use core::arch::asm;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000026use core::num::NonZeroUsize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010027use core::slice;
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
Jaewan Kimba8929b2023-01-13 11:13:29 +090055impl From<DebugPolicyError> for RebootReason {
56 fn from(error: DebugPolicyError) -> Self {
57 match error {
58 DebugPolicyError::Fdt(_, _) => RebootReason::InvalidFdt,
59 DebugPolicyError::DebugPolicyFdt(_, _) => RebootReason::InvalidConfig,
60 DebugPolicyError::OverlaidFdt(_, _) => RebootReason::InternalError,
61 }
62 }
63}
64
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010065main!(start);
66
67/// Entry point for pVM firmware.
68pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
69 // Limitations in this function:
70 // - can't access non-pvmfw memory (only statically-mapped memory)
71 // - can't access MMIO (therefore, no logging)
72
73 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +000074 Ok(entry) => jump_to_payload(fdt_address, entry.try_into().unwrap()),
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> {
88 fn new(
89 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000090 kernel: usize,
91 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000092 memory: &mut MemoryTracker,
93 ) -> Result<Self, RebootReason> {
94 // SAFETY - SIZE_2MB is non-zero.
95 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
96 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
97 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
98 // overwrite with the template DT and apply the DTBO.
99 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
100 error!("Failed to allocate the FDT range: {e}");
101 RebootReason::InternalError
102 })?;
103
104 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
105 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
106 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
107 error!("Failed to spawn the FDT wrapper: {e}");
108 RebootReason::InvalidFdt
109 })?;
110
111 debug!("Fdt passed validation!");
112
113 let memory_range = fdt
114 .memory()
115 .map_err(|e| {
116 error!("Failed to get /memory from the DT: {e}");
117 RebootReason::InvalidFdt
118 })?
119 .ok_or_else(|| {
120 error!("Node /memory was found empty");
121 RebootReason::InvalidFdt
122 })?
123 .next()
124 .ok_or_else(|| {
125 error!("Failed to read the memory size from the FDT");
126 RebootReason::InternalError
127 })?;
128
129 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
130
131 memory.shrink(&memory_range).map_err(|_| {
132 error!("Failed to use memory range value from DT: {memory_range:#x?}");
133 RebootReason::InvalidFdt
134 })?;
135
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000136 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
137 error!("Error while attempting to read the kernel range from the DT: {e}");
138 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000139 })?;
140
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000141 let kernel_range = if let Some(r) = kernel_range {
142 memory.alloc_range(&r).map_err(|e| {
143 error!("Failed to obtain the kernel range with DT range: {e}");
144 RebootReason::InternalError
145 })?
146 } else if cfg!(feature = "legacy") {
147 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
148
149 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
150 error!("Invalid kernel size: {kernel_size:#x}");
151 RebootReason::InvalidPayload
152 })?;
153
154 memory.alloc(kernel, kernel_size).map_err(|e| {
155 error!("Failed to obtain the kernel range with legacy range: {e}");
156 RebootReason::InternalError
157 })?
158 } else {
159 error!("Failed to locate the kernel from the DT");
160 return Err(RebootReason::InvalidPayload);
161 };
162
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000163 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
164 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000165 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000166
167 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
168 error!("An error occurred while locating the ramdisk in the device tree: {e}");
169 RebootReason::InternalError
170 })?;
171
172 let ramdisk = if let Some(r) = ramdisk_range {
173 debug!("Located ramdisk at {r:?}");
174 let r = memory.alloc_range(&r).map_err(|e| {
175 error!("Failed to obtain the initrd range: {e}");
176 RebootReason::InvalidRamdisk
177 })?;
178
179 // SAFETY - The region was validated by memory to be in main memory, mapped, and
180 // not overlap.
181 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
182 } else {
183 info!("Couldn't locate the ramdisk from the device tree");
184 None
185 };
186
187 Ok(Self { fdt, kernel, ramdisk })
188 }
189}
190
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100191/// Sets up the environment for main() and wraps its result for start().
192///
193/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
194/// the assumption that its environment has been properly configured.
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000195fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<usize, RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100196 // Limitations in this function:
197 // - only access MMIO once (and while) it has been mapped and configured
198 // - only perform logging once the logger has been initialized
199 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100200
201 // SAFETY - This function should and will only be called once, here.
202 unsafe { heap::init() };
203
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100204 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100205
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100206 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
207 // builds that have tweaked the logger::init() call will actually attempt to log the message.
208
209 mmio_guard::init().map_err(|e| {
210 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100211 RebootReason::InternalError
212 })?;
213
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100214 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
215 debug!("Failed to configure the UART: {e}");
216 RebootReason::InternalError
217 })?;
218
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100219 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
220 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000221 let appended_data = unsafe { get_appended_data_slice() };
222
223 // Up to this point, we were using the built-in static (from .rodata) page tables.
224
225 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
226 error!("Failed to set up the dynamic page tables: {e}");
227 RebootReason::InternalError
228 })?;
229
230 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
231 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
232 page_table.map_device(&uart_range).map_err(|e| {
233 error!("Failed to remap the UART as a dynamic page table entry: {e}");
234 RebootReason::InternalError
235 })?;
236
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100237 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
238 // linker script prevents it from overlapping with other objects.
239 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
240 error!("No valid configuration found");
241 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000242 })?;
243
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000244 let bcc_slice = appended.get_bcc_mut();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100245
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000246 debug!("Activating dynamic page table...");
247 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
248 // aware of so activating it shouldn't have any visible effect.
249 unsafe { page_table.activate() };
250 debug!("... Success!");
251
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000252 let mut memory = MemoryTracker::new(page_table);
253 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
254
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100255 // This wrapper allows main() to be blissfully ignorant of platform details.
Alice Wang843d8312023-02-15 09:47:06 +0000256 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, &mut memory)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100257
Pierre-Clément Tosi8383c542022-11-01 14:07:29 +0000258 helpers::flushed_zeroize(bcc_slice);
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000259 helpers::flush(slices.fdt.as_slice());
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100260
Jaewan Kimba8929b2023-01-13 11:13:29 +0900261 // SAFETY - As we `?` the result, there is no risk of using a bad `slices.fdt`.
262 unsafe {
263 handle_debug_policy(slices.fdt, appended.get_debug_policy()).map_err(|e| {
264 error!("Unexpected error when handling debug policy: {e:?}");
265 RebootReason::from(e)
266 })?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000267 }
268
Andrew Walbran19690632022-12-07 16:41:30 +0000269 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
270 memory.mmio_unmap_all().map_err(|e| {
271 error!("Failed to unshare MMIO ranges: {e}");
272 RebootReason::InternalError
273 })?;
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100274 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
275 error!("Failed to unshare the UART: {e}");
276 RebootReason::InternalError
277 })?;
278
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000279 Ok(slices.kernel.as_ptr() as usize)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100280}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100281
282fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000283 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100284 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000285 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100286 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000287 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100288 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000289 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100290
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000291 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 +0100292
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000293 // Disable the exception vector, caches and page table and then jump to the payload at the
294 // given address, passing it the given FDT pointer.
295 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100296 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
297 unsafe {
298 asm!(
299 "msr sctlr_el1, {sctlr_el1_val}",
300 "isb",
301 "mov x1, xzr",
302 "mov x2, xzr",
303 "mov x3, xzr",
304 "mov x4, xzr",
305 "mov x5, xzr",
306 "mov x6, xzr",
307 "mov x7, xzr",
308 "mov x8, xzr",
309 "mov x9, xzr",
310 "mov x10, xzr",
311 "mov x11, xzr",
312 "mov x12, xzr",
313 "mov x13, xzr",
314 "mov x14, xzr",
315 "mov x15, xzr",
316 "mov x16, xzr",
317 "mov x17, xzr",
318 "mov x18, xzr",
319 "mov x19, xzr",
320 "mov x20, xzr",
321 "mov x21, xzr",
322 "mov x22, xzr",
323 "mov x23, xzr",
324 "mov x24, xzr",
325 "mov x25, xzr",
326 "mov x26, xzr",
327 "mov x27, xzr",
328 "mov x28, xzr",
329 "mov x29, xzr",
330 "msr ttbr0_el1, xzr",
331 "isb",
332 "dsb nsh",
333 "br x30",
334 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
335 in("x0") fdt_address,
336 in("x30") payload_start,
337 options(nomem, noreturn, nostack),
338 );
339 };
340}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100341
342unsafe fn get_appended_data_slice() -> &'static mut [u8] {
343 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
344 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
345 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
346
347 slice::from_raw_parts_mut(base as *mut u8, size)
348}
349
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000350enum AppendedConfigType {
351 Valid,
352 Invalid,
353 NotFound,
354}
355
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100356enum AppendedPayload<'a> {
357 /// Configuration data.
358 Config(config::Config<'a>),
359 /// Deprecated raw BCC, as used in Android T.
360 LegacyBcc(&'a mut [u8]),
361}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100362
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100363impl<'a> AppendedPayload<'a> {
364 /// SAFETY - 'data' should respect the alignment of config::Header.
365 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000366 match Self::guess_config_type(data) {
367 AppendedConfigType::Valid => Some(Self::Config(config::Config::new(data).unwrap())),
368 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
369 const BCC_SIZE: usize = helpers::SIZE_4KB;
370 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
371 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
372 }
373 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100374 }
375 }
376
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000377 unsafe fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100378 // This function is necessary to prevent the borrow checker from getting confused
379 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
380 let addr = data.as_ptr();
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000381 match config::Config::new(data) {
382 Err(config::Error::InvalidMagic) => {
383 warn!("No configuration data found at {addr:?}");
384 AppendedConfigType::NotFound
385 }
386 Err(e) => {
387 error!("Invalid configuration data at {addr:?}: {e}");
388 AppendedConfigType::Invalid
389 }
390 Ok(_) => AppendedConfigType::Valid,
391 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100392 }
393
394 #[allow(dead_code)] // TODO(b/232900974)
395 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
396 match self {
397 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
398 Self::LegacyBcc(_) => None,
399 }
400 }
401
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000402 fn get_bcc_mut(&mut self) -> &mut [u8] {
403 match self {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100404 Self::LegacyBcc(ref mut bcc) => bcc,
405 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000406 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100407 }
408}