blob: 5501805afa0a8dafad535a3da7b25582a2e3e476 [file]
// 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,
);
}
}