summaryrefslogtreecommitdiff
path: root/arch/arm64/kvm/vgic
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm64/kvm/vgic')
-rw-r--r--arch/arm64/kvm/vgic/vgic-init.c8
-rw-r--r--arch/arm64/kvm/vgic/vgic-its.c26
-rw-r--r--arch/arm64/kvm/vgic/vgic-v2.c3
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3-nested.c2
-rw-r--r--arch/arm64/kvm/vgic/vgic-v3.c107
-rw-r--r--arch/arm64/kvm/vgic/vgic-v5.c5
-rw-r--r--arch/arm64/kvm/vgic/vgic.c7
-rw-r--r--arch/arm64/kvm/vgic/vgic.h19
8 files changed, 125 insertions, 52 deletions
diff --git a/arch/arm64/kvm/vgic/vgic-init.c b/arch/arm64/kvm/vgic/vgic-init.c
index 907057881b26..4012df6002ea 100644
--- a/arch/arm64/kvm/vgic/vgic-init.c
+++ b/arch/arm64/kvm/vgic/vgic-init.c
@@ -176,6 +176,7 @@ int kvm_vgic_create(struct kvm *kvm, u32 type)
}
kvm->arch.vgic.vgic_model = 0;
+ kvm->arch.vgic.in_kernel = false;
goto out_unlock;
}
@@ -210,6 +211,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;
+ if (dist->spis)
+ return 0;
+
dist->active_spis = (atomic_t)ATOMIC_INIT(0);
dist->spis = kzalloc_objs(struct vgic_irq, nr_spis, GFP_KERNEL_ACCOUNT);
if (!dist->spis)
@@ -787,7 +791,8 @@ int kvm_vgic_hyp_init(void)
if (has_mask && !gic_kvm_info->maint_irq) {
kvm_err("No vgic maintenance irq\n");
- return -ENXIO;
+ ret = -ENXIO;
+ goto out_free;
}
/*
@@ -820,6 +825,7 @@ int kvm_vgic_hyp_init(void)
kvm_vgic_global_state.maint_irq = gic_kvm_info->maint_irq;
+out_free:
kfree(gic_kvm_info);
gic_kvm_info = NULL;
diff --git a/arch/arm64/kvm/vgic/vgic-its.c b/arch/arm64/kvm/vgic/vgic-its.c
index 36ab3e492915..9e782a4fea7e 100644
--- a/arch/arm64/kvm/vgic/vgic-its.c
+++ b/arch/arm64/kvm/vgic/vgic-its.c
@@ -2035,15 +2035,16 @@ static u32 compute_next_devid_offset(struct list_head *h,
static u32 compute_next_eventid_offset(struct list_head *h, struct its_ite *ite)
{
- struct its_ite *next;
- u32 next_offset;
+ struct its_ite *next = ite;
- if (list_is_last(&ite->ite_list, h))
- return 0;
- next = list_next_entry(ite, ite_list);
- next_offset = next->event_id - ite->event_id;
+ /* Point at the next ITE that vgic_its_save_ite() stores as valid. */
+ list_for_each_entry_continue(next, h, ite_list) {
+ if (next->collection)
+ return min_t(u32, next->event_id - ite->event_id,
+ VITS_ITE_MAX_EVENTID_OFFSET);
+ }
- return min_t(u32, next_offset, VITS_ITE_MAX_EVENTID_OFFSET);
+ return 0;
}
/**
@@ -2119,6 +2120,14 @@ static int vgic_its_save_ite(struct vgic_its *its, struct its_device *dev,
u32 next_offset;
u64 val;
+ /*
+ * MAPC with V=0 keeps the ITEs mapped but drops their collection,
+ * and with it the ICID. Save a zeroed entry, which the restore path
+ * reads back as invalid.
+ */
+ if (!ite->collection)
+ return vgic_its_write_entry_lock(its, gpa, 0ULL, ite);
+
next_offset = compute_next_eventid_offset(&dev->itt_head, ite);
val = ((u64)next_offset << KVM_ITS_ITE_NEXT_SHIFT) |
((u64)ite->irq->intid << KVM_ITS_ITE_PINTID_SHIFT) |
@@ -2532,6 +2541,9 @@ static int vgic_its_save_collection_table(struct vgic_its *its)
max_size = GITS_BASER_NR_PAGES(baser) * SZ_64K;
list_for_each_entry(collection, &its->collection_list, coll_list) {
+ if (!vgic_its_check_id(its, baser, collection->collection_id, NULL))
+ return -EINVAL;
+
ret = vgic_its_save_cte(its, collection, gpa);
if (ret)
return ret;
diff --git a/arch/arm64/kvm/vgic/vgic-v2.c b/arch/arm64/kvm/vgic/vgic-v2.c
index cafa3cb32bda..7182f63fc938 100644
--- a/arch/arm64/kvm/vgic/vgic-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-v2.c
@@ -170,8 +170,9 @@ void vgic_v2_deactivate(struct kvm_vcpu *vcpu, u32 val)
/* Make sure we're in the same context as LR handling */
local_irq_save(flags);
+ /* Guest-supplied INTID: out of range yields no irq, so ignore it */
irq = vgic_get_vcpu_irq(vcpu, val);
- if (WARN_ON_ONCE(!irq))
+ if (!irq)
goto out;
/* See the corresponding v3 code for the rationale */
diff --git a/arch/arm64/kvm/vgic/vgic-v3-nested.c b/arch/arm64/kvm/vgic/vgic-v3-nested.c
index 5c69fa615823..e3e84d7f5ad5 100644
--- a/arch/arm64/kvm/vgic/vgic-v3-nested.c
+++ b/arch/arm64/kvm/vgic/vgic-v3-nested.c
@@ -152,7 +152,7 @@ static void vgic_compute_mi_state(struct kvm_vcpu *vcpu, struct mi_state *mi_sta
eisr |= BIT(i);
if (!(lr & ICH_LR_STATE))
elrsr |= BIT(i);
- pend |= (lr & ICH_LR_PENDING_BIT);
+ pend |= (lr & ICH_LR_STATE) == ICH_LR_PENDING_BIT;
}
mi_state->eisr = eisr;
diff --git a/arch/arm64/kvm/vgic/vgic-v3.c b/arch/arm64/kvm/vgic/vgic-v3.c
index 7aa417440f6a..726e20a1da6e 100644
--- a/arch/arm64/kvm/vgic/vgic-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-v3.c
@@ -496,9 +496,9 @@ void vgic_v3_reset(struct kvm_vcpu *vcpu)
}
vcpu->arch.vgic_cpu.num_id_bits = FIELD_GET(ICH_VTR_EL2_IDbits,
- kvm_vgic_global_state.ich_vtr_el2);
+ vgic_ich_vtr());
vcpu->arch.vgic_cpu.num_pri_bits = FIELD_GET(ICH_VTR_EL2_PRIbits,
- kvm_vgic_global_state.ich_vtr_el2) + 1;
+ vgic_ich_vtr()) + 1;
}
void vcpu_set_ich_hcr(struct kvm_vcpu *vcpu)
@@ -617,9 +617,13 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
bool is_pending;
bool stored;
+ irq = vgic_get_irq(kvm, index);
+ if (!irq)
+ continue;
+
vcpu = irq->target_vcpu;
if (!vcpu)
- continue;
+ goto put_irq;
pendbase = GICR_PENDBASER_ADDRESS(vcpu->arch.vgic_cpu.pendbaser);
@@ -630,7 +634,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
if (ptr != last_ptr) {
ret = kvm_read_guest_lock(kvm, ptr, &val, 1);
if (ret)
- goto out;
+ goto put_irq;
last_ptr = ptr;
}
@@ -642,7 +646,7 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
vgic_v4_get_vlpi_state(irq, &is_pending);
if (stored == is_pending)
- continue;
+ goto put_irq;
if (is_pending)
val |= 1 << bit_nr;
@@ -650,6 +654,8 @@ int vgic_v3_save_pending_tables(struct kvm *kvm)
val &= ~(1 << bit_nr);
ret = vgic_write_guest_lock(kvm, ptr, &val, 1);
+put_irq:
+ vgic_put_irq(kvm, irq);
if (ret)
goto out;
}
@@ -815,27 +821,9 @@ static int __init early_gicv4_enable(char *buf)
}
early_param("kvm-arm.vgic_v4_enable", early_gicv4_enable);
-static const struct midr_range broken_seis[] = {
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_PRO),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_PRO),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_ICESTORM_MAX),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M1_FIRESTORM_MAX),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_PRO),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_PRO),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_BLIZZARD_MAX),
- MIDR_ALL_VERSIONS(MIDR_APPLE_M2_AVALANCHE_MAX),
- {},
-};
-
-static bool vgic_v3_broken_seis(void)
-{
- return (is_kernel_in_hyp_mode() &&
- is_midr_in_range_list(broken_seis) &&
- (read_sysreg_s(SYS_ICH_VTR_EL2) & ICH_VTR_EL2_SEIS));
+static __always_inline bool vgic_v3_broken_seis(void)
+{
+ return cpus_have_cap(ARM64_WORKAROUND_GICv3_BROKEN_SEIS);
}
void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
@@ -882,6 +870,61 @@ void noinstr kvm_compute_ich_hcr_trap_bits(struct alt_instr *alt,
*updptr = cpu_to_le32(insn);
}
+void noinstr kvm_patch_ich_vtr_el2(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr,
+ int nr_inst)
+{
+ struct arm_smccc_res res = {};
+ u32 insn, oinsn, rd, vtr;
+
+ /* No KVM? Nothing to do */
+ if (!is_hyp_mode_available())
+ return;
+
+ /* No v3, compat, nor the fruity erzatz of a GIC? Bugger off */
+ if (!cpus_have_cap(ARM64_HAS_GICV5_LEGACY) &&
+ !cpus_have_cap(ARM64_HAS_GICV3_CPUIF) &&
+ !vgic_v3_broken_seis())
+ return;
+
+ /*
+ * At the point where this is called, we are guaranteed that if
+ * we're running at EL1, then the EL2 stubs are still in place.
+ */
+ if (is_kernel_in_hyp_mode())
+ res.a1 = read_sysreg_s(SYS_ICH_VTR_EL2);
+ else
+ arm_smccc_1_1_hvc(HVC_GET_ICH_VTR_EL2, &res);
+
+ if (res.a0 == HVC_STUB_ERR)
+ return;
+
+ vtr = res.a1;
+
+ if (vgic_v3_broken_seis())
+ vtr &= ~ICH_VTR_EL2_SEIS;
+
+ /* Compute target register */
+ oinsn = le32_to_cpu(*origptr);
+ rd = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RD, oinsn);
+
+ /* movz rd, #(vtr & 0xffff) */
+ insn = aarch64_insn_gen_movewide(rd,
+ (u16)vtr,
+ 0,
+ AARCH64_INSN_VARIANT_64BIT,
+ AARCH64_INSN_MOVEWIDE_ZERO);
+ *updptr++ = cpu_to_le32(insn);
+
+ /* movk rd, #((vtr >> 16) & 0xffff), lsl #16 */
+ insn = aarch64_insn_gen_movewide(rd,
+ (u16)(vtr >> 16),
+ 16,
+ AARCH64_INSN_VARIANT_64BIT,
+ AARCH64_INSN_MOVEWIDE_KEEP);
+ *updptr++ = cpu_to_le32(insn);
+}
+
void vgic_v3_enable_cpuif_traps(void)
{
u64 traps = vgic_ich_hcr_trap_bits();
@@ -905,12 +948,12 @@ void vgic_v3_enable_cpuif_traps(void)
*/
int vgic_v3_probe(const struct gic_kvm_info *info)
{
- u64 ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
+ u64 ich_vtr_el2;
bool has_v2;
int ret;
- has_v2 = ich_vtr_el2 >> 63;
- ich_vtr_el2 = (u32)ich_vtr_el2;
+ has_v2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
+ ich_vtr_el2 = vgic_ich_vtr();
/*
* The ListRegs field is 5 bits, but there is an architectural
@@ -918,7 +961,6 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
*/
kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
kvm_vgic_global_state.can_emulate_gicv2 = false;
- kvm_vgic_global_state.ich_vtr_el2 = ich_vtr_el2;
/* GICv4 support? */
if (info->has_v4) {
@@ -965,11 +1007,6 @@ int vgic_v3_probe(const struct gic_kvm_info *info)
if (has_v2)
static_branch_enable(&vgic_v3_has_v2_compat);
- if (vgic_v3_broken_seis()) {
- kvm_info("GICv3 with broken locally generated SEI\n");
- kvm_vgic_global_state.ich_vtr_el2 &= ~ICH_VTR_EL2_SEIS;
- }
-
vgic_v3_enable_cpuif_traps();
kvm_vgic_global_state.vctrl_base = NULL;
diff --git a/arch/arm64/kvm/vgic/vgic-v5.c b/arch/arm64/kvm/vgic/vgic-v5.c
index d4789ff3e740..4e3e4db24e77 100644
--- a/arch/arm64/kvm/vgic/vgic-v5.c
+++ b/arch/arm64/kvm/vgic/vgic-v5.c
@@ -40,7 +40,6 @@ static void vgic_v5_get_implemented_ppis(void)
int vgic_v5_probe(const struct gic_kvm_info *info)
{
bool v5_registered = false;
- u64 ich_vtr_el2;
int ret;
kvm_vgic_global_state.type = VGIC_V5;
@@ -83,14 +82,12 @@ skip_v5:
}
kvm_vgic_global_state.has_gcie_v3_compat = true;
- ich_vtr_el2 = kvm_call_hyp_ret(__vgic_v3_get_gic_config);
- kvm_vgic_global_state.ich_vtr_el2 = (u32)ich_vtr_el2;
/*
* The ListRegs field is 5 bits, but there is an architectural
* maximum of 16 list registers. Just ignore bit 4...
*/
- kvm_vgic_global_state.nr_lr = (ich_vtr_el2 & 0xf) + 1;
+ kvm_vgic_global_state.nr_lr = (vgic_ich_vtr() & 0xf) + 1;
ret = kvm_register_vgic_device(KVM_DEV_TYPE_ARM_VGIC_V3);
if (ret) {
diff --git a/arch/arm64/kvm/vgic/vgic.c b/arch/arm64/kvm/vgic/vgic.c
index 74bace10a22e..b25303d9919f 100644
--- a/arch/arm64/kvm/vgic/vgic.c
+++ b/arch/arm64/kvm/vgic/vgic.c
@@ -93,8 +93,9 @@ struct vgic_irq *vgic_get_irq(struct kvm *kvm, u32 intid)
/* SPIs */
if (intid >= VGIC_NR_PRIVATE_IRQS &&
intid < (kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS)) {
- intid = array_index_nospec(intid, kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS);
- return &kvm->arch.vgic.spis[intid - VGIC_NR_PRIVATE_IRQS];
+ intid -= VGIC_NR_PRIVATE_IRQS;
+ intid = array_index_nospec(intid, kvm->arch.vgic.nr_spis);
+ return &kvm->arch.vgic.spis[intid];
}
/* LPIs */
@@ -117,6 +118,8 @@ struct vgic_irq *vgic_get_vcpu_irq(struct kvm_vcpu *vcpu, u32 intid)
switch (type) {
case KVM_DEV_TYPE_ARM_VGIC_V5:
intid = vgic_v5_get_hwirq_id(intid);
+ if (intid >= VGIC_V5_NR_PRIVATE_IRQS)
+ return NULL;
intid = array_index_nospec(intid, VGIC_V5_NR_PRIVATE_IRQS);
break;
default:
diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h
index f45f7e3ec4d6..b71d486ae514 100644
--- a/arch/arm64/kvm/vgic/vgic.h
+++ b/arch/arm64/kvm/vgic/vgic.h
@@ -71,11 +71,28 @@
ICH_VTR_EL2_IDbits)
#define KVM_ICH_VTR_EL2_RES1 ICH_VTR_EL2_nV4
+void kvm_patch_ich_vtr_el2(struct alt_instr *alt,
+ __le32 *origptr, __le32 *updptr, int nr_inst);
+
+static inline u64 vgic_ich_vtr(void)
+{
+ u64 vtr;
+
+ /* All non-RES0 bits are in the bottom 32bits */
+ asm volatile(ALTERNATIVE_CB("movz %0, #0\n"
+ "movk %0, #0, lsl #16\n",
+ ARM64_ALWAYS_SYSTEM,
+ kvm_patch_ich_vtr_el2)
+ : "=r" (vtr));
+
+ return vtr;
+}
+
static inline u64 kvm_get_guest_vtr_el2(void)
{
u64 vtr;
- vtr = kvm_vgic_global_state.ich_vtr_el2;
+ vtr = vgic_ich_vtr();
vtr &= ~KVM_ICH_VTR_EL2_RES0;
vtr |= KVM_ICH_VTR_EL2_RES1;