xref: /cloud-hypervisor/hypervisor/src/arch/x86/emulator/instructions/mov.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 //
2 // Copyright © 2020 Intel Corporation
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 #![allow(non_camel_case_types)]
8 
9 //
10 // MOV-Move
11 // SDM Volume 1, Chapter 4.3
12 //   Copies the second operand (source operand) to the first operand (destination operand).
13 //
14 
15 use crate::arch::x86::emulator::instructions::*;
16 
17 macro_rules! mov_rm_r {
18     ($bound:ty) => {
19         fn emulate(
20             &self,
21             insn: &Instruction,
22             state: &mut T,
23             platform: &mut dyn PlatformEmulator<CpuState = T>,
24         ) -> Result<(), EmulationError<Exception>> {
25             let src_reg_value = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
26                 .map_err(EmulationError::PlatformEmulationError)?;
27 
28             set_op(
29                 &insn,
30                 0,
31                 std::mem::size_of::<$bound>(),
32                 state,
33                 platform,
34                 src_reg_value,
35             )
36             .map_err(EmulationError::PlatformEmulationError)?;
37 
38             Ok(())
39         }
40     };
41 }
42 
43 macro_rules! mov_rm_imm {
44     ($bound:ty) => {
45         fn emulate(
46             &self,
47             insn: &Instruction,
48             state: &mut T,
49             platform: &mut dyn PlatformEmulator<CpuState = T>,
50         ) -> Result<(), EmulationError<Exception>> {
51             let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
52                 .map_err(EmulationError::PlatformEmulationError)?;
53 
54             set_op(
55                 &insn,
56                 0,
57                 std::mem::size_of::<$bound>(),
58                 state,
59                 platform,
60                 imm,
61             )
62             .map_err(EmulationError::PlatformEmulationError)?;
63 
64             Ok(())
65         }
66     };
67 }
68 
69 macro_rules! movzx {
70     ($dest_op_size:ty, $src_op_size:ty) => {
71         fn emulate(
72             &self,
73             insn: &Instruction,
74             state: &mut T,
75             platform: &mut dyn PlatformEmulator<CpuState = T>,
76         ) -> Result<(), EmulationError<Exception>> {
77             let src_value = get_op(
78                 &insn,
79                 1,
80                 std::mem::size_of::<$src_op_size>(),
81                 state,
82                 platform,
83             )
84             .map_err(EmulationError::PlatformEmulationError)?;
85 
86             set_op(
87                 &insn,
88                 0,
89                 std::mem::size_of::<$dest_op_size>(),
90                 state,
91                 platform,
92                 src_value,
93             )
94             .map_err(EmulationError::PlatformEmulationError)?;
95 
96             Ok(())
97         }
98     };
99 }
100 
101 // MOV r/rm is a special case of MOVZX, where both operands have the same size.
102 macro_rules! mov_r_rm {
103     ($op_size:ty) => {
104         movzx!($op_size, $op_size);
105     };
106 }
107 
108 macro_rules! mov_r_imm {
109     ($bound:ty) => {
110         fn emulate(
111             &self,
112             insn: &Instruction,
113             state: &mut T,
114             platform: &mut dyn PlatformEmulator<CpuState = T>,
115         ) -> Result<(), EmulationError<Exception>> {
116             let imm = get_op(&insn, 1, std::mem::size_of::<$bound>(), state, platform)
117                 .map_err(EmulationError::PlatformEmulationError)?;
118 
119             set_op(
120                 &insn,
121                 0,
122                 std::mem::size_of::<$bound>(),
123                 state,
124                 platform,
125                 imm,
126             )
127             .map_err(EmulationError::PlatformEmulationError)?;
128 
129             Ok(())
130         }
131     };
132 }
133 
134 pub struct Mov_r8_rm8;
135 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_rm8 {
136     mov_r_rm!(u8);
137 }
138 
139 pub struct Mov_r8_imm8;
140 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r8_imm8 {
141     mov_r_imm!(u8);
142 }
143 
144 pub struct Mov_r16_rm16;
145 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_rm16 {
146     mov_r_rm!(u16);
147 }
148 
149 pub struct Mov_r16_imm16;
150 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r16_imm16 {
151     mov_r_imm!(u16);
152 }
153 
154 pub struct Mov_r32_rm32;
155 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_rm32 {
156     mov_r_rm!(u32);
157 }
158 
159 pub struct Mov_r32_imm32;
160 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r32_imm32 {
161     mov_r_imm!(u32);
162 }
163 
164 pub struct Mov_r64_rm64;
165 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_rm64 {
166     mov_r_rm!(u64);
167 }
168 
169 pub struct Mov_r64_imm64;
170 impl<T: CpuStateManager> InstructionHandler<T> for Mov_r64_imm64 {
171     mov_r_imm!(u64);
172 }
173 
174 pub struct Mov_rm8_imm8;
175 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_imm8 {
176     mov_rm_imm!(u8);
177 }
178 
179 pub struct Mov_rm8_r8;
180 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm8_r8 {
181     mov_rm_r!(u8);
182 }
183 
184 pub struct Mov_rm16_imm16;
185 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_imm16 {
186     mov_rm_imm!(u16);
187 }
188 
189 pub struct Mov_rm16_r16;
190 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm16_r16 {
191     mov_rm_r!(u16);
192 }
193 
194 pub struct Mov_rm32_imm32;
195 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_imm32 {
196     mov_rm_imm!(u32);
197 }
198 
199 pub struct Mov_rm32_r32;
200 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm32_r32 {
201     mov_rm_r!(u32);
202 }
203 
204 pub struct Mov_rm64_imm32;
205 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_imm32 {
206     mov_rm_imm!(u32);
207 }
208 
209 pub struct Mov_rm64_r64;
210 impl<T: CpuStateManager> InstructionHandler<T> for Mov_rm64_r64 {
211     mov_rm_r!(u64);
212 }
213 
214 // MOVZX
215 pub struct Movzx_r16_rm8;
216 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r16_rm8 {
217     movzx!(u16, u8);
218 }
219 
220 pub struct Movzx_r32_rm8;
221 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm8 {
222     movzx!(u32, u8);
223 }
224 
225 pub struct Movzx_r64_rm8;
226 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm8 {
227     movzx!(u64, u8);
228 }
229 
230 pub struct Movzx_r32_rm16;
231 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r32_rm16 {
232     movzx!(u32, u16);
233 }
234 
235 pub struct Movzx_r64_rm16;
236 impl<T: CpuStateManager> InstructionHandler<T> for Movzx_r64_rm16 {
237     movzx!(u64, u16);
238 }
239 
240 pub struct Mov_moffs16_AX;
241 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs16_AX {
242     movzx!(u16, u16);
243 }
244 
245 pub struct Mov_AX_moffs16;
246 impl<T: CpuStateManager> InstructionHandler<T> for Mov_AX_moffs16 {
247     movzx!(u16, u16);
248 }
249 
250 pub struct Mov_moffs32_EAX;
251 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs32_EAX {
252     movzx!(u32, u32);
253 }
254 
255 pub struct Mov_EAX_moffs32;
256 impl<T: CpuStateManager> InstructionHandler<T> for Mov_EAX_moffs32 {
257     movzx!(u32, u32);
258 }
259 
260 pub struct Mov_moffs64_RAX;
261 impl<T: CpuStateManager> InstructionHandler<T> for Mov_moffs64_RAX {
262     movzx!(u64, u64);
263 }
264 
265 pub struct Mov_RAX_moffs64;
266 impl<T: CpuStateManager> InstructionHandler<T> for Mov_RAX_moffs64 {
267     movzx!(u64, u64);
268 }
269 
270 #[cfg(test)]
271 mod tests {
272     use super::*;
273     use crate::arch::x86::emulator::mock_vmm::*;
274 
275     #[test]
276     // mov rax,rbx
277     fn test_mov_r64_r64() {
278         let rbx: u64 = 0x8899aabbccddeeff;
279         let ip: u64 = 0x1000;
280         let cpu_id = 0;
281         let insn = [0x48, 0x89, 0xd8];
282         let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], None);
283         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
284 
285         let rax: u64 = vmm
286             .cpu_state(cpu_id)
287             .unwrap()
288             .read_reg(Register::RAX)
289             .unwrap();
290         assert_eq!(rax, rbx);
291     }
292 
293     #[test]
294     // mov rax,0x1122334411223344
295     fn test_mov_r64_imm64() {
296         let imm64: u64 = 0x1122334411223344;
297         let ip: u64 = 0x1000;
298         let cpu_id = 0;
299         let insn = [0x48, 0xb8, 0x44, 0x33, 0x22, 0x11, 0x44, 0x33, 0x22, 0x11];
300         let mut vmm = MockVmm::new(ip, vec![], None);
301         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
302 
303         let rax: u64 = vmm
304             .cpu_state(cpu_id)
305             .unwrap()
306             .read_reg(Register::RAX)
307             .unwrap();
308         assert_eq!(rax, imm64);
309     }
310 
311     #[test]
312     // mov rax, [rax+rax]
313     fn test_mov_r64_m64() {
314         let target_rax: u64 = 0x1234567812345678;
315         let mut rax: u64 = 0x100;
316         let ip: u64 = 0x1000;
317         let cpu_id = 0;
318         let memory: [u8; 8] = target_rax.to_le_bytes();
319         let insn = [0x48, 0x8b, 0x04, 0x00];
320         let mut vmm = MockVmm::new(ip, vec![(Register::RAX, rax)], Some((rax + rax, &memory)));
321         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
322 
323         rax = vmm
324             .cpu_state(cpu_id)
325             .unwrap()
326             .read_reg(Register::RAX)
327             .unwrap();
328         assert_eq!(rax, target_rax);
329     }
330 
331     #[test]
332     // mov al,0x11
333     fn test_mov_r8_imm8() {
334         let imm8: u8 = 0x11;
335         let ip: u64 = 0x1000;
336         let cpu_id = 0;
337         let insn = [0xb0, 0x11];
338         let mut vmm = MockVmm::new(ip, vec![], None);
339         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
340 
341         let al = vmm
342             .cpu_state(cpu_id)
343             .unwrap()
344             .read_reg(Register::AL)
345             .unwrap();
346         assert_eq!(al as u8, imm8);
347     }
348 
349     #[test]
350     // mov eax,0x11
351     fn test_mov_r32_imm8() {
352         let imm8: u8 = 0x11;
353         let ip: u64 = 0x1000;
354         let cpu_id = 0;
355         let insn = [0xb8, 0x11, 0x00, 0x00, 0x00];
356         let mut vmm = MockVmm::new(ip, vec![], None);
357         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
358 
359         let eax = vmm
360             .cpu_state(cpu_id)
361             .unwrap()
362             .read_reg(Register::EAX)
363             .unwrap();
364         assert_eq!(eax as u8, imm8);
365     }
366 
367     #[test]
368     // mov rax,0x11223344
369     fn test_mov_r64_imm32() {
370         let imm32: u32 = 0x11223344;
371         let ip: u64 = 0x1000;
372         let cpu_id = 0;
373         let insn = [0x48, 0xc7, 0xc0, 0x44, 0x33, 0x22, 0x11];
374         let mut vmm = MockVmm::new(ip, vec![], None);
375         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
376 
377         let rax: u64 = vmm
378             .cpu_state(cpu_id)
379             .unwrap()
380             .read_reg(Register::RAX)
381             .unwrap();
382         assert_eq!(rax, imm32 as u64);
383     }
384 
385     #[test]
386     // mov byte ptr [rax],dh
387     fn test_mov_m8_r8() {
388         let rax: u64 = 0x100;
389         let dh: u8 = 0x99;
390         let ip: u64 = 0x1000;
391         let cpu_id = 0;
392         let insn = [0x88, 0x30];
393         let mut vmm = MockVmm::new(
394             ip,
395             vec![(Register::RAX, rax), (Register::DH, dh.into())],
396             None,
397         );
398         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
399 
400         let mut memory: [u8; 1] = [0; 1];
401         vmm.read_memory(rax, &mut memory).unwrap();
402 
403         assert_eq!(u8::from_le_bytes(memory), dh);
404     }
405 
406     #[test]
407     // mov dword ptr [rax],esi
408     fn test_mov_m32_r32() {
409         let rax: u64 = 0x100;
410         let esi: u32 = 0x8899;
411         let ip: u64 = 0x1000;
412         let cpu_id = 0;
413         let insn = [0x89, 0x30];
414         let mut vmm = MockVmm::new(
415             ip,
416             vec![(Register::RAX, rax), (Register::ESI, esi.into())],
417             None,
418         );
419         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
420 
421         let mut memory: [u8; 4] = [0; 4];
422         vmm.read_memory(rax, &mut memory).unwrap();
423 
424         assert_eq!(u32::from_le_bytes(memory), esi);
425     }
426 
427     #[test]
428     // mov dword ptr [rax+0x00000001],edi
429     fn test_mov_m32imm32_r32() {
430         let rax: u64 = 0x100;
431         let displacement: u64 = 0x1;
432         let edi: u32 = 0x8899;
433         let ip: u64 = 0x1000;
434         let cpu_id = 0;
435         let insn = [0x89, 0x3c, 0x05, 0x01, 0x00, 0x00, 0x00];
436         let mut vmm = MockVmm::new(
437             ip,
438             vec![(Register::RAX, rax), (Register::EDI, edi.into())],
439             None,
440         );
441         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
442 
443         let mut memory: [u8; 4] = [0; 4];
444         vmm.read_memory(rax + displacement, &mut memory).unwrap();
445 
446         assert_eq!(u32::from_le_bytes(memory), edi);
447     }
448 
449     #[test]
450     // mov eax,dword ptr [rax+10h]
451     fn test_mov_r32_m32imm32() {
452         let rax: u64 = 0x100;
453         let displacement: u64 = 0x10;
454         let eax: u32 = 0xaabbccdd;
455         let memory: [u8; 4] = eax.to_le_bytes();
456         let ip: u64 = 0x1000;
457         let cpu_id = 0;
458         let insn = [0x8b, 0x40, 0x10];
459         let mut vmm = MockVmm::new(
460             ip,
461             vec![(Register::RAX, rax)],
462             Some((rax + displacement, &memory)),
463         );
464         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
465 
466         let new_eax = vmm
467             .cpu_state(cpu_id)
468             .unwrap()
469             .read_reg(Register::EAX)
470             .unwrap();
471         assert_eq!(new_eax, eax as u64);
472     }
473 
474     #[test]
475     // mov al,byte ptr [rax+10h]
476     fn test_mov_r8_m32imm32() {
477         let rax: u64 = 0x100;
478         let displacement: u64 = 0x10;
479         let al: u8 = 0xaa;
480         let ip: u64 = 0x1000;
481         let cpu_id = 0;
482         let insn = [0x8a, 0x40, 0x10];
483         let memory: [u8; 1] = al.to_le_bytes();
484         let mut vmm = MockVmm::new(
485             ip,
486             vec![(Register::RAX, rax)],
487             Some((rax + displacement, &memory)),
488         );
489         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
490 
491         let new_al = vmm
492             .cpu_state(cpu_id)
493             .unwrap()
494             .read_reg(Register::AL)
495             .unwrap();
496         assert_eq!(new_al, al as u64);
497     }
498 
499     #[test]
500     // mov rax, 0x100
501     // mov rbx, qword ptr [rax+10h]
502     fn test_mov_r64_imm64_and_r64_m64() {
503         let target_rax: u64 = 0x1234567812345678;
504         let rax: u64 = 0x100;
505         let displacement: u64 = 0x10;
506         let ip: u64 = 0x1000;
507         let cpu_id = 0;
508         let memory: [u8; 8] = target_rax.to_le_bytes();
509         let insn = [
510             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
511             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
512         ];
513         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
514         vmm.emulate_insn(cpu_id, &insn, Some(2)).unwrap();
515 
516         let rbx: u64 = vmm
517             .cpu_state(cpu_id)
518             .unwrap()
519             .read_reg(Register::RBX)
520             .unwrap();
521         assert_eq!(rbx, target_rax);
522     }
523 
524     #[test]
525     // mov rax, 0x100
526     // mov rbx, qword ptr [rax+10h]
527     fn test_mov_r64_imm64_and_r64_m64_first_insn() {
528         let target_rax: u64 = 0x1234567812345678;
529         let rax: u64 = 0x100;
530         let displacement: u64 = 0x10;
531         let ip: u64 = 0x1000;
532         let cpu_id = 0;
533         let memory: [u8; 8] = target_rax.to_le_bytes();
534         let insn = [
535             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
536             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
537         ];
538 
539         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
540         // Only run the first instruction.
541         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
542 
543         assert_eq!(ip + 7, vmm.cpu_state(cpu_id).unwrap().ip());
544 
545         let new_rax: u64 = vmm
546             .cpu_state(cpu_id)
547             .unwrap()
548             .read_reg(Register::RAX)
549             .unwrap();
550         assert_eq!(rax, new_rax);
551     }
552 
553     #[test]
554     // mov rax, 0x100
555     // mov rbx, qword ptr [rax+10h]
556     // mov rax, 0x200
557     fn test_mov_r64_imm64_and_r64_m64_two_insns() {
558         let target_rax: u64 = 0x1234567812345678;
559         let rax: u64 = 0x100;
560         let displacement: u64 = 0x10;
561         let ip: u64 = 0x1000;
562         let cpu_id = 0;
563         let memory: [u8; 8] = target_rax.to_le_bytes();
564         let insn = [
565             0x48, 0xc7, 0xc0, 0x00, 0x01, 0x00, 0x00, // mov rax, 0x100
566             0x48, 0x8b, 0x58, 0x10, // mov rbx, qword ptr [rax+10h]
567             0x48, 0xc7, 0xc0, 0x00, 0x02, 0x00, 0x00, // mov rax, 0x200
568         ];
569 
570         let mut vmm = MockVmm::new(ip, vec![], Some((rax + displacement, &memory)));
571         // Run the 2 first instructions.
572         vmm.emulate_insn(cpu_id, &insn, Some(2)).unwrap();
573 
574         assert_eq!(ip + 7 + 4, vmm.cpu_state(cpu_id).unwrap().ip());
575 
576         let rbx: u64 = vmm
577             .cpu_state(cpu_id)
578             .unwrap()
579             .read_reg(Register::RBX)
580             .unwrap();
581         assert_eq!(rbx, target_rax);
582 
583         // Check that rax is still at 0x100
584         let new_rax: u64 = vmm
585             .cpu_state(cpu_id)
586             .unwrap()
587             .read_reg(Register::RAX)
588             .unwrap();
589         assert_eq!(rax, new_rax);
590     }
591 
592     #[test]
593     // movzx eax, bl
594     fn test_movzx_r32_r8l() {
595         let bx: u16 = 0x8899;
596         let ip: u64 = 0x1000;
597         let cpu_id = 0;
598         let insn = [0x0f, 0xb6, 0xc3];
599         let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
600         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
601 
602         let eax: u64 = vmm
603             .cpu_state(cpu_id)
604             .unwrap()
605             .read_reg(Register::EAX)
606             .unwrap();
607         assert_eq!(eax, (bx & 0xff) as u64);
608     }
609 
610     #[test]
611     // movzx eax, bh
612     fn test_movzx_r32_r8h() {
613         let bx: u16 = 0x8899;
614         let ip: u64 = 0x1000;
615         let cpu_id = 0;
616         let insn = [0x0f, 0xb6, 0xc7];
617         let mut vmm = MockVmm::new(ip, vec![(Register::BX, bx as u64)], None);
618         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
619 
620         let eax: u64 = vmm
621             .cpu_state(cpu_id)
622             .unwrap()
623             .read_reg(Register::EAX)
624             .unwrap();
625         assert_eq!(eax, (bx >> 8) as u64);
626     }
627 
628     #[test]
629     // movzx eax, byte ptr [rbx]
630     fn test_movzx_r32_m8() {
631         let rbx: u64 = 0x100;
632         let value: u8 = 0xaa;
633         let ip: u64 = 0x1000;
634         let cpu_id = 0;
635         let insn = [0x0f, 0xb7, 0x03];
636         let memory: [u8; 1] = value.to_le_bytes();
637         let mut vmm = MockVmm::new(ip, vec![(Register::RBX, rbx)], Some((rbx, &memory)));
638         vmm.emulate_first_insn(cpu_id, &insn).unwrap();
639 
640         let eax: u64 = vmm
641             .cpu_state(cpu_id)
642             .unwrap()
643             .read_reg(Register::EAX)
644             .unwrap();
645         assert_eq!(eax, value as u64);
646     }
647 
648     #[test]
649     // movabs ax, ds:0x1337
650     // movabs eax, ds:0x1337
651     // movabs rax, ds:0x1337
652     fn test_mov_memoff_ax() {
653         let test_inputs: [(Register, &[u8]); 3] = [
654             (Register::AX, &[0x66, 0xa1]),
655             (Register::EAX, &[0xa1]),
656             (Register::RAX, &[0x48, 0xa1]),
657         ];
658 
659         // Constructs the instruction with the provided inputs and emulates it.
660         fn helper(register: Register, instruction_prefix: &[u8]) {
661             let mem_addr: u64 = 0x1337;
662             let mem_value: u64 = 0x13371337deadbeef;
663             let ip: u64 = 0x1000;
664             let cpu_id = 0;
665 
666             let mut instruction_bytes = Vec::new();
667             // instruction prefix with specified register
668             instruction_bytes.extend(instruction_prefix);
669             // 64-bit memory operand
670             instruction_bytes.extend([
671                 mem_addr.to_le_bytes()[0],
672                 mem_addr.to_le_bytes()[1],
673                 0,
674                 0,
675                 0,
676                 0,
677                 0,
678                 0,
679             ]);
680 
681             let memory: [u8; 8] = mem_value.to_le_bytes();
682             let mut vmm = MockVmm::new(ip, vec![], Some((mem_addr, &memory)));
683             vmm.emulate_first_insn(cpu_id, &instruction_bytes).unwrap();
684 
685             let ax: u64 = vmm.cpu_state(cpu_id).unwrap().read_reg(register).unwrap();
686 
687             match register {
688                 Register::AX => {
689                     assert_eq!(ax as u16, mem_value as u16);
690                 }
691                 Register::EAX => {
692                     assert_eq!(ax as u32, mem_value as u32);
693                 }
694                 Register::RAX => {
695                     assert_eq!(ax, mem_value);
696                 }
697                 _ => panic!(),
698             }
699         }
700 
701         for (register, instruction_prefix) in test_inputs {
702             helper(register, instruction_prefix)
703         }
704     }
705 
706     #[test]
707     // movabs ds:0x1337, ax
708     // movabs ds:0x1337, eax
709     // movabs ds:0x1337, rax
710     fn test_mov_ax_memoff() {
711         let test_inputs: [(Register, &[u8]); 3] = [
712             (Register::AX, &[0x66, 0xa3]),
713             (Register::EAX, &[0xa3]),
714             (Register::RAX, &[0x48, 0xa3]),
715         ];
716 
717         // Constructs the instruction with the provided inputs and emulates it.
718         fn helper(register: Register, instruction_prefix: &[u8]) {
719             let mem_addr: u64 = 0x1337;
720             let ax: u64 = 0x13371337deadbeef;
721             let ip: u64 = 0x1000;
722             let cpu_id = 0;
723 
724             let mut instruction_bytes = Vec::new();
725             // instruction prefix with specified register
726             instruction_bytes.extend(instruction_prefix);
727             // 64-bit memory operand
728             instruction_bytes.extend([
729                 mem_addr.to_le_bytes()[0],
730                 mem_addr.to_le_bytes()[1],
731                 0,
732                 0,
733                 0,
734                 0,
735                 0,
736                 0,
737             ]);
738 
739             let mut vmm = MockVmm::new(ip, vec![(Register::RAX, ax)], None);
740             vmm.emulate_first_insn(cpu_id, &instruction_bytes).unwrap();
741 
742             match register {
743                 Register::AX => {
744                     let mut memory: [u8; 2] = [0; 2];
745                     vmm.read_memory(mem_addr, &mut memory).unwrap();
746                     assert_eq!(u16::from_le_bytes(memory), ax as u16);
747                 }
748                 Register::EAX => {
749                     let mut memory: [u8; 4] = [0; 4];
750                     vmm.read_memory(mem_addr, &mut memory).unwrap();
751                     assert_eq!(u32::from_le_bytes(memory), ax as u32);
752                 }
753                 Register::RAX => {
754                     let mut memory: [u8; 8] = [0; 8];
755                     vmm.read_memory(mem_addr, &mut memory).unwrap();
756                     assert_eq!(u64::from_le_bytes(memory), ax);
757                 }
758                 _ => panic!(),
759             }
760         }
761 
762         for (register, instruction_prefix) in test_inputs {
763             helper(register, instruction_prefix)
764         }
765     }
766 }
767