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