xref: /cloud-hypervisor/hypervisor/src/mshv/x86_64/emulator.rs (revision 19d36c765fdf00be749d95b3e61028bc302d6d73)
1 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2 //
3 // Copyright © 2024, Microsoft Corporation
4 //
5 
6 use iced_x86::Register;
7 use mshv_bindings::*;
8 
9 use crate::arch::emulator::{PlatformEmulator, PlatformError};
10 use crate::arch::x86::emulator::{CpuStateManager, EmulatorCpuState};
11 use crate::cpu::Vcpu;
12 use crate::mshv::MshvVcpu;
13 
14 pub struct MshvEmulatorContext<'a> {
15     pub vcpu: &'a MshvVcpu,
16     pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
17 }
18 
19 impl<'a> MshvEmulatorContext<'a> {
20     // Do the actual gva -> gpa translation
21     #[allow(non_upper_case_globals)]
22     fn translate(&self, gva: u64, flags: u32) -> Result<u64, PlatformError> {
23         if self.map.0 == gva {
24             return Ok(self.map.1);
25         }
26 
27         let (gpa, result_code) = self
28             .vcpu
29             .translate_gva(gva, flags.into())
30             .map_err(|e| PlatformError::TranslateVirtualAddress(anyhow!(e)))?;
31 
32         match result_code {
33             hv_translate_gva_result_code_HV_TRANSLATE_GVA_SUCCESS => Ok(gpa),
34             _ => Err(PlatformError::TranslateVirtualAddress(anyhow!(result_code))),
35         }
36     }
37 
38     fn read_memory_flags(
39         &self,
40         gva: u64,
41         data: &mut [u8],
42         flags: u32,
43     ) -> Result<(), PlatformError> {
44         let gpa = self.translate(gva, flags)?;
45         debug!(
46             "mshv emulator: memory read {} bytes from [{:#x} -> {:#x}]",
47             data.len(),
48             gva,
49             gpa
50         );
51 
52         if let Some(vm_ops) = &self.vcpu.vm_ops {
53             if vm_ops.guest_mem_read(gpa, data).is_err() {
54                 vm_ops
55                     .mmio_read(gpa, data)
56                     .map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
57             }
58         }
59 
60         Ok(())
61     }
62 }
63 
64 /// Platform emulation for Hyper-V
65 impl<'a> PlatformEmulator for MshvEmulatorContext<'a> {
66     type CpuState = EmulatorCpuState;
67 
68     fn read_memory(&self, gva: u64, data: &mut [u8]) -> Result<(), PlatformError> {
69         self.read_memory_flags(gva, data, HV_TRANSLATE_GVA_VALIDATE_READ)
70     }
71 
72     fn write_memory(&mut self, gva: u64, data: &[u8]) -> Result<(), PlatformError> {
73         let gpa = self.translate(gva, HV_TRANSLATE_GVA_VALIDATE_WRITE)?;
74         debug!(
75             "mshv emulator: memory write {} bytes at [{:#x} -> {:#x}]",
76             data.len(),
77             gva,
78             gpa
79         );
80 
81         if let Some(vm_ops) = &self.vcpu.vm_ops {
82             if vm_ops.guest_mem_write(gpa, data).is_err() {
83                 vm_ops
84                     .mmio_write(gpa, data)
85                     .map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
86             }
87         }
88 
89         Ok(())
90     }
91 
92     fn cpu_state(&self, cpu_id: usize) -> Result<Self::CpuState, PlatformError> {
93         if cpu_id != self.vcpu.vp_index as usize {
94             return Err(PlatformError::GetCpuStateFailure(anyhow!(
95                 "CPU id mismatch {:?} {:?}",
96                 cpu_id,
97                 self.vcpu.vp_index
98             )));
99         }
100 
101         let regs = self
102             .vcpu
103             .get_regs()
104             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
105         let sregs = self
106             .vcpu
107             .get_sregs()
108             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
109 
110         debug!("mshv emulator: Getting new CPU state");
111         debug!("mshv emulator: {:#x?}", regs);
112 
113         Ok(EmulatorCpuState { regs, sregs })
114     }
115 
116     fn set_cpu_state(&self, cpu_id: usize, state: Self::CpuState) -> Result<(), PlatformError> {
117         if cpu_id != self.vcpu.vp_index as usize {
118             return Err(PlatformError::SetCpuStateFailure(anyhow!(
119                 "CPU id mismatch {:?} {:?}",
120                 cpu_id,
121                 self.vcpu.vp_index
122             )));
123         }
124 
125         debug!("mshv emulator: Setting new CPU state");
126         debug!("mshv emulator: {:#x?}", state.regs);
127 
128         self.vcpu
129             .set_regs(&state.regs)
130             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
131         self.vcpu
132             .set_sregs(&state.sregs)
133             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))
134     }
135 
136     fn fetch(&self, ip: u64, instruction_bytes: &mut [u8]) -> Result<(), PlatformError> {
137         let rip =
138             self.cpu_state(self.vcpu.vp_index as usize)?
139                 .linearize(Register::CS, ip, false)?;
140         self.read_memory_flags(
141             rip,
142             instruction_bytes,
143             HV_TRANSLATE_GVA_VALIDATE_READ | HV_TRANSLATE_GVA_VALIDATE_EXECUTE,
144         )
145     }
146 }
147