KVM: arm64: Use the xarray as the primary sysreg/sysinsn walker
Since we always start sysreg/sysinsn handling by searching the xarray, use it as the source of the index in the correct sys_reg_desc array. This allows some cleanup, such as moving the handling of unknown sysregs in a single location. Reviewed-by: Joey Gouly <joey.gouly@arm.com> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20240214131827.2856277-16-maz@kernel.org Signed-off-by: Oliver Upton <oliver.upton@linux.dev>
This commit is contained in:
committed by
Oliver Upton
parent
19f3e7ea29
commit
cc5f84fbb0
@@ -60,7 +60,7 @@ static inline u64 translate_ttbr0_el2_to_ttbr0_el1(u64 ttbr0)
|
||||
return ttbr0 & ~GENMASK_ULL(63, 48);
|
||||
}
|
||||
|
||||
extern bool __check_nv_sr_forward(struct kvm_vcpu *vcpu);
|
||||
extern bool __check_nv_sr_forward(struct kvm_vcpu *vcpu, int *sr_idx);
|
||||
|
||||
int kvm_init_nv_sysregs(struct kvm *kvm);
|
||||
|
||||
|
||||
@@ -2007,7 +2007,7 @@ static bool check_fgt_bit(struct kvm *kvm, bool is_read,
|
||||
return !(kvm_get_sysreg_res0(kvm, sr) & BIT(tc.bit));
|
||||
}
|
||||
|
||||
bool __check_nv_sr_forward(struct kvm_vcpu *vcpu)
|
||||
bool __check_nv_sr_forward(struct kvm_vcpu *vcpu, int *sr_index)
|
||||
{
|
||||
union trap_config tc;
|
||||
enum trap_behaviour b;
|
||||
@@ -2015,9 +2015,6 @@ bool __check_nv_sr_forward(struct kvm_vcpu *vcpu)
|
||||
u32 sysreg;
|
||||
u64 esr, val;
|
||||
|
||||
if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
|
||||
return false;
|
||||
|
||||
esr = kvm_vcpu_get_esr(vcpu);
|
||||
sysreg = esr_sys64_to_sysreg(esr);
|
||||
is_read = (esr & ESR_ELx_SYS64_ISS_DIR_MASK) == ESR_ELx_SYS64_ISS_DIR_READ;
|
||||
@@ -2028,13 +2025,16 @@ bool __check_nv_sr_forward(struct kvm_vcpu *vcpu)
|
||||
* A value of 0 for the whole entry means that we know nothing
|
||||
* for this sysreg, and that it cannot be re-injected into the
|
||||
* nested hypervisor. In this situation, let's cut it short.
|
||||
*
|
||||
* Note that ultimately, we could also make use of the xarray
|
||||
* to store the index of the sysreg in the local descriptor
|
||||
* array, avoiding another search... Hint, hint...
|
||||
*/
|
||||
if (!tc.val)
|
||||
return false;
|
||||
goto local;
|
||||
|
||||
/*
|
||||
* If we're not nesting, immediately return to the caller, with the
|
||||
* sysreg index, should we have it.
|
||||
*/
|
||||
if (!vcpu_has_nv(vcpu) || is_hyp_ctxt(vcpu))
|
||||
goto local;
|
||||
|
||||
switch ((enum fgt_group_id)tc.fgt) {
|
||||
case __NO_FGT_GROUP__:
|
||||
@@ -2076,7 +2076,7 @@ bool __check_nv_sr_forward(struct kvm_vcpu *vcpu)
|
||||
case __NR_FGT_GROUP_IDS__:
|
||||
/* Something is really wrong, bail out */
|
||||
WARN_ONCE(1, "__NR_FGT_GROUP_IDS__");
|
||||
return false;
|
||||
goto local;
|
||||
}
|
||||
|
||||
if (tc.fgt != __NO_FGT_GROUP__ && check_fgt_bit(vcpu->kvm, is_read,
|
||||
@@ -2089,6 +2089,26 @@ bool __check_nv_sr_forward(struct kvm_vcpu *vcpu)
|
||||
((b & BEHAVE_FORWARD_WRITE) && !is_read))
|
||||
goto inject;
|
||||
|
||||
local:
|
||||
if (!tc.sri) {
|
||||
struct sys_reg_params params;
|
||||
|
||||
params = esr_sys64_to_params(esr);
|
||||
|
||||
/*
|
||||
* Check for the IMPDEF range, as per DDI0487 J.a,
|
||||
* D18.3.2 Reserved encodings for IMPLEMENTATION
|
||||
* DEFINED registers.
|
||||
*/
|
||||
if (!(params.Op0 == 3 && (params.CRn & 0b1011) == 0b1011))
|
||||
print_sys_reg_msg(¶ms,
|
||||
"Unsupported guest access at: %lx\n",
|
||||
*vcpu_pc(vcpu));
|
||||
kvm_inject_undefined(vcpu);
|
||||
return true;
|
||||
}
|
||||
|
||||
*sr_index = tc.sri - 1;
|
||||
return false;
|
||||
|
||||
inject:
|
||||
|
||||
+19
-44
@@ -3395,12 +3395,6 @@ int kvm_handle_cp14_32(struct kvm_vcpu *vcpu)
|
||||
return kvm_handle_cp_32(vcpu, ¶ms, cp14_regs, ARRAY_SIZE(cp14_regs));
|
||||
}
|
||||
|
||||
static bool is_imp_def_sys_reg(struct sys_reg_params *params)
|
||||
{
|
||||
// See ARM DDI 0487E.a, section D12.3.2
|
||||
return params->Op0 == 3 && (params->CRn & 0b1011) == 0b1011;
|
||||
}
|
||||
|
||||
/**
|
||||
* emulate_sys_reg - Emulate a guest access to an AArch64 system register
|
||||
* @vcpu: The VCPU pointer
|
||||
@@ -3409,46 +3403,24 @@ static bool is_imp_def_sys_reg(struct sys_reg_params *params)
|
||||
* Return: true if the system register access was successful, false otherwise.
|
||||
*/
|
||||
static bool emulate_sys_reg(struct kvm_vcpu *vcpu,
|
||||
struct sys_reg_params *params)
|
||||
struct sys_reg_params *params)
|
||||
{
|
||||
const struct sys_reg_desc *r;
|
||||
|
||||
r = find_reg(params, sys_reg_descs, ARRAY_SIZE(sys_reg_descs));
|
||||
|
||||
if (likely(r)) {
|
||||
perform_access(vcpu, params, r);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_imp_def_sys_reg(params)) {
|
||||
kvm_inject_undefined(vcpu);
|
||||
} else {
|
||||
print_sys_reg_msg(params,
|
||||
"Unsupported guest sys_reg access at: %lx [%08lx]\n",
|
||||
*vcpu_pc(vcpu), *vcpu_cpsr(vcpu));
|
||||
kvm_inject_undefined(vcpu);
|
||||
}
|
||||
print_sys_reg_msg(params,
|
||||
"Unsupported guest sys_reg access at: %lx [%08lx]\n",
|
||||
*vcpu_pc(vcpu), *vcpu_cpsr(vcpu));
|
||||
kvm_inject_undefined(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;
|
||||
@@ -3502,31 +3474,34 @@ void kvm_reset_sys_regs(struct kvm_vcpu *vcpu)
|
||||
*/
|
||||
int kvm_handle_sys_reg(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
const struct sys_reg_desc *desc = NULL;
|
||||
struct sys_reg_params params;
|
||||
unsigned long esr = kvm_vcpu_get_esr(vcpu);
|
||||
int Rt = kvm_vcpu_sys_get_rt(vcpu);
|
||||
int sr_idx;
|
||||
|
||||
trace_kvm_handle_sys_reg(esr);
|
||||
|
||||
if (__check_nv_sr_forward(vcpu))
|
||||
if (__check_nv_sr_forward(vcpu, &sr_idx))
|
||||
return 1;
|
||||
|
||||
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, ¶ms))
|
||||
return 1;
|
||||
if (params.Op0 == 2 || params.Op0 == 3)
|
||||
desc = &sys_reg_descs[sr_idx];
|
||||
else
|
||||
desc = &sys_insn_descs[sr_idx];
|
||||
|
||||
if (!params.is_write)
|
||||
vcpu_set_reg(vcpu, Rt, params.regval);
|
||||
perform_access(vcpu, ¶ms, desc);
|
||||
|
||||
return 1;
|
||||
}
|
||||
/* Read from system register? */
|
||||
if (!params.is_write &&
|
||||
(params.Op0 == 2 || params.Op0 == 3))
|
||||
vcpu_set_reg(vcpu, Rt, params.regval);
|
||||
|
||||
/* Hints, PSTATE (Op0 == 0) and System instructions (Op0 == 1) */
|
||||
return emulate_sys_instr(vcpu, ¶ms);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user