From 43138da0d784644f86e25f28e930a8a3c39c37f1 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Wed, 10 Jun 2026 09:23:43 -0700 Subject: [PATCH] hypervisor: x86: share string op helpers Move common string instruction bookkeeping into the x86 instruction emulator so MOVS, STOS, and MSHV port string I/O use one implementation for REP counts, direction-flag handling, and index advancement. This keeps existing MOVS/STOS behavior unchanged while removing the need for MSHV to open-code the same string-operation details. Signed-off-by: Wei Liu Assisted-by: Copilot:GPT-5.5 --- .../src/arch/x86/emulator/instructions/mod.rs | 42 +++++++++++++++++++ .../arch/x86/emulator/instructions/movs.rs | 17 +++----- .../arch/x86/emulator/instructions/stos.rs | 14 +++---- hypervisor/src/arch/x86/emulator/mod.rs | 1 + 4 files changed, 54 insertions(+), 20 deletions(-) diff --git a/hypervisor/src/arch/x86/emulator/instructions/mod.rs b/hypervisor/src/arch/x86/emulator/instructions/mod.rs index 1f4665bce..95ed8927a 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/mod.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/mod.rs @@ -10,6 +10,7 @@ use iced_x86::*; use crate::arch::emulator::{EmulationError, PlatformEmulator, PlatformError}; use crate::arch::x86::Exception; use crate::arch::x86::emulator::CpuStateManager; +use crate::arch::x86::regs::DF; pub mod cmp; pub mod mov; @@ -17,6 +18,22 @@ pub mod movs; pub mod or; pub mod stos; +pub fn string_op_repeat_count(has_rep_prefix: bool, rcx: u64) -> u64 { + if has_rep_prefix { rcx } else { 1 } +} + +pub fn string_op_backwards(rflags: u64) -> bool { + (rflags & DF) != 0 +} + +pub fn advance_string_op_index(index: u64, len: usize, backwards: bool) -> u64 { + if backwards { + index.wrapping_sub(len as u64) + } else { + index.wrapping_add(len as u64) + } +} + fn get_op( insn: &Instruction, op_index: u32, @@ -137,3 +154,28 @@ pub trait InstructionHandler { platform: &mut dyn PlatformEmulator, ) -> Result<(), EmulationError>; } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_string_op_repeat_count() { + assert_eq!(string_op_repeat_count(false, 0), 1); + assert_eq!(string_op_repeat_count(false, 7), 1); + assert_eq!(string_op_repeat_count(true, 7), 7); + assert_eq!(string_op_repeat_count(true, 0), 0); + } + + #[test] + fn test_string_op_backwards() { + assert!(!string_op_backwards(0)); + assert!(string_op_backwards(DF)); + } + + #[test] + fn test_advance_string_op_index() { + assert_eq!(advance_string_op_index(0x1000, 4, false), 0x1004); + assert_eq!(advance_string_op_index(0x1000, 4, true), 0xffc); + } +} diff --git a/hypervisor/src/arch/x86/emulator/instructions/movs.rs b/hypervisor/src/arch/x86/emulator/instructions/movs.rs index a4831481b..498f637c3 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/movs.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/movs.rs @@ -13,7 +13,6 @@ use anyhow::anyhow; use crate::arch::x86::emulator::instructions::*; -use crate::arch::x86::regs::DF; macro_rules! movs { ($bound:ty) => { @@ -23,13 +22,14 @@ macro_rules! movs { state: &mut T, platform: &mut dyn PlatformEmulator, ) -> Result<(), EmulationError> { - let mut count: u64 = if insn.has_rep_prefix() { + let rcx = if insn.has_rep_prefix() { state .read_reg(Register::ECX) .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))? } else { - 1 + 0 }; + let mut count = string_op_repeat_count(insn.has_rep_prefix(), rcx); let mut rsi = state .read_reg(Register::RSI) @@ -38,7 +38,7 @@ macro_rules! movs { .read_reg(Register::RDI) .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; - let df = (state.flags() & DF) != 0; + let backwards = string_op_backwards(state.flags()); let len = std::mem::size_of::<$bound>(); while count > 0 { @@ -58,13 +58,8 @@ macro_rules! movs { .write_memory(dst, &memory[0..len]) .map_err(EmulationError::PlatformEmulationError)?; - if df { - rsi = rsi.wrapping_sub(len as u64); - rdi = rdi.wrapping_sub(len as u64); - } else { - rsi = rsi.wrapping_add(len as u64); - rdi = rdi.wrapping_add(len as u64); - } + rsi = advance_string_op_index(rsi, len, backwards); + rdi = advance_string_op_index(rdi, len, backwards); count -= 1; } diff --git a/hypervisor/src/arch/x86/emulator/instructions/stos.rs b/hypervisor/src/arch/x86/emulator/instructions/stos.rs index c830126fb..a000f117e 100644 --- a/hypervisor/src/arch/x86/emulator/instructions/stos.rs +++ b/hypervisor/src/arch/x86/emulator/instructions/stos.rs @@ -13,7 +13,6 @@ use anyhow::anyhow; use crate::arch::x86::emulator::instructions::*; -use crate::arch::x86::regs::DF; macro_rules! stos { ($bound:ty) => { @@ -23,13 +22,14 @@ macro_rules! stos { state: &mut T, platform: &mut dyn PlatformEmulator, ) -> Result<(), EmulationError> { - let mut count: u64 = if insn.has_rep_prefix() { + let rcx = if insn.has_rep_prefix() { state .read_reg(Register::ECX) .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))? } else { - 1 + 0 }; + let mut count = string_op_repeat_count(insn.has_rep_prefix(), rcx); let rax = state .read_reg(Register::RAX) @@ -39,7 +39,7 @@ macro_rules! stos { .read_reg(Register::RDI) .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?; - let df = (state.flags() & DF) != 0; + let backwards = string_op_backwards(state.flags()); let len = std::mem::size_of::<$bound>(); let rax_bytes = rax.to_le_bytes(); @@ -52,11 +52,7 @@ macro_rules! stos { .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); - } + rdi = advance_string_op_index(rdi, len, backwards); count -= 1; } diff --git a/hypervisor/src/arch/x86/emulator/mod.rs b/hypervisor/src/arch/x86/emulator/mod.rs index 4b60ceb71..ea4464b9d 100644 --- a/hypervisor/src/arch/x86/emulator/mod.rs +++ b/hypervisor/src/arch/x86/emulator/mod.rs @@ -18,6 +18,7 @@ use crate::arch::x86::{ #[macro_use] mod instructions; +pub use instructions::{advance_string_op_index, string_op_backwards, string_op_repeat_count}; /// x86 CPU modes #[derive(Debug, PartialEq, Eq)]