mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-08-22 07:51:16 +00:00
hypervisor: x86: Add an address linearization method to CpuStateManager
From a CPU state and a segment, we can translate a logical (segmented) address into a linear one. Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
parent
d419e30df1
commit
7a4edecd29
@ -26,6 +26,9 @@ impl<T: Debug> Display for Exception<T> {
|
|||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum PlatformError {
|
pub enum PlatformError {
|
||||||
|
#[error("Invalid address: {0}")]
|
||||||
|
InvalidAddress(#[source] anyhow::Error),
|
||||||
|
|
||||||
#[error("Invalid register: {0}")]
|
#[error("Invalid register: {0}")]
|
||||||
InvalidRegister(#[source] anyhow::Error),
|
InvalidRegister(#[source] anyhow::Error),
|
||||||
|
|
||||||
@ -46,6 +49,9 @@ pub enum PlatformError {
|
|||||||
|
|
||||||
#[error("Unmapped virtual address: {0}")]
|
#[error("Unmapped virtual address: {0}")]
|
||||||
UnmappedGVA(#[source] anyhow::Error),
|
UnmappedGVA(#[source] anyhow::Error),
|
||||||
|
|
||||||
|
#[error("Unsupported CPU Mode: {0}")]
|
||||||
|
UnsupportedCpuMode(#[source] anyhow::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -18,13 +18,10 @@ pub mod mov;
|
|||||||
fn memory_operand_address<T: CpuStateManager>(
|
fn memory_operand_address<T: CpuStateManager>(
|
||||||
insn: &Instruction,
|
insn: &Instruction,
|
||||||
state: &T,
|
state: &T,
|
||||||
|
write: bool,
|
||||||
) -> Result<u64, PlatformError> {
|
) -> Result<u64, PlatformError> {
|
||||||
let mut address: u64 = 0;
|
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 {
|
if insn.memory_base() != iced_x86::Register::None {
|
||||||
let base: u64 = state.read_reg(insn.memory_base())?;
|
let base: u64 = state.read_reg(insn.memory_base())?;
|
||||||
address += base;
|
address += base;
|
||||||
@ -39,7 +36,8 @@ fn memory_operand_address<T: CpuStateManager>(
|
|||||||
|
|
||||||
address += insn.memory_displacement() as u64;
|
address += insn.memory_displacement() as u64;
|
||||||
|
|
||||||
Ok(address)
|
// Translate to a linear address.
|
||||||
|
state.linearize(insn.memory_segment(), address, write)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait InstructionHandler<T: CpuStateManager> {
|
pub trait InstructionHandler<T: CpuStateManager> {
|
||||||
|
@ -37,7 +37,7 @@ macro_rules! mov_rm_r {
|
|||||||
.map_err(EmulationError::PlatformEmulationError)?,
|
.map_err(EmulationError::PlatformEmulationError)?,
|
||||||
|
|
||||||
OpKind::Memory => {
|
OpKind::Memory => {
|
||||||
let addr = memory_operand_address(insn, state)
|
let addr = memory_operand_address(insn, state, true)
|
||||||
.map_err(EmulationError::PlatformEmulationError)?;
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
let src_reg_value_type: $bound = src_reg_value as $bound;
|
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)
|
.write_reg(insn.op0_register(), imm as u64)
|
||||||
.map_err(EmulationError::PlatformEmulationError)?,
|
.map_err(EmulationError::PlatformEmulationError)?,
|
||||||
OpKind::Memory => {
|
OpKind::Memory => {
|
||||||
let addr = memory_operand_address(insn, state)
|
let addr = memory_operand_address(insn, state, true)
|
||||||
.map_err(EmulationError::PlatformEmulationError)?;
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
|
|
||||||
platform
|
platform
|
||||||
@ -102,7 +102,7 @@ macro_rules! mov_r_rm {
|
|||||||
.map_err(EmulationError::PlatformEmulationError)?
|
.map_err(EmulationError::PlatformEmulationError)?
|
||||||
as $bound,
|
as $bound,
|
||||||
OpKind::Memory => {
|
OpKind::Memory => {
|
||||||
let target_address = memory_operand_address(insn, state)
|
let target_address = memory_operand_address(insn, state, false)
|
||||||
.map_err(EmulationError::PlatformEmulationError)?;
|
.map_err(EmulationError::PlatformEmulationError)?;
|
||||||
let mut memory: [u8; mem::size_of::<$bound>()] = [0; mem::size_of::<$bound>()];
|
let mut memory: [u8; mem::size_of::<$bound>()] = [0; mem::size_of::<$bound>()];
|
||||||
platform
|
platform
|
||||||
|
@ -9,7 +9,8 @@ extern crate iced_x86;
|
|||||||
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError};
|
||||||
use crate::arch::x86::emulator::instructions::*;
|
use crate::arch::x86::emulator::instructions::*;
|
||||||
use crate::arch::x86::regs::*;
|
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 crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
|
||||||
use iced_x86::*;
|
use iced_x86::*;
|
||||||
|
|
||||||
@ -111,6 +112,84 @@ pub trait CpuStateManager: Clone {
|
|||||||
|
|
||||||
/// Get the CPU mode.
|
/// Get the CPU mode.
|
||||||
fn mode(&self) -> Result<CpuMode, PlatformError>;
|
fn mode(&self) -> Result<CpuMode, PlatformError>;
|
||||||
|
|
||||||
|
/// 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<u64, PlatformError> {
|
||||||
|
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;
|
const REGISTER_MASK_64: u64 = 0xffff_ffff_ffff_ffffu64;
|
||||||
|
Loading…
Reference in New Issue
Block a user