| // Copyright 2018 The Fuchsia Authors | |
| // | |
| // Use of this source code is governed by a MIT-style | |
| // license that can be found in the LICENSE file or at | |
| // https://opensource.org/licenses/MIT | |
| //! Hypervisor ktrace helpers mirroring `<hypervisor/ktrace.h>`. | |
| use core::mem::{align_of, size_of}; | |
| use zr::static_assert; | |
| use crate::ktrace_rs::{InternedString, declare_interned_string, duration_begin, duration_end}; | |
| /// Metadata categories for VCPU blocking operations. | |
| #[repr(u8)] | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum VcpuMeta { | |
| /// Waiting for an interrupt. | |
| Interrupt = 0, | |
| /// Waiting for a port packet. | |
| Port = 1, | |
| } | |
| const VCPU_META_COUNT: usize = 2; | |
| static_assert!(size_of::<VcpuMeta>() == size_of::<u8>()); | |
| static_assert!(align_of::<VcpuMeta>() == align_of::<u8>()); | |
| /// VCPU exit reasons. | |
| #[repr(u8)] | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum VcpuExit { | |
| /// GIC underflow maintenance interrupt (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| UnderflowMaintenanceInterrupt, | |
| /// Physical interrupt (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| PhysicalInterrupt, | |
| /// WFI instruction trap (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| WfiInstruction, | |
| /// WFE instruction trap (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| WfeInstruction, | |
| /// SMC instruction trap (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| SmcInstruction, | |
| /// System register access trap (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| SystemInstruction, | |
| /// Stage-2 instruction abort (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| InstructionAbort, | |
| /// Stage-2 data abort (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| DataAbort, | |
| /// SError interrupt (arm64). | |
| #[cfg(target_arch = "aarch64")] | |
| SerrorInterrupt, | |
| /// Exception or non-maskable interrupt (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| ExceptionOrNmi, | |
| /// External interrupt (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| ExternalInterrupt, | |
| /// Interrupt-window exiting (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| InterruptWindow, | |
| /// CPUID instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Cpuid, | |
| /// HLT instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Hlt, | |
| /// Control register access trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| ControlRegisterAccess, | |
| /// I/O instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| IoInstruction, | |
| /// RDMSR instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Rdmsr, | |
| /// WRMSR instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Wrmsr, | |
| /// VM-entry failure (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| VmEntryFailure, | |
| /// EPT violation (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| EptViolation, | |
| /// XSETBV instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Xsetbv, | |
| /// PAUSE instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Pause, | |
| /// VMCALL instruction trap (x86_64). | |
| #[cfg(target_arch = "x86_64")] | |
| Vmcall, | |
| /// Unsupported guest operation or exit reason. | |
| NotSupported, | |
| /// Internal hypervisor failure handling VCPU exit. | |
| Failure, | |
| } | |
| const VCPU_EXIT_COUNT: usize = VcpuExit::Failure as usize + 1; | |
| static_assert!(size_of::<VcpuExit>() == size_of::<u8>()); | |
| static_assert!(align_of::<VcpuExit>() == align_of::<u8>()); | |
| /// VCPU block/unblock operation type. | |
| #[repr(u8)] | |
| #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
| pub enum VcpuBlockOp { | |
| /// VCPU begins blocking. | |
| Block = 0, | |
| /// VCPU finishes blocking. | |
| Unblock = 1, | |
| } | |
| static_assert!(size_of::<VcpuBlockOp>() == size_of::<u8>()); | |
| static_assert!(align_of::<VcpuBlockOp>() == align_of::<u8>()); | |
| declare_interned_string!(WAIT_INTERRUPT, "wait:interrupt"); | |
| declare_interned_string!(WAIT_PORT, "wait:port"); | |
| declare_interned_string!(VCPU_META_FALLBACK, "vcpu meta"); | |
| static VCPU_META: [&InternedString; VCPU_META_COUNT] = [WAIT_INTERRUPT, WAIT_PORT]; | |
| #[cfg(target_arch = "aarch64")] | |
| mod exit_strings { | |
| use super::VCPU_EXIT_COUNT; | |
| use crate::ktrace_rs::{InternedString, declare_interned_string}; | |
| declare_interned_string!( | |
| UNDERFLOW_MAINTENANCE_INTERRUPT, | |
| "exit:underflow_maintenance_interrupt" | |
| ); | |
| declare_interned_string!(PHYSICAL_INTERRUPT, "exit:physical_interrupt"); | |
| declare_interned_string!(WFI_INSTRUCTION, "exit:wfi_instruction"); | |
| declare_interned_string!(WFE_INSTRUCTION, "exit:wfe_instruction"); | |
| declare_interned_string!(SMC_INSTRUCTION, "exit:smc_instruction"); | |
| declare_interned_string!(SYSTEM_INSTRUCTION, "exit:system_instruction"); | |
| declare_interned_string!(INSTRUCTION_ABORT, "exit:instruction_abort"); | |
| declare_interned_string!(DATA_ABORT, "exit:data_abort"); | |
| declare_interned_string!(SERROR_INTERRUPT, "exit:serror_interrupt"); | |
| declare_interned_string!(NOT_SUPPORTED, "exit:not_supported"); | |
| declare_interned_string!(FAILURE, "exit:failure"); | |
| pub(super) static VCPU_EXIT: [&InternedString; VCPU_EXIT_COUNT] = [ | |
| UNDERFLOW_MAINTENANCE_INTERRUPT, | |
| PHYSICAL_INTERRUPT, | |
| WFI_INSTRUCTION, | |
| WFE_INSTRUCTION, | |
| SMC_INSTRUCTION, | |
| SYSTEM_INSTRUCTION, | |
| INSTRUCTION_ABORT, | |
| DATA_ABORT, | |
| SERROR_INTERRUPT, | |
| NOT_SUPPORTED, | |
| FAILURE, | |
| ]; | |
| } | |
| #[cfg(target_arch = "x86_64")] | |
| mod exit_strings { | |
| use super::VCPU_EXIT_COUNT; | |
| use crate::ktrace_rs::{InternedString, declare_interned_string}; | |
| declare_interned_string!(EXCEPTION_OR_NMI, "exit:exception_or_nmi"); | |
| declare_interned_string!(EXTERNAL_INTERRUPT, "exit:external_interrupt"); | |
| declare_interned_string!(INTERRUPT_WINDOW, "exit:interrupt_window"); | |
| declare_interned_string!(CPUID, "exit:cpuid"); | |
| declare_interned_string!(HLT, "exit:hlt"); | |
| declare_interned_string!(CONTROL_REGISTER_ACCESS, "exit:control_register_access"); | |
| declare_interned_string!(IO_INSTRUCTION, "exit:io_instruction"); | |
| declare_interned_string!(RDMSR, "exit:rdmsr"); | |
| declare_interned_string!(WRMSR, "exit:wrmsr"); | |
| declare_interned_string!(VM_ENTRY_FAILURE, "exit:vm_entry_failure"); | |
| declare_interned_string!(EPT_VIOLATION, "exit:ept_violation"); | |
| declare_interned_string!(XSETBV, "exit:xsetbv"); | |
| declare_interned_string!(PAUSE, "exit:pause"); | |
| declare_interned_string!(VMCALL, "exit:vmcall"); | |
| declare_interned_string!(NOT_SUPPORTED, "exit:not_supported"); | |
| declare_interned_string!(FAILURE, "exit:failure"); | |
| pub(super) static VCPU_EXIT: [&InternedString; VCPU_EXIT_COUNT] = [ | |
| EXCEPTION_OR_NMI, | |
| EXTERNAL_INTERRUPT, | |
| INTERRUPT_WINDOW, | |
| CPUID, | |
| HLT, | |
| CONTROL_REGISTER_ACCESS, | |
| IO_INSTRUCTION, | |
| RDMSR, | |
| WRMSR, | |
| VM_ENTRY_FAILURE, | |
| EPT_VIOLATION, | |
| XSETBV, | |
| PAUSE, | |
| VMCALL, | |
| NOT_SUPPORTED, | |
| FAILURE, | |
| ]; | |
| } | |
| #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))] | |
| mod exit_strings { | |
| use super::VCPU_EXIT_COUNT; | |
| use crate::ktrace_rs::{InternedString, declare_interned_string}; | |
| declare_interned_string!(NOT_SUPPORTED, "exit:not_supported"); | |
| declare_interned_string!(FAILURE, "exit:failure"); | |
| pub(super) static VCPU_EXIT: [&InternedString; VCPU_EXIT_COUNT] = [NOT_SUPPORTED, FAILURE]; | |
| } | |
| /// Records a VCPU block or unblock ktrace duration event. | |
| pub fn ktrace_vcpu(block_op: VcpuBlockOp, meta: VcpuMeta) { | |
| let name = VCPU_META.get(meta as usize).copied().unwrap_or(VCPU_META_FALLBACK); | |
| match block_op { | |
| VcpuBlockOp::Block => { | |
| duration_begin!("kernel:arch", name, "meta #" => meta as u32); | |
| } | |
| VcpuBlockOp::Unblock => { | |
| duration_end!("kernel:arch", name); | |
| } | |
| } | |
| } | |
| /// Records a VCPU exit ktrace duration end event. | |
| pub fn ktrace_vcpu_exit(exit: VcpuExit, exit_address: u64) { | |
| if let Some(&exit_type) = exit_strings::VCPU_EXIT.get(exit as usize) { | |
| duration_end!( | |
| "kernel:arch", | |
| "vcpu", | |
| "exit_address" => exit_address, | |
| "exit_type" => exit_type, | |
| ); | |
| } else { | |
| duration_end!( | |
| "kernel:arch", | |
| "vcpu", | |
| "exit_address" => exit_address, | |
| "exit_type" => exit as u32, | |
| ); | |
| } | |
| } |