Skip to content

Commit 7c34f2a

Browse files
committed
x86
1 parent 2cc4234 commit 7c34f2a

File tree

2 files changed

+93
-1
lines changed

2 files changed

+93
-1
lines changed

src/vmx/vcpu.rs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use alloc::collections::VecDeque;
2+
use axaddrspace::device::AccessWidth;
23
use bit_field::BitField;
34
use core::fmt::{Debug, Formatter, Result};
45
use core::{arch::naked_asm, mem::size_of};
@@ -11,7 +12,7 @@ use x86_64::registers::control::{Cr0, Cr0Flags, Cr3, Cr4, Cr4Flags, EferFlags};
1112

1213
use axaddrspace::{GuestPhysAddr, GuestVirtAddr, HostPhysAddr, NestedPageFaultInfo};
1314
use axerrno::{AxResult, ax_err, ax_err_type};
14-
use axvcpu::{AccessWidth, AxArchVCpu, AxVCpuExitReason, AxVCpuHal};
15+
use axvcpu::{AxArchVCpu, AxVCpuExitReason, AxVCpuHal};
1516

1617
use super::VmxExitInfo;
1718
use super::as_axerr;
@@ -21,6 +22,7 @@ use super::vmcs::{
2122
self, VmcsControl32, VmcsControl64, VmcsControlNW, VmcsGuest16, VmcsGuest32, VmcsGuest64,
2223
VmcsGuestNW, VmcsHost16, VmcsHost32, VmcsHost64, VmcsHostNW,
2324
};
25+
use crate::vmx::vmcs::VmcsReadOnly64;
2426
use crate::{ept::GuestPageWalkInfo, msr::Msr, regs::GeneralRegisters};
2527

2628
const VMX_PREEMPTION_TIMER_SET_VALUE: u32 = 1_000_000;
@@ -866,6 +868,10 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
866868
rflags as u64 & x86_64::registers::rflags::RFlags::INTERRUPT_FLAG.bits() != 0
867869
&& block_state == 0
868870
}
871+
/// MMIO access information.
872+
pub fn mmio_access_info(&self) -> AxResult<vmcs::MmioAccessInfo> {
873+
vmcs::mmio_access_info()
874+
}
869875

870876
/// Try to inject a pending event before next VM entry.
871877
fn inject_pending_events(&mut self) -> AxResult {
@@ -1229,6 +1235,31 @@ impl<H: AxVCpuHal> AxArchVCpu for VmxVcpu<H> {
12291235
}
12301236
}
12311237
}
1238+
VmxExitReason::EPT_VIOLATION => {
1239+
self.advance_rip(exit_info.exit_instruction_length as _)?;
1240+
self.advance_rip(exit_info.exit_instruction_length as _)?;
1241+
let mmio_info = self.mmio_access_info();
1242+
error!("VMX EPT Violation: {:#x?} of {:#x?}", mmio_info, exit_info);
1243+
if let Ok(mmio_info) = mmio_info {
1244+
if mmio_info.is_read {
1245+
self.set_gpr(0, 0x74726976);
1246+
} else {
1247+
if let Some(data) = mmio_info.data {
1248+
error!("Data write {:#x}", data);
1249+
} else {
1250+
error!("Data write None");
1251+
}
1252+
}
1253+
};
1254+
1255+
AxVCpuExitReason::Nothing
1256+
// AxVCpuExitReason::MmioRead {
1257+
// addr: mmio_info.addr,
1258+
// width: mmio_info.access_width,
1259+
// reg: mmio_info.register as usize,
1260+
// reg_width: mmio_info.access_width,
1261+
// }
1262+
}
12321263
_ => {
12331264
warn!("VMX unsupported VM-Exit: {:#x?}", exit_info);
12341265
warn!("VCpu {:#x?}", self);

src/vmx/vmcs.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![allow(non_camel_case_types)]
44
#![allow(clippy::upper_case_acronyms)]
55

6+
use axaddrspace::device::AccessWidth;
67
use bit_field::BitField;
78
use x86::bits64::vmx;
89

@@ -512,6 +513,17 @@ pub struct VmxInterruptInfo {
512513
pub valid: bool,
513514
}
514515

516+
/// MMIO Access Information extracted from EPT violation.
517+
#[derive(Debug)]
518+
pub struct MmioAccessInfo {
519+
pub addr: GuestPhysAddr,
520+
pub access_width: AccessWidth,
521+
pub is_read: bool,
522+
pub is_write: bool,
523+
pub register: u8,
524+
pub data: Option<u64>,
525+
}
526+
515527
impl VmxInterruptInfo {
516528
/// Convert from the interrupt vector and the error code.
517529
pub fn from(vector: u8, err_code: Option<u32>) -> Self {
@@ -772,3 +784,52 @@ pub fn cr_access_info() -> AxResult<CrAccessInfo> {
772784
lmsw_source_data: qualification.get_bits(16..32) as u8,
773785
})
774786
}
787+
788+
/// Extract detailed MMIO access information from EPT violation.
789+
///
790+
/// This function provides comprehensive information about MMIO device access
791+
/// that triggered an EPT violation, including the access address, width,
792+
/// read/write direction, and instruction details.
793+
///
794+
/// # Returns
795+
/// * `Ok(MmioAccessInfo)` - Detailed MMIO access information
796+
/// * `Err(AxError)` - If VMCS read operations fail
797+
pub fn mmio_access_info() -> AxResult<MmioAccessInfo> {
798+
let qualification = VmcsReadOnlyNW::EXIT_QUALIFICATION.read()?;
799+
let instruction_info = VmcsReadOnly32::VMEXIT_INSTRUCTION_INFO.read()?;
800+
let fault_guest_paddr = VmcsReadOnly64::GUEST_PHYSICAL_ADDR.read()? as usize;
801+
// VmcsGuestNW::CR0.write(guest_rax).unwrap(); // Example write to CR0, replace with actual logic
802+
// Extract access type from qualification
803+
error!("qualification: {:#x}", qualification);
804+
let is_read = qualification.get_bit(0);
805+
// Access type is determined by the second bit in the qualification
806+
let is_write = qualification.get_bit(1);
807+
// Decode instruction information to get access width and register
808+
809+
let operand_size = instruction_info.get_bits(0..3);
810+
let access_width = match operand_size + 1 {
811+
0 => AccessWidth::Word, // 16-bit
812+
1 => AccessWidth::Dword, // 32-bit
813+
2 => AccessWidth::Qword, // 64-bit
814+
3 => AccessWidth::Byte, // 8-bit
815+
_ => AccessWidth::Dword, // Default to 32-bit for unknown encodings
816+
};
817+
818+
let addr = GuestPhysAddr::from(fault_guest_paddr);
819+
let reg_index: u8 = instruction_info.get_bits(7..10) as u8;
820+
821+
let data = if is_write {
822+
Some(0 as u64)
823+
} else {
824+
None
825+
};
826+
827+
Ok(MmioAccessInfo {
828+
addr,
829+
access_width,
830+
is_read,
831+
is_write,
832+
register: reg_index,
833+
data,
834+
})
835+
}

0 commit comments

Comments
 (0)