Skip to content

Commit 169e8d2

Browse files
committed
Step 3: run a simple guest
1 parent d5808f7 commit 169e8d2

14 files changed

Lines changed: 402 additions & 37 deletions

File tree

hypervisor/Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

hypervisor/src/hv/hal.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
use rvm::{HostPhysAddr, HostVirtAddr, RvmHal};
1+
use rvm::{HostPhysAddr, HostVirtAddr, RvmHal, RvmVcpu};
22

3+
use super::vmexit;
34
use crate::mm::{address, frame};
45

56
pub struct RvmHalImpl;
@@ -20,4 +21,8 @@ impl RvmHal for RvmHalImpl {
2021
fn virt_to_phys(vaddr: HostVirtAddr) -> HostPhysAddr {
2122
address::virt_to_phys(vaddr)
2223
}
24+
25+
fn vmexit_handler(vcpu: &mut RvmVcpu<Self>) {
26+
vmexit::vmexit_handler(vcpu).unwrap()
27+
}
2328
}

hypervisor/src/hv/mod.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,36 @@
11
mod hal;
2+
mod vmexit;
23

34
use rvm::RvmPerCpu;
45

56
use self::hal::RvmHalImpl;
67

7-
pub fn run() {
8+
pub fn run() -> ! {
89
println!("Starting virtualization...");
910
println!("Hardware support: {:?}", rvm::has_hardware_support());
1011

1112
let mut percpu = RvmPerCpu::<RvmHalImpl>::new(0);
1213
percpu.hardware_enable().unwrap();
1314

14-
let mut vcpu = percpu.create_vcpu().unwrap();
15+
let mut vcpu = percpu.create_vcpu(test_guest as usize).unwrap();
1516
info!("{:#x?}", vcpu);
17+
println!("Running guest...");
1618
vcpu.run();
1719
}
20+
21+
#[naked]
22+
unsafe extern "C" fn test_guest() -> ! {
23+
core::arch::asm!(
24+
"
25+
mov rax, 0
26+
mov rdi, 2
27+
mov rsi, 3
28+
mov rdx, 3
29+
mov rcx, 3
30+
2:
31+
vmcall
32+
add rax, 1
33+
jmp 2b",
34+
options(noreturn),
35+
);
36+
}

hypervisor/src/hv/vmexit.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
use super::hal::RvmHalImpl;
2+
use rvm::arch::VmxExitReason;
3+
use rvm::{RvmResult, RvmVcpu};
4+
5+
type Vcpu = RvmVcpu<RvmHalImpl>;
6+
7+
const VM_EXIT_INSTR_LEN_VMCALL: u8 = 3;
8+
9+
fn handle_hypercall(vcpu: &mut Vcpu) -> RvmResult {
10+
let regs = vcpu.regs();
11+
info!(
12+
"VM exit: VMCALL({:#x}): {:?}",
13+
regs.rax,
14+
[regs.rdi, regs.rsi, regs.rdx, regs.rcx]
15+
);
16+
vcpu.advance_rip(VM_EXIT_INSTR_LEN_VMCALL)?;
17+
Ok(())
18+
}
19+
20+
pub fn vmexit_handler(vcpu: &mut Vcpu) -> RvmResult {
21+
let exit_info = vcpu.exit_info()?;
22+
debug!("VM exit: {:#x?}", exit_info);
23+
24+
if exit_info.entry_failure {
25+
panic!("VM entry failed: {:#x?}", exit_info);
26+
}
27+
28+
let res = match exit_info.exit_reason {
29+
VmxExitReason::VMCALL => handle_hypercall(vcpu),
30+
_ => panic!(
31+
"Unhandled VM-Exit reason {:?}:\n{:#x?}",
32+
exit_info.exit_reason, vcpu
33+
),
34+
};
35+
36+
if res.is_err() {
37+
panic!(
38+
"Failed to handle VM-exit {:?}:\n{:#x?}",
39+
exit_info.exit_reason, vcpu
40+
);
41+
}
42+
43+
Ok(())
44+
}

hypervisor/src/main.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![no_std]
22
#![no_main]
33
#![feature(asm_const)]
4+
#![feature(naked_functions)]
45
#![feature(panic_info_message, alloc_error_handler)]
56

67
#[macro_use]
@@ -76,10 +77,4 @@ fn main() -> ! {
7677
println!("Initialization completed.\n");
7778

7879
hv::run();
79-
println!("Run OK!");
80-
81-
arch::instructions::enable_irqs();
82-
loop {
83-
arch::instructions::wait_for_ints();
84-
}
8580
}

rvm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ log = "0.4"
1515
cfg-if = "1.0"
1616
bitflags = "1.3"
1717
bit_field = "0.10"
18+
numeric-enum-macro = "0.2"
1819

1920
[target.'cfg(target_arch = "x86_64")'.dependencies]
2021
x86 = "0.52"

rvm/src/arch/x86_64/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
pub(crate) mod msr;
2+
3+
#[macro_use]
24
pub(crate) mod regs;
35

46
cfg_if::cfg_if! {
57
if #[cfg(feature = "vmx")] {
68
mod vmx;
79
use vmx as vender;
10+
pub use vmx::{VmxExitInfo, VmxExitReason};
811
}
912
}
1013

