Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM updates from Paolo Bonzini: - ARM: GICv3 ITS emulation and various fixes. Removal of the old VGIC implementation. - s390: support for trapping software breakpoints, nested virtualization (vSIE), the STHYI opcode, initial extensions for CPU model support. - MIPS: support for MIPS64 hosts (32-bit guests only) and lots of cleanups, preliminary to this and the upcoming support for hardware virtualization extensions. - x86: support for execute-only mappings in nested EPT; reduced vmexit latency for TSC deadline timer (by about 30%) on Intel hosts; support for more than 255 vCPUs. - PPC: bugfixes. * tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (302 commits) KVM: PPC: Introduce KVM_CAP_PPC_HTM MIPS: Select HAVE_KVM for MIPS64_R{2,6} MIPS: KVM: Reset CP0_PageMask during host TLB flush MIPS: KVM: Fix ptr->int cast via KVM_GUEST_KSEGX() MIPS: KVM: Sign extend MFC0/RDHWR results MIPS: KVM: Fix 64-bit big endian dynamic translation MIPS: KVM: Fail if ebase doesn't fit in CP0_EBase MIPS: KVM: Use 64-bit CP0_EBase when appropriate MIPS: KVM: Set CP0_Status.KX on MIPS64 MIPS: KVM: Make entry code MIPS64 friendly MIPS: KVM: Use kmap instead of CKSEG0ADDR() MIPS: KVM: Use virt_to_phys() to get commpage PFN MIPS: Fix definition of KSEGX() for 64-bit KVM: VMX: Add VMCS to CPU's loaded VMCSs before VMPTRLD kvm: x86: nVMX: maintain internal copy of current VMCS KVM: PPC: Book3S HV: Save/restore TM state in H_CEDE KVM: PPC: Book3S HV: Pull out TM state save/restore into separate procedures KVM: arm64: vgic-its: Simplify MAPI error handling KVM: arm64: vgic-its: Make vgic_its_cmd_handle_mapi similar to other handlers KVM: arm64: vgic-its: Turn device_id validation into generic ID validation ...
此提交包含在:
@@ -157,6 +157,9 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
|
||||
struct kvm_vcpu *vcpu0 = kvm_get_vcpu(kvm, 0);
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&dist->lpi_list_head);
|
||||
spin_lock_init(&dist->lpi_list_lock);
|
||||
|
||||
dist->spis = kcalloc(nr_spis, sizeof(struct vgic_irq), GFP_KERNEL);
|
||||
if (!dist->spis)
|
||||
return -ENOMEM;
|
||||
@@ -177,6 +180,7 @@ static int kvm_vgic_dist_init(struct kvm *kvm, unsigned int nr_spis)
|
||||
spin_lock_init(&irq->irq_lock);
|
||||
irq->vcpu = NULL;
|
||||
irq->target_vcpu = vcpu0;
|
||||
kref_init(&irq->refcount);
|
||||
if (dist->vgic_model == KVM_DEV_TYPE_ARM_VGIC_V2)
|
||||
irq->targets = 0;
|
||||
else
|
||||
@@ -211,6 +215,7 @@ static void kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu)
|
||||
irq->vcpu = NULL;
|
||||
irq->target_vcpu = vcpu;
|
||||
irq->targets = 1U << vcpu->vcpu_id;
|
||||
kref_init(&irq->refcount);
|
||||
if (vgic_irq_is_sgi(i)) {
|
||||
/* SGIs */
|
||||
irq->enabled = 1;
|
||||
@@ -253,6 +258,9 @@ int vgic_init(struct kvm *kvm)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (vgic_has_its(kvm))
|
||||
dist->msis_require_devid = true;
|
||||
|
||||
kvm_for_each_vcpu(i, vcpu, kvm)
|
||||
kvm_vgic_vcpu_init(vcpu);
|
||||
|
||||
@@ -271,7 +279,6 @@ static void kvm_vgic_dist_destroy(struct kvm *kvm)
|
||||
dist->initialized = false;
|
||||
|
||||
kfree(dist->spis);
|
||||
kfree(dist->redist_iodevs);
|
||||
dist->nr_spis = 0;
|
||||
|
||||
mutex_unlock(&kvm->lock);
|
||||
|
1500
virt/kvm/arm/vgic/vgic-its.c
一般檔案
1500
virt/kvm/arm/vgic/vgic-its.c
一般檔案
檔案差異因為檔案過大而無法顯示
載入差異
@@ -21,8 +21,8 @@
|
||||
|
||||
/* common helpers */
|
||||
|
||||
static int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
|
||||
phys_addr_t addr, phys_addr_t alignment)
|
||||
int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
|
||||
phys_addr_t addr, phys_addr_t alignment)
|
||||
{
|
||||
if (addr & ~KVM_PHYS_MASK)
|
||||
return -E2BIG;
|
||||
@@ -210,20 +210,27 @@ static void vgic_destroy(struct kvm_device *dev)
|
||||
kfree(dev);
|
||||
}
|
||||
|
||||
void kvm_register_vgic_device(unsigned long type)
|
||||
int kvm_register_vgic_device(unsigned long type)
|
||||
{
|
||||
int ret = -ENODEV;
|
||||
|
||||
switch (type) {
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V2:
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v2_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
break;
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
case KVM_DEV_TYPE_ARM_VGIC_V3:
|
||||
kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
ret = kvm_register_device_ops(&kvm_arm_vgic_v3_ops,
|
||||
KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
if (ret)
|
||||
break;
|
||||
ret = kvm_vgic_register_its_device();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** vgic_attr_regs_access: allows user space to read/write VGIC registers
|
||||
@@ -428,4 +435,3 @@ struct kvm_device_ops kvm_arm_vgic_v3_ops = {
|
||||
};
|
||||
|
||||
#endif /* CONFIG_KVM_ARM_VGIC_V3 */
|
||||
|
||||
|
@@ -102,6 +102,7 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
|
||||
irq->source |= 1U << source_vcpu->vcpu_id;
|
||||
|
||||
vgic_queue_irq_unlock(source_vcpu->kvm, irq);
|
||||
vgic_put_irq(source_vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,6 +117,8 @@ static unsigned long vgic_mmio_read_target(struct kvm_vcpu *vcpu,
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->targets << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return val;
|
||||
@@ -143,6 +146,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
|
||||
irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,6 +161,8 @@ static unsigned long vgic_mmio_read_sgipend(struct kvm_vcpu *vcpu,
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->source << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -178,6 +184,7 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
|
||||
irq->pending = false;
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +208,7 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
|
||||
} else {
|
||||
spin_unlock(&irq->irq_lock);
|
||||
}
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -429,6 +437,7 @@ int vgic_v2_cpuif_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
struct vgic_io_device dev = {
|
||||
.regions = vgic_v2_cpu_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v2_cpu_registers),
|
||||
.iodev_type = IODEV_CPUIF,
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &dev, is_write, offset, val);
|
||||
@@ -440,6 +449,7 @@ int vgic_v2_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write,
|
||||
struct vgic_io_device dev = {
|
||||
.regions = vgic_v2_dist_registers,
|
||||
.nr_regions = ARRAY_SIZE(vgic_v2_dist_registers),
|
||||
.iodev_type = IODEV_DIST,
|
||||
};
|
||||
|
||||
return vgic_uaccess(vcpu, &dev, is_write, offset, val);
|
||||
|
@@ -23,12 +23,35 @@
|
||||
#include "vgic-mmio.h"
|
||||
|
||||
/* extract @num bytes at @offset bytes offset in data */
|
||||
static unsigned long extract_bytes(unsigned long data, unsigned int offset,
|
||||
unsigned int num)
|
||||
unsigned long extract_bytes(unsigned long data, unsigned int offset,
|
||||
unsigned int num)
|
||||
{
|
||||
return (data >> (offset * 8)) & GENMASK_ULL(num * 8 - 1, 0);
|
||||
}
|
||||
|
||||
/* allows updates of any half of a 64-bit register (or the whole thing) */
|
||||
u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
int lower = (offset & 4) * 8;
|
||||
int upper = lower + 8 * len - 1;
|
||||
|
||||
reg &= ~GENMASK_ULL(upper, lower);
|
||||
val &= GENMASK_ULL(len * 8 - 1, 0);
|
||||
|
||||
return reg | ((u64)val << lower);
|
||||
}
|
||||
|
||||
bool vgic_has_its(struct kvm *kvm)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
|
||||
if (dist->vgic_model != KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
return false;
|
||||
|
||||
return dist->has_its;
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
@@ -43,7 +66,12 @@ static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
|
||||
case GICD_TYPER:
|
||||
value = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS;
|
||||
value = (value >> 5) - 1;
|
||||
value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
|
||||
if (vgic_has_its(vcpu->kvm)) {
|
||||
value |= (INTERRUPT_ID_BITS_ITS - 1) << 19;
|
||||
value |= GICD_TYPER_LPIS;
|
||||
} else {
|
||||
value |= (INTERRUPT_ID_BITS_SPIS - 1) << 19;
|
||||
}
|
||||
break;
|
||||
case GICD_IIDR:
|
||||
value = (PRODUCT_ID_KVM << 24) | (IMPLEMENTER_ARM << 0);
|
||||
@@ -80,15 +108,17 @@ static unsigned long vgic_mmio_read_irouter(struct kvm_vcpu *vcpu,
|
||||
{
|
||||
int intid = VGIC_ADDR_TO_INTID(addr, 64);
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
unsigned long ret = 0;
|
||||
|
||||
if (!irq)
|
||||
return 0;
|
||||
|
||||
/* The upper word is RAZ for us. */
|
||||
if (addr & 4)
|
||||
return 0;
|
||||
if (!(addr & 4))
|
||||
ret = extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len);
|
||||
|
||||
return extract_bytes(READ_ONCE(irq->mpidr), addr & 7, len);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
|
||||
@@ -96,15 +126,17 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
|
||||
unsigned long val)
|
||||
{
|
||||
int intid = VGIC_ADDR_TO_INTID(addr, 64);
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
|
||||
if (!irq)
|
||||
return;
|
||||
struct vgic_irq *irq;
|
||||
|
||||
/* The upper word is WI for us since we don't implement Aff3. */
|
||||
if (addr & 4)
|
||||
return;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, NULL, intid);
|
||||
|
||||
if (!irq)
|
||||
return;
|
||||
|
||||
spin_lock(&irq->irq_lock);
|
||||
|
||||
/* We only care about and preserve Aff0, Aff1 and Aff2. */
|
||||
@@ -112,6 +144,32 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
|
||||
irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3r_ctlr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
return vgic_cpu->lpis_enabled ? GICR_CTLR_ENABLE_LPIS : 0;
|
||||
}
|
||||
|
||||
|
||||
static void vgic_mmio_write_v3r_ctlr(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
bool was_enabled = vgic_cpu->lpis_enabled;
|
||||
|
||||
if (!vgic_has_its(vcpu->kvm))
|
||||
return;
|
||||
|
||||
vgic_cpu->lpis_enabled = val & GICR_CTLR_ENABLE_LPIS;
|
||||
|
||||
if (!was_enabled && vgic_cpu->lpis_enabled)
|
||||
vgic_enable_lpis(vcpu);
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
|
||||
@@ -125,6 +183,8 @@ static unsigned long vgic_mmio_read_v3r_typer(struct kvm_vcpu *vcpu,
|
||||
value |= ((target_vcpu_id & 0xffff) << 8);
|
||||
if (target_vcpu_id == atomic_read(&vcpu->kvm->online_vcpus) - 1)
|
||||
value |= GICR_TYPER_LAST;
|
||||
if (vgic_has_its(vcpu->kvm))
|
||||
value |= GICR_TYPER_PLPIS;
|
||||
|
||||
return extract_bytes(value, addr & 7, len);
|
||||
}
|
||||
@@ -147,6 +207,142 @@ static unsigned long vgic_mmio_read_v3_idregs(struct kvm_vcpu *vcpu,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* We want to avoid outer shareable. */
|
||||
u64 vgic_sanitise_shareability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_OuterShareable:
|
||||
return GIC_BASER_InnerShareable;
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/* Avoid any inner non-cacheable mapping. */
|
||||
u64 vgic_sanitise_inner_cacheability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_CACHE_nCnB:
|
||||
case GIC_BASER_CACHE_nC:
|
||||
return GIC_BASER_CACHE_RaWb;
|
||||
default:
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
/* Non-cacheable or same-as-inner are OK. */
|
||||
u64 vgic_sanitise_outer_cacheability(u64 field)
|
||||
{
|
||||
switch (field) {
|
||||
case GIC_BASER_CACHE_SameAsInner:
|
||||
case GIC_BASER_CACHE_nC:
|
||||
return field;
|
||||
default:
|
||||
return GIC_BASER_CACHE_nC;
|
||||
}
|
||||
}
|
||||
|
||||
u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
|
||||
u64 (*sanitise_fn)(u64))
|
||||
{
|
||||
u64 field = (reg & field_mask) >> field_shift;
|
||||
|
||||
field = sanitise_fn(field) << field_shift;
|
||||
return (reg & ~field_mask) | field;
|
||||
}
|
||||
|
||||
#define PROPBASER_RES0_MASK \
|
||||
(GENMASK_ULL(63, 59) | GENMASK_ULL(55, 52) | GENMASK_ULL(6, 5))
|
||||
#define PENDBASER_RES0_MASK \
|
||||
(BIT_ULL(63) | GENMASK_ULL(61, 59) | GENMASK_ULL(55, 52) | \
|
||||
GENMASK_ULL(15, 12) | GENMASK_ULL(6, 0))
|
||||
|
||||
static u64 vgic_sanitise_pendbaser(u64 reg)
|
||||
{
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_SHAREABILITY_MASK,
|
||||
GICR_PENDBASER_SHAREABILITY_SHIFT,
|
||||
vgic_sanitise_shareability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_INNER_CACHEABILITY_MASK,
|
||||
GICR_PENDBASER_INNER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_inner_cacheability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PENDBASER_OUTER_CACHEABILITY_MASK,
|
||||
GICR_PENDBASER_OUTER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_outer_cacheability);
|
||||
|
||||
reg &= ~PENDBASER_RES0_MASK;
|
||||
reg &= ~GENMASK_ULL(51, 48);
|
||||
|
||||
return reg;
|
||||
}
|
||||
|
||||
static u64 vgic_sanitise_propbaser(u64 reg)
|
||||
{
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_SHAREABILITY_MASK,
|
||||
GICR_PROPBASER_SHAREABILITY_SHIFT,
|
||||
vgic_sanitise_shareability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_INNER_CACHEABILITY_MASK,
|
||||
GICR_PROPBASER_INNER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_inner_cacheability);
|
||||
reg = vgic_sanitise_field(reg, GICR_PROPBASER_OUTER_CACHEABILITY_MASK,
|
||||
GICR_PROPBASER_OUTER_CACHEABILITY_SHIFT,
|
||||
vgic_sanitise_outer_cacheability);
|
||||
|
||||
reg &= ~PROPBASER_RES0_MASK;
|
||||
reg &= ~GENMASK_ULL(51, 48);
|
||||
return reg;
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_propbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
|
||||
return extract_bytes(dist->propbaser, addr & 7, len);
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_propbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_dist *dist = &vcpu->kvm->arch.vgic;
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 propbaser = dist->propbaser;
|
||||
|
||||
/* Storing a value with LPIs already enabled is undefined */
|
||||
if (vgic_cpu->lpis_enabled)
|
||||
return;
|
||||
|
||||
propbaser = update_64bit_reg(propbaser, addr & 4, len, val);
|
||||
propbaser = vgic_sanitise_propbaser(propbaser);
|
||||
|
||||
dist->propbaser = propbaser;
|
||||
}
|
||||
|
||||
static unsigned long vgic_mmio_read_pendbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
|
||||
return extract_bytes(vgic_cpu->pendbaser, addr & 7, len);
|
||||
}
|
||||
|
||||
static void vgic_mmio_write_pendbase(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val)
|
||||
{
|
||||
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
u64 pendbaser = vgic_cpu->pendbaser;
|
||||
|
||||
/* Storing a value with LPIs already enabled is undefined */
|
||||
if (vgic_cpu->lpis_enabled)
|
||||
return;
|
||||
|
||||
pendbaser = update_64bit_reg(pendbaser, addr & 4, len, val);
|
||||
pendbaser = vgic_sanitise_pendbaser(pendbaser);
|
||||
|
||||
vgic_cpu->pendbaser = pendbaser;
|
||||
}
|
||||
|
||||
/*
|
||||
* The GICv3 per-IRQ registers are split to control PPIs and SGIs in the
|
||||
* redistributors, while SPIs are covered by registers in the distributor
|
||||
@@ -218,7 +414,7 @@ static const struct vgic_register_region vgic_v3_dist_registers[] = {
|
||||
|
||||
static const struct vgic_register_region vgic_v3_rdbase_registers[] = {
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_CTLR,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 4,
|
||||
vgic_mmio_read_v3r_ctlr, vgic_mmio_write_v3r_ctlr, 4,
|
||||
VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_IIDR,
|
||||
vgic_mmio_read_v3r_iidr, vgic_mmio_write_wi, 4,
|
||||
@@ -227,10 +423,10 @@ static const struct vgic_register_region vgic_v3_rdbase_registers[] = {
|
||||
vgic_mmio_read_v3r_typer, vgic_mmio_write_wi, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_PROPBASER,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 8,
|
||||
vgic_mmio_read_propbase, vgic_mmio_write_propbase, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_PENDBASER,
|
||||
vgic_mmio_read_raz, vgic_mmio_write_wi, 8,
|
||||
vgic_mmio_read_pendbase, vgic_mmio_write_pendbase, 8,
|
||||
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit),
|
||||
REGISTER_DESC_WITH_LENGTH(GICR_IDREGS,
|
||||
vgic_mmio_read_v3_idregs, vgic_mmio_write_wi, 48,
|
||||
@@ -285,24 +481,18 @@ unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev)
|
||||
|
||||
int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
|
||||
{
|
||||
int nr_vcpus = atomic_read(&kvm->online_vcpus);
|
||||
struct kvm_vcpu *vcpu;
|
||||
struct vgic_io_device *devices;
|
||||
int c, ret = 0;
|
||||
|
||||
devices = kmalloc(sizeof(struct vgic_io_device) * nr_vcpus * 2,
|
||||
GFP_KERNEL);
|
||||
if (!devices)
|
||||
return -ENOMEM;
|
||||
|
||||
kvm_for_each_vcpu(c, vcpu, kvm) {
|
||||
gpa_t rd_base = redist_base_address + c * SZ_64K * 2;
|
||||
gpa_t sgi_base = rd_base + SZ_64K;
|
||||
struct vgic_io_device *rd_dev = &devices[c * 2];
|
||||
struct vgic_io_device *sgi_dev = &devices[c * 2 + 1];
|
||||
struct vgic_io_device *rd_dev = &vcpu->arch.vgic_cpu.rd_iodev;
|
||||
struct vgic_io_device *sgi_dev = &vcpu->arch.vgic_cpu.sgi_iodev;
|
||||
|
||||
kvm_iodevice_init(&rd_dev->dev, &kvm_io_gic_ops);
|
||||
rd_dev->base_addr = rd_base;
|
||||
rd_dev->iodev_type = IODEV_REDIST;
|
||||
rd_dev->regions = vgic_v3_rdbase_registers;
|
||||
rd_dev->nr_regions = ARRAY_SIZE(vgic_v3_rdbase_registers);
|
||||
rd_dev->redist_vcpu = vcpu;
|
||||
@@ -317,6 +507,7 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
|
||||
|
||||
kvm_iodevice_init(&sgi_dev->dev, &kvm_io_gic_ops);
|
||||
sgi_dev->base_addr = sgi_base;
|
||||
sgi_dev->iodev_type = IODEV_REDIST;
|
||||
sgi_dev->regions = vgic_v3_sgibase_registers;
|
||||
sgi_dev->nr_regions = ARRAY_SIZE(vgic_v3_sgibase_registers);
|
||||
sgi_dev->redist_vcpu = vcpu;
|
||||
@@ -335,14 +526,15 @@ int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t redist_base_address)
|
||||
if (ret) {
|
||||
/* The current c failed, so we start with the previous one. */
|
||||
for (c--; c >= 0; c--) {
|
||||
struct vgic_cpu *vgic_cpu;
|
||||
|
||||
vcpu = kvm_get_vcpu(kvm, c);
|
||||
vgic_cpu = &vcpu->arch.vgic_cpu;
|
||||
kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
|
||||
&devices[c * 2].dev);
|
||||
&vgic_cpu->rd_iodev.dev);
|
||||
kvm_io_bus_unregister_dev(kvm, KVM_MMIO_BUS,
|
||||
&devices[c * 2 + 1].dev);
|
||||
&vgic_cpu->sgi_iodev.dev);
|
||||
}
|
||||
kfree(devices);
|
||||
} else {
|
||||
kvm->arch.vgic.redist_iodevs = devices;
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -451,5 +643,6 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
|
||||
irq->pending = true;
|
||||
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
@@ -56,6 +56,8 @@ unsigned long vgic_mmio_read_enable(struct kvm_vcpu *vcpu,
|
||||
|
||||
if (irq->enabled)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -74,6 +76,8 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
|
||||
spin_lock(&irq->irq_lock);
|
||||
irq->enabled = true;
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +96,7 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
|
||||
irq->enabled = false;
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +113,8 @@ unsigned long vgic_mmio_read_pending(struct kvm_vcpu *vcpu,
|
||||
|
||||
if (irq->pending)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -129,6 +136,7 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
|
||||
irq->soft_pending = true;
|
||||
|
||||
vgic_queue_irq_unlock(vcpu->kvm, irq);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,6 +160,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
|
||||
}
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,6 +177,8 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
|
||||
|
||||
if (irq->active)
|
||||
value |= (1U << i);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -242,6 +253,7 @@ void vgic_mmio_write_cactive(struct kvm_vcpu *vcpu,
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
vgic_mmio_change_active(vcpu, irq, false);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
vgic_change_active_finish(vcpu, intid);
|
||||
}
|
||||
@@ -257,6 +269,7 @@ void vgic_mmio_write_sactive(struct kvm_vcpu *vcpu,
|
||||
for_each_set_bit(i, &val, len * 8) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
vgic_mmio_change_active(vcpu, irq, true);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
vgic_change_active_finish(vcpu, intid);
|
||||
}
|
||||
@@ -272,6 +285,8 @@ unsigned long vgic_mmio_read_priority(struct kvm_vcpu *vcpu,
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
|
||||
val |= (u64)irq->priority << (i * 8);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return val;
|
||||
@@ -298,6 +313,8 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
|
||||
/* Narrow the priority range to what we actually support */
|
||||
irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS);
|
||||
spin_unlock(&irq->irq_lock);
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,6 +330,8 @@ unsigned long vgic_mmio_read_config(struct kvm_vcpu *vcpu,
|
||||
|
||||
if (irq->config == VGIC_CONFIG_EDGE)
|
||||
value |= (2U << (i * 2));
|
||||
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
|
||||
return value;
|
||||
@@ -326,7 +345,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len * 4; i++) {
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
struct vgic_irq *irq;
|
||||
|
||||
/*
|
||||
* The configuration cannot be changed for SGIs in general,
|
||||
@@ -337,14 +356,18 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
|
||||
if (intid + i < VGIC_NR_PRIVATE_IRQS)
|
||||
continue;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
|
||||
spin_lock(&irq->irq_lock);
|
||||
|
||||
if (test_bit(i * 2 + 1, &val)) {
|
||||
irq->config = VGIC_CONFIG_EDGE;
|
||||
} else {
|
||||
irq->config = VGIC_CONFIG_LEVEL;
|
||||
irq->pending = irq->line_level | irq->soft_pending;
|
||||
}
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,8 +473,7 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
struct kvm_vcpu *r_vcpu;
|
||||
unsigned long data;
|
||||
unsigned long data = 0;
|
||||
|
||||
region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
|
||||
addr - iodev->base_addr);
|
||||
@@ -460,8 +482,21 @@ static int dispatch_mmio_read(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
|
||||
data = region->read(r_vcpu, addr, len);
|
||||
switch (iodev->iodev_type) {
|
||||
case IODEV_CPUIF:
|
||||
data = region->read(vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_DIST:
|
||||
data = region->read(vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_REDIST:
|
||||
data = region->read(iodev->redist_vcpu, addr, len);
|
||||
break;
|
||||
case IODEV_ITS:
|
||||
data = region->its_read(vcpu->kvm, iodev->its, addr, len);
|
||||
break;
|
||||
}
|
||||
|
||||
vgic_data_host_to_mmio_bus(val, len, data);
|
||||
return 0;
|
||||
}
|
||||
@@ -471,7 +506,6 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
{
|
||||
struct vgic_io_device *iodev = kvm_to_vgic_iodev(dev);
|
||||
const struct vgic_register_region *region;
|
||||
struct kvm_vcpu *r_vcpu;
|
||||
unsigned long data = vgic_data_mmio_bus_to_host(val, len);
|
||||
|
||||
region = vgic_find_mmio_region(iodev->regions, iodev->nr_regions,
|
||||
@@ -482,8 +516,21 @@ static int dispatch_mmio_write(struct kvm_vcpu *vcpu, struct kvm_io_device *dev,
|
||||
if (!check_region(region, addr, len))
|
||||
return 0;
|
||||
|
||||
r_vcpu = iodev->redist_vcpu ? iodev->redist_vcpu : vcpu;
|
||||
region->write(r_vcpu, addr, len, data);
|
||||
switch (iodev->iodev_type) {
|
||||
case IODEV_CPUIF:
|
||||
region->write(vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_DIST:
|
||||
region->write(vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_REDIST:
|
||||
region->write(iodev->redist_vcpu, addr, len, data);
|
||||
break;
|
||||
case IODEV_ITS:
|
||||
region->its_write(vcpu->kvm, iodev->its, addr, len, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -513,6 +560,7 @@ int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
|
||||
}
|
||||
|
||||
io_device->base_addr = dist_base_address;
|
||||
io_device->iodev_type = IODEV_DIST;
|
||||
io_device->redist_vcpu = NULL;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
|
@@ -21,10 +21,19 @@ struct vgic_register_region {
|
||||
unsigned int len;
|
||||
unsigned int bits_per_irq;
|
||||
unsigned int access_flags;
|
||||
unsigned long (*read)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len);
|
||||
void (*write)(struct kvm_vcpu *vcpu, gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
union {
|
||||
unsigned long (*read)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len);
|
||||
unsigned long (*its_read)(struct kvm *kvm, struct vgic_its *its,
|
||||
gpa_t addr, unsigned int len);
|
||||
};
|
||||
union {
|
||||
void (*write)(struct kvm_vcpu *vcpu, gpa_t addr,
|
||||
unsigned int len, unsigned long val);
|
||||
void (*its_write)(struct kvm *kvm, struct vgic_its *its,
|
||||
gpa_t addr, unsigned int len,
|
||||
unsigned long val);
|
||||
};
|
||||
};
|
||||
|
||||
extern struct kvm_io_device_ops kvm_io_gic_ops;
|
||||
@@ -87,6 +96,12 @@ unsigned long vgic_data_mmio_bus_to_host(const void *val, unsigned int len);
|
||||
void vgic_data_host_to_mmio_bus(void *buf, unsigned int len,
|
||||
unsigned long data);
|
||||
|
||||
unsigned long extract_bytes(unsigned long data, unsigned int offset,
|
||||
unsigned int num);
|
||||
|
||||
u64 update_64bit_reg(u64 reg, unsigned int offset, unsigned int len,
|
||||
unsigned long val);
|
||||
|
||||
unsigned long vgic_mmio_read_raz(struct kvm_vcpu *vcpu,
|
||||
gpa_t addr, unsigned int len);
|
||||
|
||||
@@ -147,4 +162,12 @@ unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
unsigned int vgic_v3_init_dist_iodev(struct vgic_io_device *dev);
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
u64 vgic_sanitise_outer_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_inner_cacheability(u64 reg);
|
||||
u64 vgic_sanitise_shareability(u64 reg);
|
||||
u64 vgic_sanitise_field(u64 reg, u64 field_mask, int field_shift,
|
||||
u64 (*sanitise_fn)(u64));
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@@ -124,6 +124,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,20 +333,25 @@ int vgic_v2_probe(const struct gic_kvm_info *info)
|
||||
vtr = readl_relaxed(kvm_vgic_global_state.vctrl_base + GICH_VTR);
|
||||
kvm_vgic_global_state.nr_lr = (vtr & 0x3f) + 1;
|
||||
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device\n");
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = create_hyp_io_mappings(kvm_vgic_global_state.vctrl_base,
|
||||
kvm_vgic_global_state.vctrl_base +
|
||||
resource_size(&info->vctrl),
|
||||
info->vctrl.start);
|
||||
|
||||
if (ret) {
|
||||
kvm_err("Cannot map VCTRL into hyp\n");
|
||||
kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
iounmap(kvm_vgic_global_state.vctrl_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = true;
|
||||
kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
|
||||
kvm_vgic_global_state.vcpu_base = info->vcpu.start;
|
||||
kvm_vgic_global_state.type = VGIC_V2;
|
||||
kvm_vgic_global_state.max_gic_vcpus = VGIC_V2_MAX_CPUS;
|
||||
|
@@ -81,6 +81,8 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
else
|
||||
intid = val & GICH_LR_VIRTUALID;
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
|
||||
if (!irq) /* An LPI could have been unmapped. */
|
||||
continue;
|
||||
|
||||
spin_lock(&irq->irq_lock);
|
||||
|
||||
@@ -113,6 +115,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
|
||||
}
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +193,11 @@ void vgic_v3_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcrp)
|
||||
vmcrp->pmr = (vmcr & ICH_VMCR_PMR_MASK) >> ICH_VMCR_PMR_SHIFT;
|
||||
}
|
||||
|
||||
#define INITIAL_PENDBASER_VALUE \
|
||||
(GIC_BASER_CACHEABILITY(GICR_PENDBASER, INNER, RaWb) | \
|
||||
GIC_BASER_CACHEABILITY(GICR_PENDBASER, OUTER, SameAsInner) | \
|
||||
GIC_BASER_SHAREABILITY(GICR_PENDBASER, InnerShareable))
|
||||
|
||||
void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct vgic_v3_cpu_if *vgic_v3 = &vcpu->arch.vgic_cpu.vgic_v3;
|
||||
@@ -207,10 +215,12 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu)
|
||||
* way, so we force SRE to 1 to demonstrate this to the guest.
|
||||
* This goes with the spec allowing the value to be RAO/WI.
|
||||
*/
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3)
|
||||
if (vcpu->kvm->arch.vgic.vgic_model == KVM_DEV_TYPE_ARM_VGIC_V3) {
|
||||
vgic_v3->vgic_sre = ICC_SRE_EL1_SRE;
|
||||
else
|
||||
vcpu->arch.vgic_cpu.pendbaser = INITIAL_PENDBASER_VALUE;
|
||||
} else {
|
||||
vgic_v3->vgic_sre = 0;
|
||||
}
|
||||
|
||||
/* Get the show on the road... */
|
||||
vgic_v3->vgic_hcr = ICH_HCR_EN;
|
||||
@@ -296,6 +306,7 @@ out:
|
||||
int vgic_v3_probe(const struct gic_kvm_info *info)
|
||||
{
|
||||
u32 ich_vtr_el2 = kvm_call_hyp(__vgic_v3_get_ich_vtr_el2);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* The ListRegs field is 5 bits, but there is a architectural
|
||||
@@ -319,12 +330,22 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
|
||||
} else {
|
||||
kvm_vgic_global_state.vcpu_base = info->vcpu.start;
|
||||
kvm_vgic_global_state.can_emulate_gicv2 = true;
|
||||
kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv2 KVM device.\n");
|
||||
return ret;
|
||||
}
|
||||
kvm_info("vgic-v2@%llx\n", info->vcpu.start);
|
||||
}
|
||||
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
if (ret) {
|
||||
kvm_err("Cannot register GICv3 KVM device.\n");
|
||||
kvm_unregister_device_ops(KVM_DEV_TYPE_ARM_VGIC_V2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (kvm_vgic_global_state.vcpu_base == 0)
|
||||
kvm_info("disabling GICv2 emulation\n");
|
||||
kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
|
||||
|
||||
kvm_vgic_global_state.vctrl_base = NULL;
|
||||
kvm_vgic_global_state.type = VGIC_V3;
|
||||
|
@@ -33,10 +33,17 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
|
||||
|
||||
/*
|
||||
* Locking order is always:
|
||||
* vgic_cpu->ap_list_lock
|
||||
* vgic_irq->irq_lock
|
||||
* its->cmd_lock (mutex)
|
||||
* its->its_lock (mutex)
|
||||
* vgic_cpu->ap_list_lock
|
||||
* kvm->lpi_list_lock
|
||||
* vgic_irq->irq_lock
|
||||
*
|
||||
* (that is, always take the ap_list_lock before the struct vgic_irq lock).
|
||||
* If you need to take multiple locks, always take the upper lock first,
|
||||
* then the lower ones, e.g. first take the its_lock, then the irq_lock.
|
||||
* If you are already holding a lock and need to take a higher one, you
|
||||
* have to drop the lower ranking lock first and re-aquire it after having
|
||||
* taken the upper one.
|
||||
*
|
||||
* When taking more than one ap_list_lock at the same time, always take the
|
||||
* lowest numbered VCPU's ap_list_lock first, so:
|
||||
@@ -45,6 +52,41 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state;
|
||||
* spin_lock(vcpuY->arch.vgic_cpu.ap_list_lock);
|
||||
*/
|
||||
|
||||
/*
|
||||
* Iterate over the VM's list of mapped LPIs to find the one with a
|
||||
* matching interrupt ID and return a reference to the IRQ structure.
|
||||
*/
|
||||
static struct vgic_irq *vgic_get_lpi(struct kvm *kvm, u32 intid)
|
||||
{
|
||||
struct vgic_dist *dist = &kvm->arch.vgic;
|
||||
struct vgic_irq *irq = NULL;
|
||||
|
||||
spin_lock(&dist->lpi_list_lock);
|
||||
|
||||
list_for_each_entry(irq, &dist->lpi_list_head, lpi_list) {
|
||||
if (irq->intid != intid)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* This increases the refcount, the caller is expected to
|
||||
* call vgic_put_irq() later once it's finished with the IRQ.
|
||||
*/
|
||||
vgic_get_irq_kref(irq);
|
||||
goto out_unlock;
|
||||
}
|
||||
irq = NULL;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&dist->lpi_list_lock);
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
/*
|
||||
* This looks up the virtual interrupt ID to get the corresponding
|
||||
* struct vgic_irq. It also increases the refcount, so any caller is expected
|
||||
* to call vgic_put_irq() once it's finished with this IRQ.
|
||||
*/
|
||||
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||
u32 intid)
|
||||
{
|
||||
@@ -56,14 +98,43 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||
if (intid <= VGIC_MAX_SPI)
|
||||
return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
|
||||
|
||||
/* LPIs are not yet covered */
|
||||
/* LPIs */
|
||||
if (intid >= VGIC_MIN_LPI)
|
||||
return NULL;
|
||||
return vgic_get_lpi(kvm, intid);
|
||||
|
||||
WARN(1, "Looking up struct vgic_irq for reserved INTID");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* We can't do anything in here, because we lack the kvm pointer to
|
||||
* lock and remove the item from the lpi_list. So we keep this function
|
||||
* empty and use the return value of kref_put() to trigger the freeing.
|
||||
*/
|
||||
static void vgic_irq_release(struct kref *ref)
|
||||
{
|
||||
}
|
||||
|
||||
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq)
|
||||
{
|
||||
struct vgic_dist *dist;
|
||||
|
||||
if (irq->intid < VGIC_MIN_LPI)
|
||||
return;
|
||||
|
||||
if (!kref_put(&irq->refcount, vgic_irq_release))
|
||||
return;
|
||||
|
||||
dist = &kvm->arch.vgic;
|
||||
|
||||
spin_lock(&dist->lpi_list_lock);
|
||||
list_del(&irq->lpi_list);
|
||||
dist->lpi_list_count--;
|
||||
spin_unlock(&dist->lpi_list_lock);
|
||||
|
||||
kfree(irq);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_vgic_target_oracle - compute the target vcpu for an irq
|
||||
*
|
||||
@@ -236,6 +307,11 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grab a reference to the irq to reflect the fact that it is
|
||||
* now in the ap_list.
|
||||
*/
|
||||
vgic_get_irq_kref(irq);
|
||||
list_add_tail(&irq->ap_list, &vcpu->arch.vgic_cpu.ap_list_head);
|
||||
irq->vcpu = vcpu;
|
||||
|
||||
@@ -269,14 +345,17 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
|
||||
if (!irq)
|
||||
return -EINVAL;
|
||||
|
||||
if (irq->hw != mapped_irq)
|
||||
if (irq->hw != mapped_irq) {
|
||||
vgic_put_irq(kvm, irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
spin_lock(&irq->irq_lock);
|
||||
|
||||
if (!vgic_validate_injection(irq, level)) {
|
||||
/* Nothing to see here, move along... */
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(kvm, irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -288,6 +367,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
|
||||
}
|
||||
|
||||
vgic_queue_irq_unlock(kvm, irq);
|
||||
vgic_put_irq(kvm, irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -330,25 +410,28 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
|
||||
irq->hwintid = phys_irq;
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq)
|
||||
{
|
||||
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
|
||||
|
||||
BUG_ON(!irq);
|
||||
struct vgic_irq *irq;
|
||||
|
||||
if (!vgic_initialized(vcpu->kvm))
|
||||
return -EAGAIN;
|
||||
|
||||
irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
|
||||
BUG_ON(!irq);
|
||||
|
||||
spin_lock(&irq->irq_lock);
|
||||
|
||||
irq->hw = false;
|
||||
irq->hwintid = 0;
|
||||
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -386,6 +469,15 @@ retry:
|
||||
list_del(&irq->ap_list);
|
||||
irq->vcpu = NULL;
|
||||
spin_unlock(&irq->irq_lock);
|
||||
|
||||
/*
|
||||
* This vgic_put_irq call matches the
|
||||
* vgic_get_irq_kref in vgic_queue_irq_unlock,
|
||||
* where we added the LPI to the ap_list. As
|
||||
* we remove the irq from the list, we drop
|
||||
* also drop the refcount.
|
||||
*/
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -614,6 +706,15 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
|
||||
spin_lock(&irq->irq_lock);
|
||||
map_is_active = irq->hw && irq->active;
|
||||
spin_unlock(&irq->irq_lock);
|
||||
vgic_put_irq(vcpu->kvm, irq);
|
||||
|
||||
return map_is_active;
|
||||
}
|
||||
|
||||
int kvm_send_userspace_msi(struct kvm *kvm, struct kvm_msi *msi)
|
||||
{
|
||||
if (vgic_has_its(kvm))
|
||||
return vgic_its_inject_msi(kvm, msi);
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#define IS_VGIC_ADDR_UNDEF(_x) ((_x) == VGIC_ADDR_UNDEF)
|
||||
|
||||
#define INTERRUPT_ID_BITS_SPIS 10
|
||||
#define INTERRUPT_ID_BITS_ITS 16
|
||||
#define VGIC_PRI_BITS 5
|
||||
|
||||
#define vgic_irq_is_sgi(intid) ((intid) < VGIC_NR_SGIS)
|
||||
@@ -38,9 +39,13 @@ struct vgic_vmcr {
|
||||
|
||||
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
|
||||
u32 intid);
|
||||
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
|
||||
bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
|
||||
void vgic_kick_vcpus(struct kvm *kvm);
|
||||
|
||||
int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
|
||||
phys_addr_t addr, phys_addr_t alignment);
|
||||
|
||||
void vgic_v2_process_maintenance(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
void vgic_v2_populate_lr(struct kvm_vcpu *vcpu, struct vgic_irq *irq, int lr);
|
||||
@@ -59,6 +64,14 @@ int vgic_v2_map_resources(struct kvm *kvm);
|
||||
int vgic_register_dist_iodev(struct kvm *kvm, gpa_t dist_base_address,
|
||||
enum vgic_type);
|
||||
|
||||
static inline void vgic_get_irq_kref(struct vgic_irq *irq)
|
||||
{
|
||||
if (irq->intid < VGIC_MIN_LPI)
|
||||
return;
|
||||
|
||||
kref_get(&irq->refcount);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KVM_ARM_VGIC_V3
|
||||
void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu);
|
||||
void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu);
|
||||
@@ -71,6 +84,10 @@ void vgic_v3_enable(struct kvm_vcpu *vcpu);
|
||||
int vgic_v3_probe(const struct gic_kvm_info *info);
|
||||
int vgic_v3_map_resources(struct kvm *kvm);
|
||||
int vgic_register_redist_iodevs(struct kvm *kvm, gpa_t dist_base_address);
|
||||
bool vgic_has_its(struct kvm *kvm);
|
||||
int kvm_vgic_register_its_device(void);
|
||||
void vgic_enable_lpis(struct kvm_vcpu *vcpu);
|
||||
int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi);
|
||||
#else
|
||||
static inline void vgic_v3_process_maintenance(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
@@ -122,9 +139,28 @@ static inline int vgic_register_redist_iodevs(struct kvm *kvm,
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline bool vgic_has_its(struct kvm *kvm)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int kvm_vgic_register_its_device(void)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline void vgic_enable_lpis(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int vgic_its_inject_msi(struct kvm *kvm, struct kvm_msi *msi)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
|
||||
void kvm_register_vgic_device(unsigned long type);
|
||||
int kvm_register_vgic_device(unsigned long type);
|
||||
int vgic_lazy_init(struct kvm *kvm);
|
||||
int vgic_init(struct kvm *kvm);
|
||||
|
||||
|
新增問題並參考
封鎖使用者