x86emu\emulator/
rep.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4use super::AlignmentMode;
5use super::Emulator;
6use super::InternalError;
7use super::arith::ArithOp;
8use crate::Cpu;
9use crate::Segment;
10use iced_x86::Instruction;
11use iced_x86::OpKind;
12use iced_x86::Register;
13
14/// Maximum number of repetitions that will be completed in a single run for rep prefixed instructions.
15/// After this many iterations we'll return control to the processor to process any outstanding interrupts
16/// or other events. However we won't increment RIP, so we'll be re-entered where we left off.
17pub const MAX_REP_LOOPS: u64 = 1024;
18
19/// State for rep ops. See [`Emulator::rep_op`].
20struct RepState {
21    pub count_reg: Register,
22    pub done: u64,
23    pub requested: u64,
24    pub rep: Option<RepPrefix>,
25    pub size: usize,
26    pub delta: u64,
27}
28
29/// Repe and Repne contain the current status of the zero flag.
30#[derive(Clone, Copy)]
31enum RepPrefix {
32    Rep,
33    Repe(bool),
34    Repne(bool),
35}
36
37impl RepState {
38    fn update_zero(&mut self, new_zero: bool) {
39        self.rep = match self.rep {
40            Some(RepPrefix::Repe(_)) => Some(RepPrefix::Repe(new_zero)),
41            Some(RepPrefix::Repne(_)) => Some(RepPrefix::Repne(new_zero)),
42            Some(RepPrefix::Rep) => unreachable!(),
43            None => None,
44        }
45    }
46
47    fn check_done<E>(&self) -> Result<(), InternalError<E>> {
48        if !self.is_done() {
49            return Err(InternalError::Retry);
50        }
51        Ok(())
52    }
53
54    fn is_done(&self) -> bool {
55        match self.rep {
56            Some(RepPrefix::Repe(zero)) if !zero => return true,
57            Some(RepPrefix::Repne(zero)) if zero => return true,
58            _ => {}
59        }
60
61        self.done == self.requested
62    }
63}
64
65/// Gets the RCX register of the appropriate size for the given `op_kind`.
66fn sized_rcx(op_kind: OpKind) -> Register {
67    match op_kind {
68        OpKind::MemorySegSI | OpKind::MemorySegDI | OpKind::MemoryESDI => Register::CX,
69        OpKind::MemorySegESI | OpKind::MemorySegEDI | OpKind::MemoryESEDI => Register::ECX,
70        OpKind::MemorySegRSI | OpKind::MemorySegRDI | OpKind::MemoryESRDI => Register::RCX,
71        _ => unreachable!(),
72    }
73}
74
75/// Gets the RDI register of the appropriate size for the given `op_kind`.
76fn sized_rdi(op_kind: OpKind) -> Register {
77    match op_kind {
78        OpKind::MemorySegDI | OpKind::MemoryESDI => Register::DI,
79        OpKind::MemorySegEDI | OpKind::MemoryESEDI => Register::EDI,
80        OpKind::MemorySegRDI | OpKind::MemoryESRDI => Register::RDI,
81        _ => unreachable!(),
82    }
83}
84
85/// Gets the RSI register of the appropriate size for the given `op_kind`.
86fn sized_rsi(op_kind: OpKind) -> Register {
87    match op_kind {
88        OpKind::MemorySegSI => Register::SI,
89        OpKind::MemorySegESI => Register::ESI,
90        OpKind::MemorySegRSI => Register::RSI,
91        _ => unreachable!(),
92    }
93}
94
95impl<T: Cpu> Emulator<'_, T> {
96    /// Generic function for handling the optional REP op for instructions.
97    fn rep_op(
98        &mut self,
99        instr: &Instruction,
100        op_kind: OpKind,
101        is_cmps_scas: bool,
102    ) -> Result<RepState, InternalError<T::Error>> {
103        // iced doesn't provide us a good way to disambiguate between instructions that use REP and instructions that
104        // use REPE (since they're the same byte), so we just pass along a bool from each function to help us here.
105        let rep = match (
106            is_cmps_scas,
107            instr.has_rep_prefix(),
108            instr.has_repne_prefix(),
109        ) {
110            (_, false, false) => None,
111            (false, true, false) => Some(RepPrefix::Rep),
112            // Testing on actual hardware shows that a REPNE prefix on a non-cmps/scas instruction is treated as a REP
113            (false, false, true) => Some(RepPrefix::Rep),
114            (true, true, false) => Some(RepPrefix::Repe(true)),
115            (true, false, true) => Some(RepPrefix::Repne(false)),
116            (_, true, true) => unreachable!(),
117        };
118
119        let count_reg = sized_rcx(op_kind);
120        let requested = if rep.is_some() {
121            self.cpu.gp(count_reg.into())
122        } else {
123            1
124        };
125        let size = instr.memory_size().size();
126        let delta = if !self.cpu.rflags().direction() {
127            size
128        } else {
129            size.wrapping_neg()
130        };
131
132        Ok(RepState {
133            count_reg,
134            rep,
135            done: 0,
136            requested,
137            size,
138            delta: delta as u64,
139        })
140    }
141
142    fn rep_again(&mut self, rep_state: &mut RepState) -> bool {
143        if rep_state.rep.is_some() {
144            self.cpu.set_gp(
145                rep_state.count_reg.into(),
146                rep_state.requested - rep_state.done,
147            );
148        }
149        if rep_state.is_done() || rep_state.done == MAX_REP_LOOPS {
150            return false;
151        }
152        rep_state.done += 1;
153        true
154    }
155
156    /// [rep] outs dx, seg:xsi
157    ///
158    /// Return true if instruction completed.
159    pub(super) async fn outs(
160        &mut self,
161        instr: &Instruction,
162    ) -> Result<(), InternalError<T::Error>> {
163        let mut rep = self.rep_op(instr, instr.op1_kind(), false)?;
164        let rsi = sized_rsi(instr.op1_kind());
165        while self.rep_again(&mut rep) {
166            let data = &mut [0; 4][..rep.size];
167            let offset = self.memory_op_offset(instr, 1);
168            let io_register = self.cpu.gp(instr.op0_register().into()) as u16;
169
170            self.read_memory(
171                instr.memory_segment().into(),
172                offset,
173                AlignmentMode::Standard,
174                data,
175            )
176            .await?;
177            self.write_io(io_register, data).await?;
178
179            self.cpu.set_gp(rsi.into(), offset.wrapping_add(rep.delta));
180        }
181        rep.check_done()?;
182        Ok(())
183    }
184
185    /// [rep] ins es:xdi, dx
186    ///
187    /// Return true if instruction completed.
188    pub(super) async fn ins(&mut self, instr: &Instruction) -> Result<(), InternalError<T::Error>> {
189        let mut rep = self.rep_op(instr, instr.op0_kind(), false)?;
190        let rdi = sized_rdi(instr.op0_kind());
191        while self.rep_again(&mut rep) {
192            let offset = self.memory_op_offset(instr, 0);
193            let io_register = self.cpu.gp(instr.op1_register().into()) as u16;
194
195            let data = &mut [0; 4][..rep.size];
196            self.read_io(io_register, data).await?;
197            self.write_memory(Segment::ES, offset, AlignmentMode::Standard, data)
198                .await?;
199
200            self.cpu.set_gp(rdi.into(), offset.wrapping_add(rep.delta));
201        }
202        rep.check_done()?;
203        Ok(())
204    }
205
206    /// [rep] lods (r)ax, ds:xsi
207    ///
208    /// Return true if instruction completed.
209    pub(super) async fn lods(
210        &mut self,
211        instr: &Instruction,
212    ) -> Result<(), InternalError<T::Error>> {
213        let mut rep = self.rep_op(instr, instr.op1_kind(), false)?;
214        let rsi = sized_rsi(instr.op1_kind());
215        while self.rep_again(&mut rep) {
216            let offset = self.memory_op_offset(instr, 1);
217            let mut data = [0; 8];
218            self.read_memory(
219                instr.memory_segment().into(),
220                offset,
221                AlignmentMode::Standard,
222                &mut data[..rep.size],
223            )
224            .await?;
225
226            self.cpu
227                .set_gp(instr.op0_register().into(), u64::from_le_bytes(data));
228            self.cpu.set_gp(rsi.into(), offset.wrapping_add(rep.delta));
229        }
230        rep.check_done()?;
231        Ok(())
232    }
233
234    /// [rep] stos es:xdi, (r)ax
235    ///
236    /// Return true if instruction completed.
237    pub(super) async fn stos(
238        &mut self,
239        instr: &Instruction,
240    ) -> Result<(), InternalError<T::Error>> {
241        let mut rep = self.rep_op(instr, instr.op0_kind(), false)?;
242        let rdi = sized_rdi(instr.op0_kind());
243        while self.rep_again(&mut rep) {
244            let offset = self.memory_op_offset(instr, 0);
245            let data = self.cpu.gp(instr.op1_register().into()).to_le_bytes();
246            self.write_memory(
247                Segment::ES,
248                offset,
249                AlignmentMode::Standard,
250                &data[..rep.size],
251            )
252            .await?;
253
254            self.cpu.set_gp(rdi.into(), offset.wrapping_add(rep.delta));
255        }
256        rep.check_done()?;
257        Ok(())
258    }
259
260    /// [rep] movs es:xdi seg:xsi
261    ///
262    /// Return true if instruction completed.
263    pub(super) async fn movs(
264        &mut self,
265        instr: &Instruction,
266    ) -> Result<(), InternalError<T::Error>> {
267        let mut rep = self.rep_op(instr, instr.op0_kind(), false)?;
268        let rdi = sized_rdi(instr.op0_kind());
269        let rsi = sized_rsi(instr.op1_kind());
270        while self.rep_again(&mut rep) {
271            let data = &mut [0; 8][..rep.size];
272
273            let di_offset = self.memory_op_offset(instr, 0);
274            let si_offset = self.memory_op_offset(instr, 1);
275
276            self.read_memory(
277                instr.memory_segment().into(),
278                si_offset,
279                AlignmentMode::Standard,
280                data,
281            )
282            .await?;
283            self.write_memory(Segment::ES, di_offset, AlignmentMode::Standard, data)
284                .await?;
285
286            self.cpu
287                .set_gp(rsi.into(), si_offset.wrapping_add(rep.delta));
288            self.cpu
289                .set_gp(rdi.into(), di_offset.wrapping_add(rep.delta));
290        }
291        rep.check_done()?;
292        Ok(())
293    }
294
295    /// [rep] cmps es:xdi seg:xsi
296    ///
297    /// Return true if instruction completed.
298    pub(super) async fn cmps(
299        &mut self,
300        instr: &Instruction,
301    ) -> Result<(), InternalError<T::Error>> {
302        let mut rep = self.rep_op(instr, instr.op0_kind(), true)?;
303        let rsi = sized_rsi(instr.op0_kind());
304        let rdi = sized_rdi(instr.op1_kind());
305        let mut left = 0;
306        let mut right = 0;
307        while self.rep_again(&mut rep) {
308            let mut data_left = [0; 8];
309            let mut data_right = [0; 8];
310
311            let si_offset = self.memory_op_offset(instr, 0);
312            let di_offset = self.memory_op_offset(instr, 1);
313
314            self.read_memory(
315                instr.memory_segment().into(),
316                si_offset,
317                AlignmentMode::Standard,
318                &mut data_left[..rep.size],
319            )
320            .await?;
321            self.read_memory(
322                Segment::ES,
323                di_offset,
324                AlignmentMode::Standard,
325                &mut data_right[..rep.size],
326            )
327            .await?;
328
329            left = u64::from_le_bytes(data_left);
330            right = u64::from_le_bytes(data_right);
331            rep.update_zero(left == right);
332
333            self.cpu
334                .set_gp(rsi.into(), si_offset.wrapping_add(rep.delta));
335            self.cpu
336                .set_gp(rdi.into(), di_offset.wrapping_add(rep.delta));
337        }
338
339        rep.check_done()?;
340        if rep.requested != 0 {
341            let mut rflags = self.cpu.rflags();
342            let result = super::arith::CmpOp::op(left, right, rflags);
343            super::arith::CmpOp::update_flags(&mut rflags, rep.size, result, left, right);
344            self.cpu.set_rflags(rflags);
345        }
346        Ok(())
347    }
348
349    /// [rep] scas seg:xdi (r)ax
350    ///
351    /// Return true if instruction completed.
352    pub(super) async fn scas(
353        &mut self,
354        instr: &Instruction,
355    ) -> Result<(), InternalError<T::Error>> {
356        let mut rep = self.rep_op(instr, instr.op1_kind(), true)?;
357        let rax = self.cpu.gp(instr.op0_register().into());
358        let rdi = sized_rdi(instr.op1_kind());
359        let mut memval = 0;
360        while self.rep_again(&mut rep) {
361            let mut data = [0; 8];
362            let di_offset = self.memory_op_offset(instr, 1);
363
364            self.read_memory(
365                Segment::ES,
366                di_offset,
367                AlignmentMode::Standard,
368                &mut data[..rep.size],
369            )
370            .await?;
371
372            memval = u64::from_le_bytes(data);
373            rep.update_zero(memval == rax);
374
375            self.cpu
376                .set_gp(rdi.into(), di_offset.wrapping_add(rep.delta));
377        }
378
379        rep.check_done()?;
380        if rep.requested != 0 {
381            let mut rflags = self.cpu.rflags();
382            let result = super::arith::CmpOp::op(rax, memval, rflags);
383            super::arith::CmpOp::update_flags(&mut rflags, rep.size, result, rax, memval);
384            self.cpu.set_rflags(rflags);
385        }
386        Ok(())
387    }
388}