blob: 07dd0c5bc92d6651dbb6a7ed7dc448959fdf9e66 [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 Tosifc531152022-10-20 12:22:23 +010017use crate::heap;
Pierre-Clément Tosia1d3ea32022-11-01 15:05:11 +000018use crate::helpers;
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010019use crate::mmio_guard;
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000020use crate::mmu;
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +010021use core::arch::asm;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010022use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010023use log::debug;
24use log::error;
25use log::LevelFilter;
26use vmbase::{console, layout, logger, main, power::reboot};
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010027
28#[derive(Debug, Clone)]
29enum RebootReason {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010030 /// A malformed BCC was received.
31 InvalidBcc,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010032 /// An unexpected internal error happened.
33 InternalError,
34}
35
36main!(start);
37
38/// Entry point for pVM firmware.
39pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
40 // Limitations in this function:
41 // - can't access non-pvmfw memory (only statically-mapped memory)
42 // - can't access MMIO (therefore, no logging)
43
44 match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
45 Ok(_) => jump_to_payload(fdt_address, payload_start),
46 Err(_) => reboot(),
47 }
48
49 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
50}
51
52/// Sets up the environment for main() and wraps its result for start().
53///
54/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
55/// the assumption that its environment has been properly configured.
56fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
57 // Limitations in this function:
58 // - only access MMIO once (and while) it has been mapped and configured
59 // - only perform logging once the logger has been initialized
60 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +010061
62 // SAFETY - This function should and will only be called once, here.
63 unsafe { heap::init() };
64
Pierre-Clément Tosidbd72862022-10-21 14:31:02 +010065 logger::init(LevelFilter::Info).map_err(|_| RebootReason::InternalError)?;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010066
Pierre-Clément Tosia1d3ea32022-11-01 15:05:11 +000067 const FDT_MAX_SIZE: usize = helpers::SIZE_2MB;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010068 // TODO: Check that the FDT is fully contained in RAM.
69 // SAFETY - We trust the VMM, for now.
70 let fdt = unsafe { slice::from_raw_parts_mut(fdt as *mut u8, FDT_MAX_SIZE) };
71 // TODO: Check that the payload is fully contained in RAM and doesn't overlap with the FDT.
72 // SAFETY - We trust the VMM, for now.
73 let payload = unsafe { slice::from_raw_parts(payload as *const u8, payload_size) };
74
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010075 // Use debug!() to avoid printing to the UART if we failed to configure it as only local
76 // builds that have tweaked the logger::init() call will actually attempt to log the message.
77
78 mmio_guard::init().map_err(|e| {
79 debug!("{e}");
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010080 RebootReason::InternalError
81 })?;
82
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +010083 mmio_guard::map(console::BASE_ADDRESS).map_err(|e| {
84 debug!("Failed to configure the UART: {e}");
85 RebootReason::InternalError
86 })?;
87
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010088 // SAFETY - We only get the appended payload from here, once. It is mapped and the linker
89 // script prevents it from overlapping with other objects.
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +000090 let appended_data = unsafe { get_appended_data_slice() };
91
92 // Up to this point, we were using the built-in static (from .rodata) page tables.
93
94 let mut page_table = mmu::PageTable::from_static_layout().map_err(|e| {
95 error!("Failed to set up the dynamic page tables: {e}");
96 RebootReason::InternalError
97 })?;
98
99 const CONSOLE_LEN: usize = 1; // vmbase::uart::Uart only uses one u8 register.
100 let uart_range = console::BASE_ADDRESS..(console::BASE_ADDRESS + CONSOLE_LEN);
101 page_table.map_device(&uart_range).map_err(|e| {
102 error!("Failed to remap the UART as a dynamic page table entry: {e}");
103 RebootReason::InternalError
104 })?;
105
106 let appended_range = appended_data.as_ptr_range();
107 let appended_range = (appended_range.start as usize)..(appended_range.end as usize);
108 page_table.map_rodata(&appended_range).map_err(|e| {
109 error!("Failed to remap the appended payload as a dynamic page table entry: {e}");
110 RebootReason::InternalError
111 })?;
112
113 let bcc = as_bcc(appended_data).ok_or_else(|| {
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100114 error!("Invalid BCC");
115 RebootReason::InvalidBcc
116 })?;
117
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000118 debug!("Activating dynamic page table...");
119 // SAFETY - page_table duplicates the static mappings for everything that the Rust code is
120 // aware of so activating it shouldn't have any visible effect.
121 unsafe { page_table.activate() };
122 debug!("... Success!");
123
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100124 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100125 crate::main(fdt, payload, bcc);
126
127 // TODO: Overwrite BCC before jumping to payload to avoid leaking our sealing key.
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100128
Pierre-Clément Tosia99bfa62022-10-06 13:30:52 +0100129 mmio_guard::unmap(console::BASE_ADDRESS).map_err(|e| {
130 error!("Failed to unshare the UART: {e}");
131 RebootReason::InternalError
132 })?;
133
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100134 Ok(())
135}
Pierre-Clément Tosi645e90e2022-10-21 13:27:19 +0100136
137fn jump_to_payload(fdt_address: u64, payload_start: u64) -> ! {
138 const SCTLR_EL1_RES1: usize = (0b11 << 28) | (0b101 << 20) | (0b1 << 11);
139 // Stage 1 instruction access cacheability is unaffected.
140 const SCTLR_EL1_I: usize = 0b1 << 12;
141 // SETEND instruction disabled at EL0 in aarch32 mode.
142 const SCTLR_EL1_SED: usize = 0b1 << 8;
143 // Various IT instructions are disabled at EL0 in aarch32 mode.
144 const SCTLR_EL1_ITD: usize = 0b1 << 7;
145
146 const SCTLR_EL1_VAL: usize = SCTLR_EL1_RES1 | SCTLR_EL1_ITD | SCTLR_EL1_SED | SCTLR_EL1_I;
147
148 // SAFETY - We're exiting pvmfw by passing the register values we need to a noreturn asm!().
149 unsafe {
150 asm!(
151 "msr sctlr_el1, {sctlr_el1_val}",
152 "isb",
153 "mov x1, xzr",
154 "mov x2, xzr",
155 "mov x3, xzr",
156 "mov x4, xzr",
157 "mov x5, xzr",
158 "mov x6, xzr",
159 "mov x7, xzr",
160 "mov x8, xzr",
161 "mov x9, xzr",
162 "mov x10, xzr",
163 "mov x11, xzr",
164 "mov x12, xzr",
165 "mov x13, xzr",
166 "mov x14, xzr",
167 "mov x15, xzr",
168 "mov x16, xzr",
169 "mov x17, xzr",
170 "mov x18, xzr",
171 "mov x19, xzr",
172 "mov x20, xzr",
173 "mov x21, xzr",
174 "mov x22, xzr",
175 "mov x23, xzr",
176 "mov x24, xzr",
177 "mov x25, xzr",
178 "mov x26, xzr",
179 "mov x27, xzr",
180 "mov x28, xzr",
181 "mov x29, xzr",
182 "msr ttbr0_el1, xzr",
183 "isb",
184 "dsb nsh",
185 "br x30",
186 sctlr_el1_val = in(reg) SCTLR_EL1_VAL,
187 in("x0") fdt_address,
188 in("x30") payload_start,
189 options(nomem, noreturn, nostack),
190 );
191 };
192}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100193
194unsafe fn get_appended_data_slice() -> &'static mut [u8] {
195 let base = helpers::align_up(layout::binary_end(), helpers::SIZE_4KB).unwrap();
196 // pvmfw is contained in a 2MiB region so the payload can't be larger than the 2MiB alignment.
197 let size = helpers::align_up(base, helpers::SIZE_2MB).unwrap() - base;
198
199 slice::from_raw_parts_mut(base as *mut u8, size)
200}
201
202fn as_bcc(data: &mut [u8]) -> Option<&mut [u8]> {
203 const BCC_SIZE: usize = helpers::SIZE_4KB;
204
205 if cfg!(feature = "legacy") {
206 // TODO(b/256148034): return None if BccHandoverParse(bcc) != kDiceResultOk.
207 Some(&mut data[..BCC_SIZE])
208 } else {
209 None
210 }
211}