Commit 89bc63fa authored by Marc Zyngier's avatar Marc Zyngier Committed by Oliver Upton
Browse files

KVM: arm64: nv: Move system instructions to their own sys_reg_desc array



As NV results in a bunch of system instructions being trapped, it makes
sense to pull the system instructions into their own little array, where
they will eventually be joined by AT, TLBI and a bunch of other CMOs.

Based on an initial patch by Jintack Lim.

Reviewed-by: default avatarJoey Gouly <joey.gouly@arm.com>
Signed-off-by: default avatarMarc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20240214131827.2856277-13-maz@kernel.org


Signed-off-by: default avatarOliver Upton <oliver.upton@linux.dev>
parent 52571d05
Loading
Loading
Loading
Loading
+44 −15
Original line number Diff line number Diff line
@@ -2196,16 +2196,6 @@ static u64 reset_hcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r)
 * guest...
 */
static const struct sys_reg_desc sys_reg_descs[] = {
	{ SYS_DESC(SYS_DC_ISW), access_dcsw },
	{ SYS_DESC(SYS_DC_IGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_IGDSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CSW), access_dcsw },
	{ SYS_DESC(SYS_DC_CGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CGDSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CISW), access_dcsw },
	{ SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },

	DBG_BCR_BVR_WCR_WVR_EL1(0),
	DBG_BCR_BVR_WCR_WVR_EL1(1),
	{ SYS_DESC(SYS_MDCCINT_EL1), trap_debug_regs, reset_val, MDCCINT_EL1, 0 },
@@ -2737,6 +2727,18 @@ static const struct sys_reg_desc sys_reg_descs[] = {
	EL2_REG(SP_EL2, NULL, reset_unknown, 0),
};

static struct sys_reg_desc sys_insn_descs[] = {
	{ SYS_DESC(SYS_DC_ISW), access_dcsw },
	{ SYS_DESC(SYS_DC_IGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_IGDSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CSW), access_dcsw },
	{ SYS_DESC(SYS_DC_CGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CGDSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CISW), access_dcsw },
	{ SYS_DESC(SYS_DC_CIGSW), access_dcgsw },
	{ SYS_DESC(SYS_DC_CIGDSW), access_dcgsw },
};

static const struct sys_reg_desc *first_idreg;

static bool trap_dbgdidr(struct kvm_vcpu *vcpu,
@@ -3429,6 +3431,24 @@ static bool emulate_sys_reg(struct kvm_vcpu *vcpu,
	return false;
}

static int emulate_sys_instr(struct kvm_vcpu *vcpu, struct sys_reg_params *p)
{
	const struct sys_reg_desc *r;

	/* Search from the system instruction table. */
	r = find_reg(p, sys_insn_descs, ARRAY_SIZE(sys_insn_descs));

	if (likely(r)) {
		perform_access(vcpu, p, r);
	} else {
		kvm_err("Unsupported guest sys instruction at: %lx\n",
			*vcpu_pc(vcpu));
		print_sys_reg_instr(p);
		kvm_inject_undefined(vcpu);
	}
	return 1;
}

static void kvm_reset_id_regs(struct kvm_vcpu *vcpu)
{
	const struct sys_reg_desc *idreg = first_idreg;
@@ -3476,7 +3496,8 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
}

/**
 * kvm_handle_sys_reg -- handles a mrs/msr trap on a guest sys_reg access
 * kvm_handle_sys_reg -- handles a system instruction or mrs/msr instruction
 *			 trap on a guest execution
 * @vcpu: The VCPU pointer
 */
int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
@@ -3493,14 +3514,21 @@ int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
	params = esr_sys64_to_params(esr);
	params.regval = vcpu_get_reg(vcpu, Rt);

	/* System registers have Op0=={2,3}, as per DDI487 J.a C5.1.2 */
	if (params.Op0 == 2 || params.Op0 == 3) {
		if (!emulate_sys_reg(vcpu, &params))
			return 1;

		if (!params.is_write)
			vcpu_set_reg(vcpu, Rt, params.regval);

		return 1;
	}

	/* Hints, PSTATE (Op0 == 0) and System instructions (Op0 == 1) */
	return emulate_sys_instr(vcpu, &params);
}

/******************************************************************************
 * Userspace API
 *****************************************************************************/
@@ -3952,6 +3980,7 @@ int __init kvm_sys_reg_table_init(void)
	valid &= check_sysreg_table(cp15_regs, ARRAY_SIZE(cp15_regs), true);
	valid &= check_sysreg_table(cp15_64_regs, ARRAY_SIZE(cp15_64_regs), true);
	valid &= check_sysreg_table(invariant_sys_regs, ARRAY_SIZE(invariant_sys_regs), false);
	valid &= check_sysreg_table(sys_insn_descs, ARRAY_SIZE(sys_insn_descs), false);

	if (!valid)
		return -EINVAL;