hypervisor: x86: reference PlatformEmulator in Emulator

The observation here is PlatformEmulator can be seen as the context for
emulation to take place. It should be rather easy to construct a context
that satisfies the lifetime constraints for instruction emulation.

The thread doing the emulation will have full ownership over the
context, so this removes the need to wrap PlatformEmulator in Arc and
Mutex, as well as the need for the context to be either Clone or Copy.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
Wei Liu 2020-11-25 20:07:45 +00:00 committed by Samuel Ortiz
parent 72741c557a
commit c8b655490e
3 changed files with 23 additions and 72 deletions

View File

@ -11,7 +11,6 @@ use crate::arch::x86::emulator::CpuStateManager;
use crate::arch::x86::Exception; use crate::arch::x86::Exception;
use iced_x86::*; use iced_x86::*;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub mod mov; pub mod mov;
@ -48,7 +47,7 @@ pub trait InstructionHandler<T: CpuStateManager> {
&self, &self,
insn: &Instruction, insn: &Instruction,
state: &mut T, state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>>; ) -> Result<(), EmulationError<Exception>>;
} }

View File

@ -18,7 +18,6 @@ use crate::arch::emulator::{EmulationError, PlatformEmulator};
use crate::arch::x86::emulator::instructions::*; use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception; use crate::arch::x86::Exception;
use std::mem; use std::mem;
use std::sync::{Arc, Mutex};
macro_rules! mov_rm_r { macro_rules! mov_rm_r {
($bound:ty) => { ($bound:ty) => {
@ -26,7 +25,7 @@ macro_rules! mov_rm_r {
&self, &self,
insn: &Instruction, insn: &Instruction,
state: &mut T, state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> { ) -> Result<(), EmulationError<Exception>> {
let src_reg_value = state let src_reg_value = state
.read_reg(insn.op1_register()) .read_reg(insn.op1_register())
@ -42,8 +41,6 @@ macro_rules! mov_rm_r {
OpKind::Memory => { OpKind::Memory => {
let src_reg_value_type: $bound = src_reg_value as $bound; let src_reg_value_type: $bound = src_reg_value as $bound;
platform platform
.lock()
.unwrap()
.write_memory(addr, &src_reg_value_type.to_le_bytes()) .write_memory(addr, &src_reg_value_type.to_le_bytes())
.map_err(EmulationError::PlatformEmulationError)? .map_err(EmulationError::PlatformEmulationError)?
} }
@ -64,7 +61,7 @@ macro_rules! mov_rm_imm {
&self, &self,
insn: &Instruction, insn: &Instruction,
state: &mut T, state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> { ) -> Result<(), EmulationError<Exception>> {
let imm = imm_op!($type, insn); let imm = imm_op!($type, insn);
let addr = memory_operand_address(insn, state) let addr = memory_operand_address(insn, state)
@ -75,8 +72,6 @@ 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 => platform OpKind::Memory => platform
.lock()
.unwrap()
.write_memory(addr, &imm.to_le_bytes()) .write_memory(addr, &imm.to_le_bytes())
.map_err(EmulationError::PlatformEmulationError)?, .map_err(EmulationError::PlatformEmulationError)?,
k => return Err(EmulationError::InvalidOperand(anyhow!("{:?}", k))), k => return Err(EmulationError::InvalidOperand(anyhow!("{:?}", k))),
@ -95,7 +90,7 @@ macro_rules! mov_r_rm {
&self, &self,
insn: &Instruction, insn: &Instruction,
state: &mut T, state: &mut T,
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> { ) -> Result<(), EmulationError<Exception>> {
let src_value: $bound = match insn.op1_kind() { let src_value: $bound = match insn.op1_kind() {
OpKind::Register => state OpKind::Register => state
@ -107,8 +102,6 @@ macro_rules! mov_r_rm {
.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
.lock()
.unwrap()
.read_memory(target_address, &mut memory) .read_memory(target_address, &mut memory)
.map_err(EmulationError::PlatformEmulationError)?; .map_err(EmulationError::PlatformEmulationError)?;
<$bound>::from_le_bytes(memory) <$bound>::from_le_bytes(memory)
@ -134,7 +127,7 @@ macro_rules! mov_r_imm {
&self, &self,
insn: &Instruction, insn: &Instruction,
state: &mut T, state: &mut T,
_platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, _platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>> { ) -> Result<(), EmulationError<Exception>> {
state state
.write_reg(insn.op0_register(), imm_op!($type, insn) as u64) .write_reg(insn.op0_register(), imm_op!($type, insn) as u64)
@ -261,6 +254,7 @@ mod tests {
use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt}; use crate::arch::x86::gdt::{gdt_entry, segment_from_gdt};
use crate::arch::x86::Exception; use crate::arch::x86::Exception;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
struct MockVMM { struct MockVMM {
@ -340,7 +334,7 @@ mod tests {
regs: HashMap<Register, u64>, regs: HashMap<Register, u64>,
memory: Option<(u64, &[u8])>, memory: Option<(u64, &[u8])>,
num_insn: Option<usize>, num_insn: Option<usize>,
) -> Arc<Mutex<MockVMM>> { ) -> MockVMM {
let _ = env_logger::try_init(); let _ = env_logger::try_init();
let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1); let cs_reg = segment_from_gdt(gdt_entry(0xc09b, 0, 0xffffffff), 1);
let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2); let ds_reg = segment_from_gdt(gdt_entry(0xc093, 0, 0xffffffff), 2);
@ -352,11 +346,11 @@ mod tests {
initial_state.write_reg(reg, value).unwrap(); initial_state.write_reg(reg, value).unwrap();
} }
let vmm = Arc::new(Mutex::new(MockVMM::new(initial_state))); let mut vmm = MockVMM::new(initial_state);
if let Some(mem) = memory { if let Some(mem) = memory {
vmm.lock().unwrap().write_memory(mem.0, &mem.1).unwrap(); vmm.write_memory(mem.0, &mem.1).unwrap();
} }
let mut emulator = Emulator::new(vmm.clone()); let mut emulator = Emulator::new(&mut vmm);
let new_state = emulator let new_state = emulator
.emulate_insn_stream(cpu_id, &insn, num_insn) .emulate_insn_stream(cpu_id, &insn, num_insn)
@ -365,10 +359,7 @@ mod tests {
assert_eq!(ip + insn.len() as u64, new_state.ip()); assert_eq!(ip + insn.len() as u64, new_state.ip());
} }
vmm.lock() vmm.set_cpu_state(cpu_id, new_state).unwrap();
.unwrap()
.set_cpu_state(cpu_id, new_state)
.unwrap();
vmm vmm
} }
@ -379,7 +370,7 @@ mod tests {
insn: &[u8], insn: &[u8],
regs: HashMap<Register, u64>, regs: HashMap<Register, u64>,
memory: Option<(u64, &[u8])>, memory: Option<(u64, &[u8])>,
) -> Arc<Mutex<MockVMM>> { ) -> MockVMM {
_init_and_run(cpu_id, ip, insn, regs, memory, None) _init_and_run(cpu_id, ip, insn, regs, memory, None)
} }
@ -393,8 +384,6 @@ mod tests {
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![Register::RBX => rbx], None); let vmm = init_and_run(cpu_id, ip, &insn, hashmap![Register::RBX => rbx], None);
let rax: u64 = vmm let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)
@ -414,8 +403,6 @@ mod tests {
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None); let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let rax: u64 = vmm let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)
@ -443,8 +430,6 @@ mod tests {
); );
rax = vmm rax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)
@ -464,8 +449,6 @@ mod tests {
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None); let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let al = vmm let al = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::AL) .read_reg(Register::AL)
@ -485,8 +468,6 @@ mod tests {
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None); let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let eax = vmm let eax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::EAX) .read_reg(Register::EAX)
@ -506,8 +487,6 @@ mod tests {
let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None); let vmm = init_and_run(cpu_id, ip, &insn, hashmap![], None);
let rax: u64 = vmm let rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)
@ -534,7 +513,7 @@ mod tests {
); );
let mut memory: [u8; 1] = [0; 1]; let mut memory: [u8; 1] = [0; 1];
vmm.lock().unwrap().read_memory(rax, &mut memory).unwrap(); vmm.read_memory(rax, &mut memory).unwrap();
assert_eq!(u8::from_le_bytes(memory), dh); assert_eq!(u8::from_le_bytes(memory), dh);
@ -558,7 +537,7 @@ mod tests {
); );
let mut memory: [u8; 4] = [0; 4]; let mut memory: [u8; 4] = [0; 4];
vmm.lock().unwrap().read_memory(rax, &mut memory).unwrap(); vmm.read_memory(rax, &mut memory).unwrap();
assert_eq!(u32::from_le_bytes(memory), esi); assert_eq!(u32::from_le_bytes(memory), esi);
@ -583,10 +562,7 @@ mod tests {
); );
let mut memory: [u8; 4] = [0; 4]; let mut memory: [u8; 4] = [0; 4];
vmm.lock() vmm.read_memory(rax + displacement, &mut memory).unwrap();
.unwrap()
.read_memory(rax + displacement, &mut memory)
.unwrap();
assert_eq!(u32::from_le_bytes(memory), edi); assert_eq!(u32::from_le_bytes(memory), edi);
@ -612,8 +588,6 @@ mod tests {
); );
let new_eax = vmm let new_eax = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::EAX) .read_reg(Register::EAX)
@ -642,8 +616,6 @@ mod tests {
); );
let new_al = vmm let new_al = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::AL) .read_reg(Register::AL)
@ -677,8 +649,6 @@ mod tests {
); );
let rbx: u64 = vmm let rbx: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RBX) .read_reg(Register::RBX)
@ -713,14 +683,9 @@ mod tests {
Some(1), Some(1),
); );
assert_eq!( assert_eq!(ip + 7 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
ip + 7 as u64,
vmm.lock().unwrap().cpu_state(cpu_id).unwrap().ip()
);
let new_rax: u64 = vmm let new_rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)
@ -757,14 +722,9 @@ mod tests {
Some(2), Some(2),
); );
assert_eq!( assert_eq!(ip + 7 + 4 as u64, vmm.cpu_state(cpu_id).unwrap().ip());
ip + 7 + 4 as u64,
vmm.lock().unwrap().cpu_state(cpu_id).unwrap().ip()
);
let rbx: u64 = vmm let rbx: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RBX) .read_reg(Register::RBX)
@ -773,8 +733,6 @@ mod tests {
// Check that rax is still at 0x100 // Check that rax is still at 0x100
let new_rax: u64 = vmm let new_rax: u64 = vmm
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.unwrap() .unwrap()
.read_reg(Register::RAX) .read_reg(Register::RAX)

View File

@ -11,7 +11,6 @@ use crate::arch::x86::emulator::instructions::*;
use crate::arch::x86::Exception; use crate::arch::x86::Exception;
use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters}; use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters};
use iced_x86::*; use iced_x86::*;
use std::sync::{Arc, Mutex};
#[macro_use] #[macro_use]
mod instructions; mod instructions;
@ -349,13 +348,13 @@ impl CpuStateManager for EmulatorCpuState {
} }
} }
pub struct Emulator<T: CpuStateManager> { pub struct Emulator<'a, T: CpuStateManager> {
platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>, platform: &'a mut dyn PlatformEmulator<CpuState = T>,
insn_map: InstructionMap<T>, insn_map: InstructionMap<T>,
} }
impl<T: CpuStateManager> Emulator<T> { impl<'a, T: CpuStateManager> Emulator<'a, T> {
pub fn new(platform: Arc<Mutex<dyn PlatformEmulator<CpuState = T>>>) -> Emulator<T> { pub fn new(platform: &mut dyn PlatformEmulator<CpuState = T>) -> Emulator<T> {
let mut insn_map = InstructionMap::<T>::new(); let mut insn_map = InstructionMap::<T>::new();
// MOV // MOV
@ -376,10 +375,7 @@ impl<T: CpuStateManager> Emulator<T> {
insn_add!(insn_map, mov, Mov_rm64_imm32); insn_add!(insn_map, mov, Mov_rm64_imm32);
insn_add!(insn_map, mov, Mov_rm64_r64); insn_add!(insn_map, mov, Mov_rm64_r64);
Emulator { Emulator { platform, insn_map }
platform: Arc::clone(&platform),
insn_map,
}
} }
fn emulate_insn_stream( fn emulate_insn_stream(
@ -390,8 +386,6 @@ impl<T: CpuStateManager> Emulator<T> {
) -> EmulationResult<T, Exception> { ) -> EmulationResult<T, Exception> {
let mut state = self let mut state = self
.platform .platform
.lock()
.unwrap()
.cpu_state(cpu_id) .cpu_state(cpu_id)
.map_err(EmulationError::PlatformEmulationError)?; .map_err(EmulationError::PlatformEmulationError)?;
let mut decoder = Decoder::new(64, insn_stream, DecoderOptions::NONE); let mut decoder = Decoder::new(64, insn_stream, DecoderOptions::NONE);
@ -404,7 +398,7 @@ impl<T: CpuStateManager> Emulator<T> {
.ok_or_else(|| { .ok_or_else(|| {
EmulationError::UnsupportedInstruction(anyhow!("{:?}", insn.mnemonic())) EmulationError::UnsupportedInstruction(anyhow!("{:?}", insn.mnemonic()))
})? })?
.emulate(&insn, &mut state, Arc::clone(&self.platform))?; .emulate(&insn, &mut state, self.platform)?;
if let Some(num_insn) = num_insn { if let Some(num_insn) = num_insn {
if index + 1 >= num_insn { if index + 1 >= num_insn {