xref: /cloud-hypervisor/hypervisor/src/mshv/aarch64/emulator.rs (revision 77e042237dcfbbfde2da2b3d2b6b7347c8516e9e)
1 // SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause
2 //
3 // Copyright © 2025, Microsoft Corporation
4 //
5 
6 use crate::arch::aarch64::regs::{EsrEl2, ExceptionClass, IssDataAbort};
7 use crate::arch::emulator::PlatformError;
8 use crate::cpu::Vcpu;
9 use crate::mshv::MshvVcpu;
10 
11 pub struct MshvEmulatorContext<'a> {
12     pub vcpu: &'a MshvVcpu,
13     pub map: (u64, u64), // Initial GVA to GPA mapping provided by the hypervisor
14     pub syndrome: u64,
15     pub instruction_bytes: [u8; 4],
16     pub instruction_byte_count: u8,
17     pub interruption_pending: bool,
18     pub pc: u64,
19 }
20 
21 pub struct Emulator<'a> {
22     pub context: MshvEmulatorContext<'a>,
23 }
24 
25 impl<'a> Emulator<'a> {
26     /// Create a new emulator instance.
27     pub fn new(context: MshvEmulatorContext<'a>) -> Self {
28         Emulator { context }
29     }
30 
31     /// Decode & emulate the instruction using the syndrome register.
32     pub fn emulate_with_syndrome(&mut self) -> Result<bool, PlatformError> {
33         let esr_el2 = EsrEl2::from(self.context.syndrome);
34         if !matches!(
35             ExceptionClass(esr_el2.ec()),
36             ExceptionClass::DATA_ABORT | ExceptionClass::DATA_ABORT_LOWER
37         ) {
38             return Ok(false);
39         }
40 
41         let iss = IssDataAbort::from(esr_el2.iss());
42         if !iss.isv() {
43             return Ok(false);
44         }
45         let len = 1 << iss.sas();
46         let sign_extend = iss.sse();
47         let reg_index = iss.srt();
48 
49         let mut regs = self
50             .context
51             .vcpu
52             .get_regs()
53             .map_err(|e| PlatformError::GetCpuStateFailure(e.into()))?;
54         let mut gprs = regs.get_regs();
55 
56         if iss.wnr() {
57             let data: [u8; 8] = match reg_index {
58                 0..=30 => gprs[reg_index as usize],
59                 31 => 0u64,
60                 _ => unreachable!(),
61             }
62             .to_ne_bytes();
63 
64             if let Some(vm_ops) = &self.context.vcpu.vm_ops {
65                 vm_ops
66                     .mmio_write(self.context.map.1, &data[0..len])
67                     .map_err(|e| PlatformError::MemoryWriteFailure(e.into()))?;
68             }
69         } else {
70             let mut data = [0_u8; 8];
71             if let Some(vm_ops) = &self.context.vcpu.vm_ops {
72                 vm_ops
73                     .mmio_read(self.context.map.1, &mut data[0..len])
74                     .map_err(|e| PlatformError::MemoryReadFailure(e.into()))?;
75             }
76 
77             let mut data = u64::from_ne_bytes(data);
78             if sign_extend {
79                 let shift = 64 - len * 8;
80                 data = ((data as i64) << shift >> shift) as u64;
81                 if !iss.sf() {
82                     data &= 0xffffffff;
83                 }
84             }
85             gprs[reg_index as usize] = data;
86         }
87 
88         let pc = regs.get_pc();
89         regs.set_pc(if esr_el2.il() { pc + 4 } else { pc + 2 });
90         regs.set_regs(gprs);
91 
92         self.context
93             .vcpu
94             .set_regs(&regs)
95             .map_err(|e| PlatformError::SetCpuStateFailure(e.into()))?;
96 
97         Ok(true)
98     }
99 
100     /// Emulate the instruction.
101     pub fn emulate(&mut self) -> Result<(), PlatformError> {
102         match self.emulate_with_syndrome() {
103             Ok(true) => Ok(()),
104             Ok(false) => Err(PlatformError::InvalidState(anyhow!(
105                 "Failed to decode instruction using syndrome register"
106             ))),
107             Err(e) => Err(e),
108         }
109         // TODO: Add support for instruction decoding in case of failure from
110         // decode_with_syndrome. This will require aarch64 instruction emulator
111         // implementation like x86_64.
112     }
113 }
114