1 // 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
17 use crate::arch::payload::jump_to_payload;
18 use crate::config;
19 use crate::memory::MemorySlices;
20 use core::slice;
21 use log::error;
22 use log::warn;
23 use log::LevelFilter;
24 use vmbase::{
25 configure_heap, console_writeln, limit_stack_size, main,
26 memory::{
27 map_image_footer, unshare_all_memory, unshare_all_mmio_except_uart, unshare_uart,
28 MemoryTrackerError, SIZE_128KB, SIZE_4KB,
29 },
30 power::reboot,
31 };
32 use zeroize::Zeroize;
33
34 #[derive(Debug, Clone)]
35 pub enum RebootReason {
36 /// A malformed DICE handover was received.
37 InvalidDiceHandover,
38 /// An invalid configuration was appended to pvmfw.
39 InvalidConfig,
40 /// An unexpected internal error happened.
41 InternalError,
42 /// The provided FDT was invalid.
43 InvalidFdt,
44 /// The provided payload was invalid.
45 InvalidPayload,
46 /// The provided ramdisk was invalid.
47 InvalidRamdisk,
48 /// Failed to verify the payload.
49 PayloadVerificationError,
50 /// DICE layering process failed.
51 SecretDerivationError,
52 }
53
54 impl RebootReason {
as_avf_reboot_string(&self) -> &'static str55 pub fn as_avf_reboot_string(&self) -> &'static str {
56 match self {
57 Self::InvalidDiceHandover => "PVM_FIRMWARE_INVALID_DICE_HANDOVER",
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
69 main!(start);
70 configure_heap!(SIZE_128KB);
71 limit_stack_size!(SIZE_4KB * 12);
72
73 #[derive(Debug)]
74 enum NextStage {
75 LinuxBoot(usize),
76 LinuxBootWithUart(usize),
77 }
78
79 /// Entry point for pVM firmware.
start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64)80 pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
81 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();
84
85 let reboot_reason = match main_wrapper(fdt_address, payload_start, payload_size) {
86 Err(r) => r,
87 Ok((next_stage, slices)) => match next_stage {
88 NextStage::LinuxBootWithUart(ep) => jump_to_payload(ep, &slices),
89 NextStage::LinuxBoot(ep) => {
90 if let Err(e) = unshare_uart() {
91 error!("Failed to unmap UART: {e}");
92 RebootReason::InternalError
93 } else {
94 jump_to_payload(ep, &slices)
95 }
96 }
97 },
98 };
99
100 const REBOOT_REASON_CONSOLE: usize = 1;
101 console_writeln!(REBOOT_REASON_CONSOLE, "{}", reboot_reason.as_avf_reboot_string()).unwrap();
102 reboot()
103
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.
main_wrapper<'a>( fdt: usize, payload: usize, payload_size: usize, ) -> Result<(NextStage, MemorySlices<'a>), RebootReason>111 fn main_wrapper<'a>(
112 fdt: usize,
113 payload: usize,
114 payload_size: usize,
115 ) -> Result<(NextStage, MemorySlices<'a>), RebootReason> {
116 // 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
120
121 log::set_max_level(LevelFilter::Info);
122
123 let appended_data = get_appended_data_slice().map_err(|e| {
124 error!("Failed to map the appended data: {e}");
125 RebootReason::InternalError
126 })?;
127
128 let appended = AppendedPayload::new(appended_data).ok_or_else(|| {
129 error!("No valid configuration found");
130 RebootReason::InvalidConfig
131 })?;
132
133 let config_entries = appended.get_entries();
134
135 let mut slices = MemorySlices::new(fdt, payload, payload_size)?;
136
137 // This wrapper allows main() to be blissfully ignorant of platform details.
138 let (next_dice_handover, debuggable_payload) = crate::main(
139 slices.fdt,
140 slices.kernel,
141 slices.ramdisk,
142 config_entries.dice_handover.as_deref(),
143 config_entries.debug_policy,
144 config_entries.vm_dtbo,
145 config_entries.vm_ref_dt,
146 )?;
147 if let Some(r) = next_dice_handover {
148 slices.add_dice_handover(r);
149 }
150
151 // Keep UART MMIO_GUARD-ed for debuggable payloads, to enable earlycon.
152 let keep_uart = cfg!(debuggable_vms_improvements) && debuggable_payload;
153
154 // Writable-dirty regions will be flushed when MemoryTracker is dropped.
155 if let Some(r) = config_entries.dice_handover {
156 r.zeroize();
157 }
158
159 unshare_all_mmio_except_uart().map_err(|e| {
160 error!("Failed to unshare MMIO ranges: {e}");
161 RebootReason::InternalError
162 })?;
163 unshare_all_memory();
164
165 let next_stage = select_next_stage(slices.kernel, keep_uart);
166
167 Ok((next_stage, slices))
168 }
169
select_next_stage(kernel: &[u8], keep_uart: bool) -> NextStage170 fn select_next_stage(kernel: &[u8], keep_uart: bool) -> NextStage {
171 if keep_uart {
172 NextStage::LinuxBootWithUart(kernel.as_ptr() as _)
173 } else {
174 NextStage::LinuxBoot(kernel.as_ptr() as _)
175 }
176 }
177
get_appended_data_slice() -> Result<&'static mut [u8], MemoryTrackerError>178 fn get_appended_data_slice() -> Result<&'static mut [u8], MemoryTrackerError> {
179 let range = map_image_footer()?;
180 // SAFETY: This region was just mapped for the first time (as map_image_footer() didn't fail)
181 // and the linker script prevents it from overlapping with other objects.
182 Ok(unsafe { slice::from_raw_parts_mut(range.start as *mut u8, range.len()) })
183 }
184
185 enum AppendedPayload<'a> {
186 /// Configuration data.
187 Config(config::Config<'a>),
188 /// Deprecated raw DICE handover, as used in Android T.
189 LegacyDiceHandover(&'a mut [u8]),
190 }
191
192 impl<'a> AppendedPayload<'a> {
new(data: &'a mut [u8]) -> Option<Self>193 fn new(data: &'a mut [u8]) -> Option<Self> {
194 // The borrow checker gets confused about the ownership of data (see inline comments) so we
195 // intentionally obfuscate it using a raw pointer; see a similar issue (still not addressed
196 // in v1.77) in https://users.rust-lang.org/t/78467.
197 let data_ptr = data as *mut [u8];
198
199 // Config::new() borrows data as mutable ...
200 match config::Config::new(data) {
201 // ... so this branch has a mutable reference to data, from the Ok(Config<'a>). But ...
202 Ok(valid) => Some(Self::Config(valid)),
203 // ... if Config::new(data).is_err(), the Err holds no ref to data. However ...
204 Err(config::Error::InvalidMagic) if cfg!(feature = "legacy") => {
205 // ... the borrow checker still complains about a second mutable ref without this.
206 // SAFETY: Pointer to a valid mut (not accessed elsewhere), 'a lifetime re-used.
207 let data: &'a mut _ = unsafe { &mut *data_ptr };
208
209 const DICE_CHAIN_SIZE: usize = SIZE_4KB;
210 warn!(
211 "Assuming the appended data at {:?} to be a raw DICE handover",
212 data.as_ptr()
213 );
214 Some(Self::LegacyDiceHandover(&mut data[..DICE_CHAIN_SIZE]))
215 }
216 Err(e) => {
217 error!("Invalid configuration data at {data_ptr:?}: {e}");
218 None
219 }
220 }
221 }
222
get_entries(self) -> config::Entries<'a>223 fn get_entries(self) -> config::Entries<'a> {
224 match self {
225 Self::Config(cfg) => cfg.get_entries(),
226 Self::LegacyDiceHandover(d) => {
227 config::Entries { dice_handover: Some(d), ..Default::default() }
228 }
229 }
230 }
231 }
232