1use crate::translate::TranslationRegisters;
7use aarch64defs::EsrEl2;
8use aarch64defs::FaultStatusCode;
9use aarch64defs::IssInstructionAbort;
10use aarch64emu::AccessCpuState;
11use aarch64emu::InterceptState;
12use cvm_tracing::CVM_ALLOWED;
13use cvm_tracing::CVM_CONFIDENTIAL;
14use guestmem::GuestMemory;
15use guestmem::GuestMemoryError;
16use hvdef::HV_PAGE_SIZE;
17use hvdef::HvAarch64PendingEvent;
18use hvdef::HvAarch64PendingEventType;
19use hvdef::HvInterceptAccessType;
20use hvdef::HvMapGpaFlags;
21use thiserror::Error;
22use virt::EmulatorMonitorSupport;
23use virt::VpHaltReason;
24use virt::io::CpuIo;
25use vm_topology::processor::VpIndex;
26use zerocopy::FromBytes;
27use zerocopy::IntoBytes;
28
29pub trait EmulatorSupport: AccessCpuState {
31 fn vp_index(&self) -> VpIndex;
33
34 fn physical_address(&self) -> Option<u64>;
36
37 fn initial_gva_translation(&mut self) -> Option<InitialTranslation>;
39
40 fn interruption_pending(&self) -> bool;
42
43 fn check_vtl_access(
46 &mut self,
47 gpa: u64,
48 mode: TranslateMode,
49 ) -> Result<(), EmuCheckVtlAccessError>;
50
51 fn translate_gva(
53 &mut self,
54 gva: u64,
55 mode: TranslateMode,
56 ) -> Result<EmuTranslateResult, EmuTranslateError>;
57
58 fn inject_pending_event(&mut self, event_info: HvAarch64PendingEvent);
60
61 fn monitor_support(&self) -> Option<&dyn EmulatorMonitorSupport> {
63 None
64 }
65
66 fn is_gpa_mapped(&self, gpa: u64, write: bool) -> bool;
74}
75
76pub trait TranslateGvaSupport {
77 type Error;
78
79 fn guest_memory(&self) -> &GuestMemory;
81
82 fn acquire_tlb_lock(&mut self);
84
85 fn registers(&mut self) -> Result<TranslationRegisters, Self::Error>;
87}
88
89pub struct EmuTranslateResult {
91 pub gpa: u64,
93 pub overlay_page: Option<bool>,
96}
97
98#[derive(Debug)]
100pub struct InitialTranslation {
101 pub gva: u64,
103 pub gpa: u64,
105 pub translate_mode: TranslateMode,
107}
108
109#[derive(Error, Debug)]
110pub enum EmuCheckVtlAccessError {
111 #[error("failed vtl permissions access for vtl {vtl:?} and access flags {denied_flags:?}")]
112 AccessDenied {
113 vtl: hvdef::Vtl,
114 denied_flags: HvMapGpaFlags,
115 },
116}
117
118#[derive(Error, Debug)]
119#[error("translate gva to gpa returned non-successful code {code:?}")]
120pub struct EmuTranslateError {
122 pub code: hvdef::hypercall::TranslateGvaResultCode,
125 pub event_info: Option<EsrEl2>,
127}
128
129#[derive(Debug, Copy, Clone, PartialEq, Eq)]
131pub enum TranslateMode {
132 Read,
134 Write,
136 Execute,
138}
139
140#[derive(Debug)]
142pub struct UnsupportedInterceptAccessType;
143
144impl TryFrom<HvInterceptAccessType> for TranslateMode {
145 type Error = UnsupportedInterceptAccessType;
146
147 fn try_from(access_type: HvInterceptAccessType) -> Result<Self, Self::Error> {
148 match access_type {
149 HvInterceptAccessType::READ => Ok(TranslateMode::Read),
150 HvInterceptAccessType::WRITE => Ok(TranslateMode::Write),
151 HvInterceptAccessType::EXECUTE => Ok(TranslateMode::Execute),
152 _ => Err(UnsupportedInterceptAccessType),
153 }
154 }
155}
156
157#[derive(Debug, Error)]
158enum EmulationError {
159 #[error("an interrupt caused the memory access exit")]
160 InterruptionPending,
161 #[error("emulator error (instruction {bytes:02x?})")]
162 Emulator {
163 bytes: Vec<u8>,
164 #[source]
165 error: aarch64emu::Error<Error>,
166 },
167}
168
169pub async fn emulate<T: EmulatorSupport>(
171 support: &mut T,
172 intercept_state: &InterceptState,
173 emu_mem: &GuestMemory,
174 dev: &impl CpuIo,
175) -> Result<(), VpHaltReason> {
176 emulate_core(support, intercept_state, emu_mem, dev)
177 .await
178 .map_err(|e| {
179 let pc = support.pc();
180 let sp = support.sp();
181 let cpsr = support.cpsr();
182 let gpa = support.physical_address();
183 let initial_translation = support.initial_gva_translation();
184 let int_pend = support.interruption_pending();
185 let gpa_mapped = gpa.map(|a| support.is_gpa_mapped(a, false));
186 tracing::warn!(
187 CVM_ALLOWED,
188 pc,
189 sp,
190 ?cpsr,
191 gpa,
192 ?initial_translation,
193 int_pend,
194 gpa_mapped,
195 "emulation failed"
196 );
197 let xs = (0..=30).map(|i| (i, support.x(i))).collect::<Vec<_>>();
198 tracing::warn!(CVM_CONFIDENTIAL, ?xs, "emulation failed");
199 dev.fatal_error(e.into())
200 })
201}
202
203async fn emulate_core<T: EmulatorSupport>(
204 support: &mut T,
205 intercept_state: &InterceptState,
206 gm: &GuestMemory,
207 dev: &impl CpuIo,
208) -> Result<(), EmulationError> {
209 tracing::trace!(physical_address = support.physical_address(), "emulating");
210
211 if support.interruption_pending() {
212 return Err(EmulationError::InterruptionPending);
226 }
227
228 let mut cpu = EmulatorCpu::new(gm, dev, support, intercept_state.syndrome);
229 let pc = cpu.pc();
230 let result = {
231 let mut emu = aarch64emu::Emulator::new(&mut cpu, intercept_state);
232 emu.run().await
233 };
234
235 let instruction_bytes = if intercept_state.instruction_byte_count > 0 {
236 intercept_state.instruction_bytes.to_vec()
237 } else {
238 vec![0, 0, 0, 0]
239 };
240 cpu.commit();
241
242 if let Err(e) = result {
243 match *e {
244 aarch64emu::Error::MemoryAccess(addr, kind, err) => {
245 if inject_memory_access_fault(addr, &err, support, intercept_state.syndrome) {
246 return Ok(());
247 } else {
248 return Err(EmulationError::Emulator {
249 bytes: instruction_bytes,
250 error: aarch64emu::Error::MemoryAccess(addr, kind, err),
251 });
252 };
253 }
254 err => {
255 tracing::error!(
256 err = &err as &dyn std::error::Error,
257 len = instruction_bytes.len(),
258 physical_address = cpu.support.physical_address(),
259 "failed to emulate instruction"
260 );
261 let syndrome: EsrEl2 = IssInstructionAbort::new().into();
262 cpu.support
263 .inject_pending_event(make_exception_event(syndrome, pc));
264 }
265 }
266 }
267
268 Ok(())
269}
270
271struct GvaGpaCacheEntry {
273 gva_page: u64,
274 gpa_page: u64,
275 translate_mode: TranslateMode,
276}
277
278impl GvaGpaCacheEntry {
279 pub fn new(gva: u64, gpa: u64, translate_mode: TranslateMode) -> Self {
280 GvaGpaCacheEntry {
281 gva_page: gva >> hvdef::HV_PAGE_SHIFT,
282 gpa_page: gpa >> hvdef::HV_PAGE_SHIFT,
283 translate_mode,
284 }
285 }
286}
287
288struct EmulatorCpu<'a, T, U> {
289 gm: &'a GuestMemory,
290 support: &'a mut T,
291 dev: &'a U,
292 cached_translation: Option<GvaGpaCacheEntry>,
293 syndrome: EsrEl2,
294}
295
296#[derive(Debug, Error)]
297enum Error {
298 #[error("translation error")]
299 Translate(#[source] TranslateGvaError, Option<EsrEl2>),
300 #[error("vtl permissions denied access for gpa {gpa}")]
301 NoVtlAccess {
302 gpa: u64,
303 intercepting_vtl: hvdef::Vtl,
304 denied_flags: HvMapGpaFlags,
305 },
306 #[error("failed to access mapped memory")]
307 Memory(#[source] GuestMemoryError),
308}
309
310#[derive(Error, Debug)]
312enum TranslateGvaError {
313 #[error("gpa access denied code {0:?}")]
314 AccessDenied(hvdef::hypercall::TranslateGvaResultCode),
315 #[error("write on overlay page")]
316 OverlayPageWrite,
317 #[error("translation failed with unknown code {0:?}")]
318 UnknownCode(hvdef::hypercall::TranslateGvaResultCode),
319 #[error("translation failed with an intercept code")]
320 Intercept,
321 #[error("translation failed with a page fault-related code {0:?}")]
322 PageFault(hvdef::hypercall::TranslateGvaResultCode),
323}
324
325impl<T: EmulatorSupport, U> EmulatorCpu<'_, T, U> {
326 pub fn new<'a>(
327 gm: &'a GuestMemory,
328 dev: &'a U,
329 support: &'a mut T,
330 syndrome: EsrEl2,
331 ) -> EmulatorCpu<'a, T, U> {
332 let init_cache = {
333 if let Some(InitialTranslation {
334 gva,
335 gpa,
336 translate_mode,
337 }) = support.initial_gva_translation()
338 {
339 tracing::trace!(
340 ?gva,
341 ?gpa,
342 ?translate_mode,
343 "adding initial translation to cache"
344 );
345 Some(GvaGpaCacheEntry::new(gva, gpa, translate_mode))
346 } else {
347 None
348 }
349 };
350
351 EmulatorCpu {
352 gm,
353 dev,
354 support,
355 cached_translation: init_cache,
356 syndrome,
357 }
358 }
359
360 pub fn translate_gva(&mut self, gva: u64, mode: TranslateMode) -> Result<u64, Error> {
361 type TranslateCode = hvdef::hypercall::TranslateGvaResultCode;
362
363 if let Some(GvaGpaCacheEntry {
370 gva_page: cached_gva_page,
371 gpa_page: cached_gpa_page,
372 translate_mode: cached_mode,
373 }) = self.cached_translation
374 {
375 if ((gva >> hvdef::HV_PAGE_SHIFT) == cached_gva_page) && (cached_mode == mode) {
376 tracing::trace!(
377 ?gva,
378 ?cached_gva_page,
379 cached_gpa_page,
380 ?cached_mode,
381 "using cached entry"
382 );
383 return Ok((cached_gpa_page << hvdef::HV_PAGE_SHIFT) + (gva & (HV_PAGE_SIZE - 1)));
384 }
385 };
386
387 match self.support.translate_gva(gva, mode) {
388 Ok(EmuTranslateResult { gpa, overlay_page }) => {
389 if overlay_page.is_some()
390 && overlay_page
391 .expect("should've already checked that the overlay page has value")
392 && (mode == TranslateMode::Write)
393 {
394 let mut syndrome: EsrEl2 = crate::translate::Error::GpaUnmapped(3).into();
396 syndrome.set_il(self.syndrome.il());
397 return Err(Error::Translate(TranslateGvaError::OverlayPageWrite, None));
398 }
399
400 let new_cache_entry = GvaGpaCacheEntry::new(gva, gpa, mode);
401
402 self.cached_translation = Some(new_cache_entry);
403 Ok(gpa)
404 }
405 Err(EmuTranslateError { code, event_info }) => match code {
406 TranslateCode::INTERCEPT => {
407 tracing::trace!("translate gva to gpa returned an intercept event");
408 Err(Error::Translate(TranslateGvaError::Intercept, event_info))
409 }
410 TranslateCode::GPA_NO_READ_ACCESS
411 | TranslateCode::GPA_NO_WRITE_ACCESS
412 | TranslateCode::GPA_UNMAPPED
413 | TranslateCode::GPA_ILLEGAL_OVERLAY_ACCESS
414 | TranslateCode::GPA_UNACCEPTED => {
415 tracing::trace!("translate gva to gpa returned no access to page {:?}", code);
416 Err(Error::Translate(
417 TranslateGvaError::AccessDenied(code),
418 event_info,
419 ))
420 }
421 TranslateCode::PAGE_NOT_PRESENT
422 | TranslateCode::PRIVILEGE_VIOLATION
423 | TranslateCode::INVALID_PAGE_TABLE_FLAGS => {
424 tracing::trace!(gva, ?code, "translate gva to gpa returned");
425 Err(Error::Translate(
426 TranslateGvaError::PageFault(code),
427 event_info,
428 ))
429 }
430 TranslateCode::SUCCESS => unreachable!(),
431 _ => {
432 tracing::trace!(
433 "translate error: unknown translation result code {:?}",
434 code
435 );
436
437 Err(Error::Translate(TranslateGvaError::UnknownCode(code), None))
438 }
439 },
440 }
441 }
442
443 pub fn check_vtl_access(&mut self, gpa: u64, mode: TranslateMode) -> Result<(), Error> {
444 self.support
445 .check_vtl_access(gpa, mode)
446 .map_err(|e| match e {
447 EmuCheckVtlAccessError::AccessDenied { vtl, denied_flags } => Error::NoVtlAccess {
448 gpa,
449 intercepting_vtl: vtl,
450 denied_flags,
451 },
452 })
453 }
454
455 fn check_monitor_write(&self, gpa: u64, bytes: &[u8]) -> bool {
456 if let Some(monitor_support) = self.support.monitor_support() {
457 monitor_support.check_write(gpa, bytes)
458 } else {
459 false
460 }
461 }
462
463 fn check_monitor_read(&self, gpa: u64, bytes: &mut [u8]) -> bool {
464 if let Some(monitor_support) = self.support.monitor_support() {
465 monitor_support.check_read(gpa, bytes)
466 } else {
467 false
468 }
469 }
470}
471
472impl<T: EmulatorSupport, U: CpuIo> aarch64emu::Cpu for EmulatorCpu<'_, T, U> {
473 type Error = Error;
474
475 async fn read_instruction(&mut self, gva: u64, bytes: &mut [u8]) -> Result<(), Self::Error> {
476 let gpa = match self.translate_gva(gva, TranslateMode::Execute) {
477 Ok(g) => g,
478 Err(e) => return Err(e),
479 };
480 self.read_physical_memory(gpa, bytes, true).await
481 }
482
483 async fn read_memory(&mut self, gva: u64, bytes: &mut [u8]) -> Result<(), Self::Error> {
484 let gpa = match self.translate_gva(gva, TranslateMode::Read) {
485 Ok(g) => g,
486 Err(e) => return Err(e),
487 };
488 self.read_physical_memory(gpa, bytes, false).await
489 }
490
491 async fn read_physical_memory(
492 &mut self,
493 gpa: u64,
494 bytes: &mut [u8],
495 exec: bool,
496 ) -> Result<(), Self::Error> {
497 self.check_vtl_access(
498 gpa,
499 if exec {
500 TranslateMode::Execute
501 } else {
502 TranslateMode::Read
503 },
504 )?;
505
506 if self.check_monitor_read(gpa, bytes) {
507 Ok(())
508 } else if self.support.is_gpa_mapped(gpa, false) {
509 self.gm.read_at(gpa, bytes).map_err(Self::Error::Memory)
510 } else {
511 self.dev
512 .read_mmio(self.support.vp_index(), gpa, bytes)
513 .await;
514 Ok(())
515 }
516 }
517
518 async fn write_memory(&mut self, gva: u64, bytes: &[u8]) -> Result<(), Self::Error> {
519 let gpa = match self.translate_gva(gva, TranslateMode::Write) {
520 Ok(g) => g,
521 Err(e) => return Err(e),
522 };
523 self.write_physical_memory(gpa, bytes).await
524 }
525
526 async fn write_physical_memory(&mut self, gpa: u64, bytes: &[u8]) -> Result<(), Self::Error> {
527 self.check_vtl_access(gpa, TranslateMode::Write)?;
528
529 if self.support.is_gpa_mapped(gpa, true) {
530 self.gm.write_at(gpa, bytes).map_err(Self::Error::Memory)?;
531 } else {
532 self.dev
533 .write_mmio(self.support.vp_index(), gpa, bytes)
534 .await;
535 }
536 Ok(())
537 }
538
539 async fn compare_and_write_memory(
540 &mut self,
541 gva: u64,
542 current: &[u8],
543 new: &[u8],
544 success: &mut bool,
545 ) -> Result<(), Self::Error> {
546 let gpa = match self.translate_gva(gva, TranslateMode::Write) {
547 Ok(g) => g,
548 Err(e) => return Err(e),
549 };
550
551 self.check_vtl_access(gpa, TranslateMode::Write)?;
552
553 if self.check_monitor_write(gpa, new) {
554 *success = true;
555 Ok(())
556 } else if self.support.is_gpa_mapped(gpa, true) {
557 *success = match (current.len(), new.len()) {
558 (1, 1) => self
559 .gm
560 .compare_exchange(gpa, current[0], new[0])
561 .map(|r| r.is_ok()),
562 (2, 2) => self
563 .gm
564 .compare_exchange(
565 gpa,
566 u16::from_ne_bytes(current.try_into().unwrap()),
567 u16::from_ne_bytes(new.try_into().unwrap()),
568 )
569 .map(|r| r.is_ok()),
570 (4, 4) => self
571 .gm
572 .compare_exchange(
573 gpa,
574 u32::from_ne_bytes(current.try_into().unwrap()),
575 u32::from_ne_bytes(new.try_into().unwrap()),
576 )
577 .map(|r| r.is_ok()),
578 (8, 8) => self
579 .gm
580 .compare_exchange(
581 gpa,
582 u64::from_ne_bytes(current.try_into().unwrap()),
583 u64::from_ne_bytes(new.try_into().unwrap()),
584 )
585 .map(|r| r.is_ok()),
586 _ => panic!("unsupported size for compare and write memory"),
587 }
588 .map_err(Self::Error::Memory)?;
589 Ok(())
590 } else {
591 *success = true;
593 self.dev.write_mmio(self.support.vp_index(), gpa, new).await;
594 Ok(())
595 }
596 }
597}
598
599impl<T: AccessCpuState, U: CpuIo> AccessCpuState for EmulatorCpu<'_, T, U> {
600 fn commit(&mut self) {
601 self.support.commit()
602 }
603 fn x(&mut self, index: u8) -> u64 {
604 self.support.x(index)
605 }
606 fn update_x(&mut self, index: u8, data: u64) {
607 self.support.update_x(index, data)
608 }
609 fn q(&self, index: u8) -> u128 {
610 self.support.q(index)
611 }
612 fn update_q(&mut self, index: u8, data: u128) {
613 self.support.update_q(index, data)
614 }
615 fn d(&self, index: u8) -> u64 {
616 self.support.d(index)
617 }
618 fn update_d(&mut self, index: u8, data: u64) {
619 self.support.update_d(index, data)
620 }
621 fn h(&self, index: u8) -> u32 {
622 self.support.h(index)
623 }
624 fn update_h(&mut self, index: u8, data: u32) {
625 self.support.update_h(index, data)
626 }
627 fn s(&self, index: u8) -> u16 {
628 self.support.s(index)
629 }
630 fn update_s(&mut self, index: u8, data: u16) {
631 self.support.update_s(index, data)
632 }
633 fn b(&self, index: u8) -> u8 {
634 self.support.b(index)
635 }
636 fn update_b(&mut self, index: u8, data: u8) {
637 self.support.update_b(index, data)
638 }
639 fn sp(&mut self) -> u64 {
640 self.support.sp()
641 }
642 fn update_sp(&mut self, data: u64) {
643 self.support.update_sp(data)
644 }
645 fn fp(&mut self) -> u64 {
646 self.support.fp()
647 }
648 fn update_fp(&mut self, data: u64) {
649 self.support.update_fp(data)
650 }
651 fn lr(&mut self) -> u64 {
652 self.support.lr()
653 }
654 fn update_lr(&mut self, data: u64) {
655 self.support.update_lr(data)
656 }
657 fn pc(&mut self) -> u64 {
658 self.support.pc()
659 }
660 fn update_pc(&mut self, data: u64) {
661 self.support.update_pc(data)
662 }
663 fn cpsr(&mut self) -> aarch64defs::Cpsr64 {
664 self.support.cpsr()
665 }
666}
667
668pub fn make_exception_event(syndrome: EsrEl2, fault_address: u64) -> HvAarch64PendingEvent {
670 let exception_event = hvdef::HvAarch64PendingExceptionEvent {
671 header: hvdef::HvAarch64PendingEventHeader::new()
672 .with_event_pending(true)
673 .with_event_type(HvAarch64PendingEventType::EXCEPTION),
674 syndrome: syndrome.into(),
675 fault_address,
676 _padding: Default::default(),
677 };
678 let exception_event_bytes = exception_event.as_bytes();
679 let mut event = [0u8; 32];
680 event.as_mut_slice()[..exception_event_bytes.len()].copy_from_slice(exception_event_bytes);
681 HvAarch64PendingEvent::read_from_bytes(&event[..]).unwrap()
682}
683
684#[must_use]
690fn inject_memory_access_fault<T: EmulatorSupport>(
691 gva: u64,
692 result: &Error,
693 support: &mut T,
694 syndrome: EsrEl2,
695) -> bool {
696 match result {
697 Error::Translate(e, event) => {
698 tracing::trace!(
699 error = e as &dyn std::error::Error,
700 "translation failed, injecting event"
701 );
702
703 if let Some(event_info) = event {
704 support.inject_pending_event(make_exception_event(*event_info, gva));
705
706 return true;
708 }
709 false
710 }
711 Error::NoVtlAccess {
712 gpa,
713 intercepting_vtl: _,
714 denied_flags,
715 } => {
716 tracing::trace!(
717 error = result as &dyn std::error::Error,
718 ?gva,
719 ?gpa,
720 "Vtl permissions checking failed"
721 );
722
723 let event = vtl_access_event(gva, *denied_flags, &syndrome);
724 support.inject_pending_event(event);
725 true
726 }
727 Error::Memory(_) => false,
728 }
729}
730
731fn vtl_access_event(
734 gva: u64,
735 denied_access: HvMapGpaFlags,
736 cur_syndrome: &EsrEl2,
737) -> HvAarch64PendingEvent {
738 assert!(denied_access.kernel_executable() || denied_access.user_executable());
739 let inst_abort = IssInstructionAbort::new().with_ifsc(FaultStatusCode::PERMISSION_FAULT_LEVEL2);
740 let mut syndrome: EsrEl2 = inst_abort.into();
741 syndrome.set_il(cur_syndrome.il());
742 make_exception_event(syndrome, gva)
743}
744
745pub fn emulate_mnf_write_fast_path<T: EmulatorSupport>(
753 opcode: u32,
754 support: &mut T,
755 gm: &GuestMemory,
756 dev: &impl CpuIo,
757) -> Option<u64> {
758 if support.interruption_pending() {
759 return None;
760 }
761
762 if (opcode & 0x38203c00) == 0x38203000 {
765 let mut cpu = EmulatorCpu::new(gm, dev, support, EsrEl2::from_bits(0));
766 let size = (1 << (opcode >> 30)) * 8;
767 let rs = (opcode >> 16) as u8 & 0x1f;
768 let bitmask = if rs < 31 { cpu.x(rs) } else { 0 };
769 let bitmask = if size == 64 {
770 bitmask
771 } else {
772 bitmask & ((1 << size) - 1)
773 };
774 let rt = opcode as u8 & 0x1f;
775 if rt != 31 {
776 cpu.update_x(rt, 0);
777 }
778
779 let new_pc = cpu.pc().wrapping_add(4);
780 cpu.update_pc(new_pc);
781 cpu.commit();
782 Some(bitmask)
783 } else {
784 tracelimit::warn_ratelimited!(
785 opcode = format!("{:x}", opcode),
786 "MNF fast path unknown opcode"
787 );
788 None
789 }
790}