Commit 96c2f033 authored by Marc Zyngier's avatar Marc Zyngier Committed by Oliver Upton
Browse files

KVM: arm64: nv: Plumb handling of GICv3 EL2 accesses



Wire the handling of all GICv3 EL2 registers, and provide emulation
for all the non memory-backed registers (ICC_SRE_EL2, ICH_VTR_EL2,
ICH_MISR_EL2, ICH_ELRSR_EL2, and ICH_EISR_EL2).

Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20250225172930.1850838-7-maz@kernel.org


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 182f1596
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -23,7 +23,7 @@ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \
	 vgic/vgic-v3.o vgic/vgic-v4.o \
	 vgic/vgic-mmio.o vgic/vgic-mmio-v2.o \
	 vgic/vgic-mmio-v3.o vgic/vgic-kvm-device.o \
	 vgic/vgic-its.o vgic/vgic-debug.o
	 vgic/vgic-its.o vgic/vgic-debug.o vgic/vgic-v3-nested.o

kvm-$(CONFIG_HW_PERF_EVENTS)  += pmu-emul.o pmu.o
kvm-$(CONFIG_ARM64_PTR_AUTH)  += pauth.o
+94 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/mm.h>
#include <linux/printk.h>
#include <linux/uaccess.h>
#include <linux/irqchip/arm-gic-v3.h>

#include <asm/arm_pmuv3.h>
#include <asm/cacheflush.h>
@@ -531,7 +532,13 @@ static bool access_gic_sre(struct kvm_vcpu *vcpu,
	if (p->is_write)
		return ignore_write(vcpu, p);

	if (p->Op1 == 4) {	/* ICC_SRE_EL2 */
		p->regval = (ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE |
			     ICC_SRE_EL1_DIB | ICC_SRE_EL1_DFB);
	} else {		/* ICC_SRE_EL1 */
		p->regval = vcpu->arch.vgic_cpu.vgic_v3.vgic_sre;
	}

	return true;
}

@@ -2426,6 +2433,59 @@ static bool access_zcr_el2(struct kvm_vcpu *vcpu,
	vq = SYS_FIELD_GET(ZCR_ELx, LEN, p->regval) + 1;
	vq = min(vq, vcpu_sve_max_vq(vcpu));
	vcpu_write_sys_reg(vcpu, vq - 1, ZCR_EL2);

	return true;
}

static bool access_gic_vtr(struct kvm_vcpu *vcpu,
			   struct sys_reg_params *p,
			   const struct sys_reg_desc *r)
{
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);

	p->regval = kvm_vgic_global_state.ich_vtr_el2;
	p->regval &= ~(ICH_VTR_EL2_DVIM 	|
		       ICH_VTR_EL2_A3V		|
		       ICH_VTR_EL2_IDbits);
	p->regval |= ICH_VTR_EL2_nV4;

	return true;
}

static bool access_gic_misr(struct kvm_vcpu *vcpu,
			    struct sys_reg_params *p,
			    const struct sys_reg_desc *r)
{
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);

	p->regval = vgic_v3_get_misr(vcpu);

	return true;
}

static bool access_gic_eisr(struct kvm_vcpu *vcpu,
			    struct sys_reg_params *p,
			    const struct sys_reg_desc *r)
{
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);

	p->regval = vgic_v3_get_eisr(vcpu);

	return true;
}

static bool access_gic_elrsr(struct kvm_vcpu *vcpu,
			     struct sys_reg_params *p,
			     const struct sys_reg_desc *r)
{
	if (p->is_write)
		return write_to_read_only(vcpu, p, r);

	p->regval = vgic_v3_get_elrsr(vcpu);

	return true;
}