11-
pub use vender::{has_hardware_support, ArchPerCpuState, RvmVcpu};
14+
pub(crate) use vender::{has_hardware_support, ArchPerCpuState};
15+
16+
pub use regs::GeneralRegisters;
17+
pub use vender::RvmVcpu;

rvm/src/arch/x86_64/regs.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,47 @@ pub struct GeneralRegisters {
1919
pub r14: u64,
2020
pub r15: u64,
2121
}
22+
23+
macro_rules! save_regs_to_stack {
24+
() => {
25+
"
26+
push r15
27+
push r14
28+
push r13
29+
push r12
30+
push r11
31+
push r10
32+
push r9
33+
push r8
34+
push rdi
35+
push rsi
36+
push rbp
37+
sub rsp, 8
38+
push rbx
39+
push rdx
40+
push rcx
41+
push rax"
42+
};
43+
}
44+
45+
macro_rules! restore_regs_from_stack {
46+
() => {
47+
"
48+
pop rax
49+
pop rcx
50+
pop rdx
51+
pop rbx
52+
add rsp, 8
53+
pop rbp
54+
pop rsi
55+
pop rdi
56+
pop r8
57+
pop r9
58+
pop r10
59+
pop r11
60+
pop r12
61+
pop r13
62+
pop r14
63+
pop r15"
64+
};
65+
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use core::fmt::{Debug, Formatter, Result};
2+
3+
/// VM instruction error numbers. (SDM Vol. 3C, Section 30.4)
4+
pub struct VmxInstructionError(u32);
5+
6+
impl VmxInstructionError {
7+
pub fn as_str(&self) -> &str {
8+
match self.0 {
9+
0 => "OK",
10+
1 => "VMCALL executed in VMX root operation",
11+
2 => "VMCLEAR with invalid physical address",
12+
3 => "VMCLEAR with VMXON pointer",
13+
4 => "VMLAUNCH with non-clear VMCS",
14+
5 => "VMRESUME with non-launched VMCS",
15+
6 => "VMRESUME after VMXOFF (VMXOFF and VMXON between VMLAUNCH and VMRESUME)",
16+
7 => "VM entry with invalid control field(s)",
17+
8 => "VM entry with invalid host-state field(s)",
18+
9 => "VMPTRLD with invalid physical address",
19+
10 => "VMPTRLD with VMXON pointer",
20+
11 => "VMPTRLD with incorrect VMCS revision identifier",
21+
12 => "VMREAD/VMWRITE from/to unsupported VMCS component",
22+
13 => "VMWRITE to read-only VMCS component",
23+
15 => "VMXON executed in VMX root operation",
24+
16 => "VM entry with invalid executive-VMCS pointer",
25+
17 => "VM entry with non-launched executive VMCS",
26+
18 => "VM entry with executive-VMCS pointer not VMXON pointer (when attempting to deactivate the dual-monitor treatment of SMIs and SMM)",
27+
19 => "VMCALL with non-clear VMCS (when attempting to activate the dual-monitor treatment of SMIs and SMM)",
28+
20 => "VMCALL with invalid VM-exit control fields",
29+
22 => "VMCALL with incorrect MSEG revision identifier (when attempting to activate the dual-monitor treatment of SMIs and SMM)",
30+
23 => "VMXOFF under dual-monitor treatment of SMIs and SMM",
31+
24 => "VMCALL with invalid SMM-monitor features (when attempting to activate the dual-monitor treatment of SMIs and SMM)",
32+
25 => "VM entry with invalid VM-execution control fields in executive VMCS (when attempting to return from SMM)",
33+
26 => "VM entry with events blocked by MOV SS",
34+
28 => "Invalid operand to INVEPT/INVVPID",
35+
_ => "[INVALID]",
36+
}
37+
}
38+
}
39+
40+
impl From<u32> for VmxInstructionError {
41+
fn from(value: u32) -> Self {
42+
Self(value)
43+
}
44+
}
45+
46+
impl Debug for VmxInstructionError {
47+
fn fmt(&self, f: &mut Formatter) -> Result {
48+
write!(f, "VmxInstructionError({}, {:?})", self.0, self.as_str())
49+
}
50+
}
51+
52+
numeric_enum_macro::numeric_enum! {
53+
#[repr(u32)]
54+
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
55+
#[allow(non_camel_case_types)]
56+
/// VMX basic exit reasons. (SDM Vol. 3D, Appendix C)
57+
pub enum VmxExitReason {
58+
EXCEPTION_NMI = 0,
59+
EXTERNAL_INTERRUPT = 1,
60+
TRIPLE_FAULT = 2,
61+
INIT = 3,
62+
SIPI = 4,
63+
SMI = 5,
64+
OTHER_SMI = 6,
65+
INTERRUPT_WINDOW = 7,
66+
NMI_WINDOW = 8,
67+
TASK_SWITCH = 9,
68+
CPUID = 10,
69+
GETSEC = 11,
70+
HLT = 12,
71+
INVD = 13,
72+
INVLPG = 14,
73+
RDPMC = 15,
74+
RDTSC = 16,
75+
RSM = 17,
76+
VMCALL = 18,
77+
VMCLEAR = 19,
78+
VMLAUNCH = 20,
79+
VMPTRLD = 21,
80+
VMPTRST = 22,
81+
VMREAD = 23,
82+
VMRESUME = 24,
83+
VMWRITE = 25,
84+
VMOFF = 26,
85+
VMON = 27,
86+
CR_ACCESS = 28,
87+
DR_ACCESS = 29,
88+
IO_INSTRUCTION = 30,
89+
MSR_READ = 31,
90+
MSR_WRITE = 32,
91+
INVALID_GUEST_STATE = 33,
92+
MSR_LOAD_FAIL = 34,
93+
MWAIT_INSTRUCTION = 36,
94+
MONITOR_TRAP_FLAG = 37,
95+
MONITOR_INSTRUCTION = 39,
96+
PAUSE_INSTRUCTION = 40,
97+
MCE_DURING_VMENTRY = 41,
98+
TPR_BELOW_THRESHOLD = 43,
99+
APIC_ACCESS = 44,
100+
VIRTUALIZED_EOI = 45,
101+
GDTR_IDTR = 46,
102+
LDTR_TR = 47,
103+
EPT_VIOLATION = 48,
104+
EPT_MISCONFIG = 49,
105+
INVEPT = 50,
106+
RDTSCP = 51,
107+
PREEMPTION_TIMER = 52,
108+
INVVPID = 53,
109+
WBINVD = 54,
110+
XSETBV = 55,
111+
APIC_WRITE = 56,
112+
RDRAND = 57,
113+
INVPCID = 58,
114+
VMFUNC = 59,
115+
ENCLS = 60,
116+
RDSEED = 61,
117+
PML_FULL = 62,
118+
XSAVES = 63,
119+
XRSTORS = 64,
120+
PCONFIG = 65,
121+
SPP_EVENT = 66,
122+
UMWAIT = 67,
123+
TPAUSE = 68,
124+
LOADIWKEY = 69,
125+
}
126+
}

rvm/src/arch/x86_64/vmx/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod definitions;
12
mod structs;
23
mod vcpu;
34
mod vmcs;
@@ -11,7 +12,9 @@ use crate::arch::msr::Msr;
1112
use crate::error::{RvmError, RvmResult};
1213
use crate::hal::RvmHal;
1314

15+
pub use self::definitions::VmxExitReason;
1416
pub use self::vcpu::VmxVcpu as RvmVcpu;
17+
pub use self::vmcs::VmxExitInfo;
1518
pub use self::VmxPerCpuState as ArchPerCpuState;
1619

1720
pub fn has_hardware_support() -> bool {
@@ -127,6 +130,9 @@ impl<H: RvmHal> VmxPerCpuState<H> {
127130

128131
impl From<VmFail> for RvmError {
129132
fn from(err: VmFail) -> Self {
130-
rvm_err_type!(BadState, format_args!("VMX instruction failed: {:?}", err))
133+
match err {
134+
VmFail::VmFailValid => rvm_err_type!(BadState, vmcs::instruction_error().as_str()),
135+
_ => rvm_err_type!(BadState, format_args!("VMX instruction failed: {:?}", err)),
136+
}
131137
}
132138
}

0 commit comments

Comments
 (0)