Skip to main content

aarch64emu/
emulator.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! Implements an arm64 instruction emulator.
5
6use crate::Cpu;
7use crate::opcodes::Aarch64DecodeGroup;
8use crate::opcodes::Aarch64DecodeLoadStoreGroup;
9use crate::opcodes::LoadRegisterLiteral;
10use crate::opcodes::LoadStoreAtomic;
11use crate::opcodes::LoadStoreRegister;
12use crate::opcodes::LoadStoreRegisterPair;
13use crate::opcodes::decode_group;
14use aarch64defs::EsrEl2;
15use inspect::Inspect;
16use thiserror::Error;
17
18#[derive(Debug, Error)]
19pub enum Error<E> {
20    #[error("unknown instruction: {0:#x?}")]
21    UnsupportedInstruction(u32),
22    #[error("unsupported instruction group: {0:?} {1:#x?}")]
23    UnsupportedInstructionGroup(Aarch64DecodeGroup, u32),
24    #[error("unsupported load/store instruction: {0:?} {1:#x?}")]
25    UnsupportedLoadStoreInstruction(Aarch64DecodeLoadStoreGroup, u32),
26    #[error("unsupported instruction set (thumb)")]
27    UnsupportedInstructionSet,
28    #[error("memory access error - {1:?} @ {0:#x}")]
29    MemoryAccess(u64, OperationKind, #[source] E),
30}
31
32#[derive(Debug, Default, Inspect)]
33pub struct InterceptState {
34    pub instruction_bytes: [u8; 4],
35    pub instruction_byte_count: u8,
36    pub gpa: Option<u64>,
37    #[inspect(hex, with = "|&x| u64::from(x)")]
38    pub syndrome: EsrEl2,
39    pub interruption_pending: bool,
40}
41
42enum InternalError<E> {
43    /// Report an error to the caller.
44    Error(Box<Error<E>>),
45}
46
47impl<E> From<Error<E>> for InternalError<E> {
48    fn from(err: Error<E>) -> Self {
49        InternalError::Error(Box::new(err))
50    }
51}
52
53impl<E> From<Box<Error<E>>> for InternalError<E> {
54    fn from(err: Box<Error<E>>) -> Self {
55        InternalError::Error(err)
56    }
57}
58
59#[derive(Debug)]
60pub(crate) struct EmulatorOperations<T: Cpu> {
61    pub cpu: T,
62}
63
64impl<T: Cpu> EmulatorOperations<T> {
65    /// Reads an instruction to execute from the given guest VA.
66    pub async fn read_instruction(
67        &mut self,
68        gva: u64,
69        data: &mut [u8],
70    ) -> Result<(), Box<Error<T::Error>>> {
71        self.cpu
72            .read_instruction(gva, data)
73            .await
74            .map_err(|err| Error::MemoryAccess(gva, OperationKind::Read, err))?;
75        Ok(())
76    }
77
78    /// Reads memory from the given guest VA.
79    pub async fn read_memory(
80        &mut self,
81        gva: u64,
82        data: &mut [u8],
83    ) -> Result<(), Box<Error<T::Error>>> {
84        self.cpu
85            .read_memory(gva, data)
86            .await
87            .map_err(|err| Error::MemoryAccess(gva, OperationKind::Read, err))?;
88        Ok(())
89    }
90
91    /// Reads memory from the given guest PA.
92    pub async fn read_physical_memory(
93        &mut self,
94        gpa: u64,
95        data: &mut [u8],
96        exec: bool,
97    ) -> Result<(), Box<Error<T::Error>>> {
98        self.cpu
99            .read_physical_memory(gpa, data, exec)
100            .await
101            .map_err(|err| Error::MemoryAccess(gpa, OperationKind::Read, err))?;
102        Ok(())
103    }
104
105    /// Writes memory to the given guest VA.
106    pub async fn write_memory(
107        &mut self,
108        gva: u64,
109        data: &[u8],
110    ) -> Result<(), Box<Error<T::Error>>> {
111        self.cpu
112            .write_memory(gva, data)
113            .await
114            .map_err(|err| Error::MemoryAccess(gva, OperationKind::Write, err))?;
115        Ok(())
116    }
117
118    /// Writes memory to the given guest PA.
119    pub async fn write_physical_memory(
120        &mut self,
121        gpa: u64,
122        data: &[u8],
123    ) -> Result<(), Box<Error<T::Error>>> {
124        self.cpu
125            .write_physical_memory(gpa, data)
126            .await
127            .map_err(|err| Error::MemoryAccess(gpa, OperationKind::Write, err))?;
128        Ok(())
129    }
130
131    /// Writes memory to the given guest VA if the current value matches.
132    pub async fn compare_and_write_memory(
133        &mut self,
134        gva: u64,
135        current: &[u8],
136        new: &[u8],
137    ) -> Result<bool, Box<Error<T::Error>>> {
138        let mut success = false;
139        self.cpu
140            .compare_and_write_memory(gva, current, new, &mut success)
141            .await
142            .map_err(|err| Error::MemoryAccess(gva, OperationKind::Write, err))?;
143        Ok(success)
144    }
145}
146
147/// An instruction emulator.
148#[derive(Debug)]
149pub struct Emulator<'a, T: Cpu> {
150    inner: EmulatorOperations<T>,
151    intercept_state: &'a InterceptState,
152}
153
154#[derive(Debug, Clone, Copy, PartialEq)]
155pub enum OperationKind {
156    Read,
157    Write,
158    AddressComputation,
159}
160
161impl<'a, T: Cpu> Emulator<'a, T> {
162    /// Creates new emulator with the given CPU and initial state.
163    pub fn new(cpu: T, intercept_state: &'a InterceptState) -> Self {
164        Emulator {
165            inner: EmulatorOperations { cpu },
166            intercept_state,
167        }
168    }
169
170    fn advance_pc(&mut self, count: u64) {
171        let new_pc = self.inner.cpu.pc().wrapping_add(count);
172        self.inner.cpu.update_pc(new_pc);
173    }
174
175    async fn decode_with_syndrome(&mut self) -> Result<bool, InternalError<T::Error>> {
176        let Some(gpa) = self.intercept_state.gpa else {
177            return Ok(false);
178        };
179        let syndrome = self.intercept_state.syndrome;
180        if !matches!(
181            aarch64defs::ExceptionClass(syndrome.ec()),
182            aarch64defs::ExceptionClass::DATA_ABORT | aarch64defs::ExceptionClass::DATA_ABORT_LOWER
183        ) {
184            return Ok(false);
185        }
186
187        let iss: u32 = (syndrome.lower_iss() as u32)
188            | ((syndrome.wnr() as u32) << 6)
189            | ((syndrome.mid_iss() as u32) << 7)
190            | ((syndrome.b_srt() as u32) << 16)
191            | ((syndrome.a() as u32) << 21)
192            | ((syndrome.b() as u32) << 22)
193            | ((syndrome.c() as u32) << 23)
194            | ((syndrome.d() as u32) << 24);
195        let iss = aarch64defs::IssDataAbort::from(iss);
196        if !iss.isv() {
197            return Ok(false);
198        }
199        let len = 1 << iss.sas();
200        let sign_extend = iss.sse();
201
202        // Per "AArch64 System Register Descriptions/D23.2 General system control registers"
203        // the SRT field is defined as
204        //
205        // > The register number of the Wt/Xt/Rt operand of the faulting
206        // > instruction.
207        //
208        // In the A64 ISA TRM, Wt/Xt/Rt is used to designate the register number where the SP
209        // register is not used whereas the addition of `|SP` tells that the SP register might
210        // be used. Hence, the SRT field uses `0b11111` to encode `xzr`.
211        //
212        // Writing to `xzr` has no arch-observable effects, reading returns the all-zero's bit
213        // pattern.
214        let reg_index = iss.srt();
215        if iss.wnr() {
216            let data = match reg_index {
217                0..=30 => self.inner.cpu.x(reg_index),
218                31 => 0_u64,
219                _ => unreachable!(),
220            }
221            .to_ne_bytes();
222            self.inner.write_physical_memory(gpa, &data[..len]).await?;
223        } else if reg_index != 31 {
224            let mut data = [0; 8];
225            // tracing::info!(gpa, len = data.len(), "reading memory from syndrome decode");
226            self.inner
227                .read_physical_memory(gpa, &mut data[..len], false)
228                .await?;
229            let mut data = u64::from_ne_bytes(data);
230            if sign_extend {
231                let shift = 64 - len * 8;
232                data = ((data as i64) << shift >> shift) as u64;
233                if !iss.sf() {
234                    data &= 0xffffffff;
235                }
236            }
237            self.inner.cpu.update_x(reg_index, data);
238        }
239        self.advance_pc(if syndrome.il() { 4 } else { 2 });
240        Ok(true)
241    }
242
243    pub async fn run(&mut self) -> Result<(), Box<Error<T::Error>>> {
244        match self.decode_with_syndrome().await {
245            Ok(false) => (),
246            Ok(true) => return Ok(()),
247            Err(InternalError::Error(err)) => {
248                tracing::error!(%err, "Error decoding access via syndrome");
249            }
250        };
251
252        // If the intercept message did not include the instruction bytes, fetch them now.
253        let instruction = if self.intercept_state.instruction_byte_count > 0 {
254            if self.intercept_state.instruction_byte_count != 4 {
255                return Err(Box::new(Error::UnsupportedInstructionSet));
256            }
257            u32::from_ne_bytes(self.intercept_state.instruction_bytes)
258        } else {
259            let mut bytes = [0_u8; 4];
260            let pc = self.inner.cpu.pc();
261            self.inner.read_instruction(pc, &mut bytes[..]).await?;
262            u32::from_ne_bytes(bytes)
263        };
264        let instruction_type = decode_group(instruction)?;
265        match self.emulate(instruction, instruction_type).await {
266            Ok(()) => {
267                self.advance_pc(4);
268                Ok(())
269            }
270            Err(InternalError::Error(err)) => Err(err),
271        }
272    }
273
274    // DEVNOTE: The error type is boxed as a codesize optimization. See the comment on
275    //          `run()` above for more information.
276    /// Emulates the effects of an instruction.
277    async fn emulate(
278        &mut self,
279        opcode: u32,
280        instruction_type: Aarch64DecodeGroup,
281    ) -> Result<(), InternalError<T::Error>> {
282        // We should not be emulating instructions that don't touch MMIO or PIO, even though we are capable of doing so.
283        // If we are asked to do so it is usually indicative of some other problem, so abort so we can track that down.
284        let result = match instruction_type {
285            Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::UnscaledImmediate)
286            | Aarch64DecodeGroup::LoadStore(
287                Aarch64DecodeLoadStoreGroup::RegisterUnscaledImmediate,
288            )
289            | Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterUnprivileged)
290            | Aarch64DecodeGroup::LoadStore(
291                Aarch64DecodeLoadStoreGroup::RegisterImmediatePostIndex,
292            )
293            | Aarch64DecodeGroup::LoadStore(
294                Aarch64DecodeLoadStoreGroup::RegisterImmediatePreIndex,
295            )
296            | Aarch64DecodeGroup::LoadStore(
297                Aarch64DecodeLoadStoreGroup::RegisterUnsignedImmediate,
298            )
299            | Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterOffset) => {
300                LoadStoreRegister(opcode).emulate(&mut self.inner).await
301            }
302            Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterLiteral) => {
303                LoadRegisterLiteral(opcode).emulate(&mut self.inner).await
304            }
305            Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::NoAllocatePair)
306            | Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterPairPostIndex)
307            | Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterPairOffset)
308            | Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::RegisterPairPreIndex) => {
309                LoadStoreRegisterPair(opcode).emulate(&mut self.inner).await
310            }
311            Aarch64DecodeGroup::LoadStore(Aarch64DecodeLoadStoreGroup::Atomic) => {
312                LoadStoreAtomic(opcode).emulate(&mut self.inner).await
313            }
314            Aarch64DecodeGroup::LoadStore(typ) => {
315                return Err(InternalError::Error(Box::new(
316                    Error::UnsupportedLoadStoreInstruction(typ, opcode),
317                )));
318            }
319            group => {
320                return Err(InternalError::Error(Box::new(
321                    Error::UnsupportedInstructionGroup(group, opcode),
322                )));
323            }
324        };
325        result.map_err(InternalError::Error)
326    }
327}