blob: 342c8ede9c060823ba6996bf82674c115bc2c5b9 [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)]
35enum 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,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010048}
49
50main!(start);
51
52/// Entry point for pVM firmware.
53pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
54 // Limitations in this function:
55 // - can't access non-pvmfw memory (only statically-mapped memory)
56 // - can't access MMIO (therefore, no logging)
57
58 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
59 Ok(_) => jump_to_payload(fdt_address, payload_start),
60 Err(_) => reboot(),
61 }
62
63 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
64}
65
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000066struct MemorySlices<'a> {
67 fdt: &'a mut libfdt::Fdt,
68 kernel: &'a [u8],
69 ramdisk: Option<&'a [u8]>,
70}
71
72impl<'a> MemorySlices<'a> {
73 fn new(
74 fdt: usize,
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +000075 kernel: usize,
76 kernel_size: usize,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000077 memory: &mut MemoryTracker,
78 ) -> Result<Self, RebootReason> {
79 // SAFETY - SIZE_2MB is non-zero.
80 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
81 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
82 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
83 // overwrite with the template DT and apply the DTBO.
84 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
85 error!("Failed to allocate the FDT range: {e}");
86 RebootReason::InternalError
87 })?;
88
89 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
90 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
91 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
92 error!("Failed to spawn the FDT wrapper: {e}");
93 RebootReason::InvalidFdt
94 })?;
95
96 debug!("Fdt passed validation!");
97
98 let memory_range = fdt
99 .memory()
100 .map_err(|e| {
101 error!("Failed to get /memory from the DT: {e}");
102 RebootReason::InvalidFdt
103 })?
104 .ok_or_else(|| {
105 error!("Node /memory was found empty");
106 RebootReason::InvalidFdt
107 })?
108 .next()
109 .ok_or_else(|| {
110 error!("Failed to read the memory size from the FDT");
111 RebootReason::InternalError
112 })?;
113
114 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
115
116 memory.shrink(&memory_range).map_err(|_| {
117 error!("Failed to use memory range value from DT: {memory_range:#x?}");
118 RebootReason::InvalidFdt
119 })?;
120
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000121 let kernel_range = fdt::kernel_range(fdt).map_err(|e| {
122 error!("Error while attempting to read the kernel range from the DT: {e}");
123 RebootReason::InvalidFdt
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000124 })?;
125
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000126 let kernel_range = if let Some(r) = kernel_range {
127 memory.alloc_range(&r).map_err(|e| {
128 error!("Failed to obtain the kernel range with DT range: {e}");
129 RebootReason::InternalError
130 })?
131 } else if cfg!(feature = "legacy") {
132 warn!("Failed to find the kernel range in the DT; falling back to legacy ABI");
133
134 let kernel_size = NonZeroUsize::new(kernel_size).ok_or_else(|| {
135 error!("Invalid kernel size: {kernel_size:#x}");
136 RebootReason::InvalidPayload
137 })?;
138
139 memory.alloc(kernel, kernel_size).map_err(|e| {
140 error!("Failed to obtain the kernel range with legacy range: {e}");
141 RebootReason::InternalError
142 })?
143 } else {
144 error!("Failed to locate the kernel from the DT");
145 return Err(RebootReason::InvalidPayload);
146 };
147
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000148 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
149 let kernel =
Pierre-Clément Tosic3811b82022-11-29 11:24:16 +0000150 unsafe { slice::from_raw_parts(kernel_range.start as *const u8, kernel_range.len()) };
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000151
152 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
153 error!("An error occurred while locating the ramdisk in the device tree: {e}");
154 RebootReason::InternalError
155 })?;
156
157 let ramdisk = if let Some(r) = ramdisk_range {
158 debug!("Located ramdisk at {r:?}");
159 let r = memory.alloc_range(&r).map_err(|e| {
160 error!("Failed to obtain the initrd range: {e}");
161 RebootReason::InvalidRamdisk
162 })?;
163
164 // SAFETY - The region was validated by memory to be in main memory, mapped, and
165 // not overlap.
166 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
167 } else {
168 info!("Couldn't locate the ramdisk from the device tree");
169 None
170 };
171
172 Ok(Self { fdt, kernel, ramdisk })
173 }
174}
175
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100176/// Sets up the environment for main() and wraps its result for start().
177///
178/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
179/// the assumption that its environment has been properly configured.
180fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
181 // Limitations in this function:
182 // - only access MMIO once (and while) it has been mapped and configured
183 // - only perform logging once the logger has been initialized
184 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100185
186 // SAFETY - This function should and will only be called once, here.
187 unsafe { heap::init() };
188
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100189 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100190
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100191 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
192 // builds that have tweaked the logger::init() call will actually attempt to log the message.
193
194 mmio_guard::init().map_err(|e| {
195 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100196 RebootReason::InternalError
197 })?;
198
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100199 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
200 debug!("Failed to configure the UART: {e}");
201 RebootReason::InternalError
202 })?;
203
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100204 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
205 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000206 let appended_data = unsafe { get_appended_data_slice() };
207
208 // Up to this point, we were using the built-in static (from .rodata) page tables.
209
210 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
211 error!("Failed to set up the dynamic page tables: {e}");
212 RebootReason::InternalError
213 })?;
214
215 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
216 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
217 page_table.map_device(&uart_range).map_err(|e| {
218 error!("Failed to remap the UART as a dynamic page table entry: {e}");
219 RebootReason::InternalError
220 })?;
221
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100222 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
223 // linker script prevents it from overlapping with other objects.
224 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
225 error!("No valid configuration found");
226 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000227 })?;
228
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100229 let bcc = appended.get_bcc_mut().ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100230 error!("Invalid BCC");
231 RebootReason::InvalidBcc
232 })?;
233
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000234 debug!("Activating dynamic page table...");
235 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
236 // aware of so activating it shouldn't have any visible effect.
237 unsafe { page_table.activate() };
238 debug!("... Success!");
239
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000240 let mut memory = MemoryTracker::new(page_table);
241 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
242
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100243 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000244 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc);
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100245
246 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100247
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100248 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
249 error!("Failed to unshare the UART: {e}");
250 RebootReason::InternalError
251 })?;
252
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100253 Ok(())
254}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100255
256fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
257 const SCTLR_EL1_RES1: usize = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
258 // Stage 1 instruction access cacheability is unaffected.
259 const SCTLR_EL1_I: usize = 0b1 << 12;
260 // SETEND instruction disabled at EL0 in aarch32 mode.
261 const SCTLR_EL1_SED: usize = 0b1 << 8;
262 // Various IT instructions are disabled at EL0 in aarch32 mode.
263 const SCTLR_EL1_ITD: usize = 0b1 << 7;
264
265 const SCTLR_EL1_VAL: usize = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
266
267 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
268 unsafe {
269 asm!(
270 "msr sctlr_el1, {sctlr_el1_val}",
271 "isb",
272 "mov x1, xzr",
273 "mov x2, xzr",
274 "mov x3, xzr",
275 "mov x4, xzr",
276 "mov x5, xzr",
277 "mov x6, xzr",
278 "mov x7, xzr",
279 "mov x8, xzr",
280 "mov x9, xzr",
281 "mov x10, xzr",
282 "mov x11, xzr",
283 "mov x12, xzr",
284 "mov x13, xzr",
285 "mov x14, xzr",
286 "mov x15, xzr",
287 "mov x16, xzr",
288 "mov x17, xzr",
289 "mov x18, xzr",
290 "mov x19, xzr",
291 "mov x20, xzr",
292 "mov x21, xzr",
293 "mov x22, xzr",
294 "mov x23, xzr",
295 "mov x24, xzr",
296 "mov x25, xzr",
297 "mov x26, xzr",
298 "mov x27, xzr",
299 "mov x28, xzr",
300 "mov x29, xzr",
301 "msr ttbr0_el1, xzr",
302 "isb",
303 "dsb nsh",
304 "br x30",
305 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
306 in("x0") fdt_address,
307 in("x30") payload_start,
308 options(nomem, noreturn, nostack),
309 );
310 };
311}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100312
313unsafe fn get_appended_data_slice() -> &'static mut [u8] {
314 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
315 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
316 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
317
318 slice::from_raw_parts_mut(base as *mut u8, size)
319}
320
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100321enum AppendedPayload<'a> {
322 /// Configuration data.
323 Config(config::Config<'a>),
324 /// Deprecated raw BCC, as used in Android T.
325 LegacyBcc(&'a mut [u8]),
326}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100327
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100328impl<'a> AppendedPayload<'a> {
329 /// SAFETY - 'data' should respect the alignment of config::Header.
330 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
331 if Self::is_valid_config(data) {
332 Some(Self::Config(config::Config::new(data).unwrap()))
333 } else if cfg!(feature = "legacy") {
334 const BCC_SIZE: usize = helpers::SIZE_4KB;
335 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
336 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
337 } else {
338 None
339 }
340 }
341
342 unsafe fn is_valid_config(data: &mut [u8]) -> bool {
343 // This function is necessary to prevent the borrow checker from getting confused
344 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
345 let addr = data.as_ptr();
346 config::Config::new(data)
347 .map_err(|e| warn!("Invalid configuration data at {addr:?}: {e}"))
348 .is_ok()
349 }
350
351 #[allow(dead_code)] // TODO(b/232900974)
352 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
353 match self {
354 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
355 Self::LegacyBcc(_) => None,
356 }
357 }
358
359 fn get_bcc_mut(&mut self) -> Option<&mut [u8]> {
360 let bcc = match self {
361 Self::LegacyBcc(ref mut bcc) => bcc,
362 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
363 };
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100364 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100365 Some(bcc)
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100366 }
367}