pvmfw: entry: Introduce start() & main_wrapper()
As the environment needs to be fully configured before Rust code can run
without taking particular care about the memory or MMIO it accesses and
as we want to take advantage of Rust error-handling (Result<>, ?, ...)
and borrow checker, introduce a 3-stage wrapping of the main() function
where resources are progressively configured (then dropped) before
main() gets run.
Test: atest MicrodroidTestApp
Change-Id: I3faaba0f481225ae90f07e1f7f1e0b1855a26bae
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
new file mode 100644
index 0000000..430a012
--- /dev/null
+++ b/pvmfw/src/entry.rs
@@ -0,0 +1,70 @@
+// Copyright 2022, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Low-level entry and exit points of pvmfw.
+
+use crate::helpers::FDT_MAX_SIZE;
+use crate::jump_to_payload;
+use core::slice;
+use log::{error, LevelFilter};
+use vmbase::{logger, main, power::reboot};
+
+#[derive(Debug, Clone)]
+enum RebootReason {
+ /// An unexpected internal error happened.
+ InternalError,
+}
+
+main!(start);
+
+/// Entry point for pVM firmware.
+pub fn start(fdt_address: u64, payload_start: u64, payload_size: u64, _arg3: u64) {
+ // Limitations in this function:
+ // - can't access non-pvmfw memory (only statically-mapped memory)
+ // - can't access MMIO (therefore, no logging)
+
+ match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
+ Ok(_) => jump_to_payload(fdt_address, payload_start),
+ Err(_) => reboot(),
+ }
+
+ // if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
+}
+
+/// Sets up the environment for main() and wraps its result for start().
+///
+/// Provide the abstractions necessary for start() to abort the pVM boot and for main() to run with
+/// the assumption that its environment has been properly configured.
+fn main_wrapper(fdt: usize, payload: usize, payload_size: usize) -> Result<(), RebootReason> {
+ // Limitations in this function:
+ // - only access MMIO once (and while) it has been mapped and configured
+ // - only perform logging once the logger has been initialized
+ // - only access non-pvmfw memory once (and while) it has been mapped
+ logger::init(LevelFilter::Debug).map_err(|_| RebootReason::InternalError)?;
+
+ // TODO: Check that the FDT is fully contained in RAM.
+ // SAFETY - We trust the VMM, for now.
+ let fdt = unsafe { slice::from_raw_parts_mut(fdt as *mut u8, FDT_MAX_SIZE) };
+ // TODO: Check that the payload is fully contained in RAM and doesn't overlap with the FDT.
+ // SAFETY - We trust the VMM, for now.
+ let payload = unsafe { slice::from_raw_parts(payload as *const u8, payload_size) };
+
+ // This wrapper allows main() to be blissfully ignorant of platform details.
+ crate::main(fdt, payload).map_err(|e| {
+ error!("{e}");
+ RebootReason::InternalError
+ })?;
+
+ Ok(())
+}
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index ac3a48e..d8fb3ed 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -14,6 +14,7 @@
//! Miscellaneous helper functions.
+pub const FDT_MAX_SIZE: usize = 2 << 20;
pub const SIZE_4KB: usize = 4 << 10;
/// Computes the address of the page containing a given address.
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 55604e5..1fadf22 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -17,14 +17,15 @@
#![no_main]
#![no_std]
+mod entry;
mod exceptions;
mod helpers;
mod smccc;
use core::fmt;
use helpers::checked_page_of;
-use log::{debug, error, info, LevelFilter};
-use vmbase::{console, logger, main, power::reboot};
+use log::{debug, info};
+use vmbase::console;
#[derive(Debug, Clone)]
enum Error {
@@ -41,7 +42,7 @@
}
}
-fn main(fdt_address: u64, payload_start: u64, payload_size: u64, arg3: u64) -> Result<(), Error> {
+fn main(fdt: &mut [u8], payload: &[u8]) -> Result<(), Error> {
// We need to inform the hypervisor that the MMIO page containing the UART may be shared back.
let mmio_granule = smccc::mmio_guard_info().map_err(|_| Error::FailedUartSetup)?;
let uart_page = checked_page_of(console::BASE_ADDRESS, mmio_granule as usize)
@@ -50,8 +51,10 @@
info!("pVM firmware");
debug!(
- "fdt_address={:#018x}, payload_start={:#018x}, payload_size={:#018x}, x3={:#018x}",
- fdt_address, payload_start, payload_size, arg3,
+ "fdt_address={:#018x}, payload_start={:#018x}, payload_size={:#018x}",
+ fdt.as_ptr() as usize,
+ payload.as_ptr() as usize,
+ payload.len(),
);
info!("Starting payload...");
@@ -59,20 +62,6 @@
Ok(())
}
-main!(main_wrapper);
-
-/// Entry point for pVM firmware.
-pub fn main_wrapper(fdt_address: u64, payload_start: u64, payload_size: u64, arg3: u64) {
- if logger::init(LevelFilter::Debug).is_err() {
- } else if let Err(e) = main(fdt_address, payload_start, payload_size, arg3) {
- error!("Boot rejected: {e}");
- } else {
- jump_to_payload(fdt_address, payload_start);
- }
-
- reboot()
-}
-
fn jump_to_payload(fdt_address: u64, payload_start: u64) {
// Safe because this is a function we have implemented in assembly that matches its signature
// here.