Compare commits

...

7 Commits

Author SHA1 Message Date
ecad19bb5e fix warn
All checks were successful
Check / Build ISO (nightly-2025-04-27) (push) Successful in 43s
Release ISO / release (push) Successful in 57s
2025-08-22 12:29:01 +00:00
94cbcd6a1f io bitmap
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 45s
2025-08-22 12:18:50 +00:00
de21059b53 add pic
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 44s
2025-08-22 10:06:38 +00:00
7fb000f5ce intr subscriber
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 46s
2025-08-22 09:51:20 +00:00
fc93be3043 add CR handler
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 44s
2025-08-22 09:07:00 +00:00
4a44cdea8d qual, cr
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 45s
2025-08-22 09:01:32 +00:00
a0a204b43c cr vmexit
Some checks failed
Check / Build ISO (nightly-2025-04-27) (push) Failing after 46s
2025-08-22 08:34:41 +00:00
10 changed files with 735 additions and 19 deletions

View File

@ -5,6 +5,7 @@ use crate::{
interrupt::{
apic::{EOI, LAPIC},
gdt,
subscriber::InterruptContext,
},
time, warn,
};
@ -40,6 +41,17 @@ pub fn init_idt() {
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
let context = InterruptContext {
vector: 3,
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
code_segment: stack_frame.code_segment.0 as u64,
cpu_flags: stack_frame.cpu_flags.bits(),
stack_pointer: stack_frame.stack_pointer.as_u64(),
stack_segment: stack_frame.stack_segment.0 as u64,
};
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
warn!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}
@ -47,6 +59,17 @@ extern "x86-interrupt" fn double_fault_handler(
stack_frame: InterruptStackFrame,
_error_code: u64,
) -> ! {
let context = InterruptContext {
vector: 8,
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
code_segment: stack_frame.code_segment.0 as u64,
cpu_flags: stack_frame.cpu_flags.bits(),
stack_pointer: stack_frame.stack_pointer.as_u64(),
stack_segment: stack_frame.stack_segment.0 as u64,
};
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}
@ -56,6 +79,17 @@ extern "x86-interrupt" fn page_fault_handler(
) {
use x86_64::registers::control::Cr2;
let context = InterruptContext {
vector: 14,
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
code_segment: stack_frame.code_segment.0 as u64,
cpu_flags: stack_frame.cpu_flags.bits(),
stack_pointer: stack_frame.stack_pointer.as_u64(),
stack_segment: stack_frame.stack_segment.0 as u64,
};
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
panic!(
"EXCEPTION: PAGE FAULT\n{:#?}\nAccessed address: {:#x}",
stack_frame,
@ -63,7 +97,18 @@ extern "x86-interrupt" fn page_fault_handler(
);
}
extern "x86-interrupt" fn timer_handler(_stack_frame: InterruptStackFrame) {
extern "x86-interrupt" fn timer_handler(stack_frame: InterruptStackFrame) {
let context = InterruptContext {
vector: IRQ_TIMER as u8,
instruction_pointer: stack_frame.instruction_pointer.as_u64(),
code_segment: stack_frame.code_segment.0 as u64,
cpu_flags: stack_frame.cpu_flags.bits(),
stack_pointer: stack_frame.stack_pointer.as_u64(),
stack_segment: stack_frame.stack_segment.0 as u64,
};
crate::interrupt::subscriber::dispatch_to_subscribers(&context);
time::tick();
LAPIC.get().unwrap().write(EOI, 0);
}

View File

@ -1,3 +1,4 @@
pub mod apic;
pub mod gdt;
pub mod idt;
pub mod subscriber;

View File

@ -0,0 +1,68 @@
use spin::Mutex;
#[derive(Debug)]
pub struct InterruptContext {
pub vector: u8,
pub instruction_pointer: u64,
pub code_segment: u64,
pub cpu_flags: u64,
pub stack_pointer: u64,
pub stack_segment: u64,
}
pub type SubscriberCallback = fn(*mut core::ffi::c_void, &InterruptContext);
#[derive(Debug, Clone, Copy)]
pub struct Subscriber {
pub callback: SubscriberCallback,
pub context: *mut core::ffi::c_void,
}
unsafe impl Send for Subscriber {}
unsafe impl Sync for Subscriber {}
const MAX_SUBSCRIBERS: usize = 10;
static SUBSCRIBERS: Mutex<[Option<Subscriber>; MAX_SUBSCRIBERS]> =
Mutex::new([None; MAX_SUBSCRIBERS]);
pub fn subscribe(
callback: SubscriberCallback,
context: *mut core::ffi::c_void,
) -> Result<(), &'static str> {
let mut subscribers = SUBSCRIBERS.lock();
for slot in subscribers.iter_mut() {
if slot.is_none() {
*slot = Some(Subscriber { callback, context });
return Ok(());
}
}
Err("No available subscriber slots")
}
pub fn unsubscribe(callback: SubscriberCallback) -> Result<(), &'static str> {
let mut subscribers = SUBSCRIBERS.lock();
for slot in subscribers.iter_mut() {
if let Some(subscriber) = slot {
if core::ptr::fn_addr_eq(subscriber.callback, callback) {
*slot = None;
return Ok(());
}
}
}
Err("Subscriber not found")
}
pub fn dispatch_to_subscribers(context: &InterruptContext) {
let subscribers = SUBSCRIBERS.lock();
for subscriber in subscribers.iter() {
if let Some(subscriber) = subscriber {
(subscriber.callback)(subscriber.context, context);
}
}
}

