@@ -555,6 +555,12 @@ impl VirtualMachine for WhpVm {
555555 let whp_regs: [ ( WHV_REGISTER_NAME , Align16 < WHV_REGISTER_VALUE > ) ; WHP_SREGS_NAMES_LEN ] =
556556 sregs. into ( ) ;
557557
558+ // TODO: Remove this filter and instead fix the callers to pass
559+ // the correct APIC_BASE value (e.g. read it from the VP before
560+ // the first set_sregs, or use 0xFEE00900 in standard_64bit_defaults).
561+ // Filtering here causes AMD/SVM bugs because SVM_CLEAN_FIELD_AVIC
562+ // is never dirtied after WHvResetPartition (see reset_partition).
563+ //
558564 // When LAPIC emulation is active (always with hw-interrupts),
559565 // skip writing APIC_BASE. The generic CommonSpecialRegisters
560566 // defaults APIC_BASE to 0 which would globally disable the LAPIC.
@@ -730,6 +736,23 @@ impl VirtualMachine for WhpVm {
730736 #[ cfg( feature = "hw-interrupts" ) ]
731737 Self :: init_lapic_bulk ( self . partition )
732738 . map_err ( |e| RegisterError :: ResetPartition ( e. into ( ) ) ) ?;
739+
740+ // With hw-interrupts, set_sregs filters out APIC_BASE to
741+ // avoid disabling the LAPIC (the snapshot sregs default it
742+ // to 0). On AMD/SVM this means SVM_CLEAN_FIELD_AVIC is
743+ // never dirtied after the VMCB rebuild, causing the CPU to
744+ // use stale cached AVIC state on VMRUN which corrupts guest
745+ // memory reads. Re-writing APIC_BASE to its post-reset
746+ // value forces the dirty bit via ValSetEmulatedApicBase.
747+ #[ cfg( feature = "hw-interrupts" ) ]
748+ {
749+ let apic_base = self . sregs ( ) ?. apic_base ;
750+ self . set_registers ( & [ (
751+ WHvX64RegisterApicBase ,
752+ Align16 ( WHV_REGISTER_VALUE { Reg64 : apic_base } ) ,
753+ ) ] )
754+ . map_err ( |e| RegisterError :: ResetPartition ( e. into ( ) ) ) ?;
755+ }
733756 }
734757 Ok ( ( ) )
735758 }
0 commit comments