xref: /cloud-hypervisor/hypervisor/src/arch/x86/emulator/instructions/stos.rs (revision eeae63b4595fbf0cc69f62b6e9d9a79c543c4ac7)
1 //
2 // Copyright © 2024 Microsoft
3 //
4 // SPDX-License-Identifier: Apache-2.0
5 //
6 
7 #![allow(non_camel_case_types)]
8 
9 //
10 // STOS - Store String
11 //
12 
13 use crate::arch::x86::emulator::instructions::*;
14 use crate::arch::x86::regs::DF;
15 
16 macro_rules! stos {
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 rax = state
33                 .read_reg(Register::RAX)
34                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
35 
36             let mut rdi = state
37                 .read_reg(Register::RDI)
38                 .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
39 
40             let df = (state.flags() & DF) != 0;
41             let len = std::mem::size_of::<$bound>();
42             let rax_bytes = rax.to_le_bytes();
43 
44             while count > 0 {
45                 let dst = state
46                     .linearize(Register::ES, rdi, true)
47                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
48 
49                 platform
50                     .write_memory(dst, &rax_bytes[0..len])
51                     .map_err(EmulationError::PlatformEmulationError)?;
52 
53                 if df {
54                     rdi = rdi.wrapping_sub(len as u64);
55                 } else {
56                     rdi = rdi.wrapping_add(len as u64);
57                 }
58                 count -= 1;
59             }
60 
61             if insn.has_rep_prefix() {
62                 state
63                     .write_reg(Register::ECX, 0)
64                     .map_err(|e| EmulationError::InvalidOperand(anyhow!(e)))?;
65             }
66 
67             Ok(())
68         }
69     };
70 }
71 
72 pub struct Stosq_m64_RAX;
73 impl<T: CpuStateManager> InstructionHandler<T> for Stosq_m64_RAX {
74     stos!(u64);
75 }
76 
77 pub struct Stosd_m32_EAX;
78 impl<T: CpuStateManager> InstructionHandler<T> for Stosd_m32_EAX {
79     stos!(u32);
80 }
81 
82 pub struct Stosw_m16_AX;
83 impl<T: CpuStateManager> InstructionHandler<T> for Stosw_m16_AX {
84     stos!(u16);
85 }
86 
87 pub struct Stosb_m8_AL;
88 impl<T: CpuStateManager> InstructionHandler<T> for Stosb_m8_AL {
89     stos!(u8);
90 }
91 
92 #[cfg(test)]
93 mod tests {
94     use super::*;
95     use crate::arch::x86::emulator::mock_vmm::*;
96 
97     #[test]
98     fn test_rep_stosb() {
99         let ip: u64 = 0x1000;
100         let memory: [u8; 12] = [
101             0x78, 0x56, 0x34, 0x12, // 0x12345678
102             0xdd, 0xcc, 0xbb, 0xaa, // 0xaabbccdd
103             0xa5, 0x5a, 0xa5, 0x5a, // 0x5aa55aa5
104         ];
105         let insn = [0xf3, 0xaa]; // rep stosb
106         let regs = vec![
107             (Register::ECX, 3),
108             (Register::EDI, 0x0),
109             (Register::RAX, 0x123456ff),
110         ];
111         let mut data = [0u8; 4];
112 
113         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
114 
115         vmm.emulate_first_insn(0, &insn).unwrap();
116 
117         vmm.read_memory(0, &mut data).unwrap();
118         assert_eq!(0x12ffffff, <u32>::from_le_bytes(data));
119         vmm.read_memory(4, &mut data).unwrap();
120         assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
121         vmm.read_memory(8, &mut data).unwrap();
122         assert_eq!(0x5aa55aa5, <u32>::from_le_bytes(data));
123     }
124 
125     #[test]
126     fn test_stosw() {
127         let ip: u64 = 0x1000;
128         let memory: [u8; 4] = [
129             0x78, 0x56, 0x34, 0x12, // 0x12345678
130         ];
131         let insn = [0x66, 0xab]; // stosw
132         let regs = vec![(Register::EDI, 0x1), (Register::AX, 0xaabb)];
133         let mut data = [0u8; 4];
134 
135         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
136 
137         vmm.emulate_first_insn(0, &insn).unwrap();
138 
139         vmm.read_memory(0x0, &mut data).unwrap();
140         assert_eq!(0x12aabb78, <u32>::from_le_bytes(data));
141         // The rest should be default value 0 from MockVmm
142         vmm.read_memory(0x4, &mut data).unwrap();
143         assert_eq!(0x0, <u32>::from_le_bytes(data));
144         vmm.read_memory(0x8 + 8, &mut data).unwrap();
145         assert_eq!(0x0, <u32>::from_le_bytes(data));
146     }
147 
148     #[test]
149     fn test_rep_stosw() {
150         let ip: u64 = 0x1000;
151         let memory: [u8; 8] = [
152             0x78, 0x56, 0x34, 0x12, // 0x12345678
153             0x00, 0x00, 0x00, 0x00, // 0x00000000
154         ];
155         let insn = [0x66, 0xf3, 0xab]; // rep stosw
156         let regs = vec![
157             (Register::ECX, 2),
158             (Register::EDI, 0x2),
159             (Register::AX, 0xaabb),
160         ];
161         let mut data = [0u8; 4];
162 
163         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
164 
165         vmm.emulate_first_insn(0, &insn).unwrap();
166 
167         vmm.read_memory(0x0, &mut data).unwrap();
168         assert_eq!(0xaabb5678, <u32>::from_le_bytes(data));
169         vmm.read_memory(0x4, &mut data).unwrap();
170         assert_eq!(0x0000aabb, <u32>::from_le_bytes(data));
171         vmm.read_memory(0x8, &mut data).unwrap();
172         assert_eq!(0x0, <u32>::from_le_bytes(data));
173     }
174 
175     #[test]
176     fn test_rep_stosd() {
177         let ip: u64 = 0x1000;
178         let memory: [u8; 12] = [
179             0x78, 0x56, 0x34, 0x12, // 0x12345678
180             0x00, 0x00, 0x00, 0x00, // 0x00000000
181             0x00, 0x00, 0x00, 0x00, // 0x00000000
182         ];
183         let insn = [0xf3, 0xab]; // rep stosd
184         let regs = vec![
185             (Register::ECX, 2),
186             (Register::EDI, 0x8),
187             (Register::EAX, 0xaabbccdd),
188         ];
189         let mut data = [0u8; 4];
190 
191         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
192 
193         // Go backwards this time
194         let mut state = vmm.cpu_state(0).unwrap();
195         state.set_flags(state.flags() | DF);
196         vmm.set_cpu_state(0, state).unwrap();
197 
198         vmm.emulate_first_insn(0, &insn).unwrap();
199 
200         vmm.read_memory(0x0, &mut data).unwrap();
201         assert_eq!(0x12345678, <u32>::from_le_bytes(data));
202         vmm.read_memory(0x4, &mut data).unwrap();
203         assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
204         vmm.read_memory(0x8, &mut data).unwrap();
205         assert_eq!(0xaabbccdd, <u32>::from_le_bytes(data));
206         vmm.read_memory(0xc, &mut data).unwrap();
207         assert_eq!(0x0, <u32>::from_le_bytes(data));
208     }
209 
210     #[test]
211     fn test_rep_stosq() {
212         let ip: u64 = 0x1000;
213         let memory: [u8; 8] = [
214             0x78, 0x56, 0x34, 0x12, // 0x12345678
215             0x00, 0x00, 0x00, 0x00, // 0x00000000
216         ];
217         let insn = [0xf3, 0x48, 0xab]; // rep stosq
218         let regs = vec![
219             (Register::ECX, 2),
220             (Register::RDI, 0x0),
221             (Register::RAX, 0x11223344aabbccdd),
222         ];
223         let mut data = [0u8; 8];
224 
225         let mut vmm = MockVmm::new(ip, regs, Some((0, &memory)));
226 
227         vmm.emulate_first_insn(0, &insn).unwrap();
228 
229         vmm.read_memory(0x0, &mut data).unwrap();
230         assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
231         vmm.read_memory(0x8, &mut data).unwrap();
232         assert_eq!(0x11223344aabbccdd, <u64>::from_le_bytes(data));
233         vmm.read_memory(0x10, &mut data).unwrap();
234         assert_eq!(0x0, <u64>::from_le_bytes(data));
235     }
236 }
237