Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

KVM: arm64: vgic-v3: Expose GICR_TYPER.Last for userspace

Commit 23bde34771f1 ("KVM: arm64: vgic-v3: Drop the
reporting of GICR_TYPER.Last for userspace") temporarily fixed
a bug identified when attempting to access the GICR_TYPER
register before the redistributor region setting, but dropped
the support of the LAST bit.

Emulating the GICR_TYPER.Last bit still makes sense for
architecture compliance though. This patch restores its support
(if the redistributor region was set) while keeping the code safe.

We introduce a new helper, vgic_mmio_vcpu_rdist_is_last() which
computes whether a redistributor is the highest one of a series
of redistributor contributor pages.

With this new implementation we do not need to have a uaccess
read accessor anymore.

Signed-off-by: Eric Auger <eric.auger@redhat.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20210405163941.510258-9-eric.auger@redhat.com

authored by

Eric Auger and committed by
Marc Zyngier
28e9d4bc e5a35635

+34 -25
+33 -25
arch/arm64/kvm/vgic/vgic-mmio-v3.c
··· 251 251 vgic_enable_lpis(vcpu); 252 252 } 253 253 254 + static bool vgic_mmio_vcpu_rdist_is_last(struct kvm_vcpu *vcpu) 255 + { 256 + struct vgic_dist *vgic = &vcpu->kvm->arch.vgic; 257 + struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 258 + struct vgic_redist_region *iter, *rdreg = vgic_cpu->rdreg; 259 + 260 + if (!rdreg) 261 + return false; 262 + 263 + if (vgic_cpu->rdreg_index < rdreg->free_index - 1) { 264 + return false; 265 + } else if (rdreg->count && vgic_cpu->rdreg_index == (rdreg->count - 1)) { 266 + struct list_head *rd_regions = &vgic->rd_regions; 267 + gpa_t end = rdreg->base + rdreg->count * KVM_VGIC_V3_REDIST_SIZE; 268 + 269 + /* 270 + * the rdist is the last one of the redist region, 271 + * check whether there is no other contiguous rdist region 272 + */ 273 + list_for_each_entry(iter, rd_regions, list) { 274 + if (iter->base == end && iter->free_index > 0) 275 + return false; 276 + } 277 + } 278 + return true; 279 + } 280 + 254 281 static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu, 255 282 gpa_t addr, unsigned int len) 256 283 { 257 284 unsigned long mpidr = kvm_vcpu_get_mpidr_aff(vcpu); 258 - struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu; 259 - struct vgic_redist_region *rdreg = vgic_cpu->rdreg; 260 285 int target_vcpu_id = vcpu->vcpu_id; 261 - gpa_t last_rdist_typer = rdreg->base + GICR_TYPER + 262 - (rdreg->free_index - 1) * KVM_VGIC_V3_REDIST_SIZE; 263 286 u64 value; 264 287 265 288 value = (u64)(mpidr & GENMASK(23, 0)) << 32; 266 289 value |= ((target_vcpu_id & 0xffff) << 8); 267 290 268 - if (addr == last_rdist_typer) 291 + if (vgic_has_its(vcpu->kvm)) 292 + value |= GICR_TYPER_PLPIS; 293 + 294 + if (vgic_mmio_vcpu_rdist_is_last(vcpu)) 269 295 value |= GICR_TYPER_LAST; 270 - if (vgic_has_its(vcpu->kvm)) 271 - value |= GICR_TYPER_PLPIS; 272 296 273 - return extract_bytes(value, addr & 7, len); 274 - } 275 - 276 - static unsigned long vgic_uaccess_read_v3r_typer(struct kvm_vcpu *vcpu, 277 - gpa_t addr, unsigned int len) 278 - { 279 - unsigned long mpidr = kvm_vcpu_get_mpidr_aff(vcpu); 280 - int target_vcpu_id = vcpu->vcpu_id; 281 - u64 value; 282 - 283 - value = (u64)(mpidr & GENMASK(23, 0)) << 32; 284 - value |= ((target_vcpu_id & 0xffff) << 8); 285 - 286 - if (vgic_has_its(vcpu->kvm)) 287 - value |= GICR_TYPER_PLPIS; 288 - 289 - /* reporting of the Last bit is not supported for userspace */ 290 297 return extract_bytes(value, addr & 7, len); 291 298 } 292 299 ··· 619 612 VGIC_ACCESS_32bit), 620 613 REGISTER_DESC_WITH_LENGTH_UACCESS(GICR_TYPER, 621 614 vgic_mmio_read_v3r_typer, vgic_mmio_write_wi, 622 - vgic_uaccess_read_v3r_typer, vgic_mmio_uaccess_write_wi, 8, 615 + NULL, vgic_mmio_uaccess_write_wi, 8, 623 616 VGIC_ACCESS_64bit | VGIC_ACCESS_32bit), 624 617 REGISTER_DESC_WITH_LENGTH(GICR_WAKER, 625 618 vgic_mmio_read_raz, vgic_mmio_write_wi, 4, ··· 721 714 return -EINVAL; 722 715 723 716 vgic_cpu->rdreg = rdreg; 717 + vgic_cpu->rdreg_index = rdreg->free_index; 724 718 725 719 rd_base = rdreg->base + rdreg->free_index * KVM_VGIC_V3_REDIST_SIZE; 726 720
+1
include/kvm/arm_vgic.h
··· 322 322 */ 323 323 struct vgic_io_device rd_iodev; 324 324 struct vgic_redist_region *rdreg; 325 + u32 rdreg_index; 325 326 326 327 /* Contains the attributes and gpa of the LPI pending tables. */ 327 328 u64 pendbaser;