use core::fmt::{Debug, Formatter};
pub struct VmxInstructionError(u32);
impl VmxInstructionError {
pub fn as_str(&self) -> &str {
match self.0 {
0 => "OK",
1 => "VMCALL executed in VMX root operation",
2 => "VMCLEAR with invalid physical address",
3 => "VMCLEAR with VMXON pointer",
4 => "VMLAUNCH with non-clear VMCS",
5 => "VMRESUME with non-launched VMCS",
6 => "VMRESUME after VMXOFF (VMXOFF and VMXON between VMLAUNCH and VMRESUME)",
7 => "VM entry with invalid control field(s)",
8 => "VM entry with invalid host-state field(s)",
9 => "VMPTRLD with invalid physical address",
10 => "VMPTRLD with VMXON pointer",
11 => "VMPTRLD with incorrect VMCS revision identifier",
12 => "VMREAD/VMWRITE from/to unsupported VMCS component",
13 => "VMWRITE to read-only VMCS component",
15 => "VMXON executed in VMX root operation",
16 => "VM entry with invalid executive-VMCS pointer",
17 => "VM entry with non-launched executive VMCS",
18 => {
"VM entry with executive-VMCS pointer not VMXON pointer (when attempting to \
deactivate the dual-monitor treatment of SMIs and SMM)"
}
19 => {
"VMCALL with non-clear VMCS (when attempting to activate the dual-monitor \
treatment of SMIs and SMM)"
}
20 => "VMCALL with invalid VM-exit control fields",
22 => {
"VMCALL with incorrect MSEG revision identifier (when attempting to activate the \
dual-monitor treatment of SMIs and SMM)"
}
23 => "VMXOFF under dual-monitor treatment of SMIs and SMM",
24 => {
"VMCALL with invalid SMM-monitor features (when attempting to activate the \
dual-monitor treatment of SMIs and SMM)"
}
25 => {
"VM entry with invalid VM-execution control fields in executive VMCS (when \
attempting to return from SMM)"
}
26 => "VM entry with events blocked by MOV SS",
28 => "Invalid operand to INVEPT/INVVPID",
_ => "[INVALID]",
}
}
}
impl From<u32> for VmxInstructionError {
fn from(value: u32) -> Self {
Self(value)
}
}
impl Debug for VmxInstructionError {
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
write!(f, "VmxInstructionError({}, {:?})", self.0, self.as_str())
}
}
#[repr(u32)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum VmxExitReason {
ExceptionNmi = 0,
ExternalInterrupt = 1,
TripleFault = 2,
Init = 3,
Sipi = 4,
Smi = 5,
OtherSmi = 6,
InterruptWindow = 7,
NmiWindow = 8,
TaskSwitch = 9,
Cpuid = 10,
Getsec = 11,
Hlt = 12,
Invd = 13,
Invlpg = 14,
Rdpmc = 15,
Rdtsc = 16,
Rsm = 17,
Vmcall = 18,
Vmclear = 19,
Vmlaunch = 20,
Vmptrld = 21,
Vmptrst = 22,
Vmread = 23,
Vmresume = 24,
Vmwrite = 25,
Vmoff = 26,
Vmon = 27,
CrAccess = 28,
DrAccess = 29,
IoInstruction = 30,
MsrRead = 31,
MsrWrite = 32,
InvalidGuestState = 33,
MsrLoadFail = 34,
MwaitInstruction = 36,
MonitorTrapFlag = 37,
MonitorInstruction = 39,
PauseInstruction = 40,
MceDuringVmentry = 41,
TprBelowThreshold = 43,
ApicAccess = 44,
VirtualizedEoi = 45,
GdtrIdtr = 46,
LdtrTr = 47,
EptViolation = 48,
EptMisconfig = 49,
Invept = 50,
Rdtscp = 51,
PreemptionTimer = 52,
Invvpid = 53,
Wbinvd = 54,
Xsetbv = 55,
ApicWrite = 56,
Rdrand = 57,
Invpcid = 58,
Vmfunc = 59,
Encls = 60,
Rdseed = 61,
PmlFull = 62,
Xsaves = 63,
Xrstors = 64,
Pconfig = 65,
SppEvent = 66,
Umwait = 67,
Tpause = 68,
Loadiwkey = 69,
}
impl TryFrom<u32> for VmxExitReason {
type Error = u32;
fn try_from(raw: u32) -> Result<Self, Self::Error> {
match raw {
0 => Ok(Self::ExceptionNmi),
1 => Ok(Self::ExternalInterrupt),
2 => Ok(Self::TripleFault),
3 => Ok(Self::Init),
4 => Ok(Self::Sipi),
5 => Ok(Self::Smi),
6 => Ok(Self::OtherSmi),
7 => Ok(Self::InterruptWindow),
8 => Ok(Self::NmiWindow),
9 => Ok(Self::TaskSwitch),
10 => Ok(Self::Cpuid),
11 => Ok(Self::Getsec),
12 => Ok(Self::Hlt),
13 => Ok(Self::Invd),
14 => Ok(Self::Invlpg),
15 => Ok(Self::Rdpmc),
16 => Ok(Self::Rdtsc),
17 => Ok(Self::Rsm),
18 => Ok(Self::Vmcall),
19 => Ok(Self::Vmclear),
20 => Ok(Self::Vmlaunch),
21 => Ok(Self::Vmptrld),
22 => Ok(Self::Vmptrst),
23 => Ok(Self::Vmread),
24 => Ok(Self::Vmresume),
25 => Ok(Self::Vmwrite),
26 => Ok(Self::Vmoff),
27 => Ok(Self::Vmon),
28 => Ok(Self::CrAccess),
29 => Ok(Self::DrAccess),
30 => Ok(Self::IoInstruction),
31 => Ok(Self::MsrRead),
32 => Ok(Self::MsrWrite),
33 => Ok(Self::InvalidGuestState),
34 => Ok(Self::MsrLoadFail),
36 => Ok(Self::MwaitInstruction),
37 => Ok(Self::MonitorTrapFlag),
39 => Ok(Self::MonitorInstruction),
40 => Ok(Self::PauseInstruction),
41 => Ok(Self::MceDuringVmentry),
43 => Ok(Self::TprBelowThreshold),
44 => Ok(Self::ApicAccess),
45 => Ok(Self::VirtualizedEoi),
46 => Ok(Self::GdtrIdtr),
47 => Ok(Self::LdtrTr),
48 => Ok(Self::EptViolation),
49 => Ok(Self::EptMisconfig),
50 => Ok(Self::Invept),
51 => Ok(Self::Rdtscp),
52 => Ok(Self::PreemptionTimer),
53 => Ok(Self::Invvpid),
54 => Ok(Self::Wbinvd),
55 => Ok(Self::Xsetbv),
56 => Ok(Self::ApicWrite),
57 => Ok(Self::Rdrand),
58 => Ok(Self::Invpcid),
59 => Ok(Self::Vmfunc),
60 => Ok(Self::Encls),
61 => Ok(Self::Rdseed),
62 => Ok(Self::PmlFull),
63 => Ok(Self::Xsaves),
64 => Ok(Self::Xrstors),
65 => Ok(Self::Pconfig),
66 => Ok(Self::SppEvent),
67 => Ok(Self::Umwait),
68 => Ok(Self::Tpause),
69 => Ok(Self::Loadiwkey),
_ => Err(raw),
}
}
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum VmxInterruptionType {
External = 0,
Reserved = 1,
Nmi = 2,
HardException = 3,
SoftIntr = 4,
PrivSoftException = 5,
SoftException = 6,
Other = 7,
}
impl TryFrom<u8> for VmxInterruptionType {
type Error = u8;
fn try_from(raw: u8) -> Result<Self, Self::Error> {
match raw {
0 => Ok(Self::External),
1 => Ok(Self::Reserved),
2 => Ok(Self::Nmi),
3 => Ok(Self::HardException),
4 => Ok(Self::SoftIntr),
5 => Ok(Self::PrivSoftException),
6 => Ok(Self::SoftException),
7 => Ok(Self::Other),
_ => Err(raw),
}
}
}
impl VmxInterruptionType {
pub const fn vector_has_error_code(vector: u8) -> bool {
use x86::irq::*;
matches!(
vector,
DOUBLE_FAULT_VECTOR
| INVALID_TSS_VECTOR
| SEGMENT_NOT_PRESENT_VECTOR
| STACK_SEGEMENT_FAULT_VECTOR
| GENERAL_PROTECTION_FAULT_VECTOR
| PAGE_FAULT_VECTOR
| ALIGNMENT_CHECK_VECTOR
)
}
pub const fn from_vector(vector: u8) -> Self {
use x86::irq::*;
match vector {
DEBUG_VECTOR => Self::PrivSoftException,
NONMASKABLE_INTERRUPT_VECTOR => Self::Nmi,
BREAKPOINT_VECTOR | OVERFLOW_VECTOR => Self::SoftException,
0..=VIRTUALIZATION_VECTOR => Self::HardException,
32..=255 => Self::External,
_ => Self::Other,
}
}
pub const fn is_soft(&self) -> bool {
matches!(
*self,
Self::SoftIntr | Self::SoftException | Self::PrivSoftException
)
}
}
#[derive(Debug)]
pub struct VmxExitInfo {
pub entry_failure: bool,
pub exit_reason: Result<VmxExitReason, u32>,
pub exit_instruction_length: u32,
pub guest_rip: usize,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct VmxInterruptInfo {
pub vector: u8,
pub int_type: VmxInterruptionType,
pub err_code: Option<u32>,
pub valid: bool,
}
impl VmxInterruptInfo {
pub fn from(vector: u8, err_code: Option<u32>) -> Self {
Self {
vector,
int_type: VmxInterruptionType::from_vector(vector),
err_code,
valid: true,
}
}
pub fn bits(&self) -> u32 {
let mut bits = self.vector as u32;
bits |= (self.int_type as u32) << 8;
bits |= u32::from(self.err_code.is_some()) << 11;
bits |= u32::from(self.valid) << 31;
bits
}
}
#[derive(Debug)]
pub struct VmxIoExitInfo {
pub access_size: u8,
pub is_in: bool,
pub is_string: bool,
pub is_repeat: bool,
pub port: u16,
}
#[derive(Debug)]
pub struct CrAccessInfo {
pub cr_number: u8,
pub access_type: u8,
pub lmsw_op_type: u8,
pub gpr: u8,
pub lmsw_source_data: u16,
}
impl CrAccessInfo {
pub const fn decode(qualification: usize) -> Self {
Self {
cr_number: (qualification & 15) as u8,
access_type: ((qualification >> 4) & 3) as u8,
lmsw_op_type: ((qualification >> 6) & 1) as u8,
gpr: ((qualification >> 8) & 15) as u8,
lmsw_source_data: (qualification >> 16) as u16,
}
}
}
#[derive(Debug)]
pub enum ApicAccessExitType {
LinearDataRead = 0,
LinearDataWrite = 1,
LinearInstructionFetch = 2,
LinearEventDelivery = 3,
LinearMonitoring = 4,
GuestPhysicalEventDelivery = 10,
GuestPhysicalMonitoring = 11,
GuestPhysicalInstructionFetchReadWrite = 15,
}
impl TryFrom<u8> for ApicAccessExitType {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Self::LinearDataRead),
1 => Ok(Self::LinearDataWrite),
2 => Ok(Self::LinearInstructionFetch),
3 => Ok(Self::LinearEventDelivery),
4 => Ok(Self::LinearMonitoring),
10 => Ok(Self::GuestPhysicalEventDelivery),
11 => Ok(Self::GuestPhysicalMonitoring),
15 => Ok(Self::GuestPhysicalInstructionFetchReadWrite),
_ => Err(()),
}
}
}
#[derive(Debug)]
pub struct ApicAccessExitInfo {
pub offset: u16,
pub access_type: ApicAccessExitType,
pub non_event_delivery_asynchronous: bool,
}
impl<M: crate::virtualization::ControlMemory> super::VmxControls<M> {
pub fn exit_info(&self) -> Result<VmxExitInfo, crate::virtualization::VirtualizationError> {
use super::{VmcsGuestNW, VmcsReadOnly32};
let reason = self.read(VmcsReadOnly32::EXIT_REASON)?;
Ok(VmxExitInfo {
exit_reason: VmxExitReason::try_from(reason & 0xffff),
entry_failure: reason & (1 << 31) != 0,
exit_instruction_length: self.read(VmcsReadOnly32::VMEXIT_INSTRUCTION_LEN)?,
guest_rip: self.read(VmcsGuestNW::RIP)?,
})
}
pub fn interrupt_exit_info(
&self,
) -> Result<VmxInterruptInfo, crate::virtualization::VirtualizationError> {
use super::VmcsReadOnly32;
let info = self.read(VmcsReadOnly32::VMEXIT_INTERRUPTION_INFO)?;
let error = if info & ((1 << 31) | (1 << 11)) == ((1 << 31) | (1 << 11)) {
Some(self.read(VmcsReadOnly32::VMEXIT_INTERRUPTION_ERR_CODE)?)
} else {
None
};
Ok(VmxInterruptInfo::decode(info, error))
}
pub fn idt_vectoring_info(
&self,
) -> Result<VmxInterruptInfo, crate::virtualization::VirtualizationError> {
use super::VmcsReadOnly32;
let info = self.read(VmcsReadOnly32::IDT_VECTORING_INFO)?;
let error = if info & ((1 << 31) | (1 << 11)) == ((1 << 31) | (1 << 11)) {
Some(self.read(VmcsReadOnly32::IDT_VECTORING_ERR_CODE)?)
} else {
None
};
Ok(VmxInterruptInfo::decode(info, error))
}
pub fn inject_interrupt(
&mut self,
info: VmxInterruptInfo,
instruction_len: u32,
) -> Result<(), crate::virtualization::VirtualizationError> {
use super::VmcsControl32;
if let Some(error) = info.err_code {
self.write(VmcsControl32::VMENTRY_EXCEPTION_ERR_CODE, error)?;
}
if info.int_type.is_soft() {
self.write(VmcsControl32::VMENTRY_INSTRUCTION_LEN, instruction_len)?;
}
self.write(VmcsControl32::VMENTRY_INTERRUPTION_INFO_FIELD, info.bits())
}
pub fn io_exit_info(
&self,
) -> Result<VmxIoExitInfo, crate::virtualization::VirtualizationError> {
let value = self.read(super::VmcsReadOnlyNW::EXIT_QUALIFICATION)?;
Ok(VmxIoExitInfo {
access_size: (value & 7) as u8 + 1,
is_in: value & (1 << 3) != 0,
is_string: value & (1 << 4) != 0,
is_repeat: value & (1 << 5) != 0,
port: (value >> 16) as u16,
})
}
}
impl VmxInterruptInfo {
pub fn decode(info: u32, error_code: Option<u32>) -> Self {
if info & (1 << 31) == 0 {
return Self {
vector: 0,
int_type: VmxInterruptionType::External,
err_code: None,
valid: false,
};
}
let int_type = match (info >> 8) & 7 {
0 => VmxInterruptionType::External,
1 => VmxInterruptionType::Reserved,
2 => VmxInterruptionType::Nmi,
3 => VmxInterruptionType::HardException,
4 => VmxInterruptionType::SoftIntr,
5 => VmxInterruptionType::PrivSoftException,
6 => VmxInterruptionType::SoftException,
_ => VmxInterruptionType::Other,
};
Self {
vector: info as u8,
int_type,
err_code: if info & (1 << 11) != 0 {
error_code
} else {
None
},
valid: true,
}
}
}