blob: d113452083e0876658a0a4d9136b79195d069ae3 [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),
Pierre-Clément Tosid836b5b2022-12-05 10:49:38 +000060 Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010061 }
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,
75 payload: usize,
76 payload_size: usize,
77 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
121 let payload_size = NonZeroUsize::new(payload_size).ok_or_else(|| {
122 error!("Invalid payload size: {payload_size:#x}");
123 RebootReason::InvalidPayload
124 })?;
125
126 let payload_range = memory.alloc(payload, payload_size).map_err(|e| {
127 error!("Failed to obtain the payload range: {e}");
128 RebootReason::InternalError
129 })?;
130 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
131 let kernel =
132 unsafe { slice::from_raw_parts(payload_range.start as *const u8, payload_range.len()) };
133
134 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
135 error!("An error occurred while locating the ramdisk in the device tree: {e}");
136 RebootReason::InternalError
137 })?;
138
139 let ramdisk = if let Some(r) = ramdisk_range {
140 debug!("Located ramdisk at {r:?}");
141 let r = memory.alloc_range(&r).map_err(|e| {
142 error!("Failed to obtain the initrd range: {e}");
143 RebootReason::InvalidRamdisk
144 })?;
145
146 // SAFETY - The region was validated by memory to be in main memory, mapped, and
147 // not overlap.
148 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
149 } else {
150 info!("Couldn't locate the ramdisk from the device tree");
151 None
152 };
153
154 Ok(Self { fdt, kernel, ramdisk })
155 }
156}
157
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100158/// Sets up the environment for main() and wraps its result for start().
159///
160/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
161/// the assumption that its environment has been properly configured.
162fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
163 // Limitations in this function:
164 // - only access MMIO once (and while) it has been mapped and configured
165 // - only perform logging once the logger has been initialized
166 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100167
168 // SAFETY - This function should and will only be called once, here.
169 unsafe { heap::init() };
170
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100171 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100172
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100173 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
174 // builds that have tweaked the logger::init() call will actually attempt to log the message.
175
176 mmio_guard::init().map_err(|e| {
177 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100178 RebootReason::InternalError
179 })?;
180
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100181 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
182 debug!("Failed to configure the UART: {e}");
183 RebootReason::InternalError
184 })?;
185
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100186 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
187 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000188 let appended_data = unsafe { get_appended_data_slice() };
189
190 // Up to this point, we were using the built-in static (from .rodata) page tables.
191
192 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
193 error!("Failed to set up the dynamic page tables: {e}");
194 RebootReason::InternalError
195 })?;
196
197 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
198 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
199 page_table.map_device(&uart_range).map_err(|e| {
200 error!("Failed to remap the UART as a dynamic page table entry: {e}");
201 RebootReason::InternalError
202 })?;
203
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100204 // SAFETY - We only get the appended payload from here, once. It is statically mapped and the
205 // linker script prevents it from overlapping with other objects.
206 let mut appended = unsafe { AppendedPayload::new(appended_data) }.ok_or_else(|| {
207 error!("No valid configuration found");
208 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000209 })?;
210
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100211 let bcc = appended.get_bcc_mut().ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100212 error!("Invalid BCC");
213 RebootReason::InvalidBcc
214 })?;
215
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000216 debug!("Activating dynamic page table...");
217 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
218 // aware of so activating it shouldn't have any visible effect.
219 unsafe { page_table.activate() };
220 debug!("... Success!");
221
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000222 let mut memory = MemoryTracker::new(page_table);
223 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
224
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100225 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000226 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc);
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100227
228 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100229
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100230 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
231 error!("Failed to unshare the UART: {e}");
232 RebootReason::InternalError
233 })?;
234
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100235 Ok(())
236}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100237
238fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
239 const SCTLR_EL1_RES1: usize = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
240 // Stage 1 instruction access cacheability is unaffected.
241 const SCTLR_EL1_I: usize = 0b1 << 12;
242 // SETEND instruction disabled at EL0 in aarch32 mode.
243 const SCTLR_EL1_SED: usize = 0b1 << 8;
244 // Various IT instructions are disabled at EL0 in aarch32 mode.
245 const SCTLR_EL1_ITD: usize = 0b1 << 7;
246
247 const SCTLR_EL1_VAL: usize = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
248
249 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
250 unsafe {
251 asm!(
252 "msr sctlr_el1, {sctlr_el1_val}",
253 "isb",
254 "mov x1, xzr",
255 "mov x2, xzr",
256 "mov x3, xzr",
257 "mov x4, xzr",
258 "mov x5, xzr",
259 "mov x6, xzr",
260 "mov x7, xzr",
261 "mov x8, xzr",
262 "mov x9, xzr",
263 "mov x10, xzr",
264 "mov x11, xzr",
265 "mov x12, xzr",
266 "mov x13, xzr",
267 "mov x14, xzr",
268 "mov x15, xzr",
269 "mov x16, xzr",
270 "mov x17, xzr",
271 "mov x18, xzr",
272 "mov x19, xzr",
273 "mov x20, xzr",
274 "mov x21, xzr",
275 "mov x22, xzr",
276 "mov x23, xzr",
277 "mov x24, xzr",
278 "mov x25, xzr",
279 "mov x26, xzr",
280 "mov x27, xzr",
281 "mov x28, xzr",
282 "mov x29, xzr",
283 "msr ttbr0_el1, xzr",
284 "isb",
285 "dsb nsh",
286 "br x30",
287 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
288 in("x0") fdt_address,
289 in("x30") payload_start,
290 options(nomem, noreturn, nostack),
291 );
292 };
293}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100294
295unsafe fn get_appended_data_slice() -> &'static mut [u8] {
296 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
297 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
298 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
299
300 slice::from_raw_parts_mut(base as *mut u8, size)
301}
302
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100303enum AppendedPayload<'a> {
304 /// Configuration data.
305 Config(config::Config<'a>),
306 /// Deprecated raw BCC, as used in Android T.
307 LegacyBcc(&'a mut [u8]),
308}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100309
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100310impl<'a> AppendedPayload<'a> {
311 /// SAFETY - 'data' should respect the alignment of config::Header.
312 unsafe fn new(data: &'a mut [u8]) -> Option<Self> {
313 if Self::is_valid_config(data) {
314 Some(Self::Config(config::Config::new(data).unwrap()))
315 } else if cfg!(feature = "legacy") {
316 const BCC_SIZE: usize = helpers::SIZE_4KB;
317 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
318 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
319 } else {
320 None
321 }
322 }
323
324 unsafe fn is_valid_config(data: &mut [u8]) -> bool {
325 // This function is necessary to prevent the borrow checker from getting confused
326 // about the ownership of data in new(); see https://users.rust-lang.org/t/78467.
327 let addr = data.as_ptr();
328 config::Config::new(data)
329 .map_err(|e| warn!("Invalid configuration data at {addr:?}: {e}"))
330 .is_ok()
331 }
332
333 #[allow(dead_code)] // TODO(b/232900974)
334 fn get_debug_policy(&mut self) -> Option<&mut [u8]> {
335 match self {
336 Self::Config(ref mut cfg) => cfg.get_debug_policy(),
337 Self::LegacyBcc(_) => None,
338 }
339 }
340
341 fn get_bcc_mut(&mut self) -> Option<&mut [u8]> {
342 let bcc = match self {
343 Self::LegacyBcc(ref mut bcc) => bcc,
344 Self::Config(ref mut cfg) => cfg.get_bcc_mut(),
345 };
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100346 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100347 Some(bcc)
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100348 }
349}