blob: ffbc4a8dc0f55063660a3e4d312755e62074933a [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;
Jaewan Kimba8929b2023-01-13 11:13:29 +090019use crate::debug_policy::{handle_debug_policy, DebugPolicyError};
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000020use crate::fdt;
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010021use crate::heap;
Pierre-Clément Tosia1d3ea32022-11-01 15:05:11 +000022use crate::helpers;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000023use crate::memory::MemoryTracker;
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010024use crate::mmio_guard;
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000025use crate::mmu;
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +000026use crate::rand;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010027use core::arch::asm;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000028use core::num::NonZeroUsize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010029use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010030use log::debug;
31use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000032use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010033use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010034use log::LevelFilter;
35use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010036
37#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000038pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010039 /// A malformed BCC was received.
40 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010041 /// An invalid configuration was appended to pvmfw.
42 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010043 /// An unexpected internal error happened.
44 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000045 /// The provided FDT was invalid.
46 InvalidFdt,
47 /// The provided payload was invalid.
48 InvalidPayload,
49 /// The provided ramdisk was invalid.
50 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000051 /// Failed to verify the payload.
52 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000053 /// DICE layering process failed.
54 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010055}
56
Jaewan Kimba8929b2023-01-13 11:13:29 +090057impl From<DebugPolicyError> for RebootReason {
58 fn from(error: DebugPolicyError) -> Self {
59 match error {
60 DebugPolicyError::Fdt(_, _) => RebootReason::InvalidFdt,
61 DebugPolicyError::DebugPolicyFdt(_, _) => RebootReason::InvalidConfig,
62 DebugPolicyError::OverlaidFdt(_, _) => RebootReason::InternalError,
63 }
64 }
65}
66
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010067main!(start);
68
69/// Entry point for pVM firmware.
70pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
71 // Limitations in this function:
72 // - can't access non-pvmfw memory (only statically-mapped memory)
73 // - can't access MMIO (therefore, no logging)
74
75 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +000076 Ok(entry) => jump_to_payload(fdt_address, entry.try_into().unwrap()),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000077 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010078 }
79
80 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
81}
82
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000083struct MemorySlices<'a> {
84 fdt: &'a mut libfdt::Fdt,
85 kernel: &'a [u8],
86 ramdisk: Option<&'a [u8]>,
87}
88
89impl<'a> MemorySlices<'a> {
90 fn new(
91 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000092 kernel: usize,
93 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000094 memory: &mut MemoryTracker,
95 ) -> Result<Self, RebootReason> {
96 // SAFETY - SIZE_2MB is non-zero.
97 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
98 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
99 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
100 // overwrite with the template DT and apply the DTBO.
101 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
102 error!("Failed to allocate the FDT range: {e}");
103 RebootReason::InternalError
104 })?;
105
106 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
107 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
108 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
109 error!("Failed to spawn the FDT wrapper: {e}");
110 RebootReason::InvalidFdt
111 })?;
112
Jiyong Park6a8789a2023-03-21 14:50:59 +0900113 let info = fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000114 debug!("Fdt passed validation!");
115
Jiyong Park6a8789a2023-03-21 14:50:59 +0900116 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000117 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000118 memory.shrink(&memory_range).map_err(|_| {
119 error!("Failed to use memory range value from DT: {memory_range:#x?}");
120 RebootReason::InvalidFdt
121 })?;
122
Jiyong Park6a8789a2023-03-21 14:50:59 +0900123 let kernel_range = if let Some(r) = info.kernel_range {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000124 memory.alloc_range(&r).map_err(|e| {
125 error!("Failed to obtain the kernel range with DT range: {e}");
126 RebootReason::InternalError
127 })?
128 } else if cfg!(feature = "legacy") {
129 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
130
131 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
132 error!("Invalid kernel size: {kernel_size:#x}");
133 RebootReason::InvalidPayload
134 })?;
135
136 memory.alloc(kernel, kernel_size).map_err(|e| {
137 error!("Failed to obtain the kernel range with legacy range: {e}");
138 RebootReason::InternalError
139 })?
140 } else {
141 error!("Failed to locate the kernel from the DT");
142 return Err(RebootReason::InvalidPayload);
143 };
144
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000145 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
146 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000147 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000148
Jiyong Park6a8789a2023-03-21 14:50:59 +0900149 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000150 debug!("Located ramdisk at {r:?}");
151 let r = memory.alloc_range(&r).map_err(|e| {
152 error!("Failed to obtain the initrd range: {e}");
153 RebootReason::InvalidRamdisk
154 })?;
155
156 // SAFETY - The region was validated by memory to be in main memory, mapped, and
157 // not overlap.
158 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
159 } else {
160 info!("Couldn't locate the ramdisk from the device tree");
161 None
162 };
163
164 Ok(Self { fdt, kernel, ramdisk })
165 }
166}
167
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100168/// Sets up the environment for main() and wraps its result for start().
169///
170/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
171/// the assumption that its environment has been properly configured.
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000172fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<usize, RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100173 // Limitations in this function:
174 // - only access MMIO once (and while) it has been mapped and configured
175 // - only perform logging once the logger has been initialized
176 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100177
178 // SAFETY - This function should and will only be called once, here.
179 unsafe { heap::init() };
180
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100181 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100182
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100183 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
184 // builds that have tweaked the logger::init() call will actually attempt to log the message.
185
186 mmio_guard::init().map_err(|e| {
187 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100188 RebootReason::InternalError
189 })?;
190
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100191 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
192 debug!("Failed to configure the UART: {e}");
193 RebootReason::InternalError
194 })?;
195
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100196 crypto::init();
197
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100198 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
199 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000200 let appended_data = unsafe { get_appended_data_slice() };
201
202 // Up to this point, we were using the built-in static (from .rodata) page tables.
203
204 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
205 error!("Failed to set up the dynamic page tables: {e}");
206 RebootReason::InternalError
207 })?;
208
209 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
210 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
211 page_table.map_device(&uart_range).map_err(|e| {
212 error!("Failed to remap the UART as a dynamic page table entry: {e}");
213 RebootReason::InternalError
214 })?;
215
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100216 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
217 // linker script prevents it from overlapping with other objects.
218 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
219 error!("No valid configuration found");
220 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000221 })?;
222
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000223 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100224
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000225 debug!("Activating dynamic page table...");
226 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
227 // aware of so activating it shouldn't have any visible effect.
228 unsafe { page_table.activate() };
229 debug!("... Success!");
230
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000231 let mut memory = MemoryTracker::new(page_table);
232 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
233
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000234 rand::init().map_err(|e| {
235 error!("Failed to initialize rand: {e}");
236 RebootReason::InternalError
237 })?;
238
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100239 // This wrapper allows main() to be blissfully ignorant of platform details.
Alice Wang843d8312023-02-15 09:47:06 +0000240 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, &mut memory)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100241
Pierre-Clément Tosi8383c542022-11-01 14:07:29 +0000242 helpers::flushed_zeroize(bcc_slice);
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000243 helpers::flush(slices.fdt.as_slice());
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100244
Jaewan Kimba8929b2023-01-13 11:13:29 +0900245 // SAFETY - As we `?` the result, there is no risk of using a bad `slices.fdt`.
246 unsafe {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000247 handle_debug_policy(slices.fdt, debug_policy).map_err(|e| {
Jaewan Kimba8929b2023-01-13 11:13:29 +0900248 error!("Unexpected error when handling debug policy: {e:?}");
249 RebootReason::from(e)
250 })?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000251 }
252
Andrew Walbran19690632022-12-07 16:41:30 +0000253 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
254 memory.mmio_unmap_all().map_err(|e| {
255 error!("Failed to unshare MMIO ranges: {e}");
256 RebootReason::InternalError
257 })?;
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100258 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
259 error!("Failed to unshare the UART: {e}");
260 RebootReason::InternalError
261 })?;
262
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000263 Ok(slices.kernel.as_ptr() as usize)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100264}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100265
266fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000267 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100268 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000269 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100270 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000271 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100272 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000273 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100274
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000275 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 +0100276
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000277 // Disable the exception vector, caches and page table and then jump to the payload at the
278 // given address, passing it the given FDT pointer.
279 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100280 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
281 unsafe {
282 asm!(
283 "msr sctlr_el1, {sctlr_el1_val}",
284 "isb",
285 "mov x1, xzr",
286 "mov x2, xzr",
287 "mov x3, xzr",
288 "mov x4, xzr",
289 "mov x5, xzr",
290 "mov x6, xzr",
291 "mov x7, xzr",
292 "mov x8, xzr",
293 "mov x9, xzr",
294 "mov x10, xzr",
295 "mov x11, xzr",
296 "mov x12, xzr",
297 "mov x13, xzr",
298 "mov x14, xzr",
299 "mov x15, xzr",
300 "mov x16, xzr",
301 "mov x17, xzr",
302 "mov x18, xzr",
303 "mov x19, xzr",
304 "mov x20, xzr",
305 "mov x21, xzr",
306 "mov x22, xzr",
307 "mov x23, xzr",
308 "mov x24, xzr",
309 "mov x25, xzr",
310 "mov x26, xzr",
311 "mov x27, xzr",
312 "mov x28, xzr",
313 "mov x29, xzr",
314 "msr ttbr0_el1, xzr",
315 "isb",
316 "dsb nsh",
317 "br x30",
318 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
319 in("x0") fdt_address,
320 in("x30") payload_start,
321 options(nomem, noreturn, nostack),
322 );
323 };
324}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100325
326unsafe fn get_appended_data_slice() -> &'static mut [u8] {
327 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
328 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
329 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
330
331 slice::from_raw_parts_mut(base as *mut u8, size)
332}
333
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000334enum AppendedConfigType {
335 Valid,
336 Invalid,
337 NotFound,
338}
339
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100340enum AppendedPayload<'a> {
341 /// Configuration data.
342 Config(config::Config<'a>),
343 /// Deprecated raw BCC, as used in Android T.
344 LegacyBcc(&'a mut [u8]),
345}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100346
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100347impl<'a> AppendedPayload<'a> {
348 /// SAFETY - 'data' should respect the alignment of config::Header.
349 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000350 match Self::guess_config_type(data) {
351 AppendedConfigType::Valid => Some(Self::Config(config::Config::new(data).unwrap())),
352 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
353 const BCC_SIZE: usize = helpers::SIZE_4KB;
354 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
355 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
356 }
357 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100358 }
359 }
360
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000361 unsafe fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100362 // This function is necessary to prevent the borrow checker from getting confused
363 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
364 let addr = data.as_ptr();
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000365 match config::Config::new(data) {
366 Err(config::Error::InvalidMagic) => {
367 warn!("No configuration data found at {addr:?}");
368 AppendedConfigType::NotFound
369 }
370 Err(e) => {
371 error!("Invalid configuration data at {addr:?}: {e}");
372 AppendedConfigType::Invalid
373 }
374 Ok(_) => AppendedConfigType::Valid,
375 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100376 }
377
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000378 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100379 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000380 Self::Config(ref mut cfg) => cfg.get_entries(),
381 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000382 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100383 }
384}