Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ rusty-tags.vi

# We ignore Cargo.lock because `axvcpu` is just a library
Cargo.lock

tmp/
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,16 @@ axvcpu = "0.1.0"
x86_vlapic = "0.1.0"
axdevice_base = "0.1.0"
axvisor_api = "0.1.0"
tock-registers = "0.10"

spin = { version = "0.9", default-features = false }

[dev-dependencies]
memoffset = "0.9"

[features]
default = ["vmx"]
default = ["svm"]
# default = ["vmx"]
tracing = []
vmx = []
svm = []
22 changes: 16 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#![no_std]
#![feature(doc_cfg)]
#![feature(concat_idents)]
#![doc = include_str!("../README.md")]

#[macro_use]
Expand All @@ -14,18 +15,27 @@ pub(crate) mod msr;
#[macro_use]
pub(crate) mod regs;
mod ept;
pub(crate) mod xstate;

#[cfg(all(feature = "vmx", feature = "svm"))]
compile_error!("Features 'vmx' and 'svm' are mutually exclusive. Please enable only one of them.");

cfg_if::cfg_if! {
if #[cfg(feature = "vmx")] {
mod vmx;
use vmx as vender;
pub use vmx::{VmxExitInfo, VmxExitReason, VmxInterruptInfo, VmxIoExitInfo};

pub use vender::VmxArchVCpu;
pub use vender::VmxArchPerCpuState;
use vmx as vendor;
// pub use vmx::{VmxExitInfo, VmxExitReason, VmxInterruptInfo, VmxIoExitInfo};
pub use vendor::VmxArchVCpu as X86ArchVCpu;
pub use vendor::VmxArchPerCpuState as X86ArchPerCpuState;
} else if #[cfg(feature = "svm")] {
mod svm;
use svm as vendor;
pub use vendor::{
SvmArchVCpu as X86ArchVCpu, SvmArchPerCpuState as X86ArchPerCpuState,
};
}
}

