• Marc Zyngier's avatar
    KVM: arm/arm64: Sync ICH_VMCR_EL2 back when about to block · 5eeaf10e
    Marc Zyngier authored
    Since commit commit 328e5664 ("KVM: arm/arm64: vgic: Defer
    touching GICH_VMCR to vcpu_load/put"), we leave ICH_VMCR_EL2 (or
    its GICv2 equivalent) loaded as long as we can, only syncing it
    back when we're scheduled out.
    
    There is a small snag with that though: kvm_vgic_vcpu_pending_irq(),
    which is indirectly called from kvm_vcpu_check_block(), needs to
    evaluate the guest's view of ICC_PMR_EL1. At the point were we
    call kvm_vcpu_check_block(), the vcpu is still loaded, and whatever
    changes to PMR is not visible in memory until we do a vcpu_put().
    
    Things go really south if the guest does the following:
    
    	mov x0, #0	// or any small value masking interrupts
    	msr ICC_PMR_EL1, x0
    
    	[vcpu preempted, then rescheduled, VMCR sampled]
    
    	mov x0, #ff	// allow all interrupts
    	msr ICC_PMR_EL1, x0
    	wfi		// traps to EL2, so samping of VMCR
    
    	[interrupt arrives just after WFI]
    
    Here, the hypervisor's view of PMR is zero, while the guest has enabled
    its interrupts. kvm_vgic_vcpu_pending_irq() will then say that no
    interrupts are pending (despite an interrupt being received) and we'll
    block for no reason. If the guest doesn't have a periodic interrupt
    firing once it has blocked, it will stay there forever.
    
    To avoid this unfortuante situation, let's resync VMCR from
    kvm_arch_vcpu_blocking(), ensuring that a following kvm_vcpu_check_block()
    will observe the latest value of PMR.
    
    This has been found by booting an arm64 Linux guest with the pseudo NMI
    feature, and thus using interrupt priorities to mask interrupts instead
    of the usual PSTATE masking.
    
    Cc: stable@vger.kernel.org # 4.12
    Fixes: 328e5664 ("KVM: arm/arm64: vgic: Defer touching GICH_VMCR to vcpu_load/put")
    Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
    5eeaf10e
arm.c 37.4 KB