blob: 89f263714008ee39dab7c65eb995292dd141a324 [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 Tosia59103d2023-02-02 14:46:55 +000025use crate::rand;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010026use core::arch::asm;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000027use core::num::NonZeroUsize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010028use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010029use log::debug;
30use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000031use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010032use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010033use log::LevelFilter;
34use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010035
36#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000037pub enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010038 /// A malformed BCC was received.
39 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010040 /// An invalid configuration was appended to pvmfw.
41 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010042 /// An unexpected internal error happened.
43 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000044 /// The provided FDT was invalid.
45 InvalidFdt,
46 /// The provided payload was invalid.
47 InvalidPayload,
48 /// The provided ramdisk was invalid.
49 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000050 /// Failed to verify the payload.
51 PayloadVerificationError,
Pierre-Clément Tosi4f4f5eb2022-12-08 14:31:42 +000052 /// DICE layering process failed.
53 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010054}
55
Jaewan Kimba8929b2023-01-13 11:13:29 +090056impl From<DebugPolicyError> for RebootReason {
57 fn from(error: DebugPolicyError) -> Self {
58 match error {
59 DebugPolicyError::Fdt(_, _) => RebootReason::InvalidFdt,
60 DebugPolicyError::DebugPolicyFdt(_, _) => RebootReason::InvalidConfig,
61 DebugPolicyError::OverlaidFdt(_, _) => RebootReason::InternalError,
62 }
63 }
64}
65
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010066main!(start);
67
68/// Entry point for pVM firmware.
69pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
70 // Limitations in this function:
71 // - can't access non-pvmfw memory (only statically-mapped memory)
72 // - can't access MMIO (therefore, no logging)
73
74 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +000075 Ok(entry) => jump_to_payload(fdt_address, entry.try_into().unwrap()),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000076 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010077 }
78
79 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
80}
81
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000082struct MemorySlices<'a> {
83 fdt: &'a mut libfdt::Fdt,
84 kernel: &'a [u8],
85 ramdisk: Option<&'a [u8]>,
86}
87
88impl<'a> MemorySlices<'a> {
89 fn new(
90 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000091 kernel: usize,
92 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000093 memory: &mut MemoryTracker,
94 ) -> Result<Self, RebootReason> {
95 // SAFETY - SIZE_2MB is non-zero.
96 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
97 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
98 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
99 // overwrite with the template DT and apply the DTBO.
100 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
101 error!("Failed to allocate the FDT range: {e}");
102 RebootReason::InternalError
103 })?;
104
105 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
106 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
107 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
108 error!("Failed to spawn the FDT wrapper: {e}");
109 RebootReason::InvalidFdt
110 })?;
111
Jiyong Park83316122023-03-21 09:39:39 +0900112 fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000113 debug!("Fdt passed validation!");
114
115 let memory_range = fdt
116 .memory()
117 .map_err(|e| {
118 error!("Failed to get /memory from the DT: {e}");
119 RebootReason::InvalidFdt
120 })?
121 .ok_or_else(|| {
122 error!("Node /memory was found empty");
123 RebootReason::InvalidFdt
124 })?
125 .next()
126 .ok_or_else(|| {
127 error!("Failed to read the memory size from the FDT");
128 RebootReason::InternalError
129 })?;
130
131 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
132
133 memory.shrink(&memory_range).map_err(|_| {
134 error!("Failed to use memory range value from DT: {memory_range:#x?}");
135 RebootReason::InvalidFdt
136 })?;
137
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000138 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
139 error!("Error while attempting to read the kernel range from the DT: {e}");
140 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000141 })?;
142
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000143 let kernel_range = if let Some(r) = kernel_range {
144 memory.alloc_range(&r).map_err(|e| {
145 error!("Failed to obtain the kernel range with DT range: {e}");
146 RebootReason::InternalError
147 })?
148 } else if cfg!(feature = "legacy") {
149 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
150
151 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
152 error!("Invalid kernel size: {kernel_size:#x}");
153 RebootReason::InvalidPayload
154 })?;
155
156 memory.alloc(kernel, kernel_size).map_err(|e| {
157 error!("Failed to obtain the kernel range with legacy range: {e}");
158 RebootReason::InternalError
159 })?
160 } else {
161 error!("Failed to locate the kernel from the DT");
162 return Err(RebootReason::InvalidPayload);
163 };
164
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000165 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
166 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000167 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000168
169 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
170 error!("An error occurred while locating the ramdisk in the device tree: {e}");
171 RebootReason::InternalError
172 })?;
173
174 let ramdisk = if let Some(r) = ramdisk_range {
175 debug!("Located ramdisk at {r:?}");
176 let r = memory.alloc_range(&r).map_err(|e| {
177 error!("Failed to obtain the initrd range: {e}");
178 RebootReason::InvalidRamdisk
179 })?;
180
181 // SAFETY - The region was validated by memory to be in main memory, mapped, and
182 // not overlap.
183 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
184 } else {
185 info!("Couldn't locate the ramdisk from the device tree");
186 None
187 };
188
189 Ok(Self { fdt, kernel, ramdisk })
190 }
191}
192
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100193/// Sets up the environment for main() and wraps its result for start().
194///
195/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
196/// the assumption that its environment has been properly configured.
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000197fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<usize, RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100198 // Limitations in this function:
199 // - only access MMIO once (and while) it has been mapped and configured
200 // - only perform logging once the logger has been initialized
201 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100202
203 // SAFETY - This function should and will only be called once, here.
204 unsafe { heap::init() };
205
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100206 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100207
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100208 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
209 // builds that have tweaked the logger::init() call will actually attempt to log the message.
210
211 mmio_guard::init().map_err(|e| {
212 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100213 RebootReason::InternalError
214 })?;
215
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100216 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
217 debug!("Failed to configure the UART: {e}");
218 RebootReason::InternalError
219 })?;
220
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100221 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
222 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000223 let appended_data = unsafe { get_appended_data_slice() };
224
225 // Up to this point, we were using the built-in static (from .rodata) page tables.
226
227 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
228 error!("Failed to set up the dynamic page tables: {e}");
229 RebootReason::InternalError
230 })?;
231
232 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
233 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
234 page_table.map_device(&uart_range).map_err(|e| {
235 error!("Failed to remap the UART as a dynamic page table entry: {e}");
236 RebootReason::InternalError
237 })?;
238
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100239 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
240 // linker script prevents it from overlapping with other objects.
241 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
242 error!("No valid configuration found");
243 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000244 })?;
245
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000246 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100247
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000248 debug!("Activating dynamic page table...");
249 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
250 // aware of so activating it shouldn't have any visible effect.
251 unsafe { page_table.activate() };
252 debug!("... Success!");
253
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000254 let mut memory = MemoryTracker::new(page_table);
255 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
256
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000257 rand::init().map_err(|e| {
258 error!("Failed to initialize rand: {e}");
259 RebootReason::InternalError
260 })?;
261
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100262 // This wrapper allows main() to be blissfully ignorant of platform details.
Alice Wang843d8312023-02-15 09:47:06 +0000263 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc_slice, &mut memory)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100264
Pierre-Clément Tosi8383c542022-11-01 14:07:29 +0000265 helpers::flushed_zeroize(bcc_slice);
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000266 helpers::flush(slices.fdt.as_slice());
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100267
Jaewan Kimba8929b2023-01-13 11:13:29 +0900268 // SAFETY - As we `?` the result, there is no risk of using a bad `slices.fdt`.
269 unsafe {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000270 handle_debug_policy(slices.fdt, debug_policy).map_err(|e| {
Jaewan Kimba8929b2023-01-13 11:13:29 +0900271 error!("Unexpected error when handling debug policy: {e:?}");
272 RebootReason::from(e)
273 })?;
Pierre-Clément Tosi90e19352022-11-21 17:11:48 +0000274 }
275
Andrew Walbran19690632022-12-07 16:41:30 +0000276 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
277 memory.mmio_unmap_all().map_err(|e| {
278 error!("Failed to unshare MMIO ranges: {e}");
279 RebootReason::InternalError
280 })?;
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100281 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
282 error!("Failed to unshare the UART: {e}");
283 RebootReason::InternalError
284 })?;
285
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000286 Ok(slices.kernel.as_ptr() as usize)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100287}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100288
289fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
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 Tosi6c0d48b2022-11-07 11:00:32 +0000300 // Disable the exception vector, caches and page table and then jump to the payload at the
301 // given address, passing it the given FDT pointer.
302 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100303 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
304 unsafe {
305 asm!(
306 "msr sctlr_el1, {sctlr_el1_val}",
307 "isb",
308 "mov x1, xzr",
309 "mov x2, xzr",
310 "mov x3, xzr",
311 "mov x4, xzr",
312 "mov x5, xzr",
313 "mov x6, xzr",
314 "mov x7, xzr",
315 "mov x8, xzr",
316 "mov x9, xzr",
317 "mov x10, xzr",
318 "mov x11, xzr",
319 "mov x12, xzr",
320 "mov x13, xzr",
321 "mov x14, xzr",
322 "mov x15, xzr",
323 "mov x16, xzr",
324 "mov x17, xzr",
325 "mov x18, xzr",
326 "mov x19, xzr",
327 "mov x20, xzr",
328 "mov x21, xzr",
329 "mov x22, xzr",
330 "mov x23, xzr",
331 "mov x24, xzr",
332 "mov x25, xzr",
333 "mov x26, xzr",
334 "mov x27, xzr",
335 "mov x28, xzr",
336 "mov x29, xzr",
337 "msr ttbr0_el1, xzr",
338 "isb",
339 "dsb nsh",
340 "br x30",
341 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
342 in("x0") fdt_address,
343 in("x30") payload_start,
344 options(nomem, noreturn, nostack),
345 );
346 };
347}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100348
349unsafe fn get_appended_data_slice() -> &'static mut [u8] {
350 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
351 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
352 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
353
354 slice::from_raw_parts_mut(base as *mut u8, size)
355}
356
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000357enum AppendedConfigType {
358 Valid,
359 Invalid,
360 NotFound,
361}
362
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100363enum AppendedPayload<'a> {
364 /// Configuration data.
365 Config(config::Config<'a>),
366 /// Deprecated raw BCC, as used in Android T.
367 LegacyBcc(&'a mut [u8]),
368}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100369
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100370impl<'a> AppendedPayload<'a> {
371 /// SAFETY - 'data' should respect the alignment of config::Header.
372 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000373 match Self::guess_config_type(data) {
374 AppendedConfigType::Valid => Some(Self::Config(config::Config::new(data).unwrap())),
375 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
376 const BCC_SIZE: usize = helpers::SIZE_4KB;
377 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
378 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
379 }
380 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100381 }
382 }
383
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000384 unsafe fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100385 // This function is necessary to prevent the borrow checker from getting confused
386 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
387 let addr = data.as_ptr();
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000388 match config::Config::new(data) {
389 Err(config::Error::InvalidMagic) => {
390 warn!("No configuration data found at {addr:?}");
391 AppendedConfigType::NotFound
392 }
393 Err(e) => {
394 error!("Invalid configuration data at {addr:?}: {e}");
395 AppendedConfigType::Invalid
396 }
397 Ok(_) => AppendedConfigType::Valid,
398 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100399 }
400
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000401 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100402 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000403 Self::Config(ref mut cfg) => cfg.get_entries(),
404 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000405 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100406 }
407}