blob: 8ada6a15ca3dab35e356d4c6a145ca2796e45971 [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
Michał Mazurek2bbfc862024-11-29 10:43:46 +010017use crate::arch::payload::jump_to_payload;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010018use crate::config;
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +000019use crate::memory::MemorySlices;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010020use core::slice;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010021use log::error;
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +010022use log::warn;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +010023use log::LevelFilter;
Alice Wangeacb7382023-06-05 12:53:54 +000024use vmbase::{
Michał Mazurek2bbfc862024-11-29 10:43:46 +010025 configure_heap, console_writeln, limit_stack_size, main,
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000026 memory::{
Michał Mazurek2bbfc862024-11-29 10:43:46 +010027 map_image_footer, unshare_all_memory, unshare_all_mmio_except_uart, unshare_uart,
28 MemoryTrackerError, SIZE_128KB, SIZE_4KB,
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +000029 },
Alice Wangeacb7382023-06-05 12:53:54 +000030 power::reboot,
31};
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +010032use zeroize::Zeroize;
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010033
34#[derive(Debug, Clone)]
Andrew Walbran19690632022-12-07 16:41:30 +000035pub enum 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 Tosi4f4f5eb2022-12-08 14:31:42 +000050 /// DICE layering process failed.
51 SecretDerivationError,
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010052}
53
Pierre-Clément Tosib5a3ab12023-09-15 11:18:38 +010054impl RebootReason {
55 pub fn as_avf_reboot_string(&self) -> &'static str {
56 match self {
57 Self::InvalidBcc => "PVM_FIRMWARE_INVALID_BCC",
58 Self::InvalidConfig => "PVM_FIRMWARE_INVALID_CONFIG_DATA",
59 Self::InternalError => "PVM_FIRMWARE_INTERNAL_ERROR",
60 Self::InvalidFdt => "PVM_FIRMWARE_INVALID_FDT",
61 Self::InvalidPayload => "PVM_FIRMWARE_INVALID_PAYLOAD",
62 Self::InvalidRamdisk => "PVM_FIRMWARE_INVALID_RAMDISK",
63 Self::PayloadVerificationError => "PVM_FIRMWARE_PAYLOAD_VERIFICATION_FAILED",
64 Self::SecretDerivationError => "PVM_FIRMWARE_SECRET_DERIVATION_FAILED",
65 }
66 }
67}
68
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010069main!(start);
Pierre-Clément Tosi6a4808c2023-06-29 09:19:38 +000070configure_heap!(SIZE_128KB);
Pierre-Clément Tosieba83162024-11-02 12:11:48 +000071limit_stack_size!(SIZE_4KB * 12);
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010072
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +000073#[derive(Debug)]
74enum NextStage {
75 LinuxBoot(usize),
76 LinuxBootWithUart(usize),
77}
78
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010079/// Entry point for pVM firmware.
80pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +000081 let fdt_address = fdt_address.try_into().unwrap();
82 let payload_start = payload_start.try_into().unwrap();
83 let payload_size = payload_size.try_into().unwrap();
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +010084
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +000085 let reboot_reason = match main_wrapper(fdt_address, payload_start, payload_size) {
86 Err(r) => r,
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +000087 Ok((next_stage, slices)) => match next_stage {
88 NextStage::LinuxBootWithUart(ep) => jump_to_payload(ep, &slices),
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +000089 NextStage::LinuxBoot(ep) => {
90 if let Err(e) = unshare_uart() {
91 error!("Failed to unmap UART: {e}");
92 RebootReason::InternalError
93 } else {
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +000094 jump_to_payload(ep, &slices)
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +000095 }
96 }
97 },
98 };
99
100 const REBOOT_REASON_CONSOLE: usize = 1;
101 console_writeln!(REBOOT_REASON_CONSOLE, "{}", reboot_reason.as_avf_reboot_string());
102 reboot()
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100103
104 // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
105}
106
107/// Sets up the environment for main() and wraps its result for start().
108///
109/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
110/// the assumption that its environment has been properly configured.
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +0000111fn main_wrapper<'a>(
Pierre-Clément Tosi97f52492023-04-04 15:52:17 +0100112 fdt: usize,
113 payload: usize,
114 payload_size: usize,
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +0000115) -> Result<(NextStage, MemorySlices<'a>), RebootReason> {
Pierre-Clément Tosi5bbfca52022-10-21 12:14:35 +0100116 // Limitations in this function:
117 // - only access MMIO once (and while) it has been mapped and configured
118 // - only perform logging once the logger has been initialized
119 // - only access non-pvmfw memory once (and while) it has been mapped
Pierre-Clément Tosifc531152022-10-20 12:22:23 +0100120
Pierre-Clément Tosid3305482023-06-29 15:03:48 +0000121 log::set_max_level(LevelFilter::Info);
Pierre-Clément Tosi41748ed2023-03-31 18:20:40 +0100122
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000123 let appended_data = get_appended_data_slice().map_err(|e| {
124 error!("Failed to map the appended data: {e}");
125 RebootReason::InternalError
126 })?;
Alan Stokesc3829f12023-06-02 15:02:23 +0100127
Alan Stokes65618332023-12-15 14:09:25 +0000128 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100129 error!("No valid configuration found");
130 RebootReason::InvalidConfig
Pierre-Clément Tosia8a4a202022-11-03 14:16:46 +0000131 })?;
132
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000133 let config_entries = appended.get_entries();
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100134
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +0000135 let mut slices = MemorySlices::new(fdt, payload, payload_size)?;
Pierre-Clément Tosia0934c12022-11-25 20:54:11 +0000136
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100137 // This wrapper allows main() to be blissfully ignorant of platform details.
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100138 let (next_bcc, debuggable_payload) = crate::main(
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000139 slices.fdt,
140 slices.kernel,
141 slices.ramdisk,
142 config_entries.bcc,
143 config_entries.debug_policy,
Pierre-Clément Tosi92778802024-11-19 17:36:14 +0000144 config_entries.vm_dtbo,
145 config_entries.vm_ref_dt,
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000146 )?;
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +0000147 slices.add_dice_chain(next_bcc);
Pierre-Clément Tosi12f923e2024-12-04 22:30:45 +0000148 // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
149 let keep_uart = cfg!(debuggable_vms_improvements) && debuggable_payload;
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100150
Jakob Vukalovic44b1ce32023-04-17 19:10:10 +0100151 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000152 config_entries.bcc.zeroize();
Pierre-Clément Tosi072969b2022-10-19 17:32:24 +0100153
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000154 unshare_all_mmio_except_uart().map_err(|e| {
Andrew Walbran19690632022-12-07 16:41:30 +0000155 error!("Failed to unshare MMIO ranges: {e}");
156 RebootReason::InternalError
157 })?;
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000158 unshare_all_memory();
Pierre-Clément Tosi64aff642024-07-31 16:20:21 +0100159
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +0000160 let next_stage = select_next_stage(slices.kernel, keep_uart);
161
Pierre-Clément Tosibfa40602024-12-09 20:13:57 +0000162 Ok((next_stage, slices))
Pierre-Clément Tosi12f923e2024-12-04 22:30:45 +0000163}
164
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +0000165fn select_next_stage(kernel: &[u8], keep_uart: bool) -> NextStage {
166 if keep_uart {
167 NextStage::LinuxBootWithUart(kernel.as_ptr() as _)
168 } else {
169 NextStage::LinuxBoot(kernel.as_ptr() as _)
Pierre-Clément Tosi5ad1e8c2023-06-29 10:36:48 +0000170 }
Pierre-Clément Tosi9bb62ac2024-12-06 23:42:46 +0000171}
Jakob Vukalovic4c1edbe2023-04-17 19:10:57 +0100172
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000173fn get_appended_data_slice() -> Result<&'static mut [u8], MemoryTrackerError> {
Pierre-Clément Tosic26e2202024-11-01 23:12:23 +0000174 let range = map_image_footer()?;
Pierre-Clément Tosi229dd9d2024-11-02 10:34:27 +0000175 // SAFETY: This region was just mapped for the first time (as map_image_footer() didn't fail)
176 // and the linker script prevents it from overlapping with other objects.
177 Ok(unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) })
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100178}
179
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100180enum AppendedPayload<'a> {
181 /// Configuration data.
182 Config(config::Config<'a>),
183 /// Deprecated raw BCC, as used in Android T.
184 LegacyBcc(&'a mut [u8]),
185}
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100186
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100187impl<'a> AppendedPayload<'a> {
Alan Stokesc3829f12023-06-02 15:02:23 +0100188 fn new(data: &'a mut [u8]) -> Option<Self> {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100189 // The borrow checker gets confused about the ownership of data (see inline comments) so we
190 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
191 // in v1.77) in https://users.rust-lang.org/t/78467.
192 let data_ptr = data as *mut [u8];
193
194 // Config::new() borrows data as mutable ...
195 match config::Config::new(data) {
196 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
197 Ok(valid) => Some(Self::Config(valid)),
198 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
199 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
200 // ... the borrow checker still complains about a second mutable ref without this.
201 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
202 let data: &'a mut _ = unsafe { &mut *data_ptr };
203
Alice Wangeacb7382023-06-05 12:53:54 +0000204 const BCC_SIZE: usize = SIZE_4KB;
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000205 warn!("Assuming the appended data at {:?} to be a raw BCC", data.as_ptr());
206 Some(Self::LegacyBcc(&mut data[..BCC_SIZE]))
207 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000208 Err(e) => {
Pierre-Clément Tosi147addf2024-04-15 15:07:58 +0100209 error!("Invalid configuration data at {data_ptr:?}: {e}");
210 None
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000211 }
Pierre-Clément Tosi7aca7ff2022-12-12 14:04:30 +0000212 }
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100213 }
214
Alan Stokes65618332023-12-15 14:09:25 +0000215 fn get_entries(self) -> config::Entries<'a> {
Pierre-Clément Tosi20b60962022-10-17 13:35:27 +0100216 match self {
Alan Stokesd0cf3cd2023-12-12 14:36:37 +0000217 Self::Config(cfg) => cfg.get_entries(),
218 Self::LegacyBcc(bcc) => config::Entries { bcc, ..Default::default() },
Pierre-Clément Tosi8edf72e2022-12-06 16:02:57 +0000219 }
Pierre-Clément Tosie8726e42022-10-17 13:35:27 +0100220 }
221}