KVM: arm64: Use the xarray as the primary sysreg/sysinsn walker
authorMarc Zyngier <maz@kernel.org>
Wed, 14 Feb 2024 13:18:16 +0000 (13:18 +0000)
committerOliver Upton <oliver.upton@linux.dev>
Mon, 19 Feb 2024 17:13:01 +0000 (17:13 +0000)
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>
arch/arm64/include/asm/kvm_nested.h
arch/arm64/kvm/emulate-nested.c
arch/arm64/kvm/sys_regs.c

index 4882905357f43b6b5146f5d4f38d13b100f8dbb8..68465f87d30883f73e4f19c984cdc626f6b216d0 100644 (file)
@@ -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);
 
index ddf760b4dda57fc75066093c669e51439497ce94..5aeaa688755fdbc514d61912933ba41787f8bf5b 100644 (file)
@@ -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(&params,
+                                         "Unsupported guest access at: %lx\n",
+                                         *vcpu_pc(vcpu));
+               kvm_inject_undefined(vcpu);
+               return true;
+       }
+
+       *sr_index = tc.sri - 1;
        return false;
 
 inject:
index a410e99f827eefdf287574835147b2dcb397b7b3..bc076ef8440ba32c5bba1241043b2e98109a1188 100644 (file)
@@ -3395,12 +3395,6 @@ int kvm_handle_cp14_32(struct kvm_vcpu *vcpu)
        return kvm_handle_cp_32(vcpu, &params, 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,44 +3403,22 @@ 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);
-       }
-       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));
+       print_sys_reg_msg(params,
+                         "Unsupported guest sys_reg access at: %lx [%08lx]\n",
+                         *vcpu_pc(vcpu), *vcpu_cpsr(vcpu));
+       kvm_inject_undefined(vcpu);
 
-       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;
+       return false;
 }
 
 static void kvm_reset_id_regs(struct kvm_vcpu *vcpu)
@@ -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, &params))
-                       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, &params, 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, &params);
+       return 1;
 }
 
 /******************************************************************************