vmbase: Support secondary UARTs 0x2f8,0x3e8,0x2e8
Parameterize the code to allow printing to the "n-th" console and
setting up an arbitrary (up to MAX_CONSOLES) number of UARTs to be used
by client code.
Extend the layout to describe the 4 UARTs crosvm unconditionally
generates and configure console::init() to allow printing to any one of
them.
Bug: 300636104
Test: m pvmfw librialto libvmbase_example
Change-Id: I899f70dfb5eeb41e4ba399aee59d731db1264ff9
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
index 7356d7f..f829eb5 100644
--- a/vmbase/src/console.rs
+++ b/vmbase/src/console.rs
@@ -21,46 +21,64 @@
};
use spin::mutex::SpinMutex;
-// ADDRESS is the base of the MMIO region for a UART and must be mapped as device memory.
-static ADDRESS: SpinMutex<OnceCell<usize>> = SpinMutex::new(OnceCell::new());
-static CONSOLE: SpinMutex<Option<Uart>> = SpinMutex::new(None);
+// Arbitrary limit on the number of consoles that can be registered.
+//
+// Matches the UART count in crosvm.
+const MAX_CONSOLES: usize = 4;
-/// Initialises the global instance of the UART driver.
+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()),
+];
+
+/// Index of the console used by default for logging.
+pub const DEFAULT_CONSOLE_INDEX: usize = 0;
+
+/// Index of the console used by default for emergency logging.
+pub const DEFAULT_EMERGENCY_CONSOLE_INDEX: usize = DEFAULT_CONSOLE_INDEX;
+
+/// Initialises the global instance(s) of the UART driver.
///
/// This must be called before using the `print!` and `println!` macros.
///
/// # Safety
///
-/// This must be called with the base of a UART, mapped as device memory and (if necessary) shared
-/// with the host as MMIO.
-pub unsafe fn init(base_address: usize) {
- // Remember the valid address, for emergency console accesses.
- ADDRESS.lock().set(base_address).expect("console::init() called more than once");
+/// 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 = CONSOLE.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) });
+ // 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) });
+ }
}
-/// Writes a formatted string followed by a newline to the console.
+/// Writes a formatted string followed by a newline to the n-th console.
///
-/// Panics if [`init`] was not called first.
-pub(crate) fn writeln(format_args: Arguments) {
- let mut guard = CONSOLE.lock();
+/// Panics if the n-th console was not initialized by calling [`init`] first.
+pub(crate) 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 UART driver and writes a formatted string followed by a newline to it.
+/// 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 ewriteln(format_args: Arguments) {
- let Some(cell) = ADDRESS.try_lock() else { return };
+pub fn ewriteln(n: usize, format_args: Arguments) {
+ let Some(cell) = ADDRESSES[n].try_lock() else { return };
let Some(addr) = cell.get() else { return };
// SAFETY: addr contains the base of a mapped UART, passed in init().
@@ -75,7 +93,9 @@
/// Panics if the console has not yet been initialized. May hang if used in an exception context;
/// use `eprintln!` instead.
macro_rules! println {
- ($($arg:tt)*) => ($crate::console::writeln(format_args!($($arg)*)));
+ ($($arg:tt)*) => ({
+ $crate::console::writeln($crate::console::DEFAULT_CONSOLE_INDEX, format_args!($($arg)*))
+ })
}
pub(crate) use println; // Make it available in this crate.
@@ -86,5 +106,7 @@
/// Never panics.
#[macro_export]
macro_rules! eprintln {
- ($($arg:tt)*) => ($crate::console::ewriteln(format_args!($($arg)*)));
+ ($($arg:tt)*) => ({
+ $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 4a436d2..ad633ed 100644
--- a/vmbase/src/entry.rs
+++ b/vmbase/src/entry.rs
@@ -16,7 +16,7 @@
use crate::{
bionic, console, heap, hyp,
- layout::{UART_ADDR, UART_PAGE_ADDR},
+ layout::{UART_ADDRESSES, UART_PAGE_ADDR},
logger,
memory::{SIZE_16KB, SIZE_4KB},
power::{reboot, shutdown},
@@ -48,7 +48,7 @@
}
// SAFETY: UART_PAGE is mapped at stage-1 (see entry.S) and was just MMIO-guarded.
- unsafe { console::init(UART_ADDR) };
+ unsafe { console::init(&UART_ADDRESSES) };
Ok(())
}
diff --git a/vmbase/src/layout.rs b/vmbase/src/layout.rs
index e65abda..5ac435f 100644
--- a/vmbase/src/layout.rs
+++ b/vmbase/src/layout.rs
@@ -26,14 +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 address of the primary UART device.
+/// 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_ADDR: usize = 0x3f8;
+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(UART_ADDR));
+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]