Commit 94828468 authored by Marc Zyngier's avatar Marc Zyngier

KVM: arm64: vgic-v3: Expose GICR_CTLR.RWP when disabling LPIs

When disabling LPIs, a guest needs to poll GICR_CTLR.RWP in order
to be sure that the write has taken effect. We so far reported it
as 0, as we didn't advertise that LPIs could be turned off the
first place.

Start tracking this state during which LPIs are being disabled,
and expose the 'in progress' state via the RWP bit.

We also take this opportunity to disallow enabling LPIs and programming
GICR_{PEND,PROP}BASER while LPI disabling is in progress, as allowed by
the architecture (UNPRED behaviour).

We don't advertise the feature to the guest yet (which is allowed by
the architecture).
Reviewed-by: default avatarOliver Upton <oupton@google.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220405182327.205520-3-maz@kernel.org
parent 34453c2e
...@@ -683,7 +683,7 @@ int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, ...@@ -683,7 +683,7 @@ int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
if (!vcpu) if (!vcpu)
return E_ITS_INT_UNMAPPED_INTERRUPT; return E_ITS_INT_UNMAPPED_INTERRUPT;
if (!vcpu->arch.vgic_cpu.lpis_enabled) if (!vgic_lpis_enabled(vcpu))
return -EBUSY; return -EBUSY;
vgic_its_cache_translation(kvm, its, devid, eventid, ite->irq); vgic_its_cache_translation(kvm, its, devid, eventid, ite->irq);
......
...@@ -221,6 +221,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu, ...@@ -221,6 +221,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
vgic_put_irq(vcpu->kvm, irq); vgic_put_irq(vcpu->kvm, irq);
} }
bool vgic_lpis_enabled(struct kvm_vcpu *vcpu)
{
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
return atomic_read(&vgic_cpu->ctlr) == GICR_CTLR_ENABLE_LPIS;
}
static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu, static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
gpa_t addr, unsigned int len) gpa_t addr, unsigned int len)
{ {
...@@ -229,26 +236,38 @@ static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu, ...@@ -229,26 +236,38 @@ static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0; return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0;
} }
static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu, static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
gpa_t addr, unsigned int len, gpa_t addr, unsigned int len,
unsigned long val) unsigned long val)
{ {
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
bool was_enabled = vgic_cpu->lpis_enabled; u32 ctlr;
if (!vgic_has_its(vcpu->kvm)) if (!vgic_has_its(vcpu->kvm))
return; return;
vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS; if (!(val & GICR_CTLR_ENABLE_LPIS)) {
/*
* Don't disable if RWP is set, as there already an
* ongoing disable. Funky guest...
*/
ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr,
GICR_CTLR_ENABLE_LPIS,
GICR_CTLR_RWP);
if (ctlr != GICR_CTLR_ENABLE_LPIS)
return;
if (was_enabled && !vgic_cpu->lpis_enabled) {
vgic_flush_pending_lpis(vcpu); vgic_flush_pending_lpis(vcpu);
vgic_its_invalidate_cache(vcpu->kvm); vgic_its_invalidate_cache(vcpu->kvm);
} atomic_set_release(&vgic_cpu->ctlr, 0);
} else {
ctlr = atomic_cmpxchg_acquire(&vgic_cpu->ctlr, 0,
GICR_CTLR_ENABLE_LPIS);
if (ctlr != 0)
return;
if (!was_enabled && vgic_cpu->lpis_enabled)
vgic_enable_lpis(vcpu); vgic_enable_lpis(vcpu);
}
} }
static bool vgic_mmio_vcpu_rdist_is_last(struct kvm_vcpu *vcpu) static bool vgic_mmio_vcpu_rdist_is_last(struct kvm_vcpu *vcpu)
...@@ -478,11 +497,10 @@ static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu, ...@@ -478,11 +497,10 @@ static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu,
unsigned long val) unsigned long val)
{ {
struct vgic_dist *dist = &vcpu->kvm->arch.vgic; struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
u64 old_propbaser, propbaser; u64 old_propbaser, propbaser;
/* Storing a value with LPIs already enabled is undefined */ /* Storing a value with LPIs already enabled is undefined */
if (vgic_cpu->lpis_enabled) if (vgic_lpis_enabled(vcpu))
return; return;
do { do {
...@@ -513,7 +531,7 @@ static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu, ...@@ -513,7 +531,7 @@ static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu,
u64 old_pendbaser, pendbaser; u64 old_pendbaser, pendbaser;
/* Storing a value with LPIs already enabled is undefined */ /* Storing a value with LPIs already enabled is undefined */
if (vgic_cpu->lpis_enabled) if (vgic_lpis_enabled(vcpu))
return; return;
do { do {
......
...@@ -308,6 +308,7 @@ static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size) ...@@ -308,6 +308,7 @@ static inline bool vgic_dist_overlap(struct kvm *kvm, gpa_t base, size_t size)
(base < d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE); (base < d->vgic_dist_base + KVM_VGIC_V3_DIST_SIZE);
} }
bool vgic_lpis_enabled(struct kvm_vcpu *vcpu);
int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr); int vgic_copy_lpi_list(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 **intid_ptr);
int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its, int vgic_its_resolve_lpi(struct kvm *kvm, struct vgic_its *its,
u32 devid, u32 eventid, struct vgic_irq **irq); u32 devid, u32 eventid, struct vgic_irq **irq);
......
...@@ -347,8 +347,8 @@ struct vgic_cpu { ...@@ -347,8 +347,8 @@ struct vgic_cpu {
/* Contains the attributes and gpa of the LPI pending tables. */ /* Contains the attributes and gpa of the LPI pending tables. */
u64 pendbaser; u64 pendbaser;
/* GICR_CTLR.{ENABLE_LPIS,RWP} */
bool lpis_enabled; atomic_t ctlr;
/* Cache guest priority bits */ /* Cache guest priority bits */
u32 num_pri_bits; u32 num_pri_bits;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment