xref: /cloud-hypervisor/hypervisor/src/mshv/x86_64/emulator.rs (revision 108e8f9dff2392f05f57e668c2a0ab4557f1d354)
1b73d94f9SJinank Jain // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2b73d94f9SJinank Jain //
3b73d94f9SJinank Jain // Copyright © 2024, Microsoft Corporation
4b73d94f9SJinank Jain //
5b73d94f9SJinank Jain 
688a9f799SRob Bradford use iced_x86::Register;
788a9f799SRob Bradford use mshv_bindings::*;
888a9f799SRob Bradford 
9b73d94f9SJinank Jain use crate::arch::emulator::{PlatformEmulator, PlatformError};
10b73d94f9SJinank Jain use crate::arch::x86::emulator::{CpuStateManager, EmulatorCpuState};
11b73d94f9SJinank Jain use crate::cpu::Vcpu;
12b73d94f9SJinank Jain use crate::mshv::MshvVcpu;
13b73d94f9SJinank Jain 
14b73d94f9SJinank Jain pub struct MshvEmulatorContext<'a> {
15b73d94f9SJinank Jain     pub vcpu: &'a MshvVcpu,
16b73d94f9SJinank Jain     pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
17b73d94f9SJinank Jain }
18b73d94f9SJinank Jain 
19166a005bSRob Bradford impl MshvEmulatorContext<'_> {
20b73d94f9SJinank Jain     // Do the actual gva -> gpa translation
21b73d94f9SJinank Jain     #[allow(non_upper_case_globals)]
translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError>22b73d94f9SJinank Jain     fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
23b73d94f9SJinank Jain         if self.map.0 == gva {
24b73d94f9SJinank Jain             return Ok(self.map.1);
25b73d94f9SJinank Jain         }
26b73d94f9SJinank Jain 
27b73d94f9SJinank Jain         let (gpa, result_code) = self
28b73d94f9SJinank Jain             .vcpu
29b73d94f9SJinank Jain             .translate_gva(gva, flags.into())
30b73d94f9SJinank Jain             .map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
31b73d94f9SJinank Jain 
32b73d94f9SJinank Jain         match result_code {
33b73d94f9SJinank Jain             hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
34b73d94f9SJinank Jain             _ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
35b73d94f9SJinank Jain         }
36b73d94f9SJinank Jain     }
37b73d94f9SJinank Jain 
r(&self, gva: u64, data: &mut [u8], flags: u32) -> Result<(), PlatformError>38*108e8f9dSWei Liu     fn r(&self, gva: u64, data: &mut [u8], flags: u32) -> Result<(), PlatformError> {
39b73d94f9SJinank Jain         let gpa = self.translate(gva, flags)?;
40b73d94f9SJinank Jain         debug!(
41b73d94f9SJinank Jain             "mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
42b73d94f9SJinank Jain             data.len(),
43b73d94f9SJinank Jain             gva,
44b73d94f9SJinank Jain             gpa
45b73d94f9SJinank Jain         );
46b73d94f9SJinank Jain 
47b73d94f9SJinank Jain         if let Some(vm_ops) = &self.vcpu.vm_ops {
48b73d94f9SJinank Jain             if vm_ops.guest_mem_read(gpa, data).is_err() {
49b73d94f9SJinank Jain                 vm_ops
50b73d94f9SJinank Jain                     .mmio_read(gpa, data)
51b73d94f9SJinank Jain                     .map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
52b73d94f9SJinank Jain             }
53b73d94f9SJinank Jain         }
54b73d94f9SJinank Jain 
55b73d94f9SJinank Jain         Ok(())
56b73d94f9SJinank Jain     }
57*108e8f9dSWei Liu 
read_memory_flags( &self, gva: u64, data: &mut [u8], flags: u32, ) -> Result<(), PlatformError>58*108e8f9dSWei Liu     fn read_memory_flags(
59*108e8f9dSWei Liu         &self,
60*108e8f9dSWei Liu         gva: u64,
61*108e8f9dSWei Liu         data: &mut [u8],
62*108e8f9dSWei Liu         flags: u32,
63*108e8f9dSWei Liu     ) -> Result<(), PlatformError> {
64*108e8f9dSWei Liu         let mut len = data.len() as u64;
65*108e8f9dSWei Liu 
66*108e8f9dSWei Liu         // Compare the page number of the first and last byte. If they are different, this is a
67*108e8f9dSWei Liu         // cross-page access.
68*108e8f9dSWei Liu         let pg1 = gva >> HV_HYP_PAGE_SHIFT;
69*108e8f9dSWei Liu         let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
70*108e8f9dSWei Liu         let cross_page = pg1 != pg2;
71*108e8f9dSWei Liu 
72*108e8f9dSWei Liu         if cross_page {
73*108e8f9dSWei Liu             // We only handle one page cross-page access
74*108e8f9dSWei Liu             assert!(pg1 + 1 == pg2);
75*108e8f9dSWei Liu             let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
76*108e8f9dSWei Liu             len -= n;
77b73d94f9SJinank Jain         }
78b73d94f9SJinank Jain 
79*108e8f9dSWei Liu         self.r(gva, &mut data[..len as usize], flags)?;
80b73d94f9SJinank Jain 
81*108e8f9dSWei Liu         if cross_page {
82*108e8f9dSWei Liu             self.r(gva + len, &mut data[len as usize..], flags)?;
83b73d94f9SJinank Jain         }
84b73d94f9SJinank Jain 
85*108e8f9dSWei Liu         Ok(())
86*108e8f9dSWei Liu     }
87*108e8f9dSWei Liu 
w(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError>88*108e8f9dSWei Liu     fn w(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
89b73d94f9SJinank Jain         let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
90b73d94f9SJinank Jain         debug!(
91b73d94f9SJinank Jain             "mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
92b73d94f9SJinank Jain             data.len(),
93b73d94f9SJinank Jain             gva,
94b73d94f9SJinank Jain             gpa
95b73d94f9SJinank Jain         );
96b73d94f9SJinank Jain 
97b73d94f9SJinank Jain         if let Some(vm_ops) = &self.vcpu.vm_ops {
98b73d94f9SJinank Jain             if vm_ops.guest_mem_write(gpa, data).is_err() {
99b73d94f9SJinank Jain                 vm_ops
100b73d94f9SJinank Jain                     .mmio_write(gpa, data)
101b73d94f9SJinank Jain                     .map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
102b73d94f9SJinank Jain             }
103b73d94f9SJinank Jain         }
104b73d94f9SJinank Jain 
105b73d94f9SJinank Jain         Ok(())
106b73d94f9SJinank Jain     }
107*108e8f9dSWei Liu }
108*108e8f9dSWei Liu 
109*108e8f9dSWei Liu /// Platform emulation for Hyper-V
110*108e8f9dSWei Liu impl PlatformEmulator for MshvEmulatorContext<'_> {
111*108e8f9dSWei Liu     type CpuState = EmulatorCpuState;
112*108e8f9dSWei Liu 
read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError>113*108e8f9dSWei Liu     fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
114*108e8f9dSWei Liu         self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
115*108e8f9dSWei Liu     }
116*108e8f9dSWei Liu 
write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError>117*108e8f9dSWei Liu     fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
118*108e8f9dSWei Liu         let mut len = data.len() as u64;
119*108e8f9dSWei Liu 
120*108e8f9dSWei Liu         // Compare the page number of the first and last byte. If they are different, this is a
121*108e8f9dSWei Liu         // cross-page access.
122*108e8f9dSWei Liu         let pg1 = gva >> HV_HYP_PAGE_SHIFT;
123*108e8f9dSWei Liu         let pg2 = (gva + len - 1) >> HV_HYP_PAGE_SHIFT;
124*108e8f9dSWei Liu         let cross_page = pg1 != pg2;
125*108e8f9dSWei Liu 
126*108e8f9dSWei Liu         if cross_page {
127*108e8f9dSWei Liu             // We only handle one page cross-page access
128*108e8f9dSWei Liu             assert!(pg1 + 1 == pg2);
129*108e8f9dSWei Liu             let n = (gva + len) & HV_HYP_PAGE_MASK as u64;
130*108e8f9dSWei Liu             len -= n;
131*108e8f9dSWei Liu         }
132*108e8f9dSWei Liu 
133*108e8f9dSWei Liu         self.w(gva, &data[..len as usize])?;
134*108e8f9dSWei Liu 
135*108e8f9dSWei Liu         if cross_page {
136*108e8f9dSWei Liu             self.w(gva + len, &data[len as usize..])?;
137*108e8f9dSWei Liu         }
138*108e8f9dSWei Liu 
139*108e8f9dSWei Liu         Ok(())
140*108e8f9dSWei Liu     }
141b73d94f9SJinank Jain 
cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError>142b73d94f9SJinank Jain     fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
143b73d94f9SJinank Jain         if cpu_id != self.vcpu.vp_index as usize {
144b73d94f9SJinank Jain             return Err(PlatformError::GetCpuStateFailure(anyhow!(
145b73d94f9SJinank Jain                 "CPU id mismatch {:?} {:?}",
146b73d94f9SJinank Jain                 cpu_id,
147b73d94f9SJinank Jain                 self.vcpu.vp_index
148b73d94f9SJinank Jain             )));
149b73d94f9SJinank Jain         }
150b73d94f9SJinank Jain 
151b73d94f9SJinank Jain         let regs = self
152b73d94f9SJinank Jain             .vcpu
153b73d94f9SJinank Jain             .get_regs()
154b73d94f9SJinank Jain             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
155b73d94f9SJinank Jain         let sregs = self
156b73d94f9SJinank Jain             .vcpu
157b73d94f9SJinank Jain             .get_sregs()
158b73d94f9SJinank Jain             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
159b73d94f9SJinank Jain 
160b73d94f9SJinank Jain         debug!("mshv emulator: Getting new CPU state");
161b73d94f9SJinank Jain         debug!("mshv emulator: {:#x?}", regs);
162b73d94f9SJinank Jain 
163b73d94f9SJinank Jain         Ok(EmulatorCpuState { regs, sregs })
164b73d94f9SJinank Jain     }
165b73d94f9SJinank Jain 
set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError>166b73d94f9SJinank Jain     fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
167b73d94f9SJinank Jain         if cpu_id != self.vcpu.vp_index as usize {
168b73d94f9SJinank Jain             return Err(PlatformError::SetCpuStateFailure(anyhow!(
169b73d94f9SJinank Jain                 "CPU id mismatch {:?} {:?}",
170b73d94f9SJinank Jain                 cpu_id,
171b73d94f9SJinank Jain                 self.vcpu.vp_index
172b73d94f9SJinank Jain             )));
173b73d94f9SJinank Jain         }
174b73d94f9SJinank Jain 
175b73d94f9SJinank Jain         debug!("mshv emulator: Setting new CPU state");
176b73d94f9SJinank Jain         debug!("mshv emulator: {:#x?}", state.regs);
177b73d94f9SJinank Jain 
178b73d94f9SJinank Jain         self.vcpu
179b73d94f9SJinank Jain             .set_regs(&state.regs)
180b73d94f9SJinank Jain             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
181b73d94f9SJinank Jain         self.vcpu
182b73d94f9SJinank Jain             .set_sregs(&state.sregs)
183b73d94f9SJinank Jain             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
184b73d94f9SJinank Jain     }
185b73d94f9SJinank Jain 
fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError>186b73d94f9SJinank Jain     fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
187b73d94f9SJinank Jain         let rip =
188b73d94f9SJinank Jain             self.cpu_state(self.vcpu.vp_index as usize)?
189b73d94f9SJinank Jain                 .linearize(Register::CS, ip, false)?;
190b73d94f9SJinank Jain         self.read_memory_flags(
191b73d94f9SJinank Jain             rip,
192b73d94f9SJinank Jain             instruction_bytes,
193b73d94f9SJinank Jain             HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
194b73d94f9SJinank Jain         )
195b73d94f9SJinank Jain     }
196b73d94f9SJinank Jain }
197