mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-09-18 22:14:16 +00:00
Merge branch 'kvm-arm64/vgic-v4-ctl' into kvmarm/next
* kvm-arm64/vgic-v4-ctl: : Userspace control of nASSGIcap, courtesy of Raghavendra Rao Ananta : : Allow userspace to decide if support for SGIs without an active state is : advertised to the guest, allowing VMs from GICv3-only hardware to be : migrated to to GICv4.1 capable machines. Documentation: KVM: arm64: Describe VGICv3 registers writable pre-init KVM: arm64: selftests: Add test for nASSGIcap attribute KVM: arm64: vgic-v3: Allow userspace to write GICD_TYPER2.nASSGIcap KVM: arm64: vgic-v3: Allow access to GICD_IIDR prior to initialization KVM: arm64: vgic-v3: Consolidate MAINT_IRQ handling KVM: arm64: Disambiguate support for vSGIs v. vLPIs Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
commit
0d46e324c0
9 changed files with 158 additions and 50 deletions
|
@ -78,6 +78,8 @@ Groups:
|
|||
-ENXIO The group or attribute is unknown/unsupported for this device
|
||||
or hardware support is missing.
|
||||
-EFAULT Invalid user pointer for attr->addr.
|
||||
-EBUSY Attempt to write a register that is read-only after
|
||||
initialization
|
||||
======= =============================================================
|
||||
|
||||
|
||||
|
@ -120,6 +122,15 @@ Groups:
|
|||
Note that distributor fields are not banked, but return the same value
|
||||
regardless of the mpidr used to access the register.
|
||||
|
||||
Userspace is allowed to write the following register fields prior to
|
||||
initialization of the VGIC:
|
||||
|
||||
=====================
|
||||
GICD_IIDR.Revision
|
||||
GICD_TYPER2.nASSGIcap
|
||||
=====================
|
||||
|
||||
|
||||
GICD_IIDR.Revision is updated when the KVM implementation is changed in a
|
||||
way directly observable by the guest or userspace. Userspace should read
|
||||
GICD_IIDR from KVM and write back the read value to confirm its expected
|
||||
|
@ -128,6 +139,12 @@ Groups:
|
|||
behavior.
|
||||
|
||||
|
||||
GICD_TYPER2.nASSGIcap allows userspace to control the support of SGIs
|
||||
without an active state. At VGIC creation the field resets to the
|
||||
maximum capability of the system. Userspace is expected to read the field
|
||||
to determine the supported value(s) before writing to the field.
|
||||
|
||||
|
||||
The GICD_STATUSR and GICR_STATUSR registers are architecturally defined such
|
||||
that a write of a clear bit has no effect, whereas a write with a set bit
|
||||
clears that value. To allow userspace to freely set the values of these two
|
||||
|
|
|
@ -157,6 +157,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
|
|||
|
||||
kvm->arch.vgic.in_kernel = true;
|
||||
kvm->arch.vgic.vgic_model = type;
|
||||
kvm->arch.vgic.implementation_rev = KVM_VGIC_IMP_REV_LATEST;
|
||||
|
||||
kvm->arch.vgic.vgic_dist_base = VGIC_ADDR_UNDEF;
|
||||
|
||||
|
@ -165,6 +166,9 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
|
|||
else
|
||||
INIT_LIST_HEAD(&kvm->arch.vgic.rd_regions);
|
||||
|
||||
if (type == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
kvm->arch.vgic.nassgicap = system_supports_direct_sgis();
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&kvm->arch.config_lock);
|
||||
kvm_unlock_all_vcpus(kvm);
|
||||
|
@ -391,11 +395,10 @@ int vgic_init(struct kvm *kvm)
|
|||
goto out;
|
||||
|
||||
/*
|
||||
* If we have GICv4.1 enabled, unconditionally request enable the
|
||||
* v4 support so that we get HW-accelerated vSGIs. Otherwise, only
|
||||
* enable it if we present a virtual ITS to the guest.
|
||||
* Ensure vPEs are allocated if direct IRQ injection (e.g. vSGIs,
|
||||
* vLPIs) is supported.
|
||||
*/
|
||||
if (vgic_supports_direct_msis(kvm)) {
|
||||
if (vgic_supports_direct_irqs(kvm)) {
|
||||
ret = vgic_v4_init(kvm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
@ -409,15 +412,7 @@ int vgic_init(struct kvm *kvm)
|
|||
goto out;
|
||||
|
||||
vgic_debug_init(kvm);
|
||||
|
||||
/*
|
||||
* If userspace didn't set the GIC implementation revision,
|
||||
* default to the latest and greatest. You know want it.
|
||||
*/
|
||||
if (!dist->implementation_rev)
|
||||
dist->implementation_rev = KVM_VGIC_IMP_REV_LATEST;
|
||||
dist->initialized = true;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
@ -443,7 +438,7 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
|
|||
dist->vgic_cpu_base = VGIC_ADDR_UNDEF;
|
||||
}
|
||||
|
||||
if (vgic_supports_direct_msis(kvm))
|
||||
if (vgic_supports_direct_irqs(kvm))
|
||||
vgic_v4_teardown(kvm);
|
||||
|
||||
xa_destroy(&dist->lpi_xa);
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* Copyright (C) 2015 ARM Ltd.
|
||||
* Author: Marc Zyngier <marc.zyngier@arm.com>
|
||||
*/
|
||||
#include <linux/irqchip/arm-gic-v3.h>
|
||||
#include <linux/kvm_host.h>
|
||||
#include <kvm/arm_vgic.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
@ -303,12 +304,6 @@ static int vgic_get_common_attr(struct kvm_device *dev,
|
|||
VGIC_NR_PRIVATE_IRQS, uaddr);
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
|
||||
r = put_user(dev->kvm->arch.vgic.mi_intid, uaddr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
|
@ -509,6 +504,24 @@ int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow access to certain ID-like registers prior to VGIC initialization,
|
||||
* thereby allowing the VMM to provision the features / sizing of the VGIC.
|
||||
*/
|
||||
static bool reg_allowed_pre_init(struct kvm_device_attr *attr)
|
||||
{
|
||||
if (attr->group != KVM_DEV_ARM_VGIC_GRP_DIST_REGS)
|
||||
return false;
|
||||
|
||||
switch (attr->attr & KVM_DEV_ARM_VGIC_OFFSET_MASK) {
|
||||
case GICD_IIDR:
|
||||
case GICD_TYPER2:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* vgic_v3_attr_regs_access - allows user space to access VGIC v3 state
|
||||
*
|
||||
|
@ -523,7 +536,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
|
|||
struct vgic_reg_attr reg_attr;
|
||||
gpa_t addr;
|
||||
struct kvm_vcpu *vcpu;
|
||||
bool uaccess, post_init = true;
|
||||
bool uaccess;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
|
@ -539,9 +552,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
|
|||
/* Sysregs uaccess is performed by the sysreg handling code */
|
||||
uaccess = false;
|
||||
break;
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
|
||||
post_init = false;
|
||||
fallthrough;
|
||||
default:
|
||||
uaccess = true;
|
||||
}
|
||||
|
@ -561,7 +571,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
|
|||
|
||||
mutex_lock(&dev->kvm->arch.config_lock);
|
||||
|
||||
if (post_init != vgic_initialized(dev->kvm)) {
|
||||
if (!(vgic_initialized(dev->kvm) || reg_allowed_pre_init(attr))) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
|
@ -591,19 +601,6 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev,
|
|||
}
|
||||
break;
|
||||
}
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
|
||||
if (!is_write) {
|
||||
val = dev->kvm->arch.vgic.mi_intid;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = -EINVAL;
|
||||
if ((val < VGIC_NR_PRIVATE_IRQS) && (val >= VGIC_NR_SGIS)) {
|
||||
dev->kvm->arch.vgic.mi_intid = val;
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
|
@ -630,8 +627,24 @@ static int vgic_v3_set_attr(struct kvm_device *dev,
|
|||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
|
||||
return vgic_v3_attr_regs_access(dev, attr, true);
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
|
||||
u32 __user *uaddr = (u32 __user *)attr->addr;
|
||||
u32 val;
|
||||
|
||||
if (get_user(val, uaddr))
|
||||
return -EFAULT;
|
||||
|
||||
guard(mutex)(&dev->kvm->arch.config_lock);
|
||||
if (vgic_initialized(dev->kvm))
|
||||
return -EBUSY;
|
||||
|
||||
if (!irq_is_ppi(val))
|
||||
return -EINVAL;
|
||||
|
||||
dev->kvm->arch.vgic.mi_intid = val;
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return vgic_set_common_attr(dev, attr);
|
||||
}
|
||||
|
@ -645,8 +658,13 @@ static int vgic_v3_get_attr(struct kvm_device *dev,
|
|||
case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS:
|
||||
case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO:
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ:
|
||||
return vgic_v3_attr_regs_access(dev, attr, false);
|
||||
case KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ: {
|
||||
u32 __user *uaddr = (u32 __user *)(long)attr->addr;
|
||||
|
||||
guard(mutex)(&dev->kvm->arch.config_lock);
|
||||
return put_user(dev->kvm->arch.vgic.mi_intid, uaddr);
|
||||
}
|
||||
default:
|
||||
return vgic_get_common_attr(dev, attr);
|
||||
}
|
||||
|
|
|
@ -50,8 +50,17 @@ bool vgic_has_its(struct kvm *kvm)
|
|||
|
||||
bool vgic_supports_direct_msis(struct kvm *kvm)
|
||||
{
|
||||
return (kvm_vgic_global_state.has_gicv4_1 ||
|
||||
(kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm)));
|
||||
return kvm_vgic_global_state.has_gicv4 && vgic_has_its(kvm);
|
||||
}
|
||||
|
||||
bool system_supports_direct_sgis(void)
|
||||
{
|
||||
return kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi();
|
||||
}
|
||||
|
||||
bool vgic_supports_direct_sgis(struct kvm *kvm)
|
||||
{
|
||||
return kvm->arch.vgic.nassgicap;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -86,7 +95,7 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
|
|||
}
|
||||
break;
|
||||
case GICD_TYPER2:
|
||||
if (kvm_vgic_global_state.has_gicv4_1 && gic_cpuif_has_vsgi())
|
||||
if (vgic_supports_direct_sgis(vcpu->kvm))
|
||||
value = GICD_TYPER2_nASSGIcap;
|
||||
break;
|
||||
case GICD_IIDR:
|
||||
|
@ -119,7 +128,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
|
|||
dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;
|
||||
|
||||
/* Not a GICv4.1? No HW SGIs */
|
||||
if (!kvm_vgic_global_state.has_gicv4_1 || !gic_cpuif_has_vsgi())
|
||||
if (!vgic_supports_direct_sgis(vcpu->kvm))
|
||||
val &= ~GICD_CTLR_nASSGIreq;
|
||||
|
||||
/* Dist stays enabled? nASSGIreq is RO */
|
||||
|
@ -133,7 +142,7 @@ static void vgic_mmio_write_v3_misc(struct kvm_vcpu *vcpu,
|
|||
if (is_hwsgi != dist->nassgireq)
|
||||
vgic_v4_configure_vsgis(vcpu->kvm);
|
||||
|
||||
if (kvm_vgic_global_state.has_gicv4_1 &&
|
||||
if (vgic_supports_direct_sgis(vcpu->kvm) &&
|
||||
was_enabled != dist->enabled)
|
||||
kvm_make_all_cpus_request(vcpu->kvm, KVM_REQ_RELOAD_GICv4);
|
||||
else if (!was_enabled && dist->enabled)
|
||||
|
@ -159,8 +168,18 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
|
|||
|
||||
switch (addr & 0x0c) {
|
||||
case GICD_TYPER2:
|
||||
if (val != vgic_mmio_read_v3_misc(vcpu, addr, len))
|
||||
reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
|
||||
|
||||
if (reg == val)
|
||||
return 0;
|
||||
if (vgic_initialized(vcpu->kvm))
|
||||
return -EBUSY;
|
||||
if ((reg ^ val) & ~GICD_TYPER2_nASSGIcap)
|
||||
return -EINVAL;
|
||||
if (!system_supports_direct_sgis() && val)
|
||||
return -EINVAL;
|
||||
|
||||
dist->nassgicap = val & GICD_TYPER2_nASSGIcap;
|
||||
return 0;
|
||||
case GICD_IIDR:
|
||||
reg = vgic_mmio_read_v3_misc(vcpu, addr, len);
|
||||
|
@ -178,7 +197,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
|
|||
}
|
||||
case GICD_CTLR:
|
||||
/* Not a GICv4.1? No HW SGIs */
|
||||
if (!kvm_vgic_global_state.has_gicv4_1)
|
||||
if (!vgic_supports_direct_sgis(vcpu->kvm))
|
||||
val &= ~GICD_CTLR_nASSGIreq;
|
||||
|
||||
dist->enabled = val & GICD_CTLR_ENABLE_SS_G1;
|
||||
|
|
|
@ -356,7 +356,7 @@ int vgic_v4_put(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
|
||||
|
||||
if (!vgic_supports_direct_msis(vcpu->kvm) || !vpe->resident)
|
||||
if (!vgic_supports_direct_irqs(vcpu->kvm) || !vpe->resident)
|
||||
return 0;
|
||||
|
||||
return its_make_vpe_non_resident(vpe, vgic_v4_want_doorbell(vcpu));
|
||||
|
@ -367,7 +367,7 @@ int vgic_v4_load(struct kvm_vcpu *vcpu)
|
|||
struct its_vpe *vpe = &vcpu->arch.vgic_cpu.vgic_v3.its_vpe;
|
||||
int err;
|
||||
|
||||
if (!vgic_supports_direct_msis(vcpu->kvm) || vpe->resident)
|
||||
if (!vgic_supports_direct_irqs(vcpu->kvm) || vpe->resident)
|
||||
return 0;
|
||||
|
||||
if (vcpu_get_flag(vcpu, IN_WFI))
|
||||
|
|
|
@ -951,7 +951,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
* can be directly injected (GICv4).
|
||||
*/
|
||||
if (list_empty(&vcpu->arch.vgic_cpu.ap_list_head) &&
|
||||
!vgic_supports_direct_msis(vcpu->kvm))
|
||||
!vgic_supports_direct_irqs(vcpu->kvm))
|
||||
return;
|
||||
|
||||
DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
|
||||
|
@ -965,7 +965,7 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
|
|||
if (can_access_vgic_from_kernel())
|
||||
vgic_restore_state(vcpu);
|
||||
|
||||
if (vgic_supports_direct_msis(vcpu->kvm))
|
||||
if (vgic_supports_direct_irqs(vcpu->kvm))
|
||||
vgic_v4_commit(vcpu);
|
||||
}
|
||||
|
||||
|
|
|
@ -390,7 +390,23 @@ void vgic_its_invalidate_all_caches(struct kvm *kvm);
|
|||
int vgic_its_inv_lpi(struct kvm *kvm, struct vgic_irq *irq);
|
||||
int vgic_its_invall(struct kvm_vcpu *vcpu);
|
||||
|
||||
bool system_supports_direct_sgis(void);
|
||||
bool vgic_supports_direct_msis(struct kvm *kvm);
|
||||
bool vgic_supports_direct_sgis(struct kvm *kvm);
|
||||
|
||||
static inline bool vgic_supports_direct_irqs(struct kvm *kvm)
|
||||
{
|
||||
/*
|
||||
* Deliberately conflate vLPI and vSGI support on GICv4.1 hardware,
|
||||
* indirectly allowing userspace to control whether or not vPEs are
|
||||
* allocated for the VM.
|
||||
*/
|
||||
if (system_supports_direct_sgis())
|
||||
return vgic_supports_direct_sgis(kvm);
|
||||
|
||||
return vgic_supports_direct_msis(kvm);
|
||||
}
|
||||
|
||||
int vgic_v4_init(struct kvm *kvm);
|
||||
void vgic_v4_teardown(struct kvm *kvm);
|
||||
void vgic_v4_configure_vsgis(struct kvm *kvm);
|
||||
|
|
|
@ -268,6 +268,9 @@ struct vgic_dist {
|
|||
/* distributor enabled */
|
||||
bool enabled;
|
||||
|
||||
/* Supports SGIs without active state */
|
||||
bool nassgicap;
|
||||
|
||||
/* Wants SGIs without active state */
|
||||
bool nassgireq;
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "kvm_util.h"
|
||||
#include "processor.h"
|
||||
#include "vgic.h"
|
||||
#include "gic_v3.h"
|
||||
|
||||
#define NR_VCPUS 4
|
||||
|
||||
|
@ -675,6 +676,44 @@ static void test_v3_its_region(void)
|
|||
vm_gic_destroy(&v);
|
||||
}
|
||||
|
||||
static void test_v3_nassgicap(void)
|
||||
{
|
||||
struct kvm_vcpu *vcpus[NR_VCPUS];
|
||||
bool has_nassgicap;
|
||||
struct vm_gic vm;
|
||||
u32 typer2;
|
||||
int ret;
|
||||
|
||||
vm = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus);
|
||||
kvm_device_attr_get(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
GICD_TYPER2, &typer2);
|
||||
has_nassgicap = typer2 & GICD_TYPER2_nASSGIcap;
|
||||
|
||||
typer2 |= GICD_TYPER2_nASSGIcap;
|
||||
ret = __kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
GICD_TYPER2, &typer2);
|
||||
if (has_nassgicap)
|
||||
TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEVICE_ATTR_SET, ret));
|
||||
else
|
||||
TEST_ASSERT(ret && errno == EINVAL,
|
||||
"Enabled nASSGIcap even though it's unavailable");
|
||||
|
||||
typer2 &= ~GICD_TYPER2_nASSGIcap;
|
||||
kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
GICD_TYPER2, &typer2);
|
||||
|
||||
kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
|
||||
KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
|
||||
|
||||
typer2 ^= GICD_TYPER2_nASSGIcap;
|
||||
ret = __kvm_device_attr_set(vm.gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
|
||||
GICD_TYPER2, &typer2);
|
||||
TEST_ASSERT(ret && errno == EBUSY,
|
||||
"Changed nASSGIcap after initializing the VGIC");
|
||||
|
||||
vm_gic_destroy(&vm);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns 0 if it's possible to create GIC device of a given type (V2 or V3).
|
||||
*/
|
||||
|
@ -945,6 +984,7 @@ void run_tests(uint32_t gic_dev_type)
|
|||
test_v3_redist_ipa_range_check_at_vcpu_run();
|
||||
test_v3_its_region();
|
||||
test_v3_sysregs();
|
||||
test_v3_nassgicap();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue