From 824e83ab0db48c5828d24a9c06b87c2c2f2568f0 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 17 Jul 2024 17:27:48 +0000 Subject: [PATCH] hypervisor: x86: emulate STOS Signed-off-by: Wei Liu --- .../src/arch/x86/emulator/instructions/mod.rs | 1 + .../arch/x86/emulator/instructions/stos.rs | 236 ++++++++++++++++++ hypervisor/src/arch/x86/emulator/mod.rs | 7 +- 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 hypervisor/src/arch/x86/emulator/instructions/stos.rs diff --git a/hypervisor/src/arch/x86/emulator/instructions/mod.rs b/hypervisor/src/arch/x86/emulator/instructions/mod.rs index 9998537d2..5c7d90090 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mod.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mod.rs @@ -13,6 +13,7 @@ pub mod cmp; pub mod mov; pub mod movs; pub mod or; +pub mod stos; fn get_op( insn: &Instruction, diff --git a/hypervisor/src/arch/x86/emulator/instructions/stos.rs b/hypervisor/src/arch/x86/emulator/instructions/stos.rs new file mode 100644 index 000000000..43fb73cb7 --- /dev/null +++ b/hypervisor/src/arch/x86/emulator/instructions/stos.rs @@ -0,0 +1,236 @@ +// +// Copyright © 2024 Microsoft +// +// SPDX-License-Identifier: Apache-2.0 +// + +#![allow(non_camel_case_types)] + +// +// STOS - Store String +// + +use crate::arch::x86::emulator::instructions::*; +use crate::arch::x86::regs::DF; + +macro_rules! stos { + ($bound:ty) => { + fn emulate( + &self, + insn: &Instruction, + state: &mut T, + platform: &mut dyn PlatformEmulator, + ) -> Result<(), EmulationError> { + let mut count: u64 = if insn.has_rep_prefix() { + state + .read_reg(Register::ECX) + .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))? + } else { + 1 + }; + + let rax = state + .read_reg(Register::RAX) + .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; + + let mut rdi = state + .read_reg(Register::RDI) + .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; + + let df = (state.flags() & DF) != 0; + let len = std::mem::size_of::<$bound>(); + let rax_bytes = rax.to_le_bytes(); + + while count > 0 { + let dst = state + .linearize(Register::ES, rdi, true) + .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; + + platform + .write_memory(dst, &rax_bytes[0..len]) + .map_err(EmulationError::PlatformEmulationError)?; + + if df { + rdi = rdi.wrapping_sub(len as u64); + } else { + rdi = rdi.wrapping_add(len as u64); + } + count -= 1; + } + + if insn.has_rep_prefix() { + state + .write_reg(Register::ECX, 0) + .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; + } + + Ok(()) + } + }; +} + +pub struct Stosq_m64_RAX; +impl InstructionHandler for Stosq_m64_RAX { + stos!(u64); +} + +pub struct Stosd_m32_EAX; +impl InstructionHandler for Stosd_m32_EAX { + stos!(u32); +} + +pub struct Stosw_m16_AX; +impl InstructionHandler for Stosw_m16_AX { + stos!(u16); +} + +pub struct Stosb_m8_AL; +impl InstructionHandler for Stosb_m8_AL { + stos!(u8); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::arch::x86::emulator::mock_vmm::*; + + #[test] + fn test_rep_stosb() { + let ip: u64 = 0x1000; + let memory: [u8; 12] = [ + 0x78, 0x56, 0x34, 0x12, // 0x12345678 + 0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd + 0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5 + ]; + let insn = [0xf3, 0xaa]; // rep stosb + let regs = vec![ + (Register::ECX, 3), + (Register::EDI, 0x0), + (Register::RAX, 0x123456ff), + ]; + let mut data = [0u8; 4]; + + let mut vmm = MockVmm::new(ip, regs, Some((0, &memory))); + + assert!(vmm.emulate_first_insn(0, &insn).is_ok()); + + vmm.read_memory(0, &mut data).unwrap(); + assert_eq!(0x12ffffff, ::from_le_bytes(data)); + vmm.read_memory(4, &mut data).unwrap(); + assert_eq!(0xaabbccdd, ::from_le_bytes(data)); + vmm.read_memory(8, &mut data).unwrap(); + assert_eq!(0x5aa55aa5, ::from_le_bytes(data)); + } + + #[test] + fn test_stosw() { + let ip: u64 = 0x1000; + let memory: [u8; 4] = [ + 0x78, 0x56, 0x34, 0x12, // 0x12345678 + ]; + let insn = [0x66, 0xab]; // stosw + let regs = vec![(Register::EDI, 0x1), (Register::AX, 0xaabb)]; + let mut data = [0u8; 4]; + + let mut vmm = MockVmm::new(ip, regs, Some((0, &memory))); + + assert!(vmm.emulate_first_insn(0, &insn).is_ok()); + + vmm.read_memory(0x0, &mut data).unwrap(); + assert_eq!(0x12aabb78, ::from_le_bytes(data)); + // The rest should be default value 0 from MockVmm + vmm.read_memory(0x4, &mut data).unwrap(); + assert_eq!(0x0, ::from_le_bytes(data)); + vmm.read_memory(0x8 + 8, &mut data).unwrap(); + assert_eq!(0x0, ::from_le_bytes(data)); + } + + #[test] + fn test_rep_stosw() { + let ip: u64 = 0x1000; + let memory: [u8; 8] = [ + 0x78, 0x56, 0x34, 0x12, // 0x12345678 + 0x00, 0x00, 0x00, 0x00, // 0x00000000 + ]; + let insn = [0x66, 0xf3, 0xab]; // rep stosw + let regs = vec![ + (Register::ECX, 2), + (Register::EDI, 0x2), + (Register::AX, 0xaabb), + ]; + let mut data = [0u8; 4]; + + let mut vmm = MockVmm::new(ip, regs, Some((0, &memory))); + + assert!(vmm.emulate_first_insn(0, &insn).is_ok()); + + vmm.read_memory(0x0, &mut data).unwrap(); + assert_eq!(0xaabb5678, ::from_le_bytes(data)); + vmm.read_memory(0x4, &mut data).unwrap(); + assert_eq!(0x0000aabb, ::from_le_bytes(data)); + vmm.read_memory(0x8, &mut data).unwrap(); + assert_eq!(0x0, ::from_le_bytes(data)); + } + + #[test] + fn test_rep_stosd() { + let ip: u64 = 0x1000; + let memory: [u8; 12] = [ + 0x78, 0x56, 0x34, 0x12, // 0x12345678 + 0x00, 0x00, 0x00, 0x00, // 0x00000000 + 0x00, 0x00, 0x00, 0x00, // 0x00000000 + ]; + let insn = [0xf3, 0xab]; // rep stosd + let regs = vec![ + (Register::ECX, 2), + (Register::EDI, 0x8), + (Register::EAX, 0xaabbccdd), + ]; + let mut data = [0u8; 4]; + + let mut vmm = MockVmm::new(ip, regs, Some((0, &memory))); + + // Go backwards this time + let mut state = vmm.cpu_state(0).unwrap(); + state.set_flags(state.flags() | DF); + vmm.set_cpu_state(0, state).unwrap(); + + assert!(vmm.emulate_first_insn(0, &insn).is_ok()); + + vmm.read_memory(0x0, &mut data).unwrap(); + assert_eq!(0x12345678, ::from_le_bytes(data)); + vmm.read_memory(0x4, &mut data).unwrap(); + assert_eq!(0xaabbccdd, ::from_le_bytes(data)); + vmm.read_memory(0x8, &mut data).unwrap(); + assert_eq!(0xaabbccdd, ::from_le_bytes(data)); + vmm.read_memory(0xc, &mut data).unwrap(); + assert_eq!(0x0, ::from_le_bytes(data)); + } + + #[test] + fn test_rep_stosq() { + let ip: u64 = 0x1000; + let memory: [u8; 8] = [ + 0x78, 0x56, 0x34, 0x12, // 0x12345678 + 0x00, 0x00, 0x00, 0x00, // 0x00000000 + ]; + let insn = [0xf3, 0x48, 0xab]; // rep stosq + let regs = vec![ + (Register::ECX, 2), + (Register::RDI, 0x0), + (Register::RAX, 0x11223344aabbccdd), + ]; + let mut data = [0u8; 8]; + + let mut vmm = MockVmm::new(ip, regs, Some((0, &memory))); + + assert!(vmm.emulate_first_insn(0, &insn).is_ok()); + + vmm.read_memory(0x0, &mut data).unwrap(); + assert_eq!(0x11223344aabbccdd, ::from_le_bytes(data)); + vmm.read_memory(0x8, &mut data).unwrap(); + assert_eq!(0x11223344aabbccdd, ::from_le_bytes(data)); + vmm.read_memory(0x10, &mut data).unwrap(); + assert_eq!(0x0, ::from_le_bytes(data)); + } +} diff --git a/hypervisor/src/arch/x86/emulator/mod.rs b/hypervisor/src/arch/x86/emulator/mod.rs index 18bed0f60..9153aec71 100644 --- a/hypervisor/src/arch/x86/emulator/mod.rs +++ b/hypervisor/src/arch/x86/emulator/mod.rs @@ -536,7 +536,12 @@ impl<'a, T: CpuStateManager> Emulator<'a, T> { (movs, Movsw_m16_m16), (movs, Movsb_m8_m8), // OR - (or, Or_rm8_r8) + (or, Or_rm8_r8), + // STOS + (stos, Stosb_m8_AL), + (stos, Stosw_m16_AX), + (stos, Stosd_m32_EAX), + (stos, Stosq_m64_RAX) ); handler