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 <liuwe@microsoft.com>
Assisted-by: Copilot:GPT-5.5
This commit is contained in:
Wei Liu
2026-06-10 09:23:43 -07:00
parent 0cd68e8f3b
commit 43138da0d7
4 changed files with 54 additions and 20 deletions

View File

@@ -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<T: CpuStateManager>(
insn: &Instruction,
op_index: u32,
@@ -137,3 +154,28 @@ pub trait InstructionHandler<T: CpuStateManager> {
platform: &mut dyn PlatformEmulator<CpuState = T>,
) -> Result<(), EmulationError<Exception>>;
}
#[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);
}
}

View File

@@ -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<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
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;
}

View File

@@ -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<CpuState = T>,
) -> Result<(), EmulationError<Exception>> {
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;
}

View File

@@ -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)]