From 12b7a498d91843cec45ebfb8e134cd9b5f54e751 Mon Sep 17 00:00:00 2001 From: Samuel Ortiz Date: Thu, 19 Nov 2020 20:11:04 +0100 Subject: [PATCH] hypervisor: x86: Add an instruction emulator The emulator gets a CPU state from a CpuStateManager instance, emulates the passed instructions stream and returns the modified CPU state. The emulator is a skeleton for now since it comes with an empty instruction mnemonic map. Signed-off-by: Samuel Ortiz --- hypervisor/src/arch/x86/emulator/mod.rs | 75 ++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/hypervisor/src/arch/x86/emulator/mod.rs b/hypervisor/src/arch/x86/emulator/mod.rs index ce8032407..5aea661bf 100644 --- a/hypervisor/src/arch/x86/emulator/mod.rs +++ b/hypervisor/src/arch/x86/emulator/mod.rs @@ -6,9 +6,12 @@ extern crate iced_x86; -use crate::arch::emulator::PlatformError; +use crate::arch::emulator::{EmulationError, EmulationResult, PlatformEmulator, PlatformError}; +use crate::arch::x86::emulator::instructions::*; +use crate::arch::x86::Exception; use crate::x86_64::{SegmentRegister, SpecialRegisters, StandardRegisters}; use iced_x86::*; +use std::sync::{Arc, Mutex}; mod instructions; @@ -344,3 +347,73 @@ impl CpuStateManager for EmulatorCpuState { self.regs.rflags = flags; } } + +pub struct Emulator { + platform: Arc>>, + insn_map: InstructionMap, +} + +impl Emulator { + pub fn new(platform: Arc>>) -> Emulator { + let mut insn_map = InstructionMap::::new(); + + Emulator { + platform: Arc::clone(&platform), + insn_map, + } + } + + fn emulate_insn_stream( + &mut self, + cpu_id: usize, + insn_stream: &[u8], + num_insn: Option, + ) -> EmulationResult { + let mut state = self + .platform + .lock() + .unwrap() + .cpu_state(cpu_id) + .map_err(EmulationError::PlatformEmulationError)?; + let mut decoder = Decoder::new(64, insn_stream, DecoderOptions::NONE); + decoder.set_ip(state.ip()); + + for (index, insn) in &mut decoder.iter().enumerate() { + self.insn_map + .instructions + .get(&insn.code()) + .ok_or_else(|| { + EmulationError::UnsupportedInstruction(anyhow!("{:?}", insn.mnemonic())) + })? + .emulate(&insn, &mut state, Arc::clone(&self.platform))?; + + if let Some(num_insn) = num_insn { + if index + 1 >= num_insn { + // Exit the decoding loop, do not decode the next instruction. + break; + } + } + } + + state.set_ip(decoder.ip()); + Ok(state) + } + + /// Emulate all instructions from the instructions stream. + pub fn emulate(&mut self, cpu_id: usize, insn_stream: &[u8]) -> EmulationResult { + self.emulate_insn_stream(cpu_id, insn_stream, None) + } + + /// Only emulate the first instruction from the stream. + /// + /// This is useful for cases where we get readahead instruction stream + /// but implicitly must only emulate the first instruction, and then return + /// to the guest. + pub fn emulate_first_insn( + &mut self, + cpu_id: usize, + insn_stream: &[u8], + ) -> EmulationResult { + self.emulate_insn_stream(cpu_id, insn_stream, Some(1)) + } +}