diff --git a/hypervisor/src/arch/emulator/mod.rs b/hypervisor/src/arch/emulator/mod.rs index 37a6e20c8..42b5fb95c 100644 --- a/hypervisor/src/arch/emulator/mod.rs +++ b/hypervisor/src/arch/emulator/mod.rs @@ -26,6 +26,9 @@ impl Display for Exception { #[derive(Error, Debug)] pub enum PlatformError { + #[error("Invalid address: {0}")] + InvalidAddress(#[source] anyhow::Error), + #[error("Invalid register: {0}")] InvalidRegister(#[source] anyhow::Error), @@ -46,6 +49,9 @@ pub enum PlatformError { #[error("Unmapped virtual address: {0}")] UnmappedGVA(#[source] anyhow::Error), + + #[error("Unsupported CPU Mode: {0}")] + UnsupportedCpuMode(#[source] anyhow::Error), } #[derive(Error, Debug)] diff --git a/hypervisor/src/arch/x86/emulator/instructions/mod.rs b/hypervisor/src/arch/x86/emulator/instructions/mod.rs index ad74b49fe..17eb4a7ea 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mod.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mod.rs @@ -18,13 +18,10 @@ pub mod mov; fn memory_operand_address( insn: &Instruction, state: &T, + write: bool, ) -> Result { let mut address: u64 = 0; - // Get the DS or override segment base first - let segment_base = state.read_segment(insn.memory_segment())?.base; - address += segment_base; - if insn.memory_base() != iced_x86::Register::None { let base: u64 = state.read_reg(insn.memory_base())?; address += base; @@ -39,7 +36,8 @@ fn memory_operand_address( address += insn.memory_displacement() as u64; - Ok(address) + // Translate to a linear address. + state.linearize(insn.memory_segment(), address, write) } pub trait InstructionHandler { diff --git a/hypervisor/src/arch/x86/emulator/instructions/mov.rs b/hypervisor/src/arch/x86/emulator/instructions/mov.rs index 70d1b5bae..03a772cd1 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mov.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mov.rs @@ -37,7 +37,7 @@ macro_rules! mov_rm_r { .map_err(EmulationError::PlatformEmulationError)?, OpKind::Memory => { - let addr = memory_operand_address(insn, state) + let addr = memory_operand_address(insn, state, true) .map_err(EmulationError::PlatformEmulationError)?; let src_reg_value_type: $bound = src_reg_value as $bound; @@ -71,7 +71,7 @@ macro_rules! mov_rm_imm { .write_reg(insn.op0_register(), imm as u64) .map_err(EmulationError::PlatformEmulationError)?, OpKind::Memory => { - let addr = memory_operand_address(insn, state) + let addr = memory_operand_address(insn, state, true) .map_err(EmulationError::PlatformEmulationError)?; platform @@ -102,7 +102,7 @@ macro_rules! mov_r_rm { .map_err(EmulationError::PlatformEmulationError)? as $bound, OpKind::Memory => { - let target_address = memory_operand_address(insn, state) + let target_address = memory_operand_address(insn, state, false) .map_err(EmulationError::PlatformEmulationError)?; let mut memory: [u8; mem::size_of::<$bound>()] = [0; mem::size_of::<$bound>()]; platform diff --git a/hypervisor/src/arch/x86/emulator/mod.rs b/hypervisor/src/arch/x86/emulator/mod.rs index fa71b78a6..0e20166db 100644 --- a/hypervisor/src/arch/x86/emulator/mod.rs +++ b/hypervisor/src/arch/x86/emulator/mod.rs @@ -9,7 +9,8 @@ extern crate iced_x86; use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError}; use crate::arch::x86::emulator::instructions::*; use crate::arch::x86::regs::*; -use crate::arch::x86::Exception; +use crate::arch::x86::*; +use crate::arch::x86::{Exception, SegmentRegisterOps}; use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters}; use iced_x86::*; @@ -111,6 +112,84 @@ pub trait CpuStateManager: Clone { /// Get the CPU mode. fn mode(&self) -> Result; + + /// Translate a logical (segmented) address into a linear (virtual) one. + /// + /// # Arguments + /// + /// * `segment` - Which segment to use for linearization + /// * `logical_addr` - The logical address to be translated + fn linearize( + &self, + segment: Register, + logical_addr: u64, + write: bool, + ) -> Result { + let segment_register = self.read_segment(segment)?; + let mode = self.mode()?; + + match mode { + CpuMode::Long => { + // TODO Check that we got a canonical address. + Ok(logical_addr + .checked_add(segment_register.base) + .ok_or_else(|| { + PlatformError::InvalidAddress(anyhow!( + "Logical address {:#x} can not be linearized with segment {:#x?}", + logical_addr, + segment_register + )) + })?) + } + + CpuMode::Protected | CpuMode::Real => { + let segment_type = segment_register.segment_type(); + + // Must not write to a read-only segment. + if segment_type_ro(segment_type) && write { + return Err(PlatformError::InvalidAddress(anyhow!( + "Can not write to a read-only segment" + ))); + } + + let logical_addr = logical_addr & 0xffff_ffffu64; + let mut segment_limit: u32 = if segment_register.granularity() != 0 { + (segment_register.limit << 12) | 0xfff + } else { + segment_register.limit + }; + + // Expand-down segment + if segment_type_expand_down(segment_type) { + if logical_addr >= segment_limit.into() { + return Err(PlatformError::InvalidAddress(anyhow!( + "{:#x} is off limits {:#x} (expand down)", + logical_addr, + segment_limit + ))); + } + + if segment_register.db() != 0 { + segment_limit = 0xffffffff + } else { + segment_limit = 0xffff + } + } + + if logical_addr > segment_limit.into() { + return Err(PlatformError::InvalidAddress(anyhow!( + "{:#x} is off limits {:#x}", + logical_addr, + segment_limit + ))); + } + + Ok(logical_addr + segment_register.base) + } + + _ => Err(PlatformError::UnsupportedCpuMode(anyhow!("{:?}", mode))), + } + } } const REGISTER_MASK_64: u64 = 0xffff_ffff_ffff_ffffu64;