@@ -3102,7 +3162,40 @@ static const struct sys_reg_desc sys_reg_descs[] = {
	EL2_REG(RVBAR_EL2, access_rw, reset_val, 0),
	{ SYS_DESC(SYS_RMR_EL2), undef_access },

	EL2_REG_VNCR(ICH_AP0R0_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP0R1_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP0R2_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP0R3_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP1R0_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP1R1_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP1R2_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_AP1R3_EL2, reset_val, 0),

	{ SYS_DESC(SYS_ICC_SRE_EL2), access_gic_sre },

	EL2_REG_VNCR(ICH_HCR_EL2, reset_val, 0),
	{ SYS_DESC(SYS_ICH_VTR_EL2), access_gic_vtr },
	{ SYS_DESC(SYS_ICH_MISR_EL2), access_gic_misr },
	{ SYS_DESC(SYS_ICH_EISR_EL2), access_gic_eisr },
	{ SYS_DESC(SYS_ICH_ELRSR_EL2), access_gic_elrsr },
	EL2_REG_VNCR(ICH_VMCR_EL2, reset_val, 0),

	EL2_REG_VNCR(ICH_LR0_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR1_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR2_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR3_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR4_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR5_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR6_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR7_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR8_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR9_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR10_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR11_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR12_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR13_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR14_EL2, reset_val, 0),
	EL2_REG_VNCR(ICH_LR15_EL2, reset_val, 0),

	EL2_REG(CONTEXTIDR_EL2, access_rw, reset_val, 0),
	EL2_REG(TPIDR_EL2, access_rw, reset_val, 0),
+125 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <linux/cpu.h>
#include <linux/kvm.h>
#include <linux/kvm_host.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/uaccess.h>

#include <kvm/arm_vgic.h>

#include <asm/kvm_arm.h>
#include <asm/kvm_emulate.h>
#include <asm/kvm_nested.h>

#include "vgic.h"

#define ICH_LRN(n)	(ICH_LR0_EL2 + (n))

struct mi_state {
	u16	eisr;
	u16	elrsr;
	bool	pend;
};

/*
 * Nesting GICv3 support
 *
 * System register emulation:
 *
 * We get two classes of registers:
 *
 * - those backed by memory (LRs, APRs, HCR, VMCR): L1 can freely access
 *   them, and L0 doesn't see a thing.
 *
 * - those that always trap (ELRSR, EISR, MISR): these are status registers
 *   that are built on the fly based on the in-memory state.
 *
 * Only L1 can access the ICH_*_EL2 registers. A non-NV L2 obviously cannot,
 * and a NV L2 would either access the VNCR page provided by L1 (memory
 * based registers), or see the access redirected to L1 (registers that
 * trap) thanks to NV being set by L1.
 */

static bool lr_triggers_eoi(u64 lr)
{
	return !(lr & (ICH_LR_STATE | ICH_LR_HW)) && (lr & ICH_LR_EOI);
}

static void vgic_compute_mi_state(struct kvm_vcpu *vcpu, struct mi_state *mi_state)
{
	u16 eisr = 0, elrsr = 0;
	bool pend = false;

	for (int i = 0; i < kvm_vgic_global_state.nr_lr; i++) {
		u64 lr = __vcpu_sys_reg(vcpu, ICH_LRN(i));

		if (lr_triggers_eoi(lr))
			eisr |= BIT(i);
		if (!(lr & ICH_LR_STATE))
			elrsr |= BIT(i);
		pend |= (lr & ICH_LR_PENDING_BIT);
	}

	mi_state->eisr	= eisr;
	mi_state->elrsr	= elrsr;
	mi_state->pend	= pend;
}

u16 vgic_v3_get_eisr(struct kvm_vcpu *vcpu)
{
	struct mi_state mi_state;

	vgic_compute_mi_state(vcpu, &mi_state);
	return mi_state.eisr;
}

u16 vgic_v3_get_elrsr(struct kvm_vcpu *vcpu)
{
	struct mi_state mi_state;

	vgic_compute_mi_state(vcpu, &mi_state);
	return mi_state.elrsr;
}

u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu)
{
	struct mi_state mi_state;
	u64 reg = 0, hcr, vmcr;

	hcr = __vcpu_sys_reg(vcpu, ICH_HCR_EL2);
	vmcr = __vcpu_sys_reg(vcpu, ICH_VMCR_EL2);

	vgic_compute_mi_state(vcpu, &mi_state);

	if (mi_state.eisr)
		reg |= ICH_MISR_EL2_EOI;

	if (__vcpu_sys_reg(vcpu, ICH_HCR_EL2) & ICH_HCR_EL2_UIE) {
		int used_lrs = kvm_vgic_global_state.nr_lr;

		used_lrs -= hweight16(mi_state.elrsr);
		reg |= (used_lrs <= 1) ? ICH_MISR_EL2_U : 0;
	}

	if ((hcr & ICH_HCR_EL2_LRENPIE) && FIELD_GET(ICH_HCR_EL2_EOIcount_MASK, hcr))
		reg |= ICH_MISR_EL2_LRENP;

	if ((hcr & ICH_HCR_EL2_NPIE) && !mi_state.pend)
		reg |= ICH_MISR_EL2_NP;

	if ((hcr & ICH_HCR_EL2_VGrp0EIE) && (vmcr & ICH_VMCR_ENG0_MASK))
		reg |= ICH_MISR_EL2_VGrp0E;

	if ((hcr & ICH_HCR_EL2_VGrp0DIE) && !(vmcr & ICH_VMCR_ENG0_MASK))
		reg |= ICH_MISR_EL2_VGrp0D;

	if ((hcr & ICH_HCR_EL2_VGrp1EIE) && (vmcr & ICH_VMCR_ENG1_MASK))
		reg |= ICH_MISR_EL2_VGrp1E;

	if ((hcr & ICH_HCR_EL2_VGrp1DIE) && !(vmcr & ICH_VMCR_ENG1_MASK))
		reg |= ICH_MISR_EL2_VGrp1D;

	return reg;
}
+4 −0
Original line number Diff line number Diff line
@@ -389,6 +389,10 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu);
void kvm_vgic_load(struct kvm_vcpu *vcpu);
void kvm_vgic_put(struct kvm_vcpu *vcpu);

u16 vgic_v3_get_eisr(struct kvm_vcpu *vcpu);
u16 vgic_v3_get_elrsr(struct kvm_vcpu *vcpu);
u64 vgic_v3_get_misr(struct kvm_vcpu *vcpu);

#define irqchip_in_kernel(k)	(!!((k)->arch.vgic.in_kernel))
#define vgic_initialized(k)	((k)->arch.vgic.initialized)
#define vgic_ready(k)		((k)->arch.vgic.ready)