Merge "Set up timeout for graceful shutdown" into main
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 8934de0..f0eba7f 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -56,6 +56,7 @@
"libvirtualizationservice_jni",
"libvirtualmachine_jni",
],
+ native_shared_libs: ["libavf"],
// TODO(b/295593640) Unfortunately these are added to the apex even though they are unused.
// Once the build system is fixed, remove this.
unwanted_transitive_deps: [
@@ -108,7 +109,6 @@
"rialto_bin",
"android_bootloader_crosvm_aarch64",
],
- native_shared_libs: ["libavf"],
},
x86_64: {
binaries: [
@@ -129,7 +129,6 @@
prebuilts: [
"android_bootloader_crosvm_x86_64",
],
- native_shared_libs: ["libavf"],
},
},
binaries: [
diff --git a/guest/pvmfw/README.md b/guest/pvmfw/README.md
index c7f3dd6..08b0d5c 100644
--- a/guest/pvmfw/README.md
+++ b/guest/pvmfw/README.md
@@ -1,19 +1,30 @@
# Protected Virtual Machine Firmware
+## Protected VM (_"pVM"_)
+
In the context of the [Android Virtualization Framework][AVF], a hypervisor
(_e.g._ [pKVM]) enforces full memory isolation between its virtual machines
-(VMs) and the host. As a result, the host is only allowed to access memory that
-has been explicitly shared back by a VM. Such _protected VMs_ (“pVMs”) are
-therefore able to manipulate secrets without being at risk of an attacker
-stealing them by compromising the Android host.
+(VMs) and the host. As a result, such VMs are given strong guarantees regarding
+their confidentiality and integrity.
-As pVMs are started dynamically by a _virtual machine manager_ (“VMM”) running
-as a host process and as pVMs must not trust the host (see [_Why
-AVF?_][why-avf]), the virtual machine it configures can't be trusted either.
-Furthermore, even though the isolation mentioned above allows pVMs to protect
-their secrets from the host, it does not help with provisioning them during
-boot. In particular, the threat model would prohibit the host from ever having
-access to those secrets, preventing the VMM from passing them to the pVM.
+A _protected VMs_ (“pVMs”) is a VM running in the non-secure or realm world,
+started dynamically by a _virtual machine manager_ (“VMM”) running as a process
+of the untrusted Android host (see [_Why AVF?_][why-avf]) and which is isolated
+from the host OS so that access to its memory is restricted, even in the event
+of a compromised Android host. pVMs support rich environments, including
+Linux-based distributions.
+
+The pVM concept is not Google-exclusive. Partner-defined VMs (SoC/OEM) meeting
+isolation/memory access restrictions are also pVMs.
+
+## Protected VM Root-of-Trust: pvmfw
+
+As pVMs are managed by a VMM running on the untrusted host, the virtual machine
+it configures can't be trusted either. Furthermore, even though the isolation
+mentioned above allows pVMs to protect their secrets from the host, it does not
+help with provisioning them during boot. In particular, the threat model would
+prohibit the host from ever having access to those secrets, preventing the VMM
+from passing them to the pVM.
To address these concerns the hypervisor securely loads the pVM firmware
(“pvmfw”) in the pVM from a protected memory region (this prevents the host or
diff --git a/guest/pvmfw/src/arch/aarch64/exceptions.rs b/guest/pvmfw/src/arch/aarch64/exceptions.rs
index 4c867fb..c8c0156 100644
--- a/guest/pvmfw/src/arch/aarch64/exceptions.rs
+++ b/guest/pvmfw/src/arch/aarch64/exceptions.rs
@@ -18,9 +18,7 @@
arch::aarch64::exceptions::{
handle_permission_fault, handle_translation_fault, ArmException, Esr, HandleExceptionError,
},
- eprintln, logger,
- power::reboot,
- read_sysreg,
+ logger, read_sysreg,
};
fn handle_exception(exception: &ArmException) -> Result<(), HandleExceptionError> {
@@ -41,55 +39,44 @@
let exception = ArmException::from_el1_regs();
if let Err(e) = handle_exception(&exception) {
- exception.print("sync_exception_current", e, elr);
- reboot()
+ exception.print_and_reboot("sync_exception_current", e, elr);
}
}
#[no_mangle]
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
- eprintln!("irq_current");
- reboot();
+ panic!("irq_current");
}
#[no_mangle]
extern "C" fn fiq_current(_elr: u64, _spsr: u64) {
- eprintln!("fiq_current");
- reboot();
+ panic!("fiq_current");
}
#[no_mangle]
extern "C" fn serr_current(_elr: u64, _spsr: u64) {
let esr = read_sysreg!("esr_el1");
- eprintln!("serr_current");
- eprintln!("esr={esr:#08x}");
- reboot();
+ panic!("serr_current, esr={esr:#08x}");
}
#[no_mangle]
extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
let esr = read_sysreg!("esr_el1");
- eprintln!("sync_lower");
- eprintln!("esr={esr:#08x}");
- reboot();
+ panic!("sync_lower, esr={esr:#08x}");
}
#[no_mangle]
extern "C" fn irq_lower(_elr: u64, _spsr: u64) {
- eprintln!("irq_lower");
- reboot();
+ panic!("irq_lower");
}
#[no_mangle]
extern "C" fn fiq_lower(_elr: u64, _spsr: u64) {
- eprintln!("fiq_lower");
- reboot();
+ panic!("fiq_lower");
}
#[no_mangle]
extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
let esr = read_sysreg!("esr_el1");
- eprintln!("serr_lower");
- eprintln!("esr={esr:#08x}");
- reboot();
+ panic!("serr_lower, esr={esr:#08x}");
}
diff --git a/guest/pvmfw/src/bcc.rs b/guest/pvmfw/src/bcc.rs
index 9260d7f..7ce50e9 100644
--- a/guest/pvmfw/src/bcc.rs
+++ b/guest/pvmfw/src/bcc.rs
@@ -97,8 +97,6 @@
}
impl Bcc {
- /// Returns whether any node in the received DICE chain is marked as debug (and hence is not
- /// secure).
pub fn new(received_bcc: Option<&[u8]>) -> Result<Bcc> {
let received_bcc = received_bcc.unwrap_or(&[]);
if received_bcc.is_empty() {
@@ -132,6 +130,8 @@
Ok(Self { is_debug_mode, leaf_subject_pubkey })
}
+ /// Returns whether any node in the received DICE chain is marked as debug (and hence is not
+ /// secure).
pub fn is_debug_mode(&self) -> bool {
self.is_debug_mode
}
diff --git a/guest/pvmfw/src/entry.rs b/guest/pvmfw/src/entry.rs
index 8ada6a1..cde4cfe 100644
--- a/guest/pvmfw/src/entry.rs
+++ b/guest/pvmfw/src/entry.rs
@@ -98,7 +98,7 @@
};
const REBOOT_REASON_CONSOLE: usize = 1;
- console_writeln!(REBOOT_REASON_CONSOLE, "{}", reboot_reason.as_avf_reboot_string());
+ console_writeln!(REBOOT_REASON_CONSOLE, "{}", reboot_reason.as_avf_reboot_string()).unwrap();
reboot()
// if we reach this point and return, vmbase::entry::rust_entry() will call power::shutdown().
diff --git a/guest/pvmfw/src/fdt.rs b/guest/pvmfw/src/fdt.rs
index 59399b3..6f55c21 100644
--- a/guest/pvmfw/src/fdt.rs
+++ b/guest/pvmfw/src/fdt.rs
@@ -1382,7 +1382,7 @@
fdt.unpack()?;
}
- patch_dice_node(fdt, bcc.as_ptr() as usize, bcc.len())?;
+ patch_dice_node(fdt, bcc)?;
if let Some(mut chosen) = fdt.chosen_mut()? {
empty_or_delete_prop(&mut chosen, c"avf,strict-boot", strict_boot)?;
@@ -1401,16 +1401,14 @@
}
/// Patch the "google,open-dice"-compatible reserved-memory node to point to the bcc range
-fn patch_dice_node(fdt: &mut Fdt, addr: usize, size: usize) -> libfdt::Result<()> {
- // We reject DTs with missing reserved-memory node as validation should have checked that the
- // "swiotlb" subnode (compatible = "restricted-dma-pool") was present.
- let node = fdt.node_mut(c"/reserved-memory")?.ok_or(libfdt::FdtError::NotFound)?;
-
+fn patch_dice_node(fdt: &mut Fdt, handover: &[u8]) -> libfdt::Result<()> {
+ // The node is assumed to be present in the template DT.
+ let node = fdt.node_mut(c"/reserved-memory")?.ok_or(FdtError::NotFound)?;
let mut node = node.next_compatible(c"google,open-dice")?.ok_or(FdtError::NotFound)?;
- let addr: u64 = addr.try_into().unwrap();
- let size: u64 = size.try_into().unwrap();
- node.setprop_inplace(c"reg", [addr.to_be_bytes(), size.to_be_bytes()].as_flattened())
+ let addr = (handover.as_ptr() as usize).try_into().unwrap();
+ let size = handover.len().try_into().unwrap();
+ node.setprop_addrrange_inplace(c"reg", addr, size)
}
fn empty_or_delete_prop(
diff --git a/guest/rialto/src/exceptions.rs b/guest/rialto/src/exceptions.rs
index 467a3a6..c8c0156 100644
--- a/guest/rialto/src/exceptions.rs
+++ b/guest/rialto/src/exceptions.rs
@@ -18,9 +18,7 @@
arch::aarch64::exceptions::{
handle_permission_fault, handle_translation_fault, ArmException, Esr, HandleExceptionError,
},
- eprintln, logger,
- power::reboot,
- read_sysreg,
+ logger, read_sysreg,
};
fn handle_exception(exception: &ArmException) -> Result<(), HandleExceptionError> {
@@ -41,58 +39,44 @@
let exception = ArmException::from_el1_regs();
if let Err(e) = handle_exception(&exception) {
- exception.print("sync_exception_current", e, elr);
- reboot()
+ exception.print_and_reboot("sync_exception_current", e, elr);
}
}
#[no_mangle]
-extern "C" fn irq_current() {
- eprintln!("irq_current");
- reboot();
+extern "C" fn irq_current(_elr: u64, _spsr: u64) {
+ panic!("irq_current");
}
#[no_mangle]
-extern "C" fn fiq_current() {
- eprintln!("fiq_current");
- reboot();
+extern "C" fn fiq_current(_elr: u64, _spsr: u64) {
+ panic!("fiq_current");
}
#[no_mangle]
-extern "C" fn serr_current() {
- eprintln!("serr_current");
- print_esr();
- reboot();
-}
-
-#[no_mangle]
-extern "C" fn sync_lower() {
- eprintln!("sync_lower");
- print_esr();
- reboot();
-}
-
-#[no_mangle]
-extern "C" fn irq_lower() {
- eprintln!("irq_lower");
- reboot();
-}
-
-#[no_mangle]
-extern "C" fn fiq_lower() {
- eprintln!("fiq_lower");
- reboot();
-}
-
-#[no_mangle]
-extern "C" fn serr_lower() {
- eprintln!("serr_lower");
- print_esr();
- reboot();
-}
-
-#[inline]
-fn print_esr() {
+extern "C" fn serr_current(_elr: u64, _spsr: u64) {
let esr = read_sysreg!("esr_el1");
- eprintln!("esr={:#08x}", esr);
+ panic!("serr_current, esr={esr:#08x}");
+}
+
+#[no_mangle]
+extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
+ let esr = read_sysreg!("esr_el1");
+ panic!("sync_lower, esr={esr:#08x}");
+}
+
+#[no_mangle]
+extern "C" fn irq_lower(_elr: u64, _spsr: u64) {
+ panic!("irq_lower");
+}
+
+#[no_mangle]
+extern "C" fn fiq_lower(_elr: u64, _spsr: u64) {
+ panic!("fiq_lower");
+}
+
+#[no_mangle]
+extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
+ let esr = read_sysreg!("esr_el1");
+ panic!("serr_lower, esr={esr:#08x}");
}
diff --git a/guest/trusty/security_vm/security_vm.mk b/guest/trusty/security_vm/security_vm.mk
index 89c9cdc..8717399 100644
--- a/guest/trusty/security_vm/security_vm.mk
+++ b/guest/trusty/security_vm/security_vm.mk
@@ -14,10 +14,14 @@
ifeq ($(findstring enabled, $(TRUSTY_SYSTEM_VM)),enabled)
-PRODUCT_PACKAGES += \
- trusty_security_vm.elf \
+
+# This is the default set of packages to load the trusty system vm.
+# It can be overridden by device-specific configuration.
+TRUSTY_SYSTEM_VM_PRODUCT_PACKAGES ?= trusty_security_vm.elf \
trusty_security_vm_launcher \
trusty_security_vm_launcher.rc \
early_vms.xml \
+PRODUCT_PACKAGES += $(TRUSTY_SYSTEM_VM_PRODUCT_PACKAGES)
+
endif
diff --git a/guest/vmbase_example/src/exceptions.rs b/guest/vmbase_example/src/exceptions.rs
index 5d7768a..0eb415c 100644
--- a/guest/vmbase_example/src/exceptions.rs
+++ b/guest/vmbase_example/src/exceptions.rs
@@ -14,62 +14,51 @@
//! Exception handlers.
-use vmbase::{eprintln, power::reboot, read_sysreg};
+use vmbase::{arch::aarch64::exceptions::ArmException, read_sysreg};
#[no_mangle]
-extern "C" fn sync_exception_current(_elr: u64, _spsr: u64) {
- eprintln!("sync_exception_current");
- print_esr();
- reboot();
+extern "C" fn sync_exception_current(elr: u64, _spsr: u64) {
+ ArmException::from_el1_regs().print_and_reboot(
+ "sync_exception_current",
+ "Unexpected synchronous exception",
+ elr,
+ );
}
#[no_mangle]
extern "C" fn irq_current(_elr: u64, _spsr: u64) {
- eprintln!("irq_current");
- reboot();
+ panic!("irq_current");
}
#[no_mangle]
extern "C" fn fiq_current(_elr: u64, _spsr: u64) {
- eprintln!("fiq_current");
- reboot();
+ panic!("fiq_current");
}
#[no_mangle]
extern "C" fn serr_current(_elr: u64, _spsr: u64) {
- eprintln!("serr_current");
- print_esr();
- reboot();
+ let esr = read_sysreg!("esr_el1");
+ panic!("serr_current, esr={:#08x}", esr);
}
#[no_mangle]
extern "C" fn sync_lower(_elr: u64, _spsr: u64) {
- eprintln!("sync_lower");
- print_esr();
- reboot();
+ let esr = read_sysreg!("esr_el1");
+ panic!("sync_lower, esr={:#08x}", esr);
}
#[no_mangle]
extern "C" fn irq_lower(_elr: u64, _spsr: u64) {
- eprintln!("irq_lower");
- reboot();
+ panic!("irq_lower");
}
#[no_mangle]
extern "C" fn fiq_lower(_elr: u64, _spsr: u64) {
- eprintln!("fiq_lower");
- reboot();
+ panic!("fiq_lower");
}
#[no_mangle]
extern "C" fn serr_lower(_elr: u64, _spsr: u64) {
- eprintln!("serr_lower");
- print_esr();
- reboot();
-}
-
-#[inline]
-fn print_esr() {
let esr = read_sysreg!("esr_el1");
- eprintln!("esr={:#08x}", esr);
+ panic!("serr_lower, esr={:#08x}", esr);
}
diff --git a/libs/libavf/Android.bp b/libs/libavf/Android.bp
index aceb927..4469af5 100644
--- a/libs/libavf/Android.bp
+++ b/libs/libavf/Android.bp
@@ -40,30 +40,8 @@
defaults: ["libavf.default"],
}
-soong_config_module_type {
- name: "virt_cc_defaults",
- module_type: "cc_defaults",
- config_namespace: "ANDROID",
- bool_variables: [
- "avf_enabled",
- ],
- properties: [
- "apex_available",
- ],
-}
-
-virt_cc_defaults {
- name: "libavf_apex_available_defaults",
- soong_config_variables: {
- avf_enabled: {
- apex_available: ["com.android.virt"],
- },
- },
-}
-
cc_library {
name: "libavf",
- defaults: ["libavf_apex_available_defaults"],
llndk: {
symbol_file: "libavf.map.txt",
moved_to_apex: true,
@@ -79,4 +57,5 @@
stubs: {
symbol_file: "libavf.map.txt",
},
+ apex_available: ["com.android.virt"],
}
diff --git a/libs/libvmbase/README.md b/libs/libvmbase/README.md
index 28d930a..3c05cc3 100644
--- a/libs/libvmbase/README.md
+++ b/libs/libvmbase/README.md
@@ -79,23 +79,16 @@
use vmbase::power::reboot;
extern "C" fn sync_exception_current() {
- eprintln!("sync_exception_current");
-
let mut esr: u64;
unsafe {
asm!("mrs {esr}, esr_el1", esr = out(reg) esr);
}
- eprintln!("esr={:#08x}", esr);
-
- reboot();
+ panic!("sync_exception_current, esr={:#08x}", esr);
}
```
The `println!` macro shouldn't be used in exception handlers, because it relies on a global instance
of the UART driver which might be locked when the exception happens, which would result in deadlock.
-Instead you can use `eprintln!`, which will re-initialize the UART every time to ensure that it can
-be used. This should still be used with care, as it may interfere with whatever the rest of the
-program is doing with the UART.
See [example/src/exceptions.rs](examples/src/exceptions.rs) for a complete example.
diff --git a/libs/libvmbase/src/arch/aarch64/exceptions.rs b/libs/libvmbase/src/arch/aarch64/exceptions.rs
index 1868bf7..787ef37 100644
--- a/libs/libvmbase/src/arch/aarch64/exceptions.rs
+++ b/libs/libvmbase/src/arch/aarch64/exceptions.rs
@@ -14,12 +14,17 @@
//! Helper functions and structs for exception handlers.
-use crate::memory::{MemoryTrackerError, MEMORY};
use crate::{
- arch::aarch64::layout::UART_PAGE_ADDR, arch::VirtualAddress, eprintln, memory::page_4kb_of,
+ arch::{
+ aarch64::layout::UART_PAGE_ADDR,
+ platform::{emergency_uart, DEFAULT_EMERGENCY_CONSOLE_INDEX},
+ VirtualAddress,
+ },
+ memory::{page_4kb_of, MemoryTrackerError, MEMORY},
+ power::reboot,
read_sysreg,
};
-use core::fmt;
+use core::fmt::{self, Write};
use core::result;
/// Represents an error that can occur while handling an exception.
@@ -122,14 +127,23 @@
Self { esr, far: VirtualAddress(far) }
}
- /// Prints the details of an obj and the exception, excluding UART exceptions.
- pub fn print<T: fmt::Display>(&self, exception_name: &str, obj: T, elr: u64) {
+ /// Prints the details of an obj and the exception, excluding UART exceptions, and then reboots.
+ ///
+ /// This uses the emergency console so can safely be called even for synchronous exceptions
+ /// without causing a deadlock.
+ pub fn print_and_reboot<T: fmt::Display>(&self, exception_name: &str, obj: T, elr: u64) -> ! {
// Don't print to the UART if we are handling an exception it could raise.
if !self.is_uart_exception() {
- eprintln!("{exception_name}");
- eprintln!("{obj}");
- eprintln!("{}, elr={:#08x}", self, elr);
+ // SAFETY: We always reboot at the end of this method so there is no way for the
+ // original UART driver to be used after this.
+ if let Some(mut console) = unsafe { emergency_uart(DEFAULT_EMERGENCY_CONSOLE_INDEX) } {
+ // Ignore errors writing to emergency console, as we are about to reboot anyway.
+ _ = writeln!(console, "{exception_name}");
+ _ = writeln!(console, "{obj}");
+ _ = writeln!(console, "{}, elr={:#08x}", self, elr);
+ }
}
+ reboot();
}
fn is_uart_exception(&self) -> bool {
diff --git a/libs/libvmbase/src/arch/aarch64/platform.rs b/libs/libvmbase/src/arch/aarch64/platform.rs
index b33df68..7e002d0 100644
--- a/libs/libvmbase/src/arch/aarch64/platform.rs
+++ b/libs/libvmbase/src/arch/aarch64/platform.rs
@@ -104,18 +104,25 @@
/// Return platform uart with specific index
///
-/// Panics if console was not initialized by calling [`init`] first.
-pub fn uart(id: usize) -> &'static spin::mutex::SpinMutex<Uart> {
- CONSOLES[id].get().unwrap()
+/// Returns `None` if console was not initialized by calling [`init`] first.
+pub fn uart(id: usize) -> Option<&'static SpinMutex<Uart>> {
+ CONSOLES[id].get()
}
-/// Reinitializes the emergency UART driver and returns it.
+/// Reinitializes the n-th UART driver and returns 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_uart() -> Uart {
+/// instance may be locked, such as in the synchronous exception handler.
+///
+/// # Safety
+///
+/// This takes over the UART from wherever it is being used, the existing UART instance should not
+/// be used after this is called. This should only be used immediately before aborting the VM.
+pub unsafe fn emergency_uart(id: usize) -> Option<Uart> {
+ let addr = *ADDRESSES[id].get()?;
+
// SAFETY: Initialization of UART using dedicated const address.
- unsafe { Uart::new(UART_ADDRESSES[DEFAULT_EMERGENCY_CONSOLE_INDEX]) }
+ Some(unsafe { Uart::new(addr) })
}
/// Makes a `PSCI_SYSTEM_OFF` call to shutdown the VM.
diff --git a/libs/libvmbase/src/console.rs b/libs/libvmbase/src/console.rs
index 6d9a4fe..dcaf1ad 100644
--- a/libs/libvmbase/src/console.rs
+++ b/libs/libvmbase/src/console.rs
@@ -14,33 +14,26 @@
//! Console driver for 8250 UART.
-use crate::arch::platform;
-use core::fmt::{write, Arguments, Write};
+use crate::arch::platform::{self, emergency_uart, DEFAULT_EMERGENCY_CONSOLE_INDEX};
+use crate::power::reboot;
+use core::fmt::{self, write, Arguments, Write};
+use core::panic::PanicInfo;
/// 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 uart = &mut *platform::uart(n).lock();
- write(uart, format_args).unwrap();
- let _ = uart.write_str("\n");
-}
+/// Returns an error if the n-th console was not initialized by calling [`init`] first.
+pub fn writeln(n: usize, format_args: Arguments) -> fmt::Result {
+ let uart = &mut *platform::uart(n).ok_or(fmt::Error)?.lock();
-/// Reinitializes the emergency 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 mut uart = platform::emergency_uart();
- let _ = write(&mut uart, format_args);
- let _ = uart.write_str("\n");
+ write(uart, format_args)?;
+ uart.write_str("\n")?;
+ Ok(())
}
/// 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.
+/// Returns an error if the console has not yet been initialized. May deadlock if used in a
+/// synchronous exception handler.
#[macro_export]
macro_rules! console_writeln {
($n:expr, $($arg:tt)*) => ({
@@ -48,27 +41,24 @@
})
}
-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.
+/// Panics if the console has not yet been initialized. May hang if used in an exception context.
macro_rules! println {
($($arg:tt)*) => ({
- $crate::console::console_writeln!($crate::arch::platform::DEFAULT_CONSOLE_INDEX, $($arg)*)
+ $crate::console_writeln!($crate::arch::platform::DEFAULT_CONSOLE_INDEX, $($arg)*).unwrap()
})
}
pub(crate) use println; // Make it available in this crate.
-/// 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 {
- ($($arg:tt)*) => ({
- $crate::console::ewriteln(format_args!($($arg)*))
- })
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+ // SAFETY: We always reboot at the end of this method so there is no way for the
+ // original UART driver to be used after this.
+ if let Some(mut console) = unsafe { emergency_uart(DEFAULT_EMERGENCY_CONSOLE_INDEX) } {
+ // Ignore errors, to avoid a panic loop.
+ let _ = writeln!(console, "{}", info);
+ }
+ reboot()
}
diff --git a/libs/libvmbase/src/lib.rs b/libs/libvmbase/src/lib.rs
index d254038..afb62fc 100644
--- a/libs/libvmbase/src/lib.rs
+++ b/libs/libvmbase/src/lib.rs
@@ -32,12 +32,3 @@
pub mod uart;
pub mod util;
pub mod virtio;
-
-use core::panic::PanicInfo;
-use power::reboot;
-
-#[panic_handler]
-fn panic(info: &PanicInfo) -> ! {
- eprintln!("{}", info);
- reboot()
-}
diff --git a/libs/libvmbase/src/logger.rs b/libs/libvmbase/src/logger.rs
index 9130918..0059d11 100644
--- a/libs/libvmbase/src/logger.rs
+++ b/libs/libvmbase/src/logger.rs
@@ -16,7 +16,7 @@
//!
//! Internally uses the println! vmbase macro, which prints to crosvm's UART.
//! Note: may not work if the VM is in an inconsistent state. Exception handlers
-//! should avoid using this logger and instead print with eprintln!.
+//! should avoid using this logger.
use crate::console::println;
use core::sync::atomic::{AtomicBool, Ordering};