Skip to content

Commit 114e912

Browse files
committed
refactor(vmx): improve MMIO access handling and exit reason reporting
- Rename and restructure MMIO access information functions - Enhance MMIO exit reason handling in VmxVcpu - Simplify and optimize MMIO access information extraction
1 parent ae9550b commit 114e912

File tree

2 files changed

+57
-114
lines changed

2 files changed

+57
-114
lines changed

src/vmx/vcpu.rs

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -868,14 +868,9 @@ impl<H: AxVCpuHal> VmxVcpu<H> {
868868
rflags as u64 & x86_64::registers::rflags::RFlags::INTERRUPT_FLAG.bits() != 0
869869
&& block_state == 0
870870
}
871-
/// MMIO access information.
872-
pub fn mmio_access_info(&self) -> AxResult<vmcs::MmioAccessInfo> {
873-
vmcs::mmio_access_info()
874-
}
875-
876-
/// MMIO access information with guest register data.
877-
pub fn mmio_access_info_with_regs(&self) -> AxResult<vmcs::MmioAccessInfo> {
878-
vmcs::mmio_access_info_with_regs(&self.guest_regs)
871+
/// Get MMIO access exit reason from EPT violation.
872+
pub fn mmio_access_exit_reason(&self) -> AxResult<AxVCpuExitReason> {
873+
vmcs::mmio_access_exit_reason(&self.guest_regs)
879874
}
880875

881876
/// Try to inject a pending event before next VM entry.
@@ -1242,28 +1237,26 @@ impl<H: AxVCpuHal> AxArchVCpu for VmxVcpu<H> {
12421237
}
12431238
VmxExitReason::EPT_VIOLATION => {
12441239
self.advance_rip(exit_info.exit_instruction_length as _)?;
1245-
self.advance_rip(exit_info.exit_instruction_length as _)?;
1246-
let mmio_info = self.mmio_access_info_with_regs();
1247-
error!("VMX EPT Violation: {:#x?} of {:#x?}", mmio_info, exit_info);
1248-
if let Ok(mmio_info) = mmio_info {
1249-
if mmio_info.is_read {
1250-
self.set_gpr(0, 0x74726976);
1251-
} else {
1252-
if let Some(data) = mmio_info.data {
1253-
error!("Data write {:#x}", data);
1254-
} else {
1255-
error!("Data write None");
1240+
1241+
// Get the MMIO access exit reason
1242+
match self.mmio_access_exit_reason() {
1243+
Ok(exit_reason) => {
1244+
match &exit_reason {
1245+
AxVCpuExitReason::MmioRead { addr, width, reg, .. } => {
1246+
error!("MMIO Read: addr={:#x}, width={:?}, reg={}", addr, width, reg);
1247+
}
1248+
AxVCpuExitReason::MmioWrite { addr, width, data } => {
1249+
error!("MMIO Write: addr={:#x}, width={:?}, data={:#x}", addr, width, data);
1250+
}
1251+
_ => unreachable!("mmio_access_exit_reason should only return MmioRead or MmioWrite"),
12561252
}
1253+
exit_reason
1254+
}
1255+
Err(e) => {
1256+
error!("Failed to get MMIO access info: {:?}", e);
1257+
AxVCpuExitReason::Halt
12571258
}
1258-
};
1259-
1260-
AxVCpuExitReason::Nothing
1261-
// AxVCpuExitReason::MmioRead {
1262-
// addr: mmio_info.addr,
1263-
// width: mmio_info.access_width,
1264-
// reg: mmio_info.register as usize,
1265-
// reg_width: mmio_info.access_width,
1266-
// }
1259+
}
12671260
}
12681261
_ => {
12691262
warn!("VMX unsupported VM-Exit: {:#x?}", exit_info);

src/vmx/vmcs.rs

Lines changed: 36 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -785,117 +785,67 @@ pub fn cr_access_info() -> AxResult<CrAccessInfo> {
785785
})
786786
}
787787

788-
/// Extract detailed MMIO access information from EPT violation.
788+
/// Extract MMIO access information from EPT violation and return appropriate AxVCpuExitReason.
789789
///
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-
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-
// For write operations, we need to get the data from guest registers
822-
// This will be set by the caller who has access to guest registers
823-
let data = if is_write {
824-
Some(0 as u64) // Placeholder - will be filled by caller
825-
} else {
826-
None
827-
};
828-
829-
Ok(MmioAccessInfo {
830-
addr,
831-
access_width,
832-
is_read,
833-
is_write,
834-
register: reg_index,
835-
data,
836-
})
837-
}
838-
839-
/// Extract detailed MMIO access information from EPT violation with guest register data.
840-
///
841-
/// This function provides comprehensive information about MMIO device access
842-
/// that triggered an EPT violation, including the access address, width,
843-
/// read/write direction, instruction details, and actual data for write operations.
790+
/// This function analyzes the EPT violation to determine if it's a MMIO read or write operation,
791+
/// extracts the necessary information, and returns the appropriate exit reason.
844792
///
845793
/// # Arguments
846794
/// * `guest_regs` - Reference to guest general-purpose registers
847795
///
848796
/// # Returns
849-
/// * `Ok(MmioAccessInfo)` - Detailed MMIO access information with actual data
850-
/// * `Err(AxError)` - If VMCS read operations fail
851-
pub fn mmio_access_info_with_regs(guest_regs: &crate::regs::GeneralRegisters) -> AxResult<MmioAccessInfo> {
797+
/// * `Ok(AxVCpuExitReason::MmioRead)` - For read operations with addr, width, reg, reg_width
798+
/// * `Ok(AxVCpuExitReason::MmioWrite)` - For write operations with addr, width, data
799+
/// * `Err(AxError)` - If VMCS read operations fail or invalid access type
800+
pub fn mmio_access_exit_reason(guest_regs: &crate::regs::GeneralRegisters) -> AxResult<axvcpu::AxVCpuExitReason> {
852801
let qualification = VmcsReadOnlyNW::EXIT_QUALIFICATION.read()?;
853802
let instruction_info = VmcsReadOnly32::VMEXIT_INSTRUCTION_INFO.read()?;
854803
let fault_guest_paddr = VmcsReadOnly64::GUEST_PHYSICAL_ADDR.read()? as usize;
855804

856805
// Extract access type from qualification
857-
error!("qualification: {:#x}", qualification);
858806
let is_read = qualification.get_bit(0);
859-
// Access type is determined by the second bit in the qualification
860807
let is_write = qualification.get_bit(1);
861-
// Decode instruction information to get access width and register
862808

809+
// Decode instruction information to get access width and register
863810
let operand_size = instruction_info.get_bits(0..3);
864-
let access_width = match operand_size + 1 {
865-
0 => AccessWidth::Word, // 16-bit
866-
1 => AccessWidth::Dword, // 32-bit
867-
2 => AccessWidth::Qword, // 64-bit
868-
3 => AccessWidth::Byte, // 8-bit
869-
_ => AccessWidth::Dword, // Default to 32-bit for unknown encodings
811+
let access_width = match operand_size {
812+
0 => AccessWidth::Byte, // 8-bit
813+
1 => AccessWidth::Word, // 16-bit
814+
2 => AccessWidth::Dword, // 32-bit
815+
3 => AccessWidth::Qword, // 64-bit
816+
_ => AccessWidth::Dword, // Default to 32-bit for unknown encodings
870817
};
871818

872819
let addr = GuestPhysAddr::from(fault_guest_paddr);
873-
let reg_index: u8 = instruction_info.get_bits(7..10) as u8;
874-
875-
// For write operations, get the actual data from the guest register
876-
let data = if is_write {
877-
// Get the full register value
878-
let reg_value = guest_regs.get_reg_of_index(reg_index);
820+
let reg_index = instruction_info.get_bits(7..10) as usize;
821+
822+
if is_read {
823+
// For read operations, return MmioRead with register information
824+
Ok(axvcpu::AxVCpuExitReason::MmioRead {
825+
addr,
826+
width: access_width,
827+
reg: reg_index,
828+
reg_width: access_width, // Register width same as access width
829+
})
830+
} else if is_write {
831+
// For write operations, get the data from the guest register
832+
let reg_value = guest_regs.get_reg_of_index(reg_index as u8);
879833

880834
// Mask the value based on access width to get only the relevant bits
881-
let masked_value = match access_width {
835+
let data = match access_width {
882836
AccessWidth::Byte => reg_value & 0xFF,
883837
AccessWidth::Word => reg_value & 0xFFFF,
884838
AccessWidth::Dword => reg_value & 0xFFFFFFFF,
885839
AccessWidth::Qword => reg_value,
886840
};
887841

888-
Some(masked_value)
842+
Ok(axvcpu::AxVCpuExitReason::MmioWrite {
843+
addr,
844+
width: access_width,
845+
data,
846+
})
889847
} else {
890-
None
891-
};
892-
893-
Ok(MmioAccessInfo {
894-
addr,
895-
access_width,
896-
is_read,
897-
is_write,
898-
register: reg_index,
899-
data,
900-
})
848+
// Neither read nor write - this shouldn't happen for valid MMIO access
849+
Err(axerrno::AxError::InvalidInput)
850+
}
901851
}

0 commit comments

Comments
 (0)