• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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