Skip to content

Commit cdc159a

Browse files
committed
新增 MMIO 访问信息结构体及相关函数以处理 EPT 违规
1 parent 2cc4234 commit cdc159a

File tree

2 files changed

+137
-7
lines changed

2 files changed

+137
-7
lines changed

src/vmx/vcpu.rs

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
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};
56
use raw_cpuid::CpuId;
67
use x86::bits64::vmx;
7-
use x86::controlregs::{Xcr0, xcr0 as xcr0_read, xcr0_write};
8+
use x86::controlregs::{xcr0 as xcr0_read, xcr0_write, Xcr0};
89
use x86::dtables::{self, DescriptorTablePointer};
910
use x86::segmentation::SegmentSelector;
1011
use x86_64::registers::control::{Cr0, Cr0Flags, Cr3, Cr4, Cr4Flags, EferFlags};
1112

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

16-
use super::VmxExitInfo;
1717
use super::as_axerr;
1818
use super::definitions::VmxExitReason;
1919
use super::structs::{IOBitmap, MsrBitmap, VmxRegion};
2020
use super::vmcs::{
2121
self, VmcsControl32, VmcsControl64, VmcsControlNW, VmcsGuest16, VmcsGuest32, VmcsGuest64,
2222
VmcsGuestNW, VmcsHost16, VmcsHost32, VmcsHost64, VmcsHostNW,
2323
};
24+
use super::VmxExitInfo;
2425
use crate::{ept::GuestPageWalkInfo, msr::Msr, regs::GeneralRegisters};
2526

2627
const VMX_PREEMPTION_TIMER_SET_VALUE: u32 = 1_000_000;
@@ -284,6 +285,11 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
284285
vmcs::exit_info()
285286
}
286287

288+
/// MMIO access information.
289+
pub fn mmio_access_info(&self) -> AxResult<vmcs::MmioAccessInfo> {
290+
vmcs::mmio_access_info()
291+
}
292+
287293
/// Raw information for VM Exits Due to Vectored Events, See SDM 25.9.2
288294
pub fn raw_interrupt_exit_info(&self) -> AxResult<u32> {
289295
vmcs::raw_interrupt_exit_info()
@@ -953,7 +959,7 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
953959
}
954960

