Merge branch 'kvm-arm64/misc-7.3' into next

* kvm-arm64/misc-7.3:
  : Miscellaneous fixes for KVM/arm64, 7.3
  :
  :  - Fixes for saving invalid table entries as part of saving the ITS
  :    tables (Fuad Tabba)
  :
  :  - Don't reallocate the SPI array for re-attempted vgic_init(), avoiding
  :    a memory leak (Fuad Tabba)
  :
  :  - Hold a reference on an LPI when saving the pending state (Qihang)
  :
  :  - Don't WARN for out-of-range, guest-supplied INTID (Karl)
  :
  :  - Avoid corrupting GPRs for 32-bit CP64 reads (Karl)
  :
  :  - Reset 'in kernel' VGIC state when private IRQ allocation fails (Fuad)
  :
  :  - Avoid kallsyms lookup in nVHE panic unless the host stage-2 is also
  :    disabled (Vincent)
  :
  :  - Disregard Pending+Active state when computing maintenance IRQ for
  :    ICH_MISR_EL2.NP (Kajetan)
  :
  :  - Various Sashiko-identified issues dealing with GICv5 (Sascha)
  :
  :  - Fix CPU onlining in pKVM due to mismatched accesses when the MMU is
  :    disabled (Will)
  KVM: arm64: Validate GICv5 timer PPIs before claiming ownership
  KVM: arm64: vgic: Reject out-of-range GICv5 PPI IDs
  KVM: arm64: vgic: Prevent speculative SPI array underflow
  KVM: arm64: vgic: Free gic_kvm_info on initialization failure
  KVM: arm64: Avoid mismatched accesses to 'struct kvm_nvhe_init_params'
  KVM: arm64: vgic: Fix detection of MI on no pending LR
  KVM: arm64: Drop %pB on nVHE panic when stage-2 is active
  KVM: arm64: vgic: Reset in_kernel on private IRQ allocation failure
  KVM: arm64: GICv2: Don't WARN on out-of-range GICV_DIR INTID
  KVM: arm64: Preserve GPRs for AArch32 CP64 reads generating an UNDEF
  KVM: arm64: vgic-v3: take an LPI reference in vgic_v3_save_pending_tables
  KVM: arm64: vgic-its: Point saved ITEs at the next valid entry
  KVM: arm64: vgic-its: Don't save collections the table cannot hold
  KVM: arm64: vgic: Don't leak the SPI array when init is retried
  KVM: arm64: vgic-its: Don't dereference a NULL collection on ITT save

Signed-off-by: Oliver Upton <oupton@kernel.org>
This commit is contained in:
Oliver Upton 2026-08-19 14:08:24 -07:00
commit aa8e5dc6a7
13 changed files with 70 additions and 31 deletions

View File

