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