microsoft/openvmm
Publicmirrored fromhttps://github.com/microsoft/openvmmAvailable
openhcl/minimal_rt/src/enlightened_panic.rs
80lines · modecode
| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // Licensed under the MIT License. |
| 3 | |
| 4 | //! Enlightened panic for a Hyper-V guest. |
| 5 | |
| 6 | //! Hyper-V guests may choose to report a crash to the Hyper-V host |
| 7 | //! via a set of MSRs (x64) or synthetic crash registers on ARM. |
| 8 | //! The registers carry crash-specific information and may optionally |
| 9 | //! include a message buffer. |
| 10 | |
| 11 | use crate::arch::write_crash_reg; |
| 12 | use arrayvec::ArrayString; |
| 13 | use core::fmt::Write; |
| 14 | use core::sync::atomic::AtomicBool; |
| 15 | use core::sync::atomic::Ordering::Relaxed; |
| 16 | use hvdef::GuestCrashCtl; |
| 17 | |
| 18 | // This is chosen to identify the boot shim as a pre-OS environment |
| 19 | const PRE_OS_ID: u8 = 5; |
| 20 | |
| 21 | static CAN_REPORT_MSG: AtomicBool = AtomicBool::new(false); |
| 22 | |
| 23 | fn report_raw(msg: &[u8], msg_pa: Option<usize>) { |
| 24 | // Before using the guest crash MSRs, could check |
| 25 | // if these are supported. Here, we don't do that |
| 26 | // as the intention is to fault anyways. |
| 27 | |
| 28 | let crash_ctl = GuestCrashCtl::new() |
| 29 | .with_pre_os_id(PRE_OS_ID) |
| 30 | .with_no_crash_dump(true) |
| 31 | .with_crash_message(!msg.is_empty()) |
| 32 | .with_crash_notify(true); |
| 33 | |
| 34 | // SAFETY: Using the contract established in the Hyper-V TLFS. |
| 35 | unsafe { |
| 36 | write_crash_reg(0, u64::from_le_bytes(*b"BOOTSHIM")); |
| 37 | write_crash_reg(1, u64::from_be_bytes(*b"IGVMBOOT")); |
| 38 | write_crash_reg(2, u64::MAX); |
| 39 | |
| 40 | match (msg, msg_pa) { |
| 41 | (msg @ [_, ..], Some(msg_pa)) => { |
| 42 | // There is a non-empty message and a valid physical address. |
| 43 | write_crash_reg(3, msg_pa as u64); |
| 44 | write_crash_reg(4, msg.len() as u64); |
| 45 | } |
| 46 | _ => { |
| 47 | write_crash_reg(3, 0); |
| 48 | write_crash_reg(4, 0); |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | // Report crash to Hyper-V |
| 53 | write_crash_reg(5, crash_ctl.into()); |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /// Reports the panic. |
| 58 | /// |
| 59 | /// `stack_va_to_pa` takes an object on the stack and returns its physical address. |
| 60 | pub fn report( |
| 61 | panic: &core::panic::PanicInfo<'_>, |
| 62 | mut stack_va_to_pa: impl FnMut(*const ()) -> Option<usize>, |
| 63 | ) { |
| 64 | let mut panic_buffer = ArrayString::<512>::new(); |
| 65 | if CAN_REPORT_MSG.load(Relaxed) { |
| 66 | let _ = write!(panic_buffer, "{}", panic); |
| 67 | } |
| 68 | report_raw( |
| 69 | panic_buffer.as_ref().as_bytes(), |
| 70 | stack_va_to_pa(panic_buffer.as_bytes().as_ptr().cast()), |
| 71 | ); |
| 72 | } |
| 73 | |
| 74 | /// Enables writing the enlightened panic message. |
| 75 | /// |
| 76 | /// If this is not called, then [`report`] will just report that a panic occurred |
| 77 | /// and not include the panic message. |
| 78 | pub fn enable_enlightened_panic() { |
| 79 | CAN_REPORT_MSG.store(true, Relaxed); |
| 80 | } |
| 81 | |