blob: efbb179106849e81e156848c246b970c49a4a5cd [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 Tosie8726e42022-10-17 13:35:27 +010027use log::debug;
28use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000029use log::info;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010030use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010031use log::LevelFilter;
32use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010033
34#[derive(Debug, Clone)]
Alice Wang0a688d22022-12-02 09:48:41 +000035pub(crate) enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010036 /// A malformed BCC was received.
37 InvalidBcc,
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010038 /// An invalid configuration was appended to pvmfw.
39 InvalidConfig,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010040 /// An unexpected internal error happened.
41 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000042 /// The provided FDT was invalid.
43 InvalidFdt,
44 /// The provided payload was invalid.
45 InvalidPayload,
46 /// The provided ramdisk was invalid.
47 InvalidRamdisk,
Alice Wang28cbcf12022-12-01 07:58:28 +000048 /// Failed to verify the payload.
49 PayloadVerificationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010050}
51
52main!(start);
53
54/// Entry point for pVM firmware.
55pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
56 // Limitations in this function:
57 // - can't access non-pvmfw memory (only statically-mapped memory)
58 // - can't access MMIO (therefore, no logging)
59
60 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
61 Ok(_) => jump_to_payload(fdt_address, payload_start),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000062 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010063 }
64
65 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
66}
67
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000068struct MemorySlices<'a> {
69 fdt: &'a mut libfdt::Fdt,
70 kernel: &'a [u8],
71 ramdisk: Option<&'a [u8]>,
72}
73
74impl<'a> MemorySlices<'a> {
75 fn new(
76 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000077 kernel: usize,
78 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000079 memory: &mut MemoryTracker,
80 ) -> Result<Self, RebootReason> {
81 // SAFETY - SIZE_2MB is non-zero.
82 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
83 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
84 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
85 // overwrite with the template DT and apply the DTBO.
86 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
87 error!("Failed to allocate the FDT range: {e}");
88 RebootReason::InternalError
89 })?;
90
91 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
92 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
93 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
94 error!("Failed to spawn the FDT wrapper: {e}");
95 RebootReason::InvalidFdt
96 })?;
97
98 debug!("Fdt passed validation!");
99
100 let memory_range = fdt
101 .memory()
102 .map_err(|e| {
103 error!("Failed to get /memory from the DT: {e}");
104 RebootReason::InvalidFdt
105 })?
106 .ok_or_else(|| {
107 error!("Node /memory was found empty");
108 RebootReason::InvalidFdt
109 })?
110 .next()
111 .ok_or_else(|| {
112 error!("Failed to read the memory size from the FDT");
113 RebootReason::InternalError
114 })?;
115
116 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
117
118 memory.shrink(&memory_range).map_err(|_| {
119 error!("Failed to use memory range value from DT: {memory_range:#x?}");
120 RebootReason::InvalidFdt
121 })?;
122
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000123 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
124 error!("Error while attempting to read the kernel range from the DT: {e}");
125 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000126 })?;
127
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000128 let kernel_range = if let Some(r) = kernel_range {
129 memory.alloc_range(&r).map_err(|e| {
130 error!("Failed to obtain the kernel range with DT range: {e}");
131 RebootReason::InternalError
132 })?
133 } else if cfg!(feature = "legacy") {
134 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
135
136 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
137 error!("Invalid kernel size: {kernel_size:#x}");
138 RebootReason::InvalidPayload
139 })?;
140
141 memory.alloc(kernel, kernel_size).map_err(|e| {
142 error!("Failed to obtain the kernel range with legacy range: {e}");
143 RebootReason::InternalError
144 })?
145 } else {
146 error!("Failed to locate the kernel from the DT");
147 return Err(RebootReason::InvalidPayload);
148 };
149
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000150 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
151 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000152 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000153
154 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
155 error!("An error occurred while locating the ramdisk in the device tree: {e}");
156 RebootReason::InternalError
157 })?;
158
159 let ramdisk = if let Some(r) = ramdisk_range {
160 debug!("Located ramdisk at {r:?}");
161 let r = memory.alloc_range(&r).map_err(|e| {
162 error!("Failed to obtain the initrd range: {e}");
163 RebootReason::InvalidRamdisk
164 })?;
165
166 // SAFETY - The region was validated by memory to be in main memory, mapped, and
167 // not overlap.
168 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
169 } else {
170 info!("Couldn't locate the ramdisk from the device tree");
171 None
172 };
173
174 Ok(Self { fdt, kernel, ramdisk })
175 }
176}
177
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100178/// Sets up the environment for main() and wraps its result for start().
179///
180/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
181/// the assumption that its environment has been properly configured.
182fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
183 // Limitations in this function:
184 // - only access MMIO once (and while) it has been mapped and configured
185 // - only perform logging once the logger has been initialized
186 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100187
188 // SAFETY - This function should and will only be called once, here.
189 unsafe { heap::init() };
190
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100191 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100192
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100193 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
194 // builds that have tweaked the logger::init() call will actually attempt to log the message.
195
196 mmio_guard::init().map_err(|e| {
197 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100198 RebootReason::InternalError
199 })?;
200
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100201 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
202 debug!("Failed to configure the UART: {e}");
203 RebootReason::InternalError
204 })?;
205
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100206 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
207 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000208 let appended_data = unsafe { get_appended_data_slice() };
209
210 // Up to this point, we were using the built-in static (from .rodata) page tables.
211
212 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
213 error!("Failed to set up the dynamic page tables: {e}");
214 RebootReason::InternalError
215 })?;
216
217 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
218 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
219 page_table.map_device(&uart_range).map_err(|e| {
220 error!("Failed to remap the UART as a dynamic page table entry: {e}");
221 RebootReason::InternalError
222 })?;
223
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100224 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
225 // linker script prevents it from overlapping with other objects.
226 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
227 error!("No valid configuration found");
228 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000229 })?;
230
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100231 let bcc = appended.get_bcc_mut().ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100232 error!("Invalid BCC");
233 RebootReason::InvalidBcc
234 })?;
235
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000236 debug!("Activating dynamic page table...");
237 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
238 // aware of so activating it shouldn't have any visible effect.
239 unsafe { page_table.activate() };
240 debug!("... Success!");
241
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000242 let mut memory = MemoryTracker::new(page_table);
243 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
244
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100245 // This wrapper allows main() to be blissfully ignorant of platform details.
Alice Wang0a688d22022-12-02 09:48:41 +0000246 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc)?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100247
248 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100249
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100250 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
251 error!("Failed to unshare the UART: {e}");
252 RebootReason::InternalError
253 })?;
254
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100255 Ok(())
256}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100257
258fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000259 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100260 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000261 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100262 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000263 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100264 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000265 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100266
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000267 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 +0100268
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000269 // Disable the exception vector, caches and page table and then jump to the payload at the
270 // given address, passing it the given FDT pointer.
271 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100272 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
273 unsafe {
274 asm!(
275 "msr sctlr_el1, {sctlr_el1_val}",
276 "isb",
277 "mov x1, xzr",
278 "mov x2, xzr",
279 "mov x3, xzr",
280 "mov x4, xzr",
281 "mov x5, xzr",
282 "mov x6, xzr",
283 "mov x7, xzr",
284 "mov x8, xzr",
285 "mov x9, xzr",
286 "mov x10, xzr",
287 "mov x11, xzr",
288 "mov x12, xzr",
289 "mov x13, xzr",
290 "mov x14, xzr",
291 "mov x15, xzr",
292 "mov x16, xzr",
293 "mov x17, xzr",
294 "mov x18, xzr",
295 "mov x19, xzr",
296 "mov x20, xzr",
297 "mov x21, xzr",
298 "mov x22, xzr",
299 "mov x23, xzr",
300 "mov x24, xzr",
301 "mov x25, xzr",
302 "mov x26, xzr",
303 "mov x27, xzr",
304 "mov x28, xzr",
305 "mov x29, xzr",
306 "msr ttbr0_el1, xzr",
307 "isb",
308 "dsb nsh",
309 "br x30",
310 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
311 in("x0") fdt_address,
312 in("x30") payload_start,
313 options(nomem, noreturn, nostack),
314 );
315 };
316}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100317
318unsafe fn get_appended_data_slice() -> &'static mut [u8] {
319 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
320 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
321 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
322
323 slice::from_raw_parts_mut(base as *mut u8, size)
324}
325
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100326enum AppendedPayload<'a> {
327 /// Configuration data.
328 Config(config::Config<'a>),
329 /// Deprecated raw BCC, as used in Android T.
330 LegacyBcc(&'a mut [u8]),
331}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100332
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100333impl<'a> AppendedPayload<'a> {
334 /// SAFETY - 'data' should respect the alignment of config::Header.
335 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
336 if Self::is_valid_config(data) {
337 Some(Self::Config(config::Config::new(data).unwrap()))
338 } else if cfg!(feature = "legacy") {
339 const BCC_SIZE: usize = helpers::SIZE_4KB;
340 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
341 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
342 } else {
343 None
344 }
345 }
346
347 unsafe fn is_valid_config(data: &mut [u8]) -> bool {
348 // This function is necessary to prevent the borrow checker from getting confused
349 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
350 let addr = data.as_ptr();
351 config::Config::new(data)
352 .map_err(|e| warn!("Invalid configuration data at {addr:?}: {e}"))
353 .is_ok()
354 }
355
356 #[allow(dead_code)] // TODO(b/232900974)
357 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
358 match self {
359 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
360 Self::LegacyBcc(_) => None,
361 }
362 }
363
364 fn get_bcc_mut(&mut self) -> Option<&mut [u8]> {
365 let bcc = match self {
366 Self::LegacyBcc(ref mut bcc) => bcc,
367 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
368 };
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100369 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100370 Some(bcc)
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100371 }
372}