View File

@ -1,4 +1,7 @@
use crate::vmm::x86_64::{common, intel::vmcs};
use crate::vmm::x86_64::{
common,
intel::{vmcs, vmwrite},
};
pub fn setup_exec_controls() -> Result<(), &'static str> {
let basic_msr = common::read_msr(0x480);
@ -12,8 +15,8 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
raw_pin_exec_ctrl |= (reserved_bits & 0xFFFFFFFF) as u32;
raw_pin_exec_ctrl &= (reserved_bits >> 32) as u32;
let pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl);
//pin_exec_ctrl.set_external_interrupt_exiting(false);
let mut pin_exec_ctrl = vmcs::controls::PinBasedVmExecutionControls::from(raw_pin_exec_ctrl);
pin_exec_ctrl.set_external_interrupt_exiting(false);
pin_exec_ctrl.write()?;
@ -33,6 +36,8 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
primary_exec_ctrl.set_hlt(true);
primary_exec_ctrl.set_activate_secondary_controls(true);
primary_exec_ctrl.set_use_msr_bitmap(false);
primary_exec_ctrl.set_unconditional_io(false);
primary_exec_ctrl.set_use_io_bitmap(true);
primary_exec_ctrl.write()?;
@ -55,6 +60,9 @@ pub fn setup_exec_controls() -> Result<(), &'static str> {
secondary_exec_ctrl.write()?;
vmwrite(x86::vmx::vmcs::control::CR0_GUEST_HOST_MASK, u64::MAX)?;
vmwrite(x86::vmx::vmcs::control::CR4_GUEST_HOST_MASK, u64::MAX)?;
Ok(())
}
@ -101,7 +109,13 @@ pub fn setup_exit_controls() -> Result<(), &'static str> {
exit_ctrl.write()?;
//vmwrite(0x4004, 1u64 << 6)?; // EXCEPTION_BITMAP
/*vmwrite(
0x4004,
1u64 << x86::irq::DOUBLE_FAULT_VECTOR
| 1u64 << x86::irq::GENERAL_PROTECTION_FAULT_VECTOR
| 1u64 << x86::irq::PAGE_FAULT_VECTOR
| 1u64 << x86::irq::X87_FPU_VECTOR,
)?;*/
Ok(())
}

View File

