@@ -957,6 +957,258 @@ static void validate_id_reg_test(struct kunit *test)
}
}
+struct trap_config_test {
+ u64 set;
+ u64 clear;
+ u64 prev_val;
+ u64 expect_val;
+};
+
+struct trap_config_test trap_params[] = {
+ {0x30000800000, 0, 0, 0x30000800000},
+ {0, 0x30000800000, 0, 0},
+ {0x30000800000, 0, (u64)-1, (u64)-1},
+ {0, 0x30000800000, (u64)-1, (u64)0xfffffcffff7fffff},
+};
+
+static void trap_case_to_desc(struct trap_config_test *t, char *desc)
+{
+ snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+ "trap - set:0x%llx, clear:0x%llx, prev_val:0x%llx\n",
+ t->set, t->clear, t->prev_val);
+}
+
+KUNIT_ARRAY_PARAM(trap, trap_params, trap_case_to_desc);
+
+/* Tests for feature_trap_activate(). */
+static void feature_trap_activate_test(struct kunit *test)
+{
+ struct kvm_vcpu *vcpu;
+ const struct trap_config_test *trap = test->param_value;
+
+ vcpu = test_kvm_vcpu_init(test);
+ KUNIT_ASSERT_TRUE(test, vcpu);
+
+ /* Test for HCR_EL2 */
+ vcpu->arch.hcr_el2 = trap->prev_val;
+ feature_trap_activate(vcpu, VCPU_HCR_EL2, trap->set, trap->clear);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, trap->expect_val);
+
+ /* Test for MDCR_EL2 */
+ vcpu->arch.mdcr_el2 = trap->prev_val;
+ feature_trap_activate(vcpu, VCPU_MDCR_EL2, trap->set, trap->clear);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.mdcr_el2, trap->expect_val);
+
+ /* Test for CPTR_EL2 */
+ vcpu->arch.cptr_el2 = trap->prev_val;
+ feature_trap_activate(vcpu, VCPU_CPTR_EL2, trap->set, trap->clear);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.cptr_el2, trap->expect_val);
+}
+
+static u64 test_trap_set0;
+static u64 test_trap_clear0;
+static void test_trap_activate0(struct kvm_vcpu *vcpu)
+{
+ feature_trap_activate(vcpu, VCPU_HCR_EL2,
+ test_trap_set0, test_trap_clear0);
+}
+
+static u64 test_trap_set1;
+static u64 test_trap_clear1;
+static void test_trap_activate1(struct kvm_vcpu *vcpu)
+{
+ feature_trap_activate(vcpu, VCPU_HCR_EL2,
+ test_trap_set1, test_trap_clear1);
+}
+
+static u64 test_trap_set2;
+static u64 test_trap_clear2;
+static void test_trap_activate2(struct kvm_vcpu *vcpu)
+{
+ feature_trap_activate(vcpu, VCPU_HCR_EL2,
+ test_trap_set2, test_trap_clear2);
+}
+
+
+static void setup_feature_config_ctrl(struct feature_config_ctrl *config,
+ u32 id, int shift, int min, bool sign,
+ void *fn)
+{
+ memset(config, 0, sizeof(*config));
+ config->ftr_reg = id;
+ config->ftr_shift = shift;
+ config->ftr_min = min;
+ config->ftr_signed = sign;
+ config->trap_activate = fn;
+}
+
+/*
+ * Tests for id_reg_features_trap_activate.
+ * Setup a id_reg_desc with three entries in id_reg_desc->trap_features[].
+ * Check if the config register is updated to enable trap for the disabled
+ * features.
+ */
+static void id_reg_features_trap_activate_test(struct kunit *test)
+{
+ struct kvm_vcpu *vcpu;
+ u32 id;
+ u64 cfg_set, cfg_clear, id_reg_sys_val, id_reg_val;
+ struct id_reg_desc id_reg_data = {};
+ struct feature_config_ctrl config0, config1, config2;
+ struct feature_config_ctrl *trap_features[] = {
+ &config0, &config1, &config2, NULL,
+ };
+
+ vcpu = test_kvm_vcpu_init(test);
+ KUNIT_EXPECT_TRUE(test, vcpu);
+ if (!vcpu)
+ return;
+
+ /* Setup id_reg_desc */
+ id_reg_sys_val = 0x7777777777777777;
+ id = SYS_ID_AA64DFR0_EL1;
+ set_sys_desc((struct sys_reg_desc *)&id_reg_data.reg_desc, id);
+ id_reg_data.sys_val = id_reg_sys_val;
+ id_reg_data.vcpu_limit_val = (u64)-1;
+ id_reg_data.trap_features =
+ (const struct feature_config_ctrl *(*)[])trap_features;
+
+ /* Setup the 1st feature_config_ctrl */
+ test_trap_set0 = 0x3;
+ test_trap_clear0 = 0x0;
+ setup_feature_config_ctrl(&config0, id, 60, 2, FTR_UNSIGNED,
+ &test_trap_activate0);
+
+ /* Setup the 2nd feature_config_ctrl */
+ test_trap_set1 = 0x30000040;
+ test_trap_clear1 = 0x40000000;
+ setup_feature_config_ctrl(&config1, id, 0, 1, FTR_UNSIGNED,
+ &test_trap_activate1);
+
+ /* Setup the 3rd feature_config_ctrl */
+ test_trap_set2 = 0x30000000800;
+ test_trap_clear2 = 0x40000000000;
+ setup_feature_config_ctrl(&config2, id, 4, 0, FTR_SIGNED,
+ &test_trap_activate2);
+
+#define ftr_dis(cfg) \
+ ((u64)(((cfg)->ftr_min - 1) & 0xf) << (cfg)->ftr_shift)
+
+#define ftr_en(cfg) \
+ ((u64)(cfg)->ftr_min << (cfg)->ftr_shift)
+
+ /* Test with features enabled for config0, 1 and 2 */
+ id_reg_val = ftr_en(&config0) | ftr_en(&config1) | ftr_en(&config2);
+ write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+ vcpu->arch.hcr_el2 = 0;
+ id_reg_features_trap_activate(vcpu, &id_reg_data);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+
+
+ /* Test with features disabled for config0 only */
+ id_reg_val = ftr_dis(&config0) | ftr_en(&config1) | ftr_en(&config2);
+ write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+ vcpu->arch.hcr_el2 = 0;
+ cfg_set = test_trap_set0;
+ cfg_clear = test_trap_clear0;
+
+ id_reg_features_trap_activate(vcpu, &id_reg_data);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+ /* Test with features disabled for config0 and config1 */
+ id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_en(&config2);
+ write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+ vcpu->arch.hcr_el2 = 0;
+
+ cfg_set = test_trap_set0 | test_trap_set1;
+ cfg_clear = test_trap_clear0 | test_trap_clear1;
+
+ id_reg_features_trap_activate(vcpu, &id_reg_data);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+ /* Test with features disabled for config0, config1, and config2 */
+ id_reg_val = ftr_dis(&config0) | ftr_dis(&config1) | ftr_dis(&config2);
+ write_kvm_id_reg(vcpu->kvm, id, id_reg_val);
+ vcpu->arch.hcr_el2 = 0;
+
+ cfg_set = test_trap_set0 | test_trap_set1 | test_trap_set2;
+ cfg_clear = test_trap_clear0 | test_trap_clear1 | test_trap_clear2;
+
+ id_reg_features_trap_activate(vcpu, &id_reg_data);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_set, cfg_set);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2 & cfg_clear, 0);
+
+
+ /* Test with id_reg_data.trap_features = NULL */
+ id_reg_data.trap_features = NULL;
+ vcpu->arch.hcr_el2 = 0;
+ id_reg_features_trap_activate(vcpu, &id_reg_data);
+ KUNIT_EXPECT_EQ(test, vcpu->arch.hcr_el2, 0);
+}
+
+/* Tests for vcpu_need_trap_ptrauth(). */
+static void vcpu_need_trap_ptrauth_test(struct kunit *test)
+{
+ struct kvm_vcpu *vcpu;
+ u32 id = SYS_ID_AA64ISAR1_EL1;
+
+ vcpu = test_kvm_vcpu_init(test);
+ KUNIT_EXPECT_TRUE(test, vcpu);
+ if (!vcpu)
+ return;
+
+ if (system_has_full_ptr_auth()) {
+ /* Tests with PTRAUTH disabled vCPU */
+ write_kvm_id_reg(vcpu->kvm, id, 0x0);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPI = 1, API = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPI = 1, APA = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPA = 1, API = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPA = 1, APA = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+
+ /* Tests with PTRAUTH enabled vCPU */
+ vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH;
+
+ write_kvm_id_reg(vcpu->kvm, id, 0x0);
+ KUNIT_EXPECT_TRUE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPI = 1, API = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x10000100);
+ KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPI = 1, APA = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x10000010);
+ KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPA = 1, API = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x01000100);
+ KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+
+ /* GPA = 1, APA = 1 */
+ write_kvm_id_reg(vcpu->kvm, id, 0x01000010);
+ KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+ } else {
+ KUNIT_EXPECT_FALSE(test, vcpu_need_trap_ptrauth(vcpu));
+ }
+}
+
static struct kunit_case kvm_sys_regs_test_cases[] = {
KUNIT_CASE_PARAM(vcpu_id_reg_feature_frac_check_test, frac_gen_params),
KUNIT_CASE_PARAM(validate_id_aa64mmfr0_tgran2_test, tgran4_2_gen_params),
@@ -971,6 +1223,9 @@ static struct kunit_case kvm_sys_regs_test_cases[] = {
KUNIT_CASE(validate_id_dfr0_el1_test),
KUNIT_CASE(validate_mvfr1_el1_test),
KUNIT_CASE(validate_id_reg_test),
+ KUNIT_CASE(vcpu_need_trap_ptrauth_test),
+ KUNIT_CASE_PARAM(feature_trap_activate_test, trap_gen_params),
+ KUNIT_CASE(id_reg_features_trap_activate_test),
{}
};
Add KUnit tests for functions in arch/arm64/kvm/sys_regs_test.c that activates traps for disabled features. Signed-off-by: Reiji Watanabe <reijiw@google.com> --- arch/arm64/kvm/sys_regs_test.c | 255 +++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+)