@@ -69,6 +69,7 @@ use virt_support_apic::ApicClient;
69
69
use virt_support_apic:: OffloadNotSupported ;
70
70
use virt_support_x86emu:: emulate:: EmulatedMemoryOperation ;
71
71
use virt_support_x86emu:: emulate:: EmulatorSupport as X86EmulatorSupport ;
72
+ use virt_support_x86emu:: emulate:: ProbeResult ;
72
73
use virt_support_x86emu:: emulate:: TranslateMode ;
73
74
use virt_support_x86emu:: emulate:: emulate_insn_memory_op;
74
75
use virt_support_x86emu:: emulate:: emulate_io;
@@ -1998,7 +1999,6 @@ impl UhProcessor<'_, TdxBacked> {
1998
1999
& mut self . backing . vtls [ intercepted_vtl] . exit_stats . wbinvd
1999
2000
}
2000
2001
VmxExitBasic :: EPT_VIOLATION => {
2001
- let gpa = exit_info. gpa ( ) ;
2002
2002
let ept_info = VmxEptExitQualification :: from ( exit_info. qualification ( ) ) ;
2003
2003
// If this was an EPT violation while handling an iret, and
2004
2004
// that iret cleared the NMI blocking state, restore it.
@@ -2016,7 +2016,16 @@ impl UhProcessor<'_, TdxBacked> {
2016
2016
. into ( ) ;
2017
2017
assert ! ( !old_interruptibility. blocked_by_nmi( ) ) ;
2018
2018
} else {
2019
- self . handle_ept ( intercepted_vtl, dev, gpa, ept_info) . await ?;
2019
+ // Emulate the access.
2020
+ self . emulate (
2021
+ dev,
2022
+ self . backing . vtls [ intercepted_vtl]
2023
+ . interruption_information
2024
+ . valid ( ) ,
2025
+ intercepted_vtl,
2026
+ TdxEmulationCache :: default ( ) ,
2027
+ )
2028
+ . await ?;
2020
2029
}
2021
2030
2022
2031
& mut self . backing . vtls [ intercepted_vtl] . exit_stats . ept_violation
@@ -2361,94 +2370,6 @@ impl UhProcessor<'_, TdxBacked> {
2361
2370
self . backing . vtls [ vtl] . exception_error_code = 0 ;
2362
2371
}
2363
2372
2364
- fn inject_mc ( & mut self , vtl : GuestVtl ) {
2365
- self . backing . vtls [ vtl] . interruption_information = InterruptionInformation :: new ( )
2366
- . with_valid ( true )
2367
- . with_vector ( x86defs:: Exception :: MACHINE_CHECK . 0 )
2368
- . with_interruption_type ( INTERRUPT_TYPE_HARDWARE_EXCEPTION ) ;
2369
- }
2370
-
2371
- async fn handle_ept (
2372
- & mut self ,
2373
- intercepted_vtl : GuestVtl ,
2374
- dev : & impl CpuIo ,
2375
- gpa : u64 ,
2376
- ept_info : VmxEptExitQualification ,
2377
- ) -> Result < ( ) , VpHaltReason < UhRunVpError > > {
2378
- // vtom may be 0 if we are hiding isolation
2379
- let vtom = self . partition . caps . vtom . unwrap_or ( 0 ) ;
2380
- let is_shared = ( gpa & vtom) == vtom && vtom != 0 ;
2381
- let canonical_gpa = gpa & !vtom;
2382
-
2383
- // Only emulate the access if the gpa is expected to be accessible. This
2384
- // means, the gpa was described to VTL0 in some form as memory or mmio,
2385
- // and the hardware did not generate an exit for a private/shared
2386
- // violation.
2387
- let address_type = self
2388
- . partition
2389
- . lower_vtl_memory_layout
2390
- . probe_address ( canonical_gpa) ;
2391
-
2392
- match address_type {
2393
- Some ( AddressType :: Mmio ) => {
2394
- // Emulate the access.
2395
- self . emulate (
2396
- dev,
2397
- self . backing . vtls [ intercepted_vtl]
2398
- . interruption_information
2399
- . valid ( ) ,
2400
- intercepted_vtl,
2401
- TdxEmulationCache :: default ( ) ,
2402
- )
2403
- . await ?;
2404
- }
2405
- Some ( AddressType :: Ram ) => {
2406
- // TODO TDX: This path changes when we support VTL page
2407
- // protections and MNF. That will require injecting events to
2408
- // VTL1 or other handling.
2409
- //
2410
- // For now, we just check if the exit was suprious or if we
2411
- // should inject a machine check. An exit is considered spurious
2412
- // if the gpa is accessible.
2413
- if self . partition . gm [ intercepted_vtl] . check_gpa_readable ( gpa) {
2414
- tracelimit:: warn_ratelimited!( gpa, "possible spurious EPT violation, ignoring" ) ;
2415
- } else {
2416
- // TODO: It would be better to show what exact bitmap check
2417
- // failed, but that requires some refactoring of how the
2418
- // different bitmaps are stored. Do this when we support VTL
2419
- // protections or MNF.
2420
- //
2421
- // If we entered this path, it means the bitmap check on
2422
- // `check_gpa_readable` failed so we can assume that if the
2423
- // address is shared, the actual state of the page is
2424
- // private, and vice versa. This is because the address
2425
- // should have already been checked to be valid memory
2426
- // described to the guest or not.
2427
- tracelimit:: warn_ratelimited!(
2428
- gpa,
2429
- is_shared,
2430
- ?ept_info,
2431
- "guest accessed inaccessible gpa, injecting MC"
2432
- ) ;
2433
- self . inject_mc ( intercepted_vtl) ;
2434
- }
2435
- }
2436
- None => {
2437
- // The guest should never attempt to access address that are not
2438
- // described in mmio or ram. Inject a machine check.
2439
- tracelimit:: warn_ratelimited!(
2440
- gpa,
2441
- is_shared,
2442
- ?ept_info,
2443
- "guest accessed gpa not described in memory layout, injecting MC"
2444
- ) ;
2445
- self . inject_mc ( intercepted_vtl) ;
2446
- }
2447
- }
2448
-
2449
- Ok ( ( ) )
2450
- }
2451
-
2452
2373
fn handle_tdvmcall ( & mut self , dev : & impl CpuIo , intercepted_vtl : GuestVtl ) {
2453
2374
let regs = self . runner . tdx_enter_guest_gps ( ) ;
2454
2375
if regs[ TdxGp :: R10 ] == 0 {
@@ -2852,7 +2773,94 @@ impl<T: CpuIo> X86EmulatorSupport for UhEmulationState<'_, '_, T, TdxBacked> {
2852
2773
}
2853
2774
2854
2775
fn physical_address ( & self ) -> Option < u64 > {
2855
- Some ( TdxExit ( self . vp . runner . tdx_vp_enter_exit_info ( ) ) . gpa ( ) )
2776
+ let exit_info = TdxExit ( self . vp . runner . tdx_vp_enter_exit_info ( ) ) ;
2777
+
2778
+ // The gpa field on exit_info is only valid for EPT violations.
2779
+ if exit_info. code ( ) . vmx_exit ( ) . basic_reason ( ) == VmxExitBasic :: EPT_VIOLATION {
2780
+ Some ( exit_info. gpa ( ) )
2781
+ } else {
2782
+ None
2783
+ }
2784
+ }
2785
+
2786
+ fn probe_gpa ( & self , gpa : u64 , gm : & guestmem:: GuestMemory ) -> ProbeResult {
2787
+ let vtom = self . vp . partition . caps . vtom . unwrap_or ( 0 ) ;
2788
+ let hide_isolation = self . vp . cvm_partition ( ) . hide_isolation ;
2789
+ let is_shared = ( gpa & vtom) == vtom && vtom != 0 ;
2790
+ let canonical_gpa = gpa & !vtom;
2791
+
2792
+ // Only emulate the access if the gpa is expected to be accessible. This
2793
+ // means, the gpa was described to VTL0 in some form as memory or mmio,
2794
+ // and the hardware did not generate an exit for a private/shared
2795
+ // violation.
2796
+ let address_type = self
2797
+ . vp
2798
+ . partition
2799
+ . lower_vtl_memory_layout
2800
+ . probe_address ( canonical_gpa) ;
2801
+
2802
+ match address_type {
2803
+ Some ( AddressType :: Mmio ) => {
2804
+ // Emulate the access.
2805
+ ProbeResult :: Emulate
2806
+ }
2807
+ Some ( AddressType :: Ram ) => {
2808
+ // TODO TDX: This path changes when we support VTL page
2809
+ // protections and MNF. That will require injecting events to
2810
+ // VTL1 or other handling.
2811
+ //
2812
+ // For now, we just check if the exit was suprious or if we
2813
+ // should inject a machine check. An exit is considered spurious
2814
+ // if the gpa is accessible.
2815
+ if gm. check_gpa_readable ( gpa) {
2816
+ tracelimit:: warn_ratelimited!( gpa, "possible spurious EPT violation, ignoring" ) ;
2817
+ ProbeResult :: EarlyReturn
2818
+ } else {
2819
+ // TODO: It would be better to show what exact bitmap check
2820
+ // failed, but that requires some refactoring of how the
2821
+ // different bitmaps are stored. Do this when we support VTL
2822
+ // protections or MNF.
2823
+ //
2824
+ // If we entered this path, it means the bitmap check on
2825
+ // `check_gpa_readable` failed so we can assume that if the
2826
+ // address is shared, the actual state of the page is
2827
+ // private, and vice versa. This is because the address
2828
+ // should have already been checked to be valid memory
2829
+ // described to the guest or not.
2830
+ tracelimit:: warn_ratelimited!(
2831
+ gpa,
2832
+ is_shared,
2833
+ "guest accessed inaccessible gpa, injecting MC"
2834
+ ) ;
2835
+ ProbeResult :: InjectMachineCheck
2836
+ }
2837
+ }
2838
+ None => {
2839
+ if hide_isolation {
2840
+ // If isolation is being hidden, the guest is not modifying
2841
+ // visibility that would result in the need to inject
2842
+ // machine checks and instead all RAM should be accessible.
2843
+ // There may be other devices that are being emulated by the
2844
+ // paravisor (such as xapic) that do not exist in memory
2845
+ // layout, so allow all emulation.
2846
+ //
2847
+ // TODO: If we update the memory layout to be more precise
2848
+ // and include all devices, then we could revisit this
2849
+ // check.
2850
+ ProbeResult :: Emulate
2851
+ } else {
2852
+ // A guest which is told about isolation should never
2853
+ // attempt to access address that are not described in mmio
2854
+ // or ram. Inject a machine check.
2855
+ tracelimit:: warn_ratelimited!(
2856
+ gpa,
2857
+ is_shared,
2858
+ "guest accessed gpa not described in memory layout, injecting MC"
2859
+ ) ;
2860
+ ProbeResult :: InjectMachineCheck
2861
+ }
2862
+ }
2863
+ }
2856
2864
}
2857
2865
2858
2866
fn initial_gva_translation ( & self ) -> Option < virt_support_x86emu:: emulate:: InitialTranslation > {
0 commit comments