@ -0,0 +1,166 @@
use x86::vmx::vmcs;
use crate::vmm::x86_64::{
common::read_msr,
intel::{
qual::{AccessType, QualCr, Register},
vcpu::IntelVCpu,
vmread, vmwrite,
},
};
pub fn handle_cr_access(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
match qual.access_type() {
AccessType::MovTo => match qual.index() {
0 | 4 => {
passthrough_write(vcpu, qual)?;
update_ia32e(vcpu)?;
}
_ => panic!("Unsupported CR index: {}", qual.index()),
},
AccessType::MovFrom => passthrough_read(vcpu, qual)?,
_ => {
panic!("Unsupported CR access type: {:?}", qual.access_type());
}
}
Ok(())
}
fn passthrough_read(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
let value = match qual.index() {
3 => vmread(x86::vmx::vmcs::guest::CR3)?,
_ => panic!("Unsupported CR index: {}", qual.index()),
};
set_value(vcpu, qual, value)?;
Ok(())
}
fn passthrough_write(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<(), &'static str> {
let value = get_value(vcpu, qual)?;
match qual.index() {
0 => {
vmwrite(vmcs::guest::CR0, adjust_cr0(value))?;
vmwrite(vmcs::control::CR0_READ_SHADOW, value)?;
}
4 => {
vmwrite(vmcs::guest::CR4, adjust_cr4(value))?;
vmwrite(vmcs::control::CR4_READ_SHADOW, value)?;
}
_ => {
panic!("Unsupported CR index: {}", qual.index());
}
}
Ok(())
}
pub fn update_ia32e(vcpu: &mut IntelVCpu) -> Result<(), &'static str> {
let cr0 = vmread(x86::vmx::vmcs::guest::CR0)?;
let cr4 = vmread(x86::vmx::vmcs::guest::CR4)?;
let ia32e_enabled = (cr0 & 1 << 31) != 0 && (cr4 & 1 << 5) != 0;
vcpu.ia32e_enabled = ia32e_enabled;
let mut entry_ctrl = super::vmcs::controls::EntryControls::read()?;
entry_ctrl.set_ia32e_mode_guest(ia32e_enabled);
entry_ctrl.write()?;
let mut efer = vmread(x86::vmx::vmcs::guest::IA32_EFER_FULL)?;
let lma = (vcpu.ia32e_enabled as u64) << 10;
if lma != 0 {
efer |= lma;
} else {
efer &= !lma;
}
let lme = if cr0 & (1 << 31) != 0 {
efer & (1 << 10)
} else {
efer & !(1 << 8)
};
if lme != 0 {
efer |= lme;
} else {
efer &= lme;
}
vmwrite(x86::vmx::vmcs::guest::IA32_EFER_FULL, efer)?;
Ok(())
}
pub fn adjust_cr0(value: u64) -> u64 {
let mut result = value;
let cr0_fixed0 = read_msr(x86::msr::IA32_VMX_CR0_FIXED0);
let cr0_fixed1 = read_msr(x86::msr::IA32_VMX_CR0_FIXED1);
result |= cr0_fixed0;
result &= cr0_fixed1;
result
}
pub fn adjust_cr4(value: u64) -> u64 {
let mut result = value;
let cr4_fixed0 = read_msr(x86::msr::IA32_VMX_CR4_FIXED0);
let cr4_fixed1 = read_msr(x86::msr::IA32_VMX_CR4_FIXED1);
result |= cr4_fixed0;
result &= cr4_fixed1;
result
}
fn set_value(vcpu: &mut IntelVCpu, qual: &QualCr, value: u64) -> Result<(), &'static str> {
let guest_regs = &mut vcpu.guest_registers;
match qual.register() {
Register::Rax => guest_regs.rax = value,
Register::Rcx => guest_regs.rcx = value,
Register::Rdx => guest_regs.rdx = value,
Register::Rbx => guest_regs.rbx = value,
Register::Rbp => guest_regs.rbp = value,
Register::Rsi => guest_regs.rsi = value,
Register::Rdi => guest_regs.rdi = value,
Register::R8 => guest_regs.r8 = value,
Register::R9 => guest_regs.r9 = value,
Register::R10 => guest_regs.r10 = value,
Register::R11 => guest_regs.r11 = value,
Register::R12 => guest_regs.r12 = value,
Register::R13 => guest_regs.r13 = value,
Register::R14 => guest_regs.r14 = value,
Register::R15 => guest_regs.r15 = value,
Register::Rsp => vmwrite(x86::vmx::vmcs::guest::RSP, value)?,
}
Ok(())
}
fn get_value(vcpu: &mut IntelVCpu, qual: &QualCr) -> Result<u64, &'static str> {
let guest_regs = &mut vcpu.guest_registers;
Ok(match qual.register() {
Register::Rax => guest_regs.rax,
Register::Rcx => guest_regs.rcx,
Register::Rdx => guest_regs.rdx,
Register::Rbx => guest_regs.rbx,
Register::Rbp => guest_regs.rbp,
Register::Rsi => guest_regs.rsi,
Register::Rdi => guest_regs.rdi,
Register::R8 => guest_regs.r8,
Register::R9 => guest_regs.r9,
Register::R10 => guest_regs.r10,
Register::R11 => guest_regs.r11,
Register::R12 => guest_regs.r12,
Register::R13 => guest_regs.r13,
Register::R14 => guest_regs.r14,
Register::R15 => guest_regs.r15,
Register::Rsp => vmread(x86::vmx::vmcs::guest::RSP)?,
})
}

