blob: 18ff1c1fde9eb9de1c643213fddcfebee84f873f [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 Tosia0934c12022-11-25 20:54:11 +000017use crate::fdt;
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010018use crate::heap;
Pierre-Clément Tosia1d3ea32022-11-01 15:05:11 +000019use crate::helpers;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000020use crate::memory::MemoryTracker;
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010021use crate::mmio_guard;
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000022use crate::mmu;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010023use core::arch::asm;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000024use core::num::NonZeroUsize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010025use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010026use log::debug;
27use log::error;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000028use log::info;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010029use log::LevelFilter;
30use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010031
32#[derive(Debug, Clone)]
33enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010034 /// A malformed BCC was received.
35 InvalidBcc,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010036 /// An unexpected internal error happened.
37 InternalError,
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000038 /// The provided FDT was invalid.
39 InvalidFdt,
40 /// The provided payload was invalid.
41 InvalidPayload,
42 /// The provided ramdisk was invalid.
43 InvalidRamdisk,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010044}
45
46main!(start);
47
48/// Entry point for pVM firmware.
49pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
50 // Limitations in this function:
51 // - can't access non-pvmfw memory (only statically-mapped memory)
52 // - can't access MMIO (therefore, no logging)
53
54 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
55 Ok(_) => jump_to_payload(fdt_address, payload_start),
56 Err(_) => reboot(),
57 }
58
59 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
60}
61
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +000062struct MemorySlices<'a> {
63 fdt: &'a mut libfdt::Fdt,
64 kernel: &'a [u8],
65 ramdisk: Option<&'a [u8]>,
66}
67
68impl<'a> MemorySlices<'a> {
69 fn new(
70 fdt: usize,
71 payload: usize,
72 payload_size: usize,
73 memory: &mut MemoryTracker,
74 ) -> Result<Self, RebootReason> {
75 // SAFETY - SIZE_2MB is non-zero.
76 const FDT_SIZE: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(helpers::SIZE_2MB) };
77 // TODO - Only map the FDT as read-only, until we modify it right before jump_to_payload()
78 // e.g. by generating a DTBO for a template DT in main() and, on return, re-map DT as RW,
79 // overwrite with the template DT and apply the DTBO.
80 let range = memory.alloc_mut(fdt, FDT_SIZE).map_err(|e| {
81 error!("Failed to allocate the FDT range: {e}");
82 RebootReason::InternalError
83 })?;
84
85 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
86 let fdt = unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) };
87 let fdt = libfdt::Fdt::from_mut_slice(fdt).map_err(|e| {
88 error!("Failed to spawn the FDT wrapper: {e}");
89 RebootReason::InvalidFdt
90 })?;
91
92 debug!("Fdt passed validation!");
93
94 let memory_range = fdt
95 .memory()
96 .map_err(|e| {
97 error!("Failed to get /memory from the DT: {e}");
98 RebootReason::InvalidFdt
99 })?
100 .ok_or_else(|| {
101 error!("Node /memory was found empty");
102 RebootReason::InvalidFdt
103 })?
104 .next()
105 .ok_or_else(|| {
106 error!("Failed to read the memory size from the FDT");
107 RebootReason::InternalError
108 })?;
109
110 debug!("Resizing MemoryTracker to range {memory_range:#x?}");
111
112 memory.shrink(&memory_range).map_err(|_| {
113 error!("Failed to use memory range value from DT: {memory_range:#x?}");
114 RebootReason::InvalidFdt
115 })?;
116
117 let payload_size = NonZeroUsize::new(payload_size).ok_or_else(|| {
118 error!("Invalid payload size: {payload_size:#x}");
119 RebootReason::InvalidPayload
120 })?;
121
122 let payload_range = memory.alloc(payload, payload_size).map_err(|e| {
123 error!("Failed to obtain the payload range: {e}");
124 RebootReason::InternalError
125 })?;
126 // SAFETY - The tracker validated the range to be in main memory, mapped, and not overlap.
127 let kernel =
128 unsafe { slice::from_raw_parts(payload_range.start as *const u8, payload_range.len()) };
129
130 let ramdisk_range = fdt::initrd_range(fdt).map_err(|e| {
131 error!("An error occurred while locating the ramdisk in the device tree: {e}");
132 RebootReason::InternalError
133 })?;
134
135 let ramdisk = if let Some(r) = ramdisk_range {
136 debug!("Located ramdisk at {r:?}");
137 let r = memory.alloc_range(&r).map_err(|e| {
138 error!("Failed to obtain the initrd range: {e}");
139 RebootReason::InvalidRamdisk
140 })?;
141
142 // SAFETY - The region was validated by memory to be in main memory, mapped, and
143 // not overlap.
144 Some(unsafe { slice::from_raw_parts(r.start as *const u8, r.len()) })
145 } else {
146 info!("Couldn't locate the ramdisk from the device tree");
147 None
148 };
149
150 Ok(Self { fdt, kernel, ramdisk })
151 }
152}
153
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100154/// Sets up the environment for main() and wraps its result for start().
155///
156/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
157/// the assumption that its environment has been properly configured.
158fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
159 // Limitations in this function:
160 // - only access MMIO once (and while) it has been mapped and configured
161 // - only perform logging once the logger has been initialized
162 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100163
164 // SAFETY - This function should and will only be called once, here.
165 unsafe { heap::init() };
166
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +0100167 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100168
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100169 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
170 // builds that have tweaked the logger::init() call will actually attempt to log the message.
171
172 mmio_guard::init().map_err(|e| {
173 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100174 RebootReason::InternalError
175 })?;
176
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100177 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
178 debug!("Failed to configure the UART: {e}");
179 RebootReason::InternalError
180 })?;
181
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100182 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
183 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000184 let appended_data = unsafe { get_appended_data_slice() };
185
186 // Up to this point, we were using the built-in static (from .rodata) page tables.
187
188 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
189 error!("Failed to set up the dynamic page tables: {e}");
190 RebootReason::InternalError
191 })?;
192
193 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
194 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
195 page_table.map_device(&uart_range).map_err(|e| {
196 error!("Failed to remap the UART as a dynamic page table entry: {e}");
197 RebootReason::InternalError
198 })?;
199
200 let appended_range = appended_data.as_ptr_range();
201 let appended_range = (appended_range.start as usize)..(appended_range.end as usize);
202 page_table.map_rodata(&appended_range).map_err(|e| {
203 error!("Failed to remap the appended payload as a dynamic page table entry: {e}");
204 RebootReason::InternalError
205 })?;
206
207 let bcc = as_bcc(appended_data).ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100208 error!("Invalid BCC");
209 RebootReason::InvalidBcc
210 })?;
211
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000212 debug!("Activating dynamic page table...");
213 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
214 // aware of so activating it shouldn't have any visible effect.
215 unsafe { page_table.activate() };
216 debug!("... Success!");
217
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000218 let mut memory = MemoryTracker::new(page_table);
219 let slices = MemorySlices::new(fdt, payload, payload_size, &mut memory)?;
220
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100221 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000222 crate::main(slices.fdt, slices.kernel, slices.ramdisk, bcc);
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100223
224 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100225
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100226 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
227 error!("Failed to unshare the UART: {e}");
228 RebootReason::InternalError
229 })?;
230
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100231 Ok(())
232}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100233
234fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
235 const SCTLR_EL1_RES1: usize = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
236 // Stage 1 instruction access cacheability is unaffected.
237 const SCTLR_EL1_I: usize = 0b1 << 12;
238 // SETEND instruction disabled at EL0 in aarch32 mode.
239 const SCTLR_EL1_SED: usize = 0b1 << 8;
240 // Various IT instructions are disabled at EL0 in aarch32 mode.
241 const SCTLR_EL1_ITD: usize = 0b1 << 7;
242
243 const SCTLR_EL1_VAL: usize = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
244
245 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
246 unsafe {
247 asm!(
248 "msr sctlr_el1, {sctlr_el1_val}",
249 "isb",
250 "mov x1, xzr",
251 "mov x2, xzr",
252 "mov x3, xzr",
253 "mov x4, xzr",
254 "mov x5, xzr",
255 "mov x6, xzr",
256 "mov x7, xzr",
257 "mov x8, xzr",
258 "mov x9, xzr",
259 "mov x10, xzr",
260 "mov x11, xzr",
261 "mov x12, xzr",
262 "mov x13, xzr",
263 "mov x14, xzr",
264 "mov x15, xzr",
265 "mov x16, xzr",
266 "mov x17, xzr",
267 "mov x18, xzr",
268 "mov x19, xzr",
269 "mov x20, xzr",
270 "mov x21, xzr",
271 "mov x22, xzr",
272 "mov x23, xzr",
273 "mov x24, xzr",
274 "mov x25, xzr",
275 "mov x26, xzr",
276 "mov x27, xzr",
277 "mov x28, xzr",
278 "mov x29, xzr",
279 "msr ttbr0_el1, xzr",
280 "isb",
281 "dsb nsh",
282 "br x30",
283 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
284 in("x0") fdt_address,
285 in("x30") payload_start,
286 options(nomem, noreturn, nostack),
287 );
288 };
289}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100290
291unsafe fn get_appended_data_slice() -> &'static mut [u8] {
292 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
293 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
294 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
295
296 slice::from_raw_parts_mut(base as *mut u8, size)
297}
298
299fn as_bcc(data: &mut [u8]) -> Option<&mut [u8]> {
300 const BCC_SIZE: usize = helpers::SIZE_4KB;
301
302 if cfg!(feature = "legacy") {
303 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
304 Some(&mut data[..BCC_SIZE])
305 } else {
306 None
307 }
308}