xref: /cloud-hypervisor/hypervisor/src/mshv/aarch64/emulator.rs (revision d374101f386baec6e451697a95cf53182ef2a0e6)
1461e31e6SJinank Jain // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2461e31e6SJinank Jain //
3461e31e6SJinank Jain // Copyright © 2025, Microsoft Corporation
4461e31e6SJinank Jain //
5461e31e6SJinank Jain 
6461e31e6SJinank Jain use crate::arch::aarch64::regs::{EsrEl2, ExceptionClass, IssDataAbort};
7461e31e6SJinank Jain use crate::arch::emulator::PlatformError;
8461e31e6SJinank Jain use crate::cpu::Vcpu;
9461e31e6SJinank Jain use crate::mshv::MshvVcpu;
10461e31e6SJinank Jain 
11461e31e6SJinank Jain pub struct MshvEmulatorContext<'a> {
12461e31e6SJinank Jain     pub vcpu: &'a MshvVcpu,
13461e31e6SJinank Jain     pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
14461e31e6SJinank Jain     pub syndrome: u64,
15461e31e6SJinank Jain     pub instruction_bytes: [u8; 4],
16461e31e6SJinank Jain     pub instruction_byte_count: u8,
17461e31e6SJinank Jain     pub interruption_pending: bool,
18461e31e6SJinank Jain     pub pc: u64,
19461e31e6SJinank Jain }
20461e31e6SJinank Jain 
21461e31e6SJinank Jain pub struct Emulator<'a> {
22461e31e6SJinank Jain     pub context: MshvEmulatorContext<'a>,
23461e31e6SJinank Jain }
24461e31e6SJinank Jain 
25461e31e6SJinank Jain impl<'a> Emulator<'a> {
26461e31e6SJinank Jain     /// Create a new emulator instance.
new(context: MshvEmulatorContext<'a>) -> Self27461e31e6SJinank Jain     pub fn new(context: MshvEmulatorContext<'a>) -> Self {
28461e31e6SJinank Jain         Emulator { context }
29461e31e6SJinank Jain     }
30461e31e6SJinank Jain 
31*d374101fSJinank Jain     /// Decode & emulate the instruction using the syndrome register.
emulate_with_syndrome(&mut self) -> Result<bool, PlatformError>32461e31e6SJinank Jain     pub fn emulate_with_syndrome(&mut self) -> Result<bool, PlatformError> {
33461e31e6SJinank Jain         let esr_el2 = EsrEl2::from(self.context.syndrome);
34461e31e6SJinank Jain         if !matches!(
35461e31e6SJinank Jain             ExceptionClass(esr_el2.ec()),
36461e31e6SJinank Jain             ExceptionClass::DATA_ABORT | ExceptionClass::DATA_ABORT_LOWER
37461e31e6SJinank Jain         ) {
38461e31e6SJinank Jain             return Ok(false);
39461e31e6SJinank Jain         }
40461e31e6SJinank Jain 
41461e31e6SJinank Jain         let iss = IssDataAbort::from(esr_el2.iss());
42461e31e6SJinank Jain         if !iss.isv() {
43461e31e6SJinank Jain             return Ok(false);
44461e31e6SJinank Jain         }
45461e31e6SJinank Jain         let len = 1 << iss.sas();
46461e31e6SJinank Jain         let sign_extend = iss.sse();
47461e31e6SJinank Jain         let reg_index = iss.srt();
48461e31e6SJinank Jain 
49461e31e6SJinank Jain         let mut regs = self
50461e31e6SJinank Jain             .context
51461e31e6SJinank Jain             .vcpu
52461e31e6SJinank Jain             .get_regs()
53461e31e6SJinank Jain             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
54461e31e6SJinank Jain         let mut gprs = regs.get_regs();
55461e31e6SJinank Jain 
56461e31e6SJinank Jain         if iss.wnr() {
57461e31e6SJinank Jain             let data: [u8; 8] = match reg_index {
58461e31e6SJinank Jain                 0..=30 => gprs[reg_index as usize],
59461e31e6SJinank Jain                 31 => 0u64,
60461e31e6SJinank Jain                 _ => unreachable!(),
61461e31e6SJinank Jain             }
62461e31e6SJinank Jain             .to_ne_bytes();
63461e31e6SJinank Jain 
64461e31e6SJinank Jain             if let Some(vm_ops) = &self.context.vcpu.vm_ops {
65461e31e6SJinank Jain                 vm_ops
66461e31e6SJinank Jain                     .mmio_write(self.context.map.1, &data[0..len])
67461e31e6SJinank Jain                     .map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
68461e31e6SJinank Jain             }
69461e31e6SJinank Jain         } else {
70461e31e6SJinank Jain             let mut data = [0_u8; 8];
71461e31e6SJinank Jain             if let Some(vm_ops) = &self.context.vcpu.vm_ops {
72461e31e6SJinank Jain                 vm_ops
73461e31e6SJinank Jain                     .mmio_read(self.context.map.1, &mut data[0..len])
74461e31e6SJinank Jain                     .map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
75461e31e6SJinank Jain             }
76461e31e6SJinank Jain 
77461e31e6SJinank Jain             let mut data = u64::from_ne_bytes(data);
78461e31e6SJinank Jain             if sign_extend {
79461e31e6SJinank Jain                 let shift = 64 - len * 8;
80461e31e6SJinank Jain                 data = ((data as i64) << shift >> shift) as u64;
81461e31e6SJinank Jain                 if !iss.sf() {
82461e31e6SJinank Jain                     data &= 0xffffffff;
83461e31e6SJinank Jain                 }
84461e31e6SJinank Jain             }
85461e31e6SJinank Jain             gprs[reg_index as usize] = data;
86461e31e6SJinank Jain         }
87461e31e6SJinank Jain 
88461e31e6SJinank Jain         let pc = regs.get_pc();
89461e31e6SJinank Jain         regs.set_pc(if esr_el2.il() { pc + 4 } else { pc + 2 });
90461e31e6SJinank Jain         regs.set_regs(gprs);
91461e31e6SJinank Jain 
92461e31e6SJinank Jain         self.context
93461e31e6SJinank Jain             .vcpu
94461e31e6SJinank Jain             .set_regs(&regs)
95461e31e6SJinank Jain             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
96461e31e6SJinank Jain 
97461e31e6SJinank Jain         Ok(true)
98461e31e6SJinank Jain     }
99461e31e6SJinank Jain 
100461e31e6SJinank Jain     /// Emulate the instruction.
emulate(&mut self) -> Result<(), PlatformError>101461e31e6SJinank Jain     pub fn emulate(&mut self) -> Result<(), PlatformError> {
102461e31e6SJinank Jain         match self.emulate_with_syndrome() {
103461e31e6SJinank Jain             Ok(true) => Ok(()),
104461e31e6SJinank Jain             Ok(false) => Err(PlatformError::InvalidState(anyhow!(
105461e31e6SJinank Jain                 "Failed to decode instruction using syndrome register"
106461e31e6SJinank Jain             ))),
107461e31e6SJinank Jain             Err(e) => Err(e),
108461e31e6SJinank Jain         }
109461e31e6SJinank Jain         // TODO: Add support for instruction decoding in case of failure from
110461e31e6SJinank Jain         // decode_with_syndrome. This will require aarch64 instruction emulator
111461e31e6SJinank Jain         // implementation like x86_64.
112461e31e6SJinank Jain     }
113461e31e6SJinank Jain }
114