View File

@ -0,0 +1,249 @@
use x86::vmx::vmcs;
use x86_64::structures::paging::{FrameAllocator, PhysFrame, Size4KiB};
use super::qual::QualIo;
use crate::{
info,
vmm::x86_64::intel::{register::GuestRegisters, vmwrite},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InitPhase {
Uninitialized,
Phase1,
Phase2,
Phase3,
Initialized,
}
pub enum ReadSel {
IRR,
ISR,
}
pub struct PIC {
pub primary_mask: u8,
pub secondary_mask: u8,
pub primary_phase: InitPhase,
pub secondary_phase: InitPhase,
pub primary_base: u8,
pub secondary_base: u8,
pub primary_irr: u8,
pub primary_isr: u8,
pub secondary_irr: u8,
pub secondary_isr: u8,
pub primary_read_sel: ReadSel,
pub secondary_read_sel: ReadSel,
}
impl PIC {
pub fn new() -> Self {
Self {
primary_mask: 0xFF,
secondary_mask: 0xFF,
primary_phase: InitPhase::Uninitialized,
secondary_phase: InitPhase::Uninitialized,
primary_base: 0,
secondary_base: 0,
primary_irr: 0,
primary_isr: 0,
secondary_irr: 0,
secondary_isr: 0,
primary_read_sel: ReadSel::IRR,
secondary_read_sel: ReadSel::IRR,
}
}
pub fn handle_io(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
match qual.direction() {
0 => {
self.handle_io_out(regs, qual);
}
1 => {
self.handle_io_in(regs, qual);
}
_ => {}
}
}
pub fn handle_io_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
match qual.port() {
0x0CF8..=0x0CFF => regs.rax = 0,
0xC000..=0xCFFF => {} //ignore
0x20..=0x21 => self.handle_pic_in(regs, qual),
0xA0..=0xA1 => self.handle_pic_in(regs, qual),
0x0070..=0x0071 => regs.rax = 0,
_ => regs.rax = 0,
}
}
pub fn handle_io_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
match qual.port() {
0x0CF8..=0x0CFF => {} //ignore
0xC000..=0xCFFF => {} //ignore
0x20..=0x21 => self.handle_pic_out(regs, qual),
0xA0..=0xA1 => self.handle_pic_out(regs, qual),
0x0070..=0x0071 => {} //ignore
_ => {}
}
}
pub fn handle_pic_in(&self, regs: &mut GuestRegisters, qual: QualIo) {
match qual.port() {
0x20 => {
let v = match self.primary_read_sel {
ReadSel::IRR => self.primary_irr,
ReadSel::ISR => self.primary_isr,
};
regs.rax = v as u64;
}
0xA0 => {
let v = match self.secondary_read_sel {
ReadSel::IRR => self.secondary_irr,
ReadSel::ISR => self.secondary_isr,
};
regs.rax = v as u64;
}
0x21 => match self.primary_phase {
InitPhase::Uninitialized | InitPhase::Initialized => {
regs.rax = self.primary_mask as u64;
}
_ => {}
},
0xA1 => match self.secondary_phase {
InitPhase::Uninitialized | InitPhase::Initialized => {
regs.rax = self.secondary_mask as u64;
}
_ => {}
},
_ => {}
}
}
pub fn handle_pic_out(&mut self, regs: &mut GuestRegisters, qual: QualIo) {
let pic = self;
let dx = regs.rax as u8;
match qual.port() {
0x20 => match dx {
0x11 => pic.primary_phase = InitPhase::Phase1,
0x0A => pic.primary_read_sel = ReadSel::ISR,
0x0B => pic.primary_read_sel = ReadSel::IRR,
0x20 => {
pic.primary_isr = 0;
}
0x60..=0x67 => {
let irq = dx & 0x7;
pic.primary_isr &= !(1 << irq);
}
_ => panic!("Primary PIC command: {:#x}", dx),
},
0x21 => match pic.primary_phase {
InitPhase::Uninitialized | InitPhase::Initialized => pic.primary_mask = dx,
InitPhase::Phase1 => {
pic.primary_base = dx;
pic.primary_phase = InitPhase::Phase2;
}
InitPhase::Phase2 => {
pic.primary_phase = InitPhase::Phase3;
}
InitPhase::Phase3 => {
info!("Primary PIC Initialized");
pic.primary_phase = InitPhase::Initialized
}
},
0xA0 => match dx {
0x11 => pic.secondary_phase = InitPhase::Phase1,
0x0A => pic.secondary_read_sel = ReadSel::ISR,
0x0B => pic.secondary_read_sel = ReadSel::IRR,
0x20 => {
pic.secondary_isr = 0;
}
0x60..=0x67 => {
let irq = dx & 0x7;
pic.secondary_isr &= !(1 << irq);
}
_ => panic!("Secondary PIC command: {:#x}", dx),
},
0xA1 => match pic.secondary_phase {
InitPhase::Uninitialized | InitPhase::Initialized => pic.secondary_mask = dx,
InitPhase::Phase1 => {
pic.secondary_base = dx;
pic.secondary_phase = InitPhase::Phase2;
}
InitPhase::Phase2 => {
pic.secondary_phase = InitPhase::Phase3;
}
InitPhase::Phase3 => {
info!("Secondary PIC Initialized");
pic.secondary_phase = InitPhase::Initialized
}
},
_ => {}
}
}
}
pub struct IOBitmap {
pub bitmap_a: PhysFrame,
pub bitmap_b: PhysFrame,
}
impl IOBitmap {
pub fn new(frame_allocator: &mut impl FrameAllocator<Size4KiB>) -> Self {
let bitmap_a = frame_allocator
.allocate_frame()
.expect("Failed to allocate I/O bitmap A");
let bitmap_b = frame_allocator
.allocate_frame()
.expect("Failed to allocate I/O bitmap B");
Self { bitmap_a, bitmap_b }
}
pub fn setup(&mut self) -> Result<(), &'static str> {
let bitmap_a_addr = self.bitmap_a.start_address().as_u64() as usize;
let bitmap_b_addr = self.bitmap_b.start_address().as_u64() as usize;
unsafe {
core::ptr::write_bytes(bitmap_a_addr as *mut u8, u8::MAX, 4096);
core::ptr::write_bytes(bitmap_b_addr as *mut u8, u8::MAX, 4096);
}
self.set_io_ports(0x02F8..=0x03FF);
self.set_io_ports(0x0040..=0x0047);
vmwrite(vmcs::control::IO_BITMAP_A_ADDR_FULL, bitmap_a_addr as u64)?;
vmwrite(vmcs::control::IO_BITMAP_B_ADDR_FULL, bitmap_b_addr as u64)?;
Ok(())
}
pub fn set_io_ports(&mut self, ports: core::ops::RangeInclusive<u16>) {
for port in ports {
if port <= 0x7FFF {
let byte_index = port as usize / 8;
let bit_index = port as usize % 8;
self.get_bitmap_a()[byte_index] &= !(1 << bit_index);
} else {
let adjusted_port = port - 0x8000;
let byte_index = adjusted_port as usize / 8;
let bit_index = adjusted_port as usize % 8;
self.get_bitmap_b()[byte_index] &= !(1 << bit_index);
}
}
}
fn get_bitmap_a(&self) -> &mut [u8] {
unsafe {
core::slice::from_raw_parts_mut(self.bitmap_a.start_address().as_u64() as *mut u8, 4096)
}
}
fn get_bitmap_b(&self) -> &mut [u8] {
unsafe {
core::slice::from_raw_parts_mut(self.bitmap_b.start_address().as_u64() as *mut u8, 4096)
}
}
}

