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@xxxxxxxxxx> --- arch/arm64/kvm/sys_regs_test.c | 255 +++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) diff --git a/arch/arm64/kvm/sys_regs_test.c b/arch/arm64/kvm/sys_regs_test.c index 954c01876977..d26c589cbb93 100644 --- a/arch/arm64/kvm/sys_regs_test.c +++ b/arch/arm64/kvm/sys_regs_test.c @@ -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), {} }; -- 2.35.1.723.g4982287a31-goog