xref: /cloud-hypervisor/hypervisor/src/arch/x86/emulator/instructions/movs.rs (revision fa7a000dbe9637eb256af18ae8c3c4a8d5bf9c8f)
1 //
2 // Copyright © 2021 Microsoft
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 #![allow(non_camel_case_types)]
8 
9 //
10 // MOVS - Move Data from String to String
11 //
12 
13 use crate::arch::x86::emulator::instructions::*;
14 use crate::arch::x86::regs::DF;
15 
16 macro_rules! movs {
17     ($bound:ty) => {
18         fn emulate(
19             &self,
20             insn: &Instruction,
21             state: &mut T,
22             platform: &mut dyn PlatformEmulator<CpuState = T>,
23         ) -> Result<(), EmulationError<Exception>> {
24             let mut count: u64 = if insn.has_rep_prefix() {
25                 state
26                     .read_reg(Register::ECX)
27                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?
28             } else {
29                 1
30             };
31 
32             let mut rsi = state
33                 .read_reg(Register::RSI)
34                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
35             let mut rdi = state
36                 .read_reg(Register::RDI)
37                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
38 
39             let df = (state.flags() & DF) != 0;
40             let len = std::mem::size_of::<$bound>();
41 
42             while count > 0 {
43                 let mut memory: [u8; 4] = [0; 4];
44 
45                 let src = state
46                     .linearize(Register::DS, rsi, false)
47                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
48                 let dst = state
49                     .linearize(Register::ES, rdi, true)
50                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
51 
52                 platform
53                     .read_memory(src, &mut memory[0..len])
54                     .map_err(EmulationError::PlatformEmulationError)?;
55                 platform
56                     .write_memory(dst, &memory[0..len])
57                     .map_err(EmulationError::PlatformEmulationError)?;
58 
59                 if df {
60                     rsi = rsi.wrapping_sub(len as u64);
61                     rdi = rdi.wrapping_sub(len as u64);
62                 } else {
63                     rsi = rsi.wrapping_add(len as u64);
64                     rdi = rdi.wrapping_add(len as u64);
65                 }
66                 count -= 1;
67             }
68 
69             state
70                 .write_reg(Register::RSI, rsi)
71                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
72             state
73                 .write_reg(Register::RDI, rdi)
74                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
75             if insn.has_rep_prefix() {
76                 state
77                     .write_reg(Register::ECX, 0)
78                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
79             }
80 
81             Ok(())
82         }
83     };
84 }
85 
86 pub struct Movsd_m32_m32;
87 impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
88     movs!(u32);
89 }
90 
91 pub struct Movsw_m16_m16;
92 impl<T: CpuStateManager> InstructionHandler<T> for Movsw_m16_m16 {
93     movs!(u16);
94 }
95 
96 pub struct Movsb_m8_m8;
97 impl<T: CpuStateManager> InstructionHandler<T> for Movsb_m8_m8 {
98     movs!(u8);
99 }
100 
101 #[cfg(test)]
102 mod tests {
103     use super::*;
104     use crate::arch::x86::emulator::mock_vmm::*;
105 
106     #[test]
107     fn test_rep_movsd_m32_m32() {
108         let ip: u64 = 0x1000;
109         let memory: [u8; 24] = [
110             0x78, 0x56, 0x34, 0x12, // 0x12345678
111             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
112             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
113             0x00, 0x00, 0x00, 0x00, // 0x00000000
114             0x00, 0x00, 0x00, 0x00, // 0x00000000
115             0x00, 0x00, 0x00, 0x00, // 0x00000000
116         ];
117         let insn = [0xf3, 0xa5]; // rep movsd
118         let regs = vec![(Register::ECX, 3), (Register::ESI, 0), (Register::EDI, 0xc)];
119         let mut data = [0u8; 4];
120 
121         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
122 
123         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
124 
125         vmm.read_memory(0xc, &mut data).unwrap();
126         assert_eq!(0x12345678, <u32>::from_le_bytes(data));
127         vmm.read_memory(0xc + 4, &mut data).unwrap();
128         assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
129         vmm.read_memory(0xc + 8, &mut data).unwrap();
130         assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
131         // The rest should be default value 0 from MockVmm
132         vmm.read_memory(0xc + 12, &mut data).unwrap();
133         assert_eq!(0x0, <u32>::from_le_bytes(data));
134     }
135 
136     #[test]
137     fn test_movsd_m32_m32() {
138         let ip: u64 = 0x1000;
139         let memory: [u8; 4] = [
140             0x78, 0x56, 0x34, 0x12, // 0x12345678
141         ];
142         let insn = [0xa5]; // movsd
143         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
144         let mut data = [0u8; 4];
145 
146         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
147 
148         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
149 
150         vmm.read_memory(0x8, &mut data).unwrap();
151         assert_eq!(0x12345678, <u32>::from_le_bytes(data));
152         // The rest should be default value 0 from MockVmm
153         vmm.read_memory(0x4, &mut data).unwrap();
154         assert_eq!(0x0, <u32>::from_le_bytes(data));
155         vmm.read_memory(0x8 + 8, &mut data).unwrap();
156         assert_eq!(0x0, <u32>::from_le_bytes(data));
157     }
158 
159     #[test]
160     fn test_rep_movsw_m16_m16() {
161         let ip: u64 = 0x1000;
162         let memory: [u8; 24] = [
163             0x78, 0x56, 0x34, 0x12, // 0x12345678
164             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
165             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
166             0x00, 0x00, 0x00, 0x00, // 0x00000000
167             0x00, 0x00, 0x00, 0x00, // 0x00000000
168             0x00, 0x00, 0x00, 0x00, // 0x00000000
169         ];
170         let insn = [0x66, 0xf3, 0xa5]; // rep movsw
171         let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0xc)];
172         let mut data = [0u8; 2];
173 
174         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
175 
176         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
177 
178         vmm.read_memory(0xc, &mut data).unwrap();
179         assert_eq!(0x5678, <u16>::from_le_bytes(data));
180         vmm.read_memory(0xc + 2, &mut data).unwrap();
181         assert_eq!(0x1234, <u16>::from_le_bytes(data));
182         vmm.read_memory(0xc + 4, &mut data).unwrap();
183         assert_eq!(0xccdd, <u16>::from_le_bytes(data));
184         vmm.read_memory(0xc + 6, &mut data).unwrap();
185         assert_eq!(0xaabb, <u16>::from_le_bytes(data));
186         vmm.read_memory(0xc + 8, &mut data).unwrap();
187         assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
188         vmm.read_memory(0xc + 10, &mut data).unwrap();
189         assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
190         // The rest should be default value 0 from MockVmm
191         vmm.read_memory(0xc + 12, &mut data).unwrap();
192         assert_eq!(0x0, <u16>::from_le_bytes(data));
193     }
194 
195     #[test]
196     fn test_movsw_m16_m16() {
197         let ip: u64 = 0x1000;
198         let memory: [u8; 4] = [
199             0x78, 0x56, 0x34, 0x12, // 0x12345678
200         ];
201         let insn = [0x66, 0xa5]; // movsw
202         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
203         let mut data = [0u8; 2];
204 
205         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
206 
207         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
208 
209         vmm.read_memory(0x8, &mut data).unwrap();
210         assert_eq!(0x5678, <u16>::from_le_bytes(data));
211         // Only two bytes were copied, so the value at 0xa should be zero
212         vmm.read_memory(0xa, &mut data).unwrap();
213         assert_eq!(0x0, <u16>::from_le_bytes(data));
214         // The rest should be default value 0 from MockVmm
215         vmm.read_memory(0x4, &mut data).unwrap();
216         assert_eq!(0x0, <u16>::from_le_bytes(data));
217         vmm.read_memory(0x8 + 8, &mut data).unwrap();
218         assert_eq!(0x0, <u16>::from_le_bytes(data));
219     }
220 
221     #[test]
222     fn test_movsb_m8_m8() {
223         let ip: u64 = 0x1000;
224         let memory: [u8; 4] = [
225             0x78, 0x56, 0x34, 0x12, // 0x12345678
226         ];
227         let insn = [0x66, 0xa4]; // movsb
228         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
229         let mut data = [0u8; 1];
230 
231         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
232 
233         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
234 
235         vmm.read_memory(0x8, &mut data).unwrap();
236         assert_eq!(0x78, data[0]);
237         // Only one byte was copied, so the value at 0x9 should be zero
238         vmm.read_memory(0x9, &mut data).unwrap();
239         assert_eq!(0x0, data[0]);
240         // The rest should be default value 0 from MockVmm
241         vmm.read_memory(0x4, &mut data).unwrap();
242         assert_eq!(0x0, data[0]);
243         // the src value is left as is after movb
244         vmm.read_memory(0x0, &mut data).unwrap();
245         assert_eq!(0x78, data[0]);
246     }
247 
248     #[test]
249     fn test_rep_movsb_m8_m8() {
250         let ip: u64 = 0x1000;
251         let memory: [u8; 16] = [
252             0x78, 0x56, 0x34, 0x12, // 0x12345678
253             0xbb, 0xaa, 0x00, 0x00, // 0x0000aabb
254             0x00, 0x00, 0x00, 0x00, // 0x00000000
255             0x00, 0x00, 0x00, 0x00, // 0x00000000
256         ];
257         let insn = [0x66, 0xf3, 0xa4]; // rep movsw
258         let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0x8)];
259         let mut data = [0u8; 1];
260 
261         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
262 
263         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
264 
265         vmm.read_memory(0x8, &mut data).unwrap();
266         assert_eq!(0x78, data[0]);
267         vmm.read_memory(0x8 + 1, &mut data).unwrap();
268         assert_eq!(0x56, data[0]);
269         vmm.read_memory(0x8 + 2, &mut data).unwrap();
270         assert_eq!(0x34, data[0]);
271         vmm.read_memory(0x8 + 3, &mut data).unwrap();
272         assert_eq!(0x12, data[0]);
273         vmm.read_memory(0x8 + 4, &mut data).unwrap();
274         assert_eq!(0xbb, data[0]);
275         vmm.read_memory(0x8 + 5, &mut data).unwrap();
276         assert_eq!(0xaa, data[0]);
277         // The rest should be default value 0 from MockVmm
278         vmm.read_memory(0x8 + 6, &mut data).unwrap();
279         assert_eq!(0x0, data[0]);
280     }
281 }
282