Merge changes I313c5c19,Ia09dd908 into main
* changes:
Add docs for Microdroid vendor modules
pvmfw/README.md: add subheaders for different versions of pvmfw data
diff --git a/pvmfw/src/entry.rs b/pvmfw/src/entry.rs
index cc6e302..0ff7270 100644
--- a/pvmfw/src/entry.rs
+++ b/pvmfw/src/entry.rs
@@ -30,7 +30,7 @@
use log::LevelFilter;
use vmbase::util::RangeExt as _;
use vmbase::{
- configure_heap,
+ configure_heap, console_writeln,
hyp::{get_mem_sharer, get_mmio_guard},
layout::{self, crosvm, UART_PAGE_ADDR},
main,
@@ -59,6 +59,21 @@
SecretDerivationError,
}
+impl RebootReason {
+ pub fn as_avf_reboot_string(&self) -> &'static str {
+ match self {
+ Self::InvalidBcc => "PVM_FIRMWARE_INVALID_BCC",
+ Self::InvalidConfig => "PVM_FIRMWARE_INVALID_CONFIG_DATA",
+ Self::InternalError => "PVM_FIRMWARE_INTERNAL_ERROR",
+ Self::InvalidFdt => "PVM_FIRMWARE_INVALID_FDT",
+ Self::InvalidPayload => "PVM_FIRMWARE_INVALID_PAYLOAD",
+ Self::InvalidRamdisk => "PVM_FIRMWARE_INVALID_RAMDISK",
+ Self::PayloadVerificationError => "PVM_FIRMWARE_PAYLOAD_VERIFICATION_FAILED",
+ Self::SecretDerivationError => "PVM_FIRMWARE_SECRET_DERIVATION_FAILED",
+ }
+ }
+}
+
main!(start);
configure_heap!(SIZE_128KB);
@@ -66,11 +81,15 @@
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)
+ // - can't access MMIO (except the console, already configured by vmbase)
match main_wrapper(fdt_address as usize, payload_start as usize, payload_size as usize) {
Ok((entry, bcc)) => jump_to_payload(fdt_address, entry.try_into().unwrap(), bcc),
- Err(_) => reboot(), // TODO(b/220071963) propagate the reason back to the host.
+ Err(e) => {
+ const REBOOT_REASON_CONSOLE: usize = 1;
+ console_writeln!(REBOOT_REASON_CONSOLE, "{}", e.as_avf_reboot_string());
+ reboot()
+ }
}
// if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
index a7d37b4..bbbcb07 100644
--- a/vmbase/src/console.rs
+++ b/vmbase/src/console.rs
@@ -15,91 +15,111 @@
//! Console driver for 8250 UART.
use crate::uart::Uart;
-use core::fmt::{write, Arguments, Write};
+use core::{
+ cell::OnceCell,
+ fmt::{write, Arguments, Write},
+};
use spin::mutex::SpinMutex;
-/// Base memory-mapped address of the primary UART device.
-pub const BASE_ADDRESS: usize = 0x3f8;
+// Arbitrary limit on the number of consoles that can be registered.
+//
+// Matches the UART count in crosvm.
+const MAX_CONSOLES: usize = 4;
-static CONSOLE: SpinMutex<Option<Uart>> = SpinMutex::new(None);
+static CONSOLES: [SpinMutex<Option<Uart>>; MAX_CONSOLES] =
+ [SpinMutex::new(None), SpinMutex::new(None), SpinMutex::new(None), SpinMutex::new(None)];
+static ADDRESSES: [SpinMutex<OnceCell<usize>>; MAX_CONSOLES] = [
+ SpinMutex::new(OnceCell::new()),
+ SpinMutex::new(OnceCell::new()),
+ SpinMutex::new(OnceCell::new()),
+ SpinMutex::new(OnceCell::new()),
+];
-/// Initialises a new instance of the UART driver and returns it.
-fn create() -> Uart {
- // SAFETY: BASE_ADDRESS is the base of the MMIO region for a UART and is mapped as device
- // memory.
- unsafe { Uart::new(BASE_ADDRESS) }
-}
+/// Index of the console used by default for logging.
+pub const DEFAULT_CONSOLE_INDEX: usize = 0;
-/// Initialises the global instance of the UART driver. This must be called before using
-/// the `print!` and `println!` macros.
-pub fn init() {
- let uart = create();
- CONSOLE.lock().replace(uart);
-}
+/// Index of the console used by default for emergency logging.
+pub const DEFAULT_EMERGENCY_CONSOLE_INDEX: usize = DEFAULT_CONSOLE_INDEX;
-/// Writes a string to the console.
+/// Initialises the global instance(s) of the UART driver.
///
-/// Panics if [`init`] was not called first.
-pub(crate) fn write_str(s: &str) {
- CONSOLE.lock().as_mut().unwrap().write_str(s).unwrap();
-}
-
-/// Writes a formatted string to the console.
+/// This must be called before using the `print!` and `println!` macros.
///
-/// Panics if [`init`] was not called first.
-pub(crate) fn write_args(format_args: Arguments) {
- write(CONSOLE.lock().as_mut().unwrap(), format_args).unwrap();
+/// # Safety
+///
+/// This must be called once with the bases of UARTs, mapped as device memory and (if necessary)
+/// shared with the host as MMIO, to which no other references must be held.
+pub unsafe fn init(base_addresses: &[usize]) {
+ for (i, &base_address) in base_addresses.iter().enumerate() {
+ // Remember the valid address, for emergency console accesses.
+ ADDRESSES[i].lock().set(base_address).expect("console::init() called more than once");
+
+ // Initialize the console driver, for normal console accesses.
+ let mut console = CONSOLES[i].lock();
+ assert!(console.is_none(), "console::init() called more than once");
+ // SAFETY: base_address must be the base of a mapped UART.
+ console.replace(unsafe { Uart::new(base_address) });
+ }
}
-/// Reinitializes the UART driver and writes a string to it.
+/// Writes a formatted string followed by a newline to the n-th console.
+///
+/// Panics if the n-th console was not initialized by calling [`init`] first.
+pub fn writeln(n: usize, format_args: Arguments) {
+ let mut guard = CONSOLES[n].lock();
+ let uart = guard.as_mut().unwrap();
+
+ write(uart, format_args).unwrap();
+ let _ = uart.write_str("\n");
+}
+
+/// Reinitializes the n-th UART driver and writes a formatted string followed by a newline to it.
///
/// This is intended for use in situations where the UART may be in an unknown state or the global
/// instance may be locked, such as in an exception handler or panic handler.
-pub fn emergency_write_str(s: &str) {
- let mut uart = create();
- let _ = uart.write_str(s);
-}
+pub fn ewriteln(n: usize, format_args: Arguments) {
+ let Some(cell) = ADDRESSES[n].try_lock() else { return };
+ let Some(addr) = cell.get() else { return };
-/// Reinitializes the UART driver and writes a formatted string to it.
-///
-/// This is intended for use in situations where the UART may be in an unknown state or the global
-/// instance may be locked, such as in an exception handler or panic handler.
-pub fn emergency_write_args(format_args: Arguments) {
- let mut uart = create();
+ // SAFETY: addr contains the base of a mapped UART, passed in init().
+ let mut uart = unsafe { Uart::new(*addr) };
+
let _ = write(&mut uart, format_args);
+ let _ = uart.write_str("\n");
}
+/// Prints the given formatted string to the n-th console, followed by a newline.
+///
+/// Panics if the console has not yet been initialized. May hang if used in an exception context;
+/// use `eprintln!` instead.
+#[macro_export]
+macro_rules! console_writeln {
+ ($n:expr, $($arg:tt)*) => ({
+ $crate::console::writeln($n, format_args!($($arg)*))
+ })
+}
+
+pub(crate) use console_writeln;
+
/// Prints the given formatted string to the console, followed by a newline.
///
/// Panics if the console has not yet been initialized. May hang if used in an exception context;
/// use `eprintln!` instead.
macro_rules! println {
- () => ($crate::console::write_str("\n"));
($($arg:tt)*) => ({
- $crate::console::write_args(format_args!($($arg)*))};
- $crate::console::write_str("\n");
- );
+ $crate::console::console_writeln!($crate::console::DEFAULT_CONSOLE_INDEX, $($arg)*)
+ })
}
pub(crate) use println; // Make it available in this crate.
-/// Prints the given string to the console in an emergency, such as an exception handler.
-///
-/// Never panics.
-#[macro_export]
-macro_rules! eprint {
- ($($arg:tt)*) => ($crate::console::emergency_write_args(format_args!($($arg)*)));
-}
-
/// Prints the given string followed by a newline to the console in an emergency, such as an
/// exception handler.
///
/// Never panics.
#[macro_export]
macro_rules! eprintln {
- () => ($crate::console::emergency_write_str("\n"));
($($arg:tt)*) => ({
- $crate::console::emergency_write_args(format_args!($($arg)*))};
- $crate::console::emergency_write_str("\n");
- );
+ $crate::console::ewriteln($crate::console::DEFAULT_EMERGENCY_CONSOLE_INDEX, format_args!($($arg)*))
+ })
}
diff --git a/vmbase/src/entry.rs b/vmbase/src/entry.rs
index dedc6ae..ad633ed 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -16,7 +16,7 @@
use crate::{
bionic, console, heap, hyp,
- layout::UART_PAGE_ADDR,
+ layout::{UART_ADDRESSES, UART_PAGE_ADDR},
logger,
memory::{SIZE_16KB, SIZE_4KB},
power::{reboot, shutdown},
@@ -26,8 +26,6 @@
use static_assertions::const_assert_eq;
fn try_console_init() -> Result<(), hyp::Error> {
- console::init();
-
if let Some(mmio_guard) = hyp::get_mmio_guard() {
mmio_guard.enroll()?;
@@ -49,6 +47,9 @@
mmio_guard.map(UART_PAGE_ADDR)?;
}
+ // SAFETY: UART_PAGE is mapped at stage-1 (see entry.S) and was just MMIO-guarded.
+ unsafe { console::init(&UART_ADDRESSES) };
+
Ok(())
}
diff --git a/vmbase/src/layout.rs b/vmbase/src/layout.rs
index 993141d..5ac435f 100644
--- a/vmbase/src/layout.rs
+++ b/vmbase/src/layout.rs
@@ -16,7 +16,6 @@
pub mod crosvm;
-use crate::console;
use crate::linker::__stack_chk_guard;
use crate::memory::{page_4kb_of, PAGE_SIZE};
use aarch64_paging::paging::VirtualAddress;
@@ -27,9 +26,17 @@
/// First address that can't be translated by a level 1 TTBR0_EL1.
pub const MAX_VIRT_ADDR: usize = 1 << 40;
+/// Base memory-mapped addresses of the UART devices.
+///
+/// See SERIAL_ADDR in https://crosvm.dev/book/appendix/memory_layout.html#common-layout.
+pub const UART_ADDRESSES: [usize; 4] = [0x3f8, 0x2f8, 0x3e8, 0x2e8];
+
/// Address of the single page containing all the UART devices.
pub const UART_PAGE_ADDR: usize = 0;
-const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(console::BASE_ADDRESS));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[0]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[1]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[2]));
+const_assert_eq!(UART_PAGE_ADDR, page_4kb_of(UART_ADDRESSES[3]));
/// Get an address from a linker-defined symbol.
#[macro_export]