Skip to content

Commit e2f17ec

Browse files
committed
Refactor VMX module to use unified Result type and improve error handling
- Replaced AxResult with a custom Result type across the VMX module for consistency. - Updated function signatures in structs and implementations to reflect the new Result type. - Enhanced error handling by using VmxError for specific error cases. - Adjusted VM exit reason handling to use VmxRawExitReason instead of VmxExitReason. - Improved debug formatting in VmxVcpu to handle potential read errors gracefully. - Cleaned up unnecessary imports and comments for better code clarity.
1 parent d2ab97d commit e2f17ec

File tree

9 files changed

+356
-232
lines changed

9 files changed

+356
-232
lines changed

Cargo.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ x86_64 = "0.15"
2323
raw-cpuid = "11.0"
2424
numeric-enum-macro = "0.2"
2525

26-
axerrno = "0.1.0"
26+
# axerrno = "0.1.0" # 移除,改用 thiserror
27+
thiserror = {version = "2.0", default-features = false}
2728
page_table_entry = "0.5"
2829
memory_addr = "0.4"
29-
crate_interface = "0.1"
30+
# crate_interface = "0.1"
3031

31-
# axaddrspace = "0.2.0"
32-
axvcpu = "0.1.0"
32+
axaddrspace = "0.2.0"
33+
# axvcpu = "0.1.0" # 移除 axvcpu 依赖,使用自己的 Hal trait
3334
x86_vlapic = "0.1.0"
3435
axdevice_base = "0.1.0"
3536

src/lib.rs

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
extern crate log;
88

99
extern crate alloc;
10+
use alloc::string::String;
11+
12+
use thiserror::Error;
1013