955961
fn handle_cpuid(&mut self) -> AxResult {
956-
use raw_cpuid::{CpuIdResult, cpuid};
962+
use raw_cpuid::{cpuid, CpuIdResult};
957963

958964
const VM_EXIT_INSTR_LEN_CPUID: u8 = 2;
959965
const LEAF_FEATURE_INFO: u32 = 0x1;
@@ -984,7 +990,7 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
984990
if regs_clone.rcx == 0 {
985991
// Bit 05: WAITPKG.
986992
res.ecx.set_bit(5, false); // clear waitpkg
987-
// Bit 16: LA57. Supports 57-bit linear addresses and five-level paging if 1.
993+
// Bit 16: LA57. Supports 57-bit linear addresses and five-level paging if 1.
988994
res.ecx.set_bit(16, false); // clear LA57
989995
}
990996

@@ -1028,7 +1034,9 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
10281034

10291035
trace!(
10301036
"VM exit: CPUID({:#x}, {:#x}): {:?}",
1031-
regs_clone.rax, regs_clone.rcx, res
1037+
regs_clone.rax,
1038+
regs_clone.rcx,
1039+
res
10321040
);
10331041

10341042
let regs = self.regs_mut();
@@ -1229,6 +1237,50 @@ impl<H: AxVCpuHal> AxArchVCpu for VmxVcpu<H> {
12291237
}
12301238
}
12311239
}
1240+
VmxExitReason::EPT_VIOLATION => {
1241+
// SDM Vol. 3C, Section 27.2.1, Table 27-7
1242+
if let Ok(mmio_info) = self.mmio_access_info() {
1243+
if mmio_info.is_read && !mmio_info.is_write {
1244+
// MMIO access
1245+
self.advance_rip(exit_info.exit_instruction_length as _)?;
1246+
error!(
1247+
"VMX unsupported MMIO-Exit: {:#x?} of {:#x?}",
1248+
mmio_info, exit_info
1249+
);
1250+
error!("VCpu {:#x?}", self);
1251+
self.set_gpr(0, 0x1234);
1252+
AxVCpuExitReason::Nothing
1253+
// AxVCpuExitReason::MmioRead {
1254+
// addr: mmio_info.addr,
1255+
// width: mmio_info.access_width,
1256+
// reg: mmio_info.register.unwrap_or(0) as usize,
1257+
// reg_width: mmio_info.access_width,
1258+
// }
1259+
} else if mmio_info.is_write && !mmio_info.is_read {
1260+
// MMIO access
1261+
self.advance_rip(exit_info.exit_instruction_length as _)?;
1262+
AxVCpuExitReason::MmioWrite {
1263+
addr: mmio_info.addr,
1264+
width: mmio_info.access_width,
1265+
data: self.regs().get_reg_of_index(
1266+
mmio_info.register.unwrap_or(0) as u8,
1267+
),
1268+
}
1269+
}
1270+
else {
1271+
warn!(
1272+
"VMX unsupported EPT-Exit: {:#x?} of {:#x?}",
1273+
mmio_info, exit_info
1274+
);
1275+
warn!("VCpu {:#x?}", self);
1276+
AxVCpuExitReason::Halt
1277+
}
1278+
} else {
1279+
warn!("VMX unsupported EPT-Exit: {:#x?}", exit_info);
1280+
warn!("VCpu {:#x?}", self);
1281+
AxVCpuExitReason::Halt
1282+
}
1283+
}
12321284
_ => {
12331285
warn!("VMX unsupported VM-Exit: {:#x?}", exit_info);
12341286
warn!("VCpu {:#x?}", self);

src/vmx/vmcs.rs

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

6+
use core::error;
7+
8+
use axaddrspace::device::AccessWidth;
69
use bit_field::BitField;
710
use x86::bits64::vmx;
811

@@ -499,6 +502,16 @@ pub struct VmxExitInfo {
499502
pub guest_rip: usize,
500503
}
501504

505+
/// MMIO Access Information extracted from EPT violation.
506+
#[derive(Debug)]
507+
pub struct MmioAccessInfo {
508+
pub addr: GuestPhysAddr,
509+
pub access_width: AccessWidth,
510+
pub is_read: bool,
511+
pub is_write: bool,
512+
pub register: Option<u8>,
513+
}
514+
502515
/// VM-Entry/VM-Exit Interruption-Information Field. (SDM Vol. 3C, Section 24.8.3, 24.9.2)
503516
#[derive(Debug)]
504517
pub struct VmxInterruptInfo {
@@ -581,6 +594,71 @@ pub struct CrAccessInfo {
581594
pub lmsw_source_data: u8,
582595
}
583596

597+
598+
599+
/// Decode VM-Exit instruction information to extract access width and register information.
600+
///
601+
/// * Optional register number (if applicable)
602+
pub fn decode_mmio_instruction_info(instruction_info: u32) -> (AccessWidth, Option<u8>) {
603+
// SDM Vol. 3C, Section 27.2.1, Table 27-13
604+
// Bits 2:0 - Operand size encoding
605+
let operand_size = instruction_info.get_bits(0..3);
606+
let access_width = match operand_size + 1 {
607+
0 => AccessWidth::Word, // 16-bit
608+
1 => AccessWidth::Dword, // 32-bit
609+
2 => AccessWidth::Qword, // 64-bit
610+
3 => AccessWidth::Byte, // 8-bit
611+
_ => AccessWidth::Dword, // Default to 32-bit for unknown encodings
612+
};
613+
614+
// Bits 10:7
615+
error!("Decoding register index from instruction info: {:#x}", instruction_info);
616+
let reg_index: u8 = instruction_info.get_bits(7..10) as u8;
617+
618+
(access_width, Some(reg_index))
619+
}
620+
621+
/// Extract detailed MMIO access information from EPT violation.
622+
///
623+
/// This function provides comprehensive information about MMIO device access
624+
/// that triggered an EPT violation, including the access address, width,
625+
/// read/write direction, and instruction details.
626+
///
627+
/// # Returns
628+
/// * `Ok(MmioAccessInfo)` - Detailed MMIO access information
629+
/// * `Err(AxError)` - If VMCS read operations fail
630+
pub fn mmio_access_info() -> AxResult<MmioAccessInfo> {
631+
let qualification = VmcsReadOnlyNW::EXIT_QUALIFICATION.read()?;
632+
let instruction_info = VmcsReadOnly32::VMEXIT_INSTRUCTION_INFO.read()?;
633+
let fault_guest_paddr = VmcsReadOnly64::GUEST_PHYSICAL_ADDR.read()? as usize;
634+
let cr0_val = VmcsGuestNW::RIP.read().unwrap();
635+
error!("MMIO access info: cr0_val={:#x}", cr0_val);
636+
let guest_rax = (cr0_val & 0xffffffff00000000) | 0x1234;
637+
// VmcsGuestNW::CR0.write(guest_rax).unwrap(); // Example write to CR0, replace with actual logic
638+
// Extract access type from qualification
639+
let is_read = true;
640+
let is_write = false;
641+
// Decode instruction information to get access width and register
642+
643+
let operand_size = instruction_info.get_bits(0..3);
644+
let access_width = match operand_size + 1 {
645+
0 => AccessWidth::Word, // 16-bit
646+
1 => AccessWidth::Dword, // 32-bit
647+
2 => AccessWidth::Qword, // 64-bit
648+
3 => AccessWidth::Byte, // 8-bit
649+
_ => AccessWidth::Dword, // Default to 32-bit for unknown encodings
650+
};
651+
652+
let addr = GuestPhysAddr::from(fault_guest_paddr);
653+
654+
Ok(MmioAccessInfo {
655+
addr,
656+
access_width,
657+
is_read,
658+
is_write,
659+
register: None,
660+
})
661+
}
584662
pub mod controls {
585663
pub use x86::vmx::vmcs::control::{EntryControls, ExitControls};
586664
pub use x86::vmx::vmcs::control::{PinbasedControls, PrimaryControls, SecondaryControls};

0 commit comments

Comments
 (0)