Move common VM functionality into a library.
Bug: 223166344
Test: Ran unprotected VM under crosvm.
Change-Id: I3b027c3a5de037c2adc75cf816518d8db5e10587
diff --git a/vmbase/Android.bp b/vmbase/Android.bp
new file mode 100644
index 0000000..972cd1b
--- /dev/null
+++ b/vmbase/Android.bp
@@ -0,0 +1,18 @@
+rust_library_rlib {
+ name: "libvmbase",
+ host_supported: false,
+ crate_name: "vmbase",
+ srcs: ["src/lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libpsci",
+ "libspin_nostd",
+ ],
+ enabled: false,
+ target: {
+ android_arm64: {
+ enabled: true,
+ },
+ },
+ apex_available: ["com.android.virt"],
+}
diff --git a/vmbase/src/console.rs b/vmbase/src/console.rs
new file mode 100644
index 0000000..b52d924
--- /dev/null
+++ b/vmbase/src/console.rs
@@ -0,0 +1,112 @@
+// 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.
+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);
+}
+
+/// Reinitialises 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();
+ let _ = write(&mut uart, format_args);
+}
+
+/// Prints the given string to the console.
+///
+/// Panics if the console has not yet been initialised. May hang if used in an exception context;
+/// use `eprint!` instead.
+#[macro_export]
+macro_rules! print {
+ ($($arg:tt)*) => ($crate::console::write_args(format_args!($($arg)*)));
+}
+
+/// Prints the given formatted string to the console, followed by a newline.
+///
+/// Panics if the console has not yet been initialised. May hang if used in an exception context;
+/// use `eprintln!` instead.
+#[macro_export]
+macro_rules! println {
+ () => ($crate::console::write_str("\n"));
+ ($($arg:tt)*) => ({
+ $crate::console::write_args(format_args!($($arg)*))};
+ $crate::console::write_str("\n");
+ );
+}
+
+/// 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");
+ );
+}
diff --git a/vmbase/src/lib.rs b/vmbase/src/lib.rs
new file mode 100644
index 0000000..0901e04
--- /dev/null
+++ b/vmbase/src/lib.rs
@@ -0,0 +1,30 @@
+// 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.
+
+//! Basic functionality for bare-metal binaries to run in a VM under crosvm.
+
+#![no_std]
+
+pub mod console;
+pub mod power;
+pub mod uart;
+
+use core::panic::PanicInfo;
+use power::reboot;
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ eprintln!("{}", info);
+ reboot()
+}
diff --git a/vmbase/src/power.rs b/vmbase/src/power.rs
new file mode 100644
index 0000000..10a5e5d
--- /dev/null
+++ b/vmbase/src/power.rs
@@ -0,0 +1,35 @@
+// 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.
+
+//! Functions for shutting down the VM.
+
+use psci::{system_off, system_reset};
+
+/// Makes a `PSCI_SYSTEM_OFF` call to shutdown the VM.
+///
+/// Panics if it returns an error.
+pub fn shutdown() -> ! {
+ system_off().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
+
+/// Makes a `PSCI_SYSTEM_RESET` call to shutdown the VM abnormally.
+///
+/// Panics if it returns an error.
+pub fn reboot() -> ! {
+ system_reset().unwrap();
+ #[allow(clippy::empty_loop)]
+ loop {}
+}
diff --git a/vmbase/src/uart.rs b/vmbase/src/uart.rs
new file mode 100644
index 0000000..0fc2494
--- /dev/null
+++ b/vmbase/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 {}