View File

@ -2,8 +2,11 @@ pub mod asm;
mod auditor;
mod controls;
mod cpuid;
mod cr;
mod ept;
mod io;
mod msr;
mod qual;
mod register;
pub mod vcpu;
mod vmcs;

View File

@ -92,7 +92,7 @@ pub fn register_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
Ok(())
}
pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
pub fn _update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
info!("updating MSRs");
let indices_to_update: alloc::vec::Vec<u32> = vcpu
.host_msr
@ -101,8 +101,6 @@ pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
.map(|entry| entry.index)
.collect();
info!("1");
for index in indices_to_update {
info!("{}", index);
let value = read_msr(index);
@ -110,25 +108,21 @@ pub fn update_msrs(vcpu: &mut IntelVCpu) -> Result<(), MsrError> {
vcpu.host_msr.set_by_index(index, value).unwrap();
}
info!("2");
vmwrite(
vmcs::control::VMEXIT_MSR_LOAD_COUNT,
vcpu.host_msr.saved_ents().len() as u64,
)
.unwrap();
info!("3");
vmwrite(
vmcs::control::VMEXIT_MSR_STORE_COUNT,
vcpu.guest_msr.saved_ents().len() as u64,
)
.unwrap();
info!("4");
vmwrite(
vmcs::control::VMENTRY_MSR_LOAD_COUNT,
vcpu.guest_msr.saved_ents().len() as u64,
)
.unwrap();
info!("5");
Ok(())
}

