@@ -23,7 +23,6 @@ use crate::UhPartitionInner;
23
23
use crate :: UhPartitionNewParams ;
24
24
use crate :: UhProcessor ;
25
25
use crate :: WakeReason ;
26
- use guestmem:: GuestMemory ;
27
26
use hcl:: ioctl:: ProcessorRunner ;
28
27
use hcl:: ioctl:: tdx:: Tdx ;
29
28
use hcl:: ioctl:: tdx:: TdxPrivateRegs ;
@@ -69,14 +68,13 @@ use virt::x86::TableRegister;
69
68
use virt_support_apic:: ApicClient ;
70
69
use virt_support_apic:: OffloadNotSupported ;
71
70
use virt_support_x86emu:: emulate:: EmulatedMemoryOperation ;
72
- use virt_support_x86emu:: emulate:: EmulationPolicy ;
73
71
use virt_support_x86emu:: emulate:: EmulatorSupport as X86EmulatorSupport ;
74
- use virt_support_x86emu:: emulate:: ProbeResult ;
75
72
use virt_support_x86emu:: emulate:: TranslateMode ;
76
73
use virt_support_x86emu:: emulate:: emulate_insn_memory_op;
77
74
use virt_support_x86emu:: emulate:: emulate_io;
78
75
use virt_support_x86emu:: emulate:: emulate_translate_gva;
79
76
use virt_support_x86emu:: translate:: TranslationRegisters ;
77
+ use vm_topology:: memory:: AddressType ;
80
78
use vmcore:: vmtime:: VmTimeAccess ;
81
79
use x86defs:: RFlags ;
82
80
use x86defs:: X64_CR0_ET ;
@@ -1770,7 +1768,6 @@ impl UhProcessor<'_, TdxBacked> {
1770
1768
. valid ( ) ,
1771
1769
intercepted_vtl,
1772
1770
TdxEmulationCache :: default ( ) ,
1773
- EmulationPolicy :: EmulateAll ,
1774
1771
)
1775
1772
. await ?;
1776
1773
} else {
@@ -2001,6 +1998,7 @@ impl UhProcessor<'_, TdxBacked> {
2001
1998
& mut self . backing . vtls [ intercepted_vtl] . exit_stats . wbinvd
2002
1999
}
2003
2000
VmxExitBasic :: EPT_VIOLATION => {
2001
+ let gpa = exit_info. gpa ( ) ;
2004
2002
let ept_info = VmxEptExitQualification :: from ( exit_info. qualification ( ) ) ;
2005
2003
// If this was an EPT violation while handling an iret, and
2006
2004
// that iret cleared the NMI blocking state, restore it.
@@ -2018,16 +2016,7 @@ impl UhProcessor<'_, TdxBacked> {
2018
2016
. into ( ) ;
2019
2017
assert ! ( !old_interruptibility. blocked_by_nmi( ) ) ;
2020
2018
} else {
2021
- self . emulate (
2022
- dev,
2023
- self . backing . vtls [ intercepted_vtl]
2024
- . interruption_information
2025
- . valid ( ) ,
2026
- intercepted_vtl,
2027
- TdxEmulationCache :: default ( ) ,
2028
- EmulationPolicy :: ProbeGpa ( & self . partition . lower_vtl_memory_layout ) ,
2029
- )
2030
- . await ?;
2019
+ self . handle_ept ( intercepted_vtl, dev, gpa, ept_info) . await ?;
2031
2020
}
2032
2021
2033
2022
& mut self . backing . vtls [ intercepted_vtl] . exit_stats . ept_violation
@@ -2372,6 +2361,94 @@ impl UhProcessor<'_, TdxBacked> {
2372
2361
self . backing . vtls [ vtl] . exception_error_code = 0 ;
2373
2362
}
2374
2363
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
+
2375
2452
fn handle_tdvmcall ( & mut self , dev : & impl CpuIo , intercepted_vtl : GuestVtl ) {
2376
2453
let regs = self . runner . tdx_enter_guest_gps ( ) ;
2377
2454
if regs[ TdxGp :: R10 ] == 0 {
@@ -2778,63 +2855,6 @@ impl<T: CpuIo> X86EmulatorSupport for UhEmulationState<'_, '_, T, TdxBacked> {
2778
2855
Some ( TdxExit ( self . vp . runner . tdx_vp_enter_exit_info ( ) ) . gpa ( ) )
2779
2856
}
2780
2857
2781
- fn vtom ( & self ) -> Option < u64 > {
2782
- self . vp . partition . caps . vtom
2783
- }
2784
-
2785
- fn probe_ram ( & self , gpa : u64 , gm : & GuestMemory ) -> ProbeResult {
2786
- let vtom = self . vtom ( ) . unwrap_or ( 0 ) ;
2787
- let is_shared = ( gpa & vtom) == vtom && vtom != 0 ;
2788
-
2789
- // TODO TDX: This path changes when we support VTL page
2790
- // protections and MNF. That will require injecting events to
2791
- // VTL1 or other handling.
2792
- //
2793
- // For now, we just check if the exit was suprious or if we
2794
- // should inject a machine check. An exit is considered spurious
2795
- // if the gpa is accessible.
2796
- if gm. check_gpa_readable ( gpa) {
2797
- tracelimit:: warn_ratelimited!(
2798
- is_shared,
2799
- gpa,
2800
- "possible spurious EPT violation, ignoring"
2801
- ) ;
2802
- ProbeResult :: Ignore
2803
- } else {
2804
- // TODO: It would be better to show what exact bitmap check
2805
- // failed, but that requires some refactoring of how the
2806
- // different bitmaps are stored. Do this when we support VTL
2807
- // protections or MNF.
2808
- //
2809
- // If we entered this path, it means the bitmap check on
2810
- // `check_gpa_readable` failed so we can assume that if the
2811
- // address is shared, the actual state of the page is
2812
- // private, and vice versa. This is because the address
2813
- // should have already been checked to be valid memory
2814
- // described to the guest or not.
2815
- tracelimit:: warn_ratelimited!(
2816
- gpa,
2817
- is_shared,
2818
- "guest accessed inaccessible gpa, injecting MC"
2819
- ) ;
2820
- ProbeResult :: InjectMachineCheck
2821
- }
2822
- }
2823
-
2824
- fn probe_none ( & self , gpa : u64 ) -> ProbeResult {
2825
- let vtom = self . vtom ( ) . unwrap_or ( 0 ) ;
2826
- let is_shared = ( gpa & vtom) == vtom && vtom != 0 ;
2827
-
2828
- // The guest should never attempt to access address that are not
2829
- // described in mmio or ram. Inject a machine check.
2830
- tracelimit:: warn_ratelimited!(
2831
- gpa,
2832
- is_shared,
2833
- "guest accessed gpa not described in memory layout, injecting MC"
2834
- ) ;
2835
- ProbeResult :: InjectMachineCheck
2836
- }
2837
-
2838
2858
fn initial_gva_translation ( & self ) -> Option < virt_support_x86emu:: emulate:: InitialTranslation > {
2839
2859
let exit_info = TdxExit ( self . vp . runner . tdx_vp_enter_exit_info ( ) ) ;
2840
2860
let ept_info = VmxEptExitQualification :: from ( exit_info. qualification ( ) ) ;
0 commit comments