pub use ept::GuestPageWalkInfo;
pub use regs::GeneralRegisters;
pub use vender::has_hardware_support;
pub use vendor::has_hardware_support;
130 changes: 16 additions & 114 deletions src/msr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ use x86::msr::{rdmsr, wrmsr};
pub enum Msr {
IA32_FEATURE_CONTROL = 0x3a,

IA32_SYSENTER_CS = 0x174,
IA32_SYSENTER_ESP = 0x175,
IA32_SYSENTER_EIP = 0x176,

IA32_PAT = 0x277,

IA32_VMX_BASIC = 0x480,
Expand Down Expand Up @@ -37,6 +41,18 @@ pub enum Msr {
IA32_FS_BASE = 0xc000_0100,
IA32_GS_BASE = 0xc000_0101,
IA32_KERNEL_GSBASE = 0xc000_0102,

// SVM Related MSRs:
VM_CR = 0xc001_0114,
IGNNE = 0xc001_0115,
VM_HSAVE_PA = 0xc001_0117,

PERF_EVT_SEL0 = 0xc001_0200,
PERF_EVT_SEL1 = 0xc001_0202,
PERF_EVT_SEL2 = 0xc001_0204,
PERF_EVT_SEL3 = 0xc001_0206,
PERF_EVT_SEL4 = 0xc001_0208,
PERF_EVT_SEL5 = 0xc001_020a,
}

impl Msr {
Expand Down Expand Up @@ -71,117 +87,3 @@ pub(super) trait MsrReadWrite {
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use alloc::format;

#[test]
fn test_msr_enum_values() {
// Test that MSR enum values match the expected constants
assert_eq!(Msr::IA32_FEATURE_CONTROL as u32, 0x3a);
assert_eq!(Msr::IA32_PAT as u32, 0x277);
assert_eq!(Msr::IA32_VMX_BASIC as u32, 0x480);
assert_eq!(Msr::IA32_EFER as u32, 0xc000_0080);
assert_eq!(Msr::IA32_LSTAR as u32, 0xc000_0082);
}

#[test]
fn test_msr_debug() {
// Test that MSR implements Debug properly
let msr = Msr::IA32_VMX_BASIC;
let debug_str = format!("{:?}", msr);
assert!(!debug_str.is_empty());
assert!(debug_str.contains("IA32_VMX_BASIC"));
}

#[test]
fn test_msr_copy_clone() {
// Test that MSR implements Copy and Clone
let msr1 = Msr::IA32_EFER;
let msr2 = msr1; // Copy
let msr3 = msr1.clone(); // Clone

assert_eq!(msr1 as u32, msr2 as u32);
assert_eq!(msr1 as u32, msr3 as u32);
}

#[test]
fn test_vmx_msr_ranges() {
// Test VMX MSR values are in the correct range
assert!(Msr::IA32_VMX_BASIC as u32 >= 0x480);
assert!(Msr::IA32_VMX_TRUE_ENTRY_CTLS as u32 <= 0x490);

// Test that VMX MSRs are consecutive where expected
assert_eq!(
Msr::IA32_VMX_BASIC as u32 + 1,
Msr::IA32_VMX_PINBASED_CTLS as u32
);
assert_eq!(
Msr::IA32_VMX_PINBASED_CTLS as u32 + 1,
Msr::IA32_VMX_PROCBASED_CTLS as u32
);
}

#[test]
fn test_fs_gs_base_msr_values() {
// Test FS/GS base MSRs
assert_eq!(Msr::IA32_FS_BASE as u32, 0xc000_0100);
assert_eq!(Msr::IA32_GS_BASE as u32, 0xc000_0101);
assert_eq!(Msr::IA32_KERNEL_GSBASE as u32, 0xc000_0102);

// These should be consecutive
assert_eq!(Msr::IA32_FS_BASE as u32 + 1, Msr::IA32_GS_BASE as u32);
assert_eq!(Msr::IA32_GS_BASE as u32 + 1, Msr::IA32_KERNEL_GSBASE as u32);
}

#[test]
fn test_system_call_msr_values() {
// Test system call related MSRs
assert_eq!(Msr::IA32_STAR as u32, 0xc000_0081);
assert_eq!(Msr::IA32_LSTAR as u32, 0xc000_0082);
assert_eq!(Msr::IA32_CSTAR as u32, 0xc000_0083);
assert_eq!(Msr::IA32_FMASK as u32, 0xc000_0084);

// These should be consecutive
assert_eq!(Msr::IA32_STAR as u32 + 1, Msr::IA32_LSTAR as u32);
assert_eq!(Msr::IA32_LSTAR as u32 + 1, Msr::IA32_CSTAR as u32);
assert_eq!(Msr::IA32_CSTAR as u32 + 1, Msr::IA32_FMASK as u32);
}

// Note: We can't test the actual read/write methods without running on real hardware
// and having the appropriate privileges. Those would be integration tests.

// Mock implementation for testing the MsrReadWrite trait
struct TestMsr;

impl MsrReadWrite for TestMsr {
const MSR: Msr = Msr::IA32_PAT;
}

#[test]
fn test_msr_read_write_trait() {
// Test that the trait compiles and has the expected methods
// We can't actually call read_raw() without MSR access
assert_eq!(TestMsr::MSR as u32, 0x277);
}

#[test]
fn test_msr_as_u32_conversion() {
// Test that we can convert MSR enum to u32 properly
let msrs = [
Msr::IA32_FEATURE_CONTROL,
Msr::IA32_VMX_BASIC,
Msr::IA32_EFER,
Msr::IA32_LSTAR,
];

for msr in msrs.iter() {
let value = *msr as u32;
assert!(value > 0);
// Values should be reasonable MSR numbers
assert!(value < 0xffff_ffff);
}
}
}
39 changes: 39 additions & 0 deletions src/regs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,26 @@ macro_rules! save_regs_to_stack {
push rcx
push rax"
};
(norax) => {
"
push r15
push r14
push r13
push r12
push r11
push r10
push r9
push r8
push rdi
push rsi
push rbp
sub rsp, 8
push rbx
push rdx
push rcx
sub rsp, 8
"
};
}

macro_rules! restore_regs_from_stack {
Expand All @@ -105,4 +125,23 @@ macro_rules! restore_regs_from_stack {
pop r14
pop r15"
};
(norax) => {
"
add rsp, 8
pop rcx
pop rdx
pop rbx
add rsp, 8
pop rbp
pop rsi
pop rdi
pop r8
pop r9
pop r10
pop r11
pop r12
pop r13
pop r14
pop r15"
};
}
Loading
Loading