diff --git a/nel_os_kernel/src/vmm/x86_64/intel/cr.rs b/nel_os_kernel/src/vmm/x86_64/intel/cr.rs new file mode 100644 index 0000000..111992a --- /dev/null +++ b/nel_os_kernel/src/vmm/x86_64/intel/cr.rs @@ -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 AccessType::try_from(qual.access_type()).unwrap() { + 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 { + 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)?, + }) +} diff --git a/nel_os_kernel/src/vmm/x86_64/intel/mod.rs b/nel_os_kernel/src/vmm/x86_64/intel/mod.rs index 47c66ea..93a406b 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/mod.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/mod.rs @@ -2,8 +2,10 @@ pub mod asm; mod auditor; mod controls; mod cpuid; +mod cr; mod ept; mod msr; +mod qual; mod register; pub mod vcpu; mod vmcs; diff --git a/nel_os_kernel/src/vmm/x86_64/intel/qual.rs b/nel_os_kernel/src/vmm/x86_64/intel/qual.rs new file mode 100644 index 0000000..a6fa188 --- /dev/null +++ b/nel_os_kernel/src/vmm/x86_64/intel/qual.rs @@ -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 for AccessType { + type Error = &'static str; + fn try_from(value: u8) -> Result { + 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 for LmswOperandType { + type Error = &'static str; + fn try_from(value: u8) -> Result { + 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 for Register { + type Error = &'static str; + fn try_from(value: u8) -> Result { + 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, +} diff --git a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs index 185f5ff..e9dc63f 100644 --- a/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs +++ b/nel_os_kernel/src/vmm/x86_64/intel/vcpu.rs @@ -42,6 +42,7 @@ pub struct IntelVCpu { guest_memory_size: u64, pub host_msr: ShadowMsr, pub guest_msr: ShadowMsr, + pub ia32e_enabled: bool, } impl IntelVCpu { @@ -666,6 +667,7 @@ impl VCpu for IntelVCpu { guest_memory_size: 1024 * 1024 * 256, // 256 MiB host_msr: ShadowMsr::new(), guest_msr: ShadowMsr::new(), + ia32e_enabled: false, }) }