xref: /cloud-hypervisor/hypervisor/src/arch/x86/emulator/instructions/movs.rs (revision 5f814308d6b19037f2afb3d36fe49b0aa14c0b22)
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; 8] = [0; 8];
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 Movsq_m64_m64;
87 impl<T: CpuStateManager> InstructionHandler<T> for Movsq_m64_m64 {
88     movs!(u64);
89 }
90 
91 pub struct Movsd_m32_m32;
92 impl<T: CpuStateManager> InstructionHandler<T> for Movsd_m32_m32 {
93     movs!(u32);
94 }
95 
96 pub struct Movsw_m16_m16;
97 impl<T: CpuStateManager> InstructionHandler<T> for Movsw_m16_m16 {
98     movs!(u16);
99 }
100 
101 pub struct Movsb_m8_m8;
102 impl<T: CpuStateManager> InstructionHandler<T> for Movsb_m8_m8 {
103     movs!(u8);
104 }
105 
106 #[cfg(test)]
107 mod tests {
108     use super::*;
109     use crate::arch::x86::emulator::mock_vmm::*;
110 
111     #[test]
112     fn test_rep_movsq_m64_m64() {
113         let ip: u64 = 0x1000;
114         let memory: [u8; 32] = [
115             0x78, 0x56, 0x34, 0x12, // 0x12345678
116             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
117             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
118             0xcd, 0xcd, 0xcd, 0xcd, // 0xcdcdcdcd
119             0x00, 0x00, 0x00, 0x00, // 0x00000000
120             0x00, 0x00, 0x00, 0x00, // 0x00000000
121             0x00, 0x00, 0x00, 0x00, // 0x00000000
122             0x00, 0x00, 0x00, 0x00, // 0x00000000
123         ];
124         let insn = [0xf3, 0x48, 0xa5]; // rep movsq
125         let regs = vec![
126             (Register::ECX, 2),
127             (Register::ESI, 0),
128             (Register::EDI, 0x10),
129         ];
130         let mut data = [0u8; 8];
131 
132         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
133 
134         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
135 
136         vmm.read_memory(0x10, &mut data).unwrap();
137         assert_eq!(0xaabbccdd12345678, <u64>::from_le_bytes(data));
138         vmm.read_memory(0x18, &mut data).unwrap();
139         assert_eq!(0xcdcdcdcd5aa55aa5, <u64>::from_le_bytes(data));
140         // The rest should be default value 0 from MockVmm
141         vmm.read_memory(0x20, &mut data).unwrap();
142         assert_eq!(0x0, <u64>::from_le_bytes(data));
143     }
144 
145     #[test]
146     fn test_rep_movsd_m32_m32() {
147         let ip: u64 = 0x1000;
148         let memory: [u8; 24] = [
149             0x78, 0x56, 0x34, 0x12, // 0x12345678
150             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
151             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
152             0x00, 0x00, 0x00, 0x00, // 0x00000000
153             0x00, 0x00, 0x00, 0x00, // 0x00000000
154             0x00, 0x00, 0x00, 0x00, // 0x00000000
155         ];
156         let insn = [0xf3, 0xa5]; // rep movsd
157         let regs = vec![(Register::ECX, 3), (Register::ESI, 0), (Register::EDI, 0xc)];
158         let mut data = [0u8; 4];
159 
160         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
161 
162         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
163 
164         vmm.read_memory(0xc, &mut data).unwrap();
165         assert_eq!(0x12345678, <u32>::from_le_bytes(data));
166         vmm.read_memory(0xc + 4, &mut data).unwrap();
167         assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
168         vmm.read_memory(0xc + 8, &mut data).unwrap();
169         assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
170         // The rest should be default value 0 from MockVmm
171         vmm.read_memory(0xc + 12, &mut data).unwrap();
172         assert_eq!(0x0, <u32>::from_le_bytes(data));
173     }
174 
175     #[test]
176     fn test_movsd_m32_m32() {
177         let ip: u64 = 0x1000;
178         let memory: [u8; 4] = [
179             0x78, 0x56, 0x34, 0x12, // 0x12345678
180         ];
181         let insn = [0xa5]; // movsd
182         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
183         let mut data = [0u8; 4];
184 
185         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
186 
187         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
188 
189         vmm.read_memory(0x8, &mut data).unwrap();
190         assert_eq!(0x12345678, <u32>::from_le_bytes(data));
191         // The rest should be default value 0 from MockVmm
192         vmm.read_memory(0x4, &mut data).unwrap();
193         assert_eq!(0x0, <u32>::from_le_bytes(data));
194         vmm.read_memory(0x8 + 8, &mut data).unwrap();
195         assert_eq!(0x0, <u32>::from_le_bytes(data));
196     }
197 
198     #[test]
199     fn test_rep_movsw_m16_m16() {
200         let ip: u64 = 0x1000;
201         let memory: [u8; 24] = [
202             0x78, 0x56, 0x34, 0x12, // 0x12345678
203             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
204             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
205             0x00, 0x00, 0x00, 0x00, // 0x00000000
206             0x00, 0x00, 0x00, 0x00, // 0x00000000
207             0x00, 0x00, 0x00, 0x00, // 0x00000000
208         ];
209         let insn = [0x66, 0xf3, 0xa5]; // rep movsw
210         let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0xc)];
211         let mut data = [0u8; 2];
212 
213         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
214 
215         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
216 
217         vmm.read_memory(0xc, &mut data).unwrap();
218         assert_eq!(0x5678, <u16>::from_le_bytes(data));
219         vmm.read_memory(0xc + 2, &mut data).unwrap();
220         assert_eq!(0x1234, <u16>::from_le_bytes(data));
221         vmm.read_memory(0xc + 4, &mut data).unwrap();
222         assert_eq!(0xccdd, <u16>::from_le_bytes(data));
223         vmm.read_memory(0xc + 6, &mut data).unwrap();
224         assert_eq!(0xaabb, <u16>::from_le_bytes(data));
225         vmm.read_memory(0xc + 8, &mut data).unwrap();
226         assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
227         vmm.read_memory(0xc + 10, &mut data).unwrap();
228         assert_eq!(0x5aa5, <u16>::from_le_bytes(data));
229         // The rest should be default value 0 from MockVmm
230         vmm.read_memory(0xc + 12, &mut data).unwrap();
231         assert_eq!(0x0, <u16>::from_le_bytes(data));
232     }
233 
234     #[test]
235     fn test_movsw_m16_m16() {
236         let ip: u64 = 0x1000;
237         let memory: [u8; 4] = [
238             0x78, 0x56, 0x34, 0x12, // 0x12345678
239         ];
240         let insn = [0x66, 0xa5]; // movsw
241         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
242         let mut data = [0u8; 2];
243 
244         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
245 
246         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
247 
248         vmm.read_memory(0x8, &mut data).unwrap();
249         assert_eq!(0x5678, <u16>::from_le_bytes(data));
250         // Only two bytes were copied, so the value at 0xa should be zero
251         vmm.read_memory(0xa, &mut data).unwrap();
252         assert_eq!(0x0, <u16>::from_le_bytes(data));
253         // The rest should be default value 0 from MockVmm
254         vmm.read_memory(0x4, &mut data).unwrap();
255         assert_eq!(0x0, <u16>::from_le_bytes(data));
256         vmm.read_memory(0x8 + 8, &mut data).unwrap();
257         assert_eq!(0x0, <u16>::from_le_bytes(data));
258     }
259 
260     #[test]
261     fn test_movsb_m8_m8() {
262         let ip: u64 = 0x1000;
263         let memory: [u8; 4] = [
264             0x78, 0x56, 0x34, 0x12, // 0x12345678
265         ];
266         let insn = [0x66, 0xa4]; // movsb
267         let regs = vec![(Register::ESI, 0), (Register::EDI, 0x8)];
268         let mut data = [0u8; 1];
269 
270         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
271 
272         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
273 
274         vmm.read_memory(0x8, &mut data).unwrap();
275         assert_eq!(0x78, data[0]);
276         // Only one byte was copied, so the value at 0x9 should be zero
277         vmm.read_memory(0x9, &mut data).unwrap();
278         assert_eq!(0x0, data[0]);
279         // The rest should be default value 0 from MockVmm
280         vmm.read_memory(0x4, &mut data).unwrap();
281         assert_eq!(0x0, data[0]);
282         // the src value is left as is after movb
283         vmm.read_memory(0x0, &mut data).unwrap();
284         assert_eq!(0x78, data[0]);
285     }
286 
287     #[test]
288     fn test_rep_movsb_m8_m8() {
289         let ip: u64 = 0x1000;
290         let memory: [u8; 16] = [
291             0x78, 0x56, 0x34, 0x12, // 0x12345678
292             0xbb, 0xaa, 0x00, 0x00, // 0x0000aabb
293             0x00, 0x00, 0x00, 0x00, // 0x00000000
294             0x00, 0x00, 0x00, 0x00, // 0x00000000
295         ];
296         let insn = [0x66, 0xf3, 0xa4]; // rep movsw
297         let regs = vec![(Register::ECX, 6), (Register::ESI, 0), (Register::EDI, 0x8)];
298         let mut data = [0u8; 1];
299 
300         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
301 
302         assert!(vmm.emulate_first_insn(0, &insn).is_ok());
303 
304         vmm.read_memory(0x8, &mut data).unwrap();
305         assert_eq!(0x78, data[0]);
306         vmm.read_memory(0x8 + 1, &mut data).unwrap();
307         assert_eq!(0x56, data[0]);
308         vmm.read_memory(0x8 + 2, &mut data).unwrap();
309         assert_eq!(0x34, data[0]);
310         vmm.read_memory(0x8 + 3, &mut data).unwrap();
311         assert_eq!(0x12, data[0]);
312         vmm.read_memory(0x8 + 4, &mut data).unwrap();
313         assert_eq!(0xbb, data[0]);
314         vmm.read_memory(0x8 + 5, &mut data).unwrap();
315         assert_eq!(0xaa, data[0]);
316         // The rest should be default value 0 from MockVmm
317         vmm.read_memory(0x8 + 6, &mut data).unwrap();
318         assert_eq!(0x0, data[0]);
319     }
320 }
321