diff --git a/hypervisor/src/arch/emulator/mod.rs b/hypervisor/src/arch/emulator/mod.rs index fb02d222f..6fb4551ff 100644 --- a/hypervisor/src/arch/emulator/mod.rs +++ b/hypervisor/src/arch/emulator/mod.rs @@ -52,6 +52,9 @@ pub enum PlatformError { #[error("Unsupported CPU Mode: {0}")] UnsupportedCpuMode(#[source] anyhow::Error), + + #[error("Invalid instruction operand: {0}")] + InvalidOperand(#[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 9c99b26e2..64b754cce 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mod.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mod.rs @@ -47,6 +47,89 @@ macro_rules! imm_op { pub mod mov; +fn get_op( + insn: &Instruction, + op_index: u32, + op_size: usize, + state: &mut T, + platform: &mut dyn PlatformEmulator, +) -> Result { + if insn.op_count() < op_index + 1 { + return Err(PlatformError::InvalidOperand(anyhow!( + "Invalid operand {:?}", + op_index + ))); + } + + match op_size { + 1 | 2 | 4 | 8 => {} + _ => { + return Err(PlatformError::InvalidOperand(anyhow!( + "Invalid operand size {:?}", + op_size + ))) + } + } + + let value = match insn.op_kind(op_index) { + OpKind::Register => state.read_reg(insn.op_register(op_index))?, + OpKind::Memory => { + let addr = memory_operand_address(insn, state, false)?; + let mut memory: [u8; 8] = [0; 8]; + platform.read_memory(addr, &mut memory[0..op_size])?; + ::from_le_bytes(memory) + } + OpKind::Immediate8 => insn.immediate8() as u64, + OpKind::Immediate8to16 => insn.immediate8to16() as u64, + OpKind::Immediate8to32 => insn.immediate8to32() as u64, + OpKind::Immediate8to64 => insn.immediate8to64() as u64, + OpKind::Immediate16 => insn.immediate16() as u64, + OpKind::Immediate32 => insn.immediate32() as u64, + OpKind::Immediate32to64 => insn.immediate32to64() as u64, + OpKind::Immediate64 => insn.immediate64() as u64, + k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))), + }; + + Ok(value) +} + +fn set_op( + insn: &Instruction, + op_index: u32, + op_size: usize, + state: &mut T, + platform: &mut dyn PlatformEmulator, + value: u64, +) -> Result<(), PlatformError> { + if insn.op_count() < op_index + 1 { + return Err(PlatformError::InvalidOperand(anyhow!( + "Invalid operand {:?}", + op_index + ))); + } + + match op_size { + 1 | 2 | 4 | 8 => {} + _ => { + return Err(PlatformError::InvalidOperand(anyhow!( + "Invalid operand size {:?}", + op_size + ))) + } + } + + match insn.op_kind(op_index) { + OpKind::Register => state.write_reg(insn.op_register(op_index), value)?, + OpKind::Memory => { + let addr = memory_operand_address(insn, state, true)?; + platform.write_memory(addr, &value.to_le_bytes()[..op_size])?; + } + k => return Err(PlatformError::InvalidOperand(anyhow!("{:?}", k))), + }; + + Ok(()) +} + // Returns the linear a.k.a. virtual address for a memory operand. fn memory_operand_address( insn: &Instruction,