@ -215,7 +215,6 @@ struct kvm_nvhe_init_params {
unsigned long hcr_el2;
unsigned long vttbr;
unsigned long vtcr;
unsigned long tmp;
};
/*

View File

@ -124,7 +124,6 @@ int main(void)
DEFINE(NVHE_INIT_HCR_EL2, offsetof(struct kvm_nvhe_init_params, hcr_el2));
DEFINE(NVHE_INIT_VTTBR, offsetof(struct kvm_nvhe_init_params, vttbr));
DEFINE(NVHE_INIT_VTCR, offsetof(struct kvm_nvhe_init_params, vtcr));
DEFINE(NVHE_INIT_TMP, offsetof(struct kvm_nvhe_init_params, tmp));
#endif
#ifdef CONFIG_CPU_PM
DEFINE(CPU_CTX_SP, offsetof(struct cpu_suspend_ctx, sp));

View File

@ -1535,13 +1535,14 @@ static bool timer_irqs_are_valid(struct kvm_vcpu *vcpu)
ctx = vcpu_get_timer(vcpu, i);
irq = timer_irq(ctx);
if (kvm_vgic_set_owner(vcpu, irq, ctx))
break;
/* With GICv5, the default PPI is what you get -- nothing else */
if (vgic_is_v5(vcpu->kvm) && irq != get_vgic_ppi(vcpu->kvm, default_ppi[i]))
break;
if (kvm_vgic_set_owner(vcpu, irq, ctx))
break;
/*
* We know by construction that we only have PPIs, so all values
* are less than 32 for non-GICv5 VGICs. On GICv5, they are

View File

@ -530,10 +530,20 @@ void handle_exit_early(struct kvm_vcpu *vcpu, int exception_index)
kvm_handle_guest_serror(vcpu, kvm_vcpu_get_esr(vcpu));
}
static bool nvhe_hyp_panic_host_s2_disabled(void)
{
return !is_protected_kvm_enabled() ||
IS_ENABLED(CONFIG_PKVM_DISABLE_STAGE2_ON_PANIC);
}
static void print_nvhe_hyp_panic(const char *name, u64 panic_addr)
{
kvm_err("nVHE hyp %s at: [<%016llx>] %pB!\n", name, panic_addr,
(void *)(panic_addr + kaslr_offset()));
/* Kallsyms might not be mapped in the host stage-2 */
if (nvhe_hyp_panic_host_s2_disabled())
kvm_err("nVHE hyp %s at: [<%016llx>] %pB!\n", name, panic_addr,
(void *)(panic_addr + kaslr_offset()));
else
kvm_err("nVHE hyp %s at: %016llx!\n", name, panic_addr);
}
static void kvm_nvhe_report_cfi_failure(u64 panic_addr)
@ -561,8 +571,7 @@ void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr,
unsigned int line = 0;
/* All hyp bugs, including warnings, are treated as fatal. */
if (!is_protected_kvm_enabled() ||
IS_ENABLED(CONFIG_PKVM_DISABLE_STAGE2_ON_PANIC)) {
if (nvhe_hyp_panic_host_s2_disabled()) {
struct bug_entry *bug = find_bug(elr_in_kimg);
if (bug)

View File

@ -106,16 +106,19 @@ SYM_CODE_START_LOCAL(___kvm_hyp_init)
and x2, x1, x2
cbz x2, 1f
// hVHE: Replay the EL2 setup to account for the E2H bit
// TPIDR_EL2 is used to preserve x0 across the macro maze...
/*
* hVHE: Replay the EL2 setup to account for the E2H bit
* TPIDR_EL2 and FAR_EL2 are used to preserve x0 and LR across
* the macro maze...
*/
isb
msr tpidr_el2, x0
str lr, [x0, #NVHE_INIT_TMP]
msr far_el2, lr
bl __kvm_init_el2_state
mrs lr, far_el2
mrs x0, tpidr_el2
ldr lr, [x0, #NVHE_INIT_TMP]
1:
ldr x1, [x0, #NVHE_INIT_TPIDR_EL2]

View File

@ -4969,10 +4969,8 @@ static int kvm_handle_cp_64(struct kvm_vcpu *vcpu,
* Make a 64-bit value out of Rt and Rt2. As we use the same trap
* backends between AArch32 and AArch64, we get away with it.
*/
if (params.is_write) {
params.regval = vcpu_get_reg(vcpu, Rt) & 0xffffffff;
params.regval |= vcpu_get_reg(vcpu, Rt2) << 32;
}
params.regval = vcpu_get_reg(vcpu, Rt) & 0xffffffff;
params.regval |= vcpu_get_reg(vcpu, Rt2) << 32;
/*
* If the table contains a handler, handle the

View File

@ -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;

View File

@ -2026,15 +2026,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;
}
/**
@ -2110,6 +2111,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) |
@ -2523,6 +2532,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;

View File

@ -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 */

View File

@ -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;

View File

@ -611,9 +611,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);
@ -624,7 +628,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;
}
@ -636,7 +640,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;
@ -644,6 +648,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;
}

View File

@ -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:

View File

@ -65,6 +65,8 @@
switch (t) { \
case KVM_DEV_TYPE_ARM_VGIC_V5: \
__ret = is_v5_type(GICV5_HWIRQ_TYPE_PPI, (i)); \
__ret &= FIELD_GET(GICV5_HWIRQ_ID, (i)) < \
VGIC_V5_NR_PRIVATE_IRQS; \
break; \
default: \
__ret = (i) >= VGIC_NR_SGIS; \