Add minimal UART driver for console.
Bug: 223166344
Test: Ran unprotected VM under crosvm.
Change-Id: I4e779ab0c87597db9384247f269b2b28f6e18b30
diff --git a/pvmfw/Android.bp b/pvmfw/Android.bp
index 7123da4..ce096b6 100644
--- a/pvmfw/Android.bp
+++ b/pvmfw/Android.bp
@@ -8,6 +8,9 @@
"libcompiler_builtins.rust_sysroot",
"libcore.rust_sysroot",
],
+ rustlibs: [
+ "libspin_nostd",
+ ],
enabled: false,
target: {
android_arm64: {
diff --git a/pvmfw/src/console.rs b/pvmfw/src/console.rs
new file mode 100644
index 0000000..d44eb92
--- /dev/null
+++ b/pvmfw/src/console.rs
@@ -0,0 +1,61 @@
+// 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.
+
+//! Console driver for 8250 UART.
+
+use crate::uart::Uart;
+use core::fmt::{write, Arguments, Write};
+use spin::mutex::SpinMutex;
+
+const BASE_ADDRESS: usize = 0x3f8;
+
+static CONSOLE: SpinMutex<Option<Uart>> = SpinMutex::new(None);
+
+/// Initialises a new instance of the UART driver and returns it.
+fn create() -> Uart {
+ // Safe because 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.
+pub fn init() {
+ let uart = create();
+ CONSOLE.lock().replace(uart);
+}
+
+/// Writes a string to the console.
+///
+/// Panics if [`init`] was not called first.
+pub fn write_str(s: &str) {
+ CONSOLE.lock().as_mut().unwrap().write_str(s).unwrap();
+}
+
+/// Writes a formatted string to the console.
+///
+/// Panics if [`init`] was not called first.
+#[allow(unused)]
+pub fn write_args(format_args: Arguments) {
+ write(CONSOLE.lock().as_mut().unwrap(), format_args).unwrap();
+}
+
+/// Reinitialises the UART driver and writes a 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_str(s: &str) {
+ let mut uart = create();
+ let _ = uart.write_str(s);
+}
diff --git a/pvmfw/src/main.rs b/pvmfw/src/main.rs
index 0a359f6..7156213 100644
--- a/pvmfw/src/main.rs
+++ b/pvmfw/src/main.rs
@@ -17,14 +17,20 @@
#![no_main]
#![no_std]
+mod console;
mod psci;
+mod uart;
+use console::emergency_write_str;
use core::panic::PanicInfo;
use psci::{system_off, system_reset};
/// Entry point for pVM firmware.
#[no_mangle]
pub extern "C" fn main() -> ! {
+ console::init();
+ console::write_str("Hello world\n");
+
system_off();
#[allow(clippy::empty_loop)]
loop {}
@@ -32,6 +38,7 @@
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
+ emergency_write_str("panic\n");
system_reset();
loop {}
}
diff --git a/pvmfw/src/uart.rs b/pvmfw/src/uart.rs
new file mode 100644
index 0000000..0fc2494
--- /dev/null
+++ b/pvmfw/src/uart.rs
@@ -0,0 +1,59 @@
+// 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.
+
+//! Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
+//! provided by crosvm, and won't work with real hardware.
+
+use core::fmt::{self, Write};
+use core::ptr::write_volatile;
+
+/// Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
+/// provided by crosvm, and won't work with real hardware.
+pub struct Uart {
+ base_address: *mut u8,
+}
+
+impl Uart {
+ /// Constructs a new instance of the UART driver for a device at the given base address.
+ ///
+ /// # Safety
+ ///
+ /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+ /// device, which must be mapped into the address space of the process as device memory and not
+ /// have any other aliases.
+ pub unsafe fn new(base_address: usize) -> Self {
+ Self { base_address: base_address as *mut u8 }
+ }
+
+ /// Writes a single byte to the UART.
+ pub fn write_byte(&self, byte: u8) {
+ // Safe because we know that the base address points to the control registers of an UART
+ // device which is appropriately mapped.
+ unsafe {
+ write_volatile(self.base_address, byte);
+ }
+ }
+}
+
+impl Write for Uart {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ for c in s.as_bytes() {
+ self.write_byte(*c);
+ }
+ Ok(())
+ }
+}
+
+// Safe because it just contains a pointer to device memory, which can be accessed from any context.
+unsafe impl Send for Uart {}