1114
#[cfg(test)]
1215
mod test_utils;
@@ -21,7 +24,7 @@ cfg_if::cfg_if! {
2124
if #[cfg(feature = "vmx")] {
2225
mod vmx;
2326
use vmx as vender;
24-
pub use vmx::{VmxExitInfo, VmxExitReason, VmxInterruptInfo, VmxIoExitInfo};
27+
pub use vmx::{VmxExitInfo, VmxRawExitReason, VmxInterruptInfo, VmxIoExitInfo};
2528

2629
pub use vender::VmxArchVCpu;
2730
pub use vender::VmxArchPerCpuState;
@@ -35,6 +38,69 @@ pub use vender::has_hardware_support;
3538
pub type HostVirtAddr = usize;
3639
pub type HostPhysAddr = usize;
3740

41+
/// x86 VCPU 错误类型
42+
#[derive(Error, Debug)]
43+
pub enum VmxError {
44+
/// VMX 指令错误
45+
#[error("VMX instruction error: {0}")]
46+
VmxInstructionError(String),
47+
48+
/// VMCS 指针无效
49+
#[error("VMCS pointer is not valid")]
50+
InvalidVmcsPtr,
51+
52+
/// VMX 未被启用
53+
#[error("VMX is not enabled")]
54+
VmxNotEnabled,
55+
56+
/// VMX 已被启用
57+
#[error("VMX is already enabled")]
58+
VmxAlreadyEnabled,
59+
60+
/// 内存分配失败
61+
#[error("memory allocation failed")]
62+
MemoryAllocationFailed,
63+
64+
/// 无效的物理地址
65+
#[error("invalid physical address: {0:#x}")]
66+
InvalidPhysAddr(usize),
67+
68+
/// 无效的虚拟地址
69+
#[error("invalid virtual address: {0:#x}")]
70+
InvalidVirtAddr(usize),
71+
72+
/// 无效的 VMCS 配置
73+
#[error("invalid VMCS configuration: {0}")]
74+
InvalidVmcsConfig(String),
75+
76+
/// EPT 违规
77+
#[error("EPT violation at GPA {0:#x}, error code {1:#x}")]
78+
EptViolation(usize, u64),
79+
80+
/// IO 指令错误
81+
#[error("IO instruction error: port={0:#x}, width={1}")]
82+
IoError(u16, u8),
83+
84+
/// MSR 访问错误
85+
#[error("MSR access error: {0:#x}")]
86+
MsrError(u32),
87+
88+
/// VCpu 未绑定
89+
#[error("VCpu is not bound to current CPU")]
90+
VCPUNotBound,
91+
92+
/// 不支持的 VMX 功能
93+
#[error("unsupported VMX feature: {0}")]
94+
UnsupportedFeature(String),
95+
96+
/// 其他 VMX 错误
97+
#[error("VMX error: {0}")]
98+
Other(String),
99+
}
100+
101+
/// x86 VCPU Result 类型
102+
pub type Result<T> = core::result::Result<T, VmxError>;
103+
38104
/// Hardware abstraction layer for memory management.
39105
pub trait Hal {
40106
/// Allocates a frame and returns its host physical address. The
@@ -73,3 +139,37 @@ pub trait Hal {
73139
/// * `HostPhysAddr` - The corresponding physical address.
74140
fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr;
75141
}
142+
143+
// ==================== x86 特定的 VCPU 退出原因定义 ====================
144+
// 这些类型替代 axvcpu 中的定义,使 x86_vcpu 完全独立
145+
146+
use axaddrspace::{
147+
GuestPhysAddr,
148+
device::{AccessWidth, Port, SysRegAddr},
149+
};
150+
151+
/// x86 VCPU 退出原因
152+
#[derive(Debug)]
153+
pub enum VmxExitReason {
154+
/// 超级调用
155+
Hypercall { nr: usize, args: [usize; 8] },
156+
/// IO 读
157+
IoRead { port: Port, width: AccessWidth },
158+
/// IO 写
159+
IoWrite { port: Port, width: AccessWidth, data: u32 },
160+
/// 系统寄存器读
161+
SysRegRead { addr: SysRegAddr, reg: usize },
162+
/// 系统寄存器写
163+
SysRegWrite { addr: SysRegAddr, value: u64 },
164+
/// 外部中断
165+
ExternalInterrupt { vector: usize },
166+
/// CPU 启动
167+
CpuUp { target_cpu: usize, entry_point: GuestPhysAddr, arg: u64 },
168+
/// CPU 关闭
169+
CpuDown { state: usize },
170+
/// 系统关闭
171+
SystemDown,
172+
/// 无操作
173+
Nothing,
174+
}
175+

src/mem.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use core::marker::PhantomData;
22

3-
use crate::{Hal, HostPhysAddr};
3+
use memory_addr::PAGE_SIZE_4K as PAGE_SIZE;
4+
use crate::{Hal, HostPhysAddr, Result, VmxError};
45

56
/// A physical frame which will be automatically deallocated when dropped.
67
///
@@ -14,18 +15,18 @@ pub struct PhysFrame<H: Hal> {
1415

1516
impl<H: Hal> PhysFrame<H> {
1617
/// Allocate a [`PhysFrame`].
17-
pub fn alloc() -> AxResult<Self> {
18+
pub fn alloc() -> Result<Self> {
1819
let start_paddr = H::alloc_frame()
19-
.ok_or_else(|| ax_err_type!(NoMemory, "allocate physical frame failed"))?;
20-
assert_ne!(start_paddr.as_usize(), 0);
20+
.ok_or_else(|| VmxError::MemoryAllocationFailed)?;
21+
assert_ne!(start_paddr, 0);
2122
Ok(Self {
2223
start_paddr: Some(start_paddr),
2324
_marker: PhantomData,
2425
})
2526
}
2627

2728
/// Allocate a [`PhysFrame`] and fill it with zeros.
28-
pub fn alloc_zero() -> AxResult<Self> {
29+
pub fn alloc_zero() -> Result<Self> {
2930
let mut f = Self::alloc()?;
3031
f.fill(0);
3132
Ok(f)
@@ -51,7 +52,7 @@ impl<H: Hal> PhysFrame<H> {
5152

5253
/// Get a mutable pointer to the frame.
5354
pub fn as_mut_ptr(&self) -> *mut u8 {
54-
H::phys_to_virt(self.start_paddr()).as_mut_ptr()
55+
H::phys_to_virt(self.start_paddr()) as *mut u8
5556
}
5657

5758
/// Fill the frame with a byte. Works only when the frame is 4 KiB in size.
@@ -60,7 +61,7 @@ impl<H: Hal> PhysFrame<H> {
6061
}
6162
}
6263

63-
impl<H: AxMmHal> Drop for PhysFrame<H> {
64+
impl<H: Hal> Drop for PhysFrame<H> {
6465
fn drop(&mut self) {
6566
if let Some(start_paddr) = self.start_paddr {
6667
H::dealloc_frame(start_paddr);

src/vmx/definitions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl From<u32> for VmxInstructionError {
5454
}
5555

5656
impl Debug for VmxInstructionError {
57-
fn fmt(&self, f: &mut Formatter) -> Result {
57+
fn fmt(&self, f: &mut Formatter) -> core::fmt::Result {
5858
write!(f, "VmxInstructionError({}, {:?})", self.0, self.as_str())
5959
}
6060
}
@@ -69,7 +69,7 @@ numeric_enum_macro::numeric_enum! {
6969
/// the execution of a virtual machine in VMX (Virtual Machine Extensions) mode.
7070
/// Each variant corresponds to a specific exit reason that can be identified
7171
/// and handled by the hypervisor.
72-
pub enum VmxExitReason {
72+
pub enum VmxRawExitReason {
7373
/// Exception or non-maskable interrupt (NMI) occurred.
7474
EXCEPTION_NMI = 0,
7575
/// An external interrupt was received.

src/vmx/mod.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ mod vcpu;
66
mod vmcs;
77

88
use self::structs::VmxBasic;
9-
use axerrno::ax_err_type;
109

11-
pub use self::definitions::VmxExitReason;
10+
pub use self::definitions::VmxRawExitReason;
1211
pub use self::percpu::VmxPerCpuState as VmxArchPerCpuState;
1312
pub use self::vcpu::VmxVcpu as VmxArchVCpu;
1413
pub use self::vmcs::{VmxExitInfo, VmxInterruptInfo, VmxIoExitInfo};
1514

15+
// 导出自定义错误类型
16+
pub use crate::{VmxError, Result};
17+
1618
/// Return if current platform support virtualization extension.
1719
pub fn has_hardware_support() -> bool {
1820
if let Some(feature) = raw_cpuid::CpuId::new().get_feature_info() {
@@ -26,10 +28,12 @@ pub fn read_vmcs_revision_id() -> u32 {
2628
VmxBasic::read().revision_id
2729
}
2830

29-
fn as_axerr(err: x86::vmx::VmFail) -> axerrno::AxError {
31+
fn as_axerr(err: x86::vmx::VmFail) -> VmxError {
3032
use x86::vmx::VmFail;
3133
match err {
32-
VmFail::VmFailValid => ax_err_type!(BadState, vmcs::instruction_error().as_str()),
33-
VmFail::VmFailInvalid => ax_err_type!(BadState, "VMCS pointer is not valid"),
34+
VmFail::VmFailValid => {
35+
VmxError::VmxInstructionError(alloc::string::String::from(vmcs::instruction_error().as_str()))
36+
}
37+
VmFail::VmFailInvalid => VmxError::InvalidVmcsPtr,
3438
}
3539
}

src/vmx/percpu.rs

Lines changed: 23 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use x86::bits64::vmx;
22
use x86_64::registers::control::{Cr0, Cr4, Cr4Flags};
33

4-
use axerrno::{AxResult, ax_err, ax_err_type};
5-
use axvcpu::{AxArchPerCpu, AxVCpuHal};
4+
use alloc::format;
65
use memory_addr::PAGE_SIZE_4K as PAGE_SIZE;
76

7+
use crate::{Hal, Result, VmxError};
88
use crate::msr::Msr;
99
use crate::vmx::has_hardware_support;
1010
use crate::vmx::structs::{FeatureControl, FeatureControlFlags, VmxBasic, VmxRegion};
@@ -15,7 +15,7 @@ use crate::vmx::structs::{FeatureControl, FeatureControlFlags, VmxBasic, VmxRegi
1515
/// when operating in VMX mode, including the VMCS revision identifier and
1616
/// the VMX region.
1717
#[derive(Debug)]
18-
pub struct VmxPerCpuState<H: AxVCpuHal> {
18+
pub struct VmxPerCpuState<H: Hal> {
1919
/// The VMCS (Virtual Machine Control Structure) revision identifier.
2020
///
2121
/// This identifier is used to ensure compatibility between the software
@@ -26,27 +26,27 @@ pub struct VmxPerCpuState<H: AxVCpuHal> {
2626
///
2727
/// This region typically contains the VMCS and other state information
2828
/// required for managing virtual machines on this particular CPU.
29-
vmx_region: VmxRegion<H::MmHal>,
29+
vmx_region: VmxRegion<H>,
3030
}
3131

32-
impl<H: AxVCpuHal> AxArchPerCpu for VmxPerCpuState<H> {
33-
fn new(_cpu_id: usize) -> AxResult<Self> {
32+
impl<H: Hal> VmxPerCpuState<H> {
33+
pub fn new(_cpu_id: usize) -> Result<Self> {
3434
Ok(Self {
3535
vmcs_revision_id: 0,
3636
vmx_region: unsafe { VmxRegion::uninit() },
3737
})
3838
}
3939

40-
fn is_enabled(&self) -> bool {
40+
pub fn is_enabled(&self) -> bool {
4141
Cr4::read().contains(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS)
4242
}
4343

44-
fn hardware_enable(&mut self) -> AxResult {
44+
pub fn hardware_enable(&mut self) -> Result<()> {
4545
if !has_hardware_support() {
46-
return ax_err!(Unsupported, "CPU does not support feature VMX");
46+
return Err(VmxError::UnsupportedFeature("CPU does not support feature VMX".into()));
4747
}
4848
if self.is_enabled() {
49-
return ax_err!(ResourceBusy, "VMX is already turned on");
49+
return Err(VmxError::VmxAlreadyEnabled);
5050
}
5151

5252
// Enable XSAVE/XRSTOR.
@@ -61,7 +61,7 @@ impl<H: AxVCpuHal> AxArchPerCpu for VmxPerCpuState<H> {
6161
ctrl | FeatureControlFlags::LOCKED | FeatureControlFlags::VMXON_ENABLED_OUTSIDE_SMX,
6262
)
6363
} else if !vmxon_outside {
64-
return ax_err!(Unsupported, "VMX disabled by BIOS");
64+
return Err(VmxError::UnsupportedFeature("VMX disabled by BIOS".into()));
6565
}
6666

6767
// Check control registers are in a VMX-friendly state. (SDM Vol. 3C, Appendix A.7, A.8)
@@ -77,28 +77,28 @@ impl<H: AxVCpuHal> AxArchPerCpu for VmxPerCpuState<H> {
7777
}};
7878
}
7979
if !cr_is_valid!(Cr0::read().bits(), CR0) {
80-
return ax_err!(BadState, "host CR0 is not valid in VMX operation");
80+
return Err(VmxError::InvalidVmcsConfig("host CR0 is not valid in VMX operation".into()));
8181
}
8282
if !cr_is_valid!(Cr4::read().bits(), CR4) {
83-
return ax_err!(BadState, "host CR4 is not valid in VMX operation");
83+
return Err(VmxError::InvalidVmcsConfig("host CR4 is not valid in VMX operation".into()));
8484
}
8585

8686
// Get VMCS revision identifier in IA32_VMX_BASIC MSR.
8787
let vmx_basic = VmxBasic::read();
8888
if vmx_basic.region_size as usize != PAGE_SIZE {
89-
return ax_err!(Unsupported);
89+
return Err(VmxError::UnsupportedFeature("VMX region size is not 4K".into()));
9090
}
9191
if vmx_basic.mem_type != VmxBasic::VMX_MEMORY_TYPE_WRITE_BACK {
92-
return ax_err!(Unsupported);
92+
return Err(VmxError::UnsupportedFeature("VMX memory type is not write-back".into()));
9393
}
9494
if vmx_basic.is_32bit_address {
95-
return ax_err!(Unsupported);
95+
return Err(VmxError::UnsupportedFeature("32-bit VMX not supported".into()));
9696
}
9797
if !vmx_basic.io_exit_info {
98-
return ax_err!(Unsupported);
98+
return Err(VmxError::UnsupportedFeature("IO exit info not supported".into()));
9999
}
100100
if !vmx_basic.vmx_flex_controls {
101-
return ax_err!(Unsupported);
101+
return Err(VmxError::UnsupportedFeature("VMX flex controls not supported".into()));
102102
}
103103
self.vmcs_revision_id = vmx_basic.revision_id;
104104
self.vmx_region = VmxRegion::new(self.vmcs_revision_id, false)?;
@@ -107,30 +107,24 @@ impl<H: AxVCpuHal> AxArchPerCpu for VmxPerCpuState<H> {
107107
// Enable VMX using the VMXE bit.
108108
Cr4::write(Cr4::read() | Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS);
109109
// Execute VMXON.
110-
vmx::vmxon(self.vmx_region.phys_addr().as_usize() as _).map_err(|err| {
111-
ax_err_type!(
112-
BadState,
113-
format_args!("VMX instruction vmxon failed: {:?}", err)
114-
)
110+
vmx::vmxon(self.vmx_region.phys_addr() as _).map_err(|err| {
111+
VmxError::VmxInstructionError(format!("VMX instruction vmxon failed: {:?}", err))
115112
})?;
116113
}
117114
info!("[AxVM] succeeded to turn on VMX.");
118115

119116
Ok(())
120117
}
121118

122-
fn hardware_disable(&mut self) -> AxResult {
119+
pub fn hardware_disable(&mut self) -> Result<()> {
123120
if !self.is_enabled() {
124-
return ax_err!(BadState, "VMX is not enabled");
121+
return Err(VmxError::VmxNotEnabled);
125122
}
126123

127124
unsafe {
128125
// Execute VMXOFF.
129126
vmx::vmxoff().map_err(|err| {
130-
ax_err_type!(
131-
BadState,
132-
format_args!("VMX instruction vmxoff failed: {:?}", err)
133-
)
127+
VmxError::VmxInstructionError(format!("VMX instruction vmxoff failed: {:?}", err))
134128
})?;
135129
// Remove VMXE bit in CR4.
136130
Cr4::update(|cr4| cr4.remove(Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS));

0 commit comments

Comments
 (0)