blob: a274210c28e3758aba2aa776b7734174cc02103c [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,
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),
62 Err(_) => reboot(),
63 }
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,
77 payload: usize,
78 payload_size: usize,
79 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
123 let payload_size = NonZeroUsize::new(payload_size).ok_or_else(|| {
124 error!("Invalid payload size: {payload_size:#x}");
125 RebootReason::InvalidPayload
126 })?;
127
128 let payload_range = memory.alloc(payload, payload_size).map_err(|e| {
129 error!("Failed to obtain the payload range: {e}");
130 RebootReason::InternalError
131 })?;
132 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
133 let kernel =
134 unsafe { slice::from_raw_parts(payload_range.start as *const u8, payload_range.len()) };
135
136 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
137 error!("An error occurred while locating the ramdisk in the device tree: {e}");
138 RebootReason::InternalError
139 })?;
140
141 let ramdisk = if let Some(r) = ramdisk_range {
142 debug!("Located ramdisk at {r:?}");
143 let r = memory.alloc_range(&r).map_err(|e| {
144 error!("Failed to obtain the initrd range: {e}");
145 RebootReason::InvalidRamdisk
146 })?;
147
148 // SAFETY - The region was validated by memory to be in main memory, mapped, and
149 // not overlap.
150 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
151 } else {
152 info!("Couldn't locate the ramdisk from the device tree");
153 None
154 };
155
156 Ok(Self { fdt, kernel, ramdisk })
157 }
158}
159
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100160/// Sets up the environment for main() and wraps its result for start().
161///
162/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
163/// the assumption that its environment has been properly configured.
164fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
165 // Limitations in this function:
166 // - only access MMIO once (and while) it has been mapped and configured
167 // - only perform logging once the logger has been initialized
168 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100169
170 // SAFETY - This function should and will only be called once, here.
171 unsafe { heap::init() };
172
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100173 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100174
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100175 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
176 // builds that have tweaked the logger::init() call will actually attempt to log the message.
177
178 mmio_guard::init().map_err(|e| {
179 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100180 RebootReason::InternalError
181 })?;
182
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100183 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
184 debug!("Failed to configure the UART: {e}");
185 RebootReason::InternalError
186 })?;
187
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100188 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
189 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000190 let appended_data = unsafe { get_appended_data_slice() };
191
192 // Up to this point, we were using the built-in static (from .rodata) page tables.
193
194 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
195 error!("Failed to set up the dynamic page tables: {e}");
196 RebootReason::InternalError
197 })?;
198
199 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
200 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
201 page_table.map_device(&uart_range).map_err(|e| {
202 error!("Failed to remap the UART as a dynamic page table entry: {e}");
203 RebootReason::InternalError
204 })?;
205
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100206 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
207 // linker script prevents it from overlapping with other objects.
208 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
209 error!("No valid configuration found");
210 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000211 })?;
212
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100213 let bcc = appended.get_bcc_mut().ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100214 error!("Invalid BCC");
215 RebootReason::InvalidBcc
216 })?;
217
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000218 debug!("Activating dynamic page table...");
219 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
220 // aware of so activating it shouldn't have any visible effect.
221 unsafe { page_table.activate() };
222 debug!("... Success!");
223
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000224 let mut memory = MemoryTracker::new(page_table);
225 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
226
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100227 // This wrapper allows main() to be blissfully ignorant of platform details.
Alice Wang28cbcf12022-12-01 07:58:28 +0000228 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc).map_err(|e| {
229 error!("Failed to verify the payload: {e}");
230 RebootReason::PayloadVerificationError
231 })?;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100232
233 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100234
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100235 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
236 error!("Failed to unshare the UART: {e}");
237 RebootReason::InternalError
238 })?;
239
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100240 Ok(())
241}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100242
243fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000244 const SCTLR_EL1_RES1: u64 = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100245 // Stage 1 instruction access cacheability is unaffected.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000246 const SCTLR_EL1_I: u64 = 0b1 << 12;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100247 // SETEND instruction disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000248 const SCTLR_EL1_SED: u64 = 0b1 << 8;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100249 // Various IT instructions are disabled at EL0 in aarch32 mode.
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000250 const SCTLR_EL1_ITD: u64 = 0b1 << 7;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100251
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000252 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 +0100253
Pierre-Clément Tosi6c0d48b2022-11-07 11:00:32 +0000254 // Disable the exception vector, caches and page table and then jump to the payload at the
255 // given address, passing it the given FDT pointer.
256 //
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100257 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
258 unsafe {
259 asm!(
260 "msr sctlr_el1, {sctlr_el1_val}",
261 "isb",
262 "mov x1, xzr",
263 "mov x2, xzr",
264 "mov x3, xzr",
265 "mov x4, xzr",
266 "mov x5, xzr",
267 "mov x6, xzr",
268 "mov x7, xzr",
269 "mov x8, xzr",
270 "mov x9, xzr",
271 "mov x10, xzr",
272 "mov x11, xzr",
273 "mov x12, xzr",
274 "mov x13, xzr",
275 "mov x14, xzr",
276 "mov x15, xzr",
277 "mov x16, xzr",
278 "mov x17, xzr",
279 "mov x18, xzr",
280 "mov x19, xzr",
281 "mov x20, xzr",
282 "mov x21, xzr",
283 "mov x22, xzr",
284 "mov x23, xzr",
285 "mov x24, xzr",
286 "mov x25, xzr",
287 "mov x26, xzr",
288 "mov x27, xzr",
289 "mov x28, xzr",
290 "mov x29, xzr",
291 "msr ttbr0_el1, xzr",
292 "isb",
293 "dsb nsh",
294 "br x30",
295 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
296 in("x0") fdt_address,
297 in("x30") payload_start,
298 options(nomem, noreturn, nostack),
299 );
300 };
301}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100302
303unsafe fn get_appended_data_slice() -> &'static mut [u8] {
304 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
305 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
306 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
307
308 slice::from_raw_parts_mut(base as *mut u8, size)
309}
310
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100311enum AppendedPayload<'a> {
312 /// Configuration data.
313 Config(config::Config<'a>),
314 /// Deprecated raw BCC, as used in Android T.
315 LegacyBcc(&'a mut [u8]),
316}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100317
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100318impl<'a> AppendedPayload<'a> {
319 /// SAFETY - 'data' should respect the alignment of config::Header.
320 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
321 if Self::is_valid_config(data) {
322 Some(Self::Config(config::Config::new(data).unwrap()))
323 } else if cfg!(feature = "legacy") {
324 const BCC_SIZE: usize = helpers::SIZE_4KB;
325 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
326 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
327 } else {
328 None
329 }
330 }
331
332 unsafe fn is_valid_config(data: &mut [u8]) -> bool {
333 // This function is necessary to prevent the borrow checker from getting confused
334 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
335 let addr = data.as_ptr();
336 config::Config::new(data)
337 .map_err(|e| warn!("Invalid configuration data at {addr:?}: {e}"))
338 .is_ok()
339 }
340
341 #[allow(dead_code)] // TODO(b/232900974)
342 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
343 match self {
344 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
345 Self::LegacyBcc(_) => None,
346 }
347 }
348
349 fn get_bcc_mut(&mut self) -> Option<&mut [u8]> {
350 let bcc = match self {
351 Self::LegacyBcc(ref mut bcc) => bcc,
352 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
353 };
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100354 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100355 Some(bcc)
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100356 }
357}