View File

@ -0,0 +1,124 @@
#![allow(non_snake_case)]
use core::convert::TryFrom;
use core::fmt::Debug;
use modular_bitfield::prelude::{B1, B16, B32, B4, B8};
use modular_bitfield::{bitfield, Specifier};
#[repr(u8)]
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
pub enum AccessType {
MovTo = 0,
MovFrom = 1,
Clts = 2,
Lmsw = 3,
}
impl TryFrom<u8> for AccessType {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(AccessType::MovTo),
1 => Ok(AccessType::MovFrom),
2 => Ok(AccessType::Clts),
3 => Ok(AccessType::Lmsw),
_ => Err("Invalid AccessType value"),
}
}
}
#[repr(u8)]
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
pub enum LmswOperandType {
Reg = 0,
Mem = 1,
}
impl TryFrom<u8> for LmswOperandType {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(LmswOperandType::Reg),
1 => Ok(LmswOperandType::Mem),
_ => Err("Invalid LmswOperandType value"),
}
}
}
#[repr(u8)]
#[derive(Specifier, Debug, Clone, Copy, PartialEq, Eq)]
pub enum Register {
Rax = 0,
Rcx = 1,
Rdx = 2,
Rbx = 3,
Rsp = 4,
Rbp = 5,
Rsi = 6,
Rdi = 7,
R8 = 8,
R9 = 9,
R10 = 10,
R11 = 11,
R12 = 12,
R13 = 13,
R14 = 14,
R15 = 15,
}
impl TryFrom<u8> for Register {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(Register::Rax),
1 => Ok(Register::Rcx),
2 => Ok(Register::Rdx),
3 => Ok(Register::Rbx),
4 => Ok(Register::Rsp),
5 => Ok(Register::Rbp),
6 => Ok(Register::Rsi),
7 => Ok(Register::Rdi),
8 => Ok(Register::R8),
9 => Ok(Register::R9),
10 => Ok(Register::R10),
11 => Ok(Register::R11),
12 => Ok(Register::R12),
13 => Ok(Register::R13),
14 => Ok(Register::R14),
15 => Ok(Register::R15),
_ => Err("Invalid Register value"),
}
}
}
#[bitfield]
#[repr(u64)]
#[derive(Debug, Clone, Copy)]
pub struct QualCr {
pub index: B4,
#[bits = 2]
pub access_type: AccessType,
#[bits = 1]
pub lmsw_operand_type: LmswOperandType,
_reserved1: B1,
#[bits = 4]
pub register: Register,
_reserved2: B4,
pub lmsw_source: B16,
_reseved3: B32,
}
#[bitfield]
#[repr(u64)]
#[derive(Debug, Clone, Copy)]
pub struct QualIo {
pub size: B4,
pub direction: B1,
pub string: B1,
pub rep: B1,
pub operand_encoding: B1,
_reserved1: B8,
pub port: B16,
_reserved2: B32,
}

View File

