vmbase: Improve safety of console::init()

As the console should only be accessed once it has been MMIO guarded and
mapped in the MMU, prevent eprintln!() from writing to the UART before
console::init() has been called.

Mark console::init() as unsafe, documenting the constraints on when it
should be called and, in the entry code, move it _after_ we've called
mmio_guard.map(UART_PAGE).

Move BASE_ADDRESS to the more appropriate layout module and allow the
caller to pass the UART base to console::init() and rename it UART_ADDR,
for clarity.

Test: m pvmfw librialto libvmbase_example
Change-Id: I9543765fd0429b38b45bc2e7b7c83bedf79194f3
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
index 657a62b..7356d7f 100644
--- a/vmbase/src/console.rs
+++ b/vmbase/src/console.rs
@@ -15,26 +15,33 @@
 //! 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;
-
+// 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);
 
-/// 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) }
-}
+/// Initialises the global instance 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");
 
-/// 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);
+    // 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) });
 }
 
 /// Writes a formatted string followed by a newline to the console.
@@ -53,7 +60,12 @@
 /// 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 mut uart = create();
+    let Some(cell) = ADDRESS.try_lock() else { return };
+    let Some(addr) = cell.get() else { return };
+
+    // 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");
 }