blob: 965d6b99342909d08a6006787df957b33147da97 [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;
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;
Jakob Vukalovic85a00d72023-04-20 09:51:10 +010022use crate::memory::{MemoryTracker, MEMORY};
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000023use crate::mmu;
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +000024use crate::rand;
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;
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -070028use hyp::{get_hypervisor, HypervisorCap};
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
56main!(start);
57
58/// Entry point for pVM firmware.
59pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
60 // Limitations in this function:
61 // - can't access non-pvmfw memory (only statically-mapped memory)
62 // - can't access MMIO (therefore, no logging)
63
64 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +000065 Ok(entry) => jump_to_payload(fdt_address, entry.try_into().unwrap()),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000066 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010067 }
68
69 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
70}
71
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000072struct MemorySlices<'a> {
73 fdt: &'a mut libfdt::Fdt,
74 kernel: &'a [u8],
75 ramdisk: Option<&'a [u8]>,
76}
77
78impl<'a> MemorySlices<'a> {
79 fn new(
80 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000081 kernel: usize,
82 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000083 memory: &mut MemoryTracker,
84 ) -> Result<Self, RebootReason> {
85 // SAFETY - SIZE_2MB is non-zero.
86 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
87 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
88 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
89 // overwrite with the template DT and apply the DTBO.
90 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
91 error!("Failed to allocate the FDT range: {e}");
92 RebootReason::InternalError
93 })?;
94
95 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
96 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
97 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
98 error!("Failed to spawn the FDT wrapper: {e}");
99 RebootReason::InvalidFdt
100 })?;
101
Jiyong Park6a8789a2023-03-21 14:50:59 +0900102 let info = fdt::sanitize_device_tree(fdt)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000103 debug!("Fdt passed validation!");
104
Jiyong Park6a8789a2023-03-21 14:50:59 +0900105 let memory_range = info.memory_range;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000106 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000107 memory.shrink(&memory_range).map_err(|_| {
108 error!("Failed to use memory range value from DT: {memory_range:#x?}");
109 RebootReason::InvalidFdt
110 })?;
111
Srivatsa Vaddagiri37713ec2023-04-20 04:04:08 -0700112 if !get_hypervisor().has_cap(HypervisorCap::DYNAMIC_MEM_SHARE) {
113 let range = info.swiotlb_info.fixed_range().ok_or_else(|| {
114 error!("Pre-shared pool range not specified in swiotlb node");
115 RebootReason::InvalidFdt
116 })?;
117
118 memory.init_shared_pool(range).map_err(|e| {
119 error!("Failed to initialize pre-shared pool {e}");
120 RebootReason::InvalidFdt
121 })?;
122 }
123
Jiyong Park6a8789a2023-03-21 14:50:59 +0900124 let kernel_range = if let Some(r) = info.kernel_range {
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000125 memory.alloc_range(&r).map_err(|e| {
126 error!("Failed to obtain the kernel range with DT range: {e}");
127 RebootReason::InternalError
128 })?
129 } else if cfg!(feature = "legacy") {
130 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
131
132 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
133 error!("Invalid kernel size: {kernel_size:#x}");
134 RebootReason::InvalidPayload
135 })?;
136
137 memory.alloc(kernel, kernel_size).map_err(|e| {
138 error!("Failed to obtain the kernel range with legacy range: {e}");
139 RebootReason::InternalError
140 })?
141 } else {
142 error!("Failed to locate the kernel from the DT");
143 return Err(RebootReason::InvalidPayload);
144 };
145
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000146 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
147 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000148 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000149
Jiyong Park6a8789a2023-03-21 14:50:59 +0900150 let ramdisk = if let Some(r) = info.initrd_range {
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000151 debug!("Located ramdisk at {r:?}");
152 let r = memory.alloc_range(&r).map_err(|e| {
153 error!("Failed to obtain the initrd range: {e}");
154 RebootReason::InvalidRamdisk
155 })?;
156
157 // SAFETY - The region was validated by memory to be in main memory, mapped, and
158 // not overlap.
159 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
160 } else {
161 info!("Couldn't locate the ramdisk from the device tree");
162 None
163 };
164
165 Ok(Self { fdt, kernel, ramdisk })
166 }
167}
168
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100169/// Sets up the environment for main() and wraps its result for start().
170///
171/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
172/// the assumption that its environment has been properly configured.
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000173fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<usize, RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100174 // Limitations in this function:
175 // - only access MMIO once (and while) it has been mapped and configured
176 // - only perform logging once the logger has been initialized
177 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100178
179 // SAFETY - This function should and will only be called once, here.
180 unsafe { heap::init() };
181
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100182 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100183
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100184 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
185 // builds that have tweaked the logger::init() call will actually attempt to log the message.
186
Alice Wang90e6f162023-04-17 13:49:45 +0000187 get_hypervisor().mmio_guard_init().map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100188 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100189 RebootReason::InternalError
190 })?;
191
Alice Wang90e6f162023-04-17 13:49:45 +0000192 get_hypervisor().mmio_guard_map(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100193 debug!("Failed to configure the UART: {e}");
194 RebootReason::InternalError
195 })?;
196
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100197 crypto::init();
198
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100199 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
200 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000201 let appended_data = unsafe { get_appended_data_slice() };
202
203 // Up to this point, we were using the built-in static (from .rodata) page tables.
204
205 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
206 error!("Failed to set up the dynamic page tables: {e}");
207 RebootReason::InternalError
208 })?;
209
210 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
211 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
212 page_table.map_device(&uart_range).map_err(|e| {
213 error!("Failed to remap the UART as a dynamic page table entry: {e}");
214 RebootReason::InternalError
215 })?;
216
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100217 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
218 // linker script prevents it from overlapping with other objects.
219 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
220 error!("No valid configuration found");
221 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000222 })?;
223
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000224 let (bcc_slice, debug_policy) = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100225
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000226 debug!("Activating dynamic page table...");
227 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
228 // aware of so activating it shouldn't have any visible effect.
229 unsafe { page_table.activate() };
230 debug!("... Success!");
231
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100232 MEMORY.lock().replace(MemoryTracker::new(page_table));
233 let slices = MemorySlices::new(fdt, payload, payload_size, MEMORY.lock().as_mut().unwrap())?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000234
Pierre-Clément Tosia59103d2023-02-02 14:46:55 +0000235 rand::init().map_err(|e| {
236 error!("Failed to initialize rand: {e}");
237 RebootReason::InternalError
238 })?;
239
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100240 // This wrapper allows main() to be blissfully ignorant of platform details.
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100241 crate::main(
242 slices.fdt,
243 slices.kernel,
244 slices.ramdisk,
245 bcc_slice,
246 debug_policy,
247 MEMORY.lock().as_mut().unwrap(),
248 )?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100249
Pierre-Clément Tosi8383c542022-11-01 14:07:29 +0000250 helpers::flushed_zeroize(bcc_slice);
Pierre-Clément Tosidb74cb12022-12-08 13:56:25 +0000251 helpers::flush(slices.fdt.as_slice());
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100252
Andrew Walbran19690632022-12-07 16:41:30 +0000253 info!("Expecting a bug making MMIO_GUARD_UNMAP return NOT_SUPPORTED on success");
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100254 MEMORY.lock().as_mut().unwrap().mmio_unmap_all().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000255 error!("Failed to unshare MMIO ranges: {e}");
256 RebootReason::InternalError
257 })?;
Alice Wang90e6f162023-04-17 13:49:45 +0000258 get_hypervisor().mmio_guard_unmap(console::BASE_ADDRESS).map_err(|e| {
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100259 error!("Failed to unshare the UART: {e}");
260 RebootReason::InternalError
261 })?;
Jakob Vukalovic85a00d72023-04-20 09:51:10 +0100262 MEMORY.lock().take().unwrap();
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100263
Pierre-Clément Tosib8e7cec2023-01-31 22:11:34 +0000264 Ok(slices.kernel.as_ptr() as usize)
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100265}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100266
267fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000268 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100269 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000270 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100271 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000272 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100273 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000274 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100275
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000276 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 +0100277
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000278 // Disable the exception vector, caches and page table and then jump to the payload at the
279 // given address, passing it the given FDT pointer.
280 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100281 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
282 unsafe {
283 asm!(
284 "msr sctlr_el1, {sctlr_el1_val}",
285 "isb",
286 "mov x1, xzr",
287 "mov x2, xzr",
288 "mov x3, xzr",
289 "mov x4, xzr",
290 "mov x5, xzr",
291 "mov x6, xzr",
292 "mov x7, xzr",
293 "mov x8, xzr",
294 "mov x9, xzr",
295 "mov x10, xzr",
296 "mov x11, xzr",
297 "mov x12, xzr",
298 "mov x13, xzr",
299 "mov x14, xzr",
300 "mov x15, xzr",
301 "mov x16, xzr",
302 "mov x17, xzr",
303 "mov x18, xzr",
304 "mov x19, xzr",
305 "mov x20, xzr",
306 "mov x21, xzr",
307 "mov x22, xzr",
308 "mov x23, xzr",
309 "mov x24, xzr",
310 "mov x25, xzr",
311 "mov x26, xzr",
312 "mov x27, xzr",
313 "mov x28, xzr",
314 "mov x29, xzr",
315 "msr ttbr0_el1, xzr",
316 "isb",
317 "dsb nsh",
318 "br x30",
319 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
320 in("x0") fdt_address,
321 in("x30") payload_start,
322 options(nomem, noreturn, nostack),
323 );
324 };
325}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100326
327unsafe fn get_appended_data_slice() -> &'static mut [u8] {
328 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
329 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
330 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
331
Alan Stokesa0e42962023-04-14 17:59:50 +0100332 // SAFETY: This region is mapped and the linker script prevents it from overlapping with other
333 // objects.
334 unsafe { slice::from_raw_parts_mut(base as *mut u8, size) }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100335}
336
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000337enum AppendedConfigType {
338 Valid,
339 Invalid,
340 NotFound,
341}
342
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100343enum AppendedPayload<'a> {
344 /// Configuration data.
345 Config(config::Config<'a>),
346 /// Deprecated raw BCC, as used in Android T.
347 LegacyBcc(&'a mut [u8]),
348}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100349
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100350impl<'a> AppendedPayload<'a> {
351 /// SAFETY - 'data' should respect the alignment of config::Header.
352 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
Alan Stokesa0e42962023-04-14 17:59:50 +0100353 // Safety: This fn has the same constraint as us.
354 match unsafe { Self::guess_config_type(data) } {
355 AppendedConfigType::Valid => {
356 // Safety: This fn has the same constraint as us.
357 let config = unsafe { config::Config::new(data) };
358 Some(Self::Config(config.unwrap()))
359 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000360 AppendedConfigType::NotFound if cfg!(feature = "legacy") => {
361 const BCC_SIZE: usize = helpers::SIZE_4KB;
362 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
363 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
364 }
365 _ => None,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100366 }
367 }
368
Alan Stokesa0e42962023-04-14 17:59:50 +0100369 /// SAFETY - 'data' should respect the alignment of config::Header.
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000370 unsafe fn guess_config_type(data: &mut [u8]) -> AppendedConfigType {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100371 // This function is necessary to prevent the borrow checker from getting confused
372 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
373 let addr = data.as_ptr();
Alan Stokesa0e42962023-04-14 17:59:50 +0100374
375 // Safety: This fn has the same constraint as us.
376 match unsafe { config::Config::new(data) } {
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000377 Err(config::Error::InvalidMagic) => {
378 warn!("No configuration data found at {addr:?}");
379 AppendedConfigType::NotFound
380 }
381 Err(e) => {
382 error!("Invalid configuration data at {addr:?}: {e}");
383 AppendedConfigType::Invalid
384 }
385 Ok(_) => AppendedConfigType::Valid,
386 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100387 }
388
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000389 fn get_entries(&mut self) -> (&mut [u8], Option<&mut [u8]>) {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100390 match self {
Pierre-Clément Tosiefe780c2023-02-21 21:36:30 +0000391 Self::Config(ref mut cfg) => cfg.get_entries(),
392 Self::LegacyBcc(ref mut bcc) => (bcc, None),
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000393 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100394 }
395}