@ -12,7 +12,9 @@ use crate::{
common::{self, read_msr},
intel::{
auditor, controls, cpuid, ept,
io::IOBitmap,
msr::{self, ShadowMsr},
qual::{QualCr, QualIo},
register::GuestRegisters,
vmcs::{
self,
@ -42,6 +44,9 @@ pub struct IntelVCpu {
guest_memory_size: u64,
pub host_msr: ShadowMsr,
pub guest_msr: ShadowMsr,
pub ia32e_enabled: bool,
pic: super::io::PIC,
io_bitmap: IOBitmap,
}
impl IntelVCpu {
@ -76,7 +81,6 @@ impl IntelVCpu {
self.step_next_inst()?;
}
VmxExitReason::CPUID => {
info!("VM exit reason: CPUID");
cpuid::handle_cpuid_vmexit(self);
self.step_next_inst()?;
}
@ -88,11 +92,53 @@ impl IntelVCpu {
msr::ShadowMsr::handle_wrmsr_vmexit(self);
self.step_next_inst()?;
}
VmxExitReason::CONTROL_REGISTER_ACCESSES => {
let qual = vmread(vmcs::ro::EXIT_QUALIFICATION)?;
let qual = QualCr::from(qual);
super::cr::handle_cr_access(self, &qual)?;
self.step_next_inst()?;
}
VmxExitReason::IO_INSTRUCTION => {
let qual = vmread(vmcs::ro::EXIT_QUALIFICATION)?;
let qual_io = QualIo::from(qual);
self.pic.handle_io(&mut self.guest_registers, qual_io);
self.step_next_inst()?;
}
VmxExitReason::EPT_VIOLATION => {
let guest_address = vmread(vmcs::ro::GUEST_PHYSICAL_ADDR_FULL)?;
info!("EPT Violation at guest address: {:#x}", guest_address);
return Err("EPT Violation");
}
VmxExitReason::TRIPLE_FAULT => {
info!("Triple fault detected");
return Err("Triple fault");
}
VmxExitReason::EXCEPTION => {
let vmexit_intr_info = vmread(vmcs::ro::VMEXIT_INTERRUPTION_INFO)?;
let vector = (vmexit_intr_info & 0xFF) as u8;
let error_code = (vmexit_intr_info >> 8) & 0b111;
let error_code_valid = (vmexit_intr_info >> 11) & 0b1 != 0;
let idt_vectoring_info = vmread(vmcs::ro::IDT_VECTORING_INFO)?;
info!("idt valid: {}", idt_vectoring_info >> 31 & 0b1 != 0);
let rip = vmread(vmcs::guest::RIP)?;
let hpa = self.ept.get_phys_addr(rip).unwrap();
if error_code_valid {
info!(
"VM exit due to exception: vector {}, error code {}, at RIP {:#x} (hpa: {:#x})",
vector, error_code, rip, hpa
);
} else {
info!("VM exit due to exception: vector {}", vector);
}
return Err("VM exit due to exception");
}
_ => {
info!("VM exit reason: {:?}", exit_reason);
return Err("Unhandled VM exit reason");
@ -114,8 +160,6 @@ impl IntelVCpu {
}
fn vmentry(&mut self) -> Result<(), InstructionError> {
msr::update_msrs(self).unwrap();
auditor::controls::check_vmcs_control_fields().unwrap();
let success = {
@ -152,6 +196,7 @@ impl IntelVCpu {
controls::setup_exit_controls()?;
Self::setup_host_state()?;
self.setup_guest_state()?;
self.io_bitmap.setup()?;
self.init_guest_memory(frame_allocator)?;
@ -233,14 +278,18 @@ impl IntelVCpu {
fn setup_guest_state(&mut self) -> Result<(), &'static str> {
use x86::{controlregs::*, vmx::vmcs};
let cr0 = Cr0::empty()
let cr0 = (Cr0::empty()
| Cr0::CR0_PROTECTED_MODE
| Cr0::CR0_NUMERIC_ERROR & !Cr0::CR0_ENABLE_PAGING;
| Cr0::CR0_NUMERIC_ERROR
| Cr0::CR0_EXTENSION_TYPE)
& !Cr0::CR0_ENABLE_PAGING;
vmwrite(vmcs::guest::CR0, cr0.bits() as u64)?;
vmwrite(vmcs::guest::CR3, unsafe { cr3() })?;
vmwrite(vmcs::guest::CR3, 0)?;
vmwrite(
vmcs::guest::CR4,
vmread(vmcs::guest::CR4)? & !Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits(),
vmread(vmcs::guest::CR4)?
| Cr4Flags::VIRTUAL_MACHINE_EXTENSIONS.bits()
& !Cr4Flags::PHYSICAL_ADDRESS_EXTENSION.bits(),
)?;
vmwrite(vmcs::guest::CS_BASE, 0)?;
@ -638,6 +687,9 @@ impl VCpu for IntelVCpu {
guest_memory_size: 1024 * 1024 * 256, // 256 MiB
host_msr: ShadowMsr::new(),
guest_msr: ShadowMsr::new(),
ia32e_enabled: false,
pic: super::io::PIC::new(),
io_bitmap: IOBitmap::new(frame_allocator),
})
}