| // SPDX-License-Identifier: GPL-2.0-or-later | |
| #include <linux/init.h> | |
| #include <linux/log2.h> | |
| #include <kunit/test.h> | |
| #include <asm/guest-state-buffer.h> | |
| static void test_creating_buffer(struct kunit *test) | |
| { | |
| struct kvmppc_gs_buff *gsb; | |
| size_t size = 0x100; | |
| gsb = kvmppc_gsb_new(size, 0, 0, GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsb); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsb->hdr); | |
| KUNIT_EXPECT_EQ(test, gsb->capacity, roundup_pow_of_two(size)); | |
| KUNIT_EXPECT_EQ(test, gsb->len, sizeof(__be32)); | |
| kvmppc_gsb_free(gsb); | |
| } | |
| static void test_adding_element(struct kunit *test) | |
| { | |
| const struct kvmppc_gs_elem *head, *curr; | |
| union { | |
| __vector128 v; | |
| u64 dw[2]; | |
| } u; | |
| int rem; | |
| struct kvmppc_gs_buff *gsb; | |
| size_t size = 0x1000; | |
| int i, rc; | |
| u64 data; | |
| gsb = kvmppc_gsb_new(size, 0, 0, GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsb); | |
| /* Single elements, direct use of __kvmppc_gse_put() */ | |
| data = 0xdeadbeef; | |
| rc = __kvmppc_gse_put(gsb, KVMPPC_GSID_GPR(0), 8, &data); | |
| KUNIT_EXPECT_GE(test, rc, 0); | |
| head = kvmppc_gsb_data(gsb); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_iden(head), KVMPPC_GSID_GPR(0)); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_len(head), 8); | |
| data = 0; | |
| memcpy(&data, kvmppc_gse_data(head), 8); | |
| KUNIT_EXPECT_EQ(test, data, 0xdeadbeef); | |
| /* Multiple elements, simple wrapper */ | |
| rc = kvmppc_gse_put_u64(gsb, KVMPPC_GSID_GPR(1), 0xcafef00d); | |
| KUNIT_EXPECT_GE(test, rc, 0); | |
| u.dw[0] = 0x1; | |
| u.dw[1] = 0x2; | |
| rc = kvmppc_gse_put_vector128(gsb, KVMPPC_GSID_VSRS(0), &u.v); | |
| KUNIT_EXPECT_GE(test, rc, 0); | |
| u.dw[0] = 0x0; | |
| u.dw[1] = 0x0; | |
| kvmppc_gsb_for_each_elem(i, curr, gsb, rem) { | |
| switch (i) { | |
| case 0: | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_iden(curr), | |
| KVMPPC_GSID_GPR(0)); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_len(curr), 8); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_get_be64(curr), | |
| 0xdeadbeef); | |
| break; | |
| case 1: | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_iden(curr), | |
| KVMPPC_GSID_GPR(1)); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_len(curr), 8); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_get_u64(curr), | |
| 0xcafef00d); | |
| break; | |
| case 2: | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_iden(curr), | |
| KVMPPC_GSID_VSRS(0)); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gse_len(curr), 16); | |
| kvmppc_gse_get_vector128(curr, &u.v); | |
| KUNIT_EXPECT_EQ(test, u.dw[0], 0x1); | |
| KUNIT_EXPECT_EQ(test, u.dw[1], 0x2); | |
| break; | |
| } | |
| } | |
| KUNIT_EXPECT_EQ(test, i, 3); | |
| kvmppc_gsb_reset(gsb); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gsb_nelems(gsb), 0); | |
| KUNIT_EXPECT_EQ(test, kvmppc_gsb_len(gsb), | |
| sizeof(struct kvmppc_gs_header)); | |
| kvmppc_gsb_free(gsb); | |
| } | |
| static void test_gs_parsing(struct kunit *test) | |
| { | |
| struct kvmppc_gs_elem *gse; | |
| struct kvmppc_gs_parser gsp = { 0 }; | |
| struct kvmppc_gs_buff *gsb; | |
| size_t size = 0x1000; | |
| u64 tmp1, tmp2; | |
| gsb = kvmppc_gsb_new(size, 0, 0, GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsb); | |
| tmp1 = 0xdeadbeefull; | |
| kvmppc_gse_put_u64(gsb, KVMPPC_GSID_GPR(0), tmp1); | |
| KUNIT_EXPECT_GE(test, kvmppc_gse_parse(&gsp, gsb), 0); | |
| gse = kvmppc_gsp_lookup(&gsp, KVMPPC_GSID_GPR(0)); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gse); | |
| tmp2 = kvmppc_gse_get_u64(gse); | |
| KUNIT_EXPECT_EQ(test, tmp2, 0xdeadbeefull); | |
| kvmppc_gsb_free(gsb); | |
| } | |
| static void test_gs_bitmap(struct kunit *test) | |
| { | |
| struct kvmppc_gs_bitmap gsbm = { 0 }; | |
| struct kvmppc_gs_bitmap gsbm1 = { 0 }; | |
| struct kvmppc_gs_bitmap gsbm2 = { 0 }; | |
| u16 iden; | |
| int i, j; | |
| i = 0; | |
| for (u16 iden = KVMPPC_GSID_HOST_STATE_SIZE; | |
| iden <= KVMPPC_GSID_PROCESS_TABLE; iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| for (u16 iden = KVMPPC_GSID_RUN_INPUT; iden <= KVMPPC_GSID_VPA; | |
| iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| for (u16 iden = KVMPPC_GSID_GPR(0); iden <= KVMPPC_GSE_DW_REGS_END; iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| for (u16 iden = KVMPPC_GSID_CR; iden <= KVMPPC_GSID_PSPB; iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| for (u16 iden = KVMPPC_GSID_VSRS(0); iden <= KVMPPC_GSID_VSRS(63); | |
| iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| for (u16 iden = KVMPPC_GSID_HDAR; iden <= KVMPPC_GSID_ASDR; iden++) { | |
| kvmppc_gsbm_set(&gsbm, iden); | |
| kvmppc_gsbm_set(&gsbm1, iden); | |
| KUNIT_EXPECT_TRUE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| kvmppc_gsbm_clear(&gsbm, iden); | |
| KUNIT_EXPECT_FALSE(test, kvmppc_gsbm_test(&gsbm, iden)); | |
| i++; | |
| } | |
| j = 0; | |
| kvmppc_gsbm_for_each(&gsbm1, iden) | |
| { | |
| kvmppc_gsbm_set(&gsbm2, iden); | |
| j++; | |
| } | |
| KUNIT_EXPECT_EQ(test, i, j); | |
| KUNIT_EXPECT_MEMEQ(test, &gsbm1, &gsbm2, sizeof(gsbm1)); | |
| } | |
| struct kvmppc_gs_msg_test1_data { | |
| u64 a; | |
| u32 b; | |
| struct kvmppc_gs_part_table c; | |
| struct kvmppc_gs_proc_table d; | |
| struct kvmppc_gs_buff_info e; | |
| }; | |
| static size_t test1_get_size(struct kvmppc_gs_msg *gsm) | |
| { | |
| size_t size = 0; | |
| u16 ids[] = { | |
| KVMPPC_GSID_PARTITION_TABLE, | |
| KVMPPC_GSID_PROCESS_TABLE, | |
| KVMPPC_GSID_RUN_INPUT, | |
| KVMPPC_GSID_GPR(0), | |
| KVMPPC_GSID_CR, | |
| }; | |
| for (int i = 0; i < ARRAY_SIZE(ids); i++) | |
| size += kvmppc_gse_total_size(kvmppc_gsid_size(ids[i])); | |
| return size; | |
| } | |
| static int test1_fill_info(struct kvmppc_gs_buff *gsb, | |
| struct kvmppc_gs_msg *gsm) | |
| { | |
| struct kvmppc_gs_msg_test1_data *data = gsm->data; | |
| if (kvmppc_gsm_includes(gsm, KVMPPC_GSID_GPR(0))) | |
| kvmppc_gse_put_u64(gsb, KVMPPC_GSID_GPR(0), data->a); | |
| if (kvmppc_gsm_includes(gsm, KVMPPC_GSID_CR)) | |
| kvmppc_gse_put_u32(gsb, KVMPPC_GSID_CR, data->b); | |
| if (kvmppc_gsm_includes(gsm, KVMPPC_GSID_PARTITION_TABLE)) | |
| kvmppc_gse_put_part_table(gsb, KVMPPC_GSID_PARTITION_TABLE, | |
| data->c); | |
| if (kvmppc_gsm_includes(gsm, KVMPPC_GSID_PROCESS_TABLE)) | |
| kvmppc_gse_put_proc_table(gsb, KVMPPC_GSID_PARTITION_TABLE, | |
| data->d); | |
| if (kvmppc_gsm_includes(gsm, KVMPPC_GSID_RUN_INPUT)) | |
| kvmppc_gse_put_buff_info(gsb, KVMPPC_GSID_RUN_INPUT, data->e); | |
| return 0; | |
| } | |
| static int test1_refresh_info(struct kvmppc_gs_msg *gsm, | |
| struct kvmppc_gs_buff *gsb) | |
| { | |
| struct kvmppc_gs_parser gsp = { 0 }; | |
| struct kvmppc_gs_msg_test1_data *data = gsm->data; | |
| struct kvmppc_gs_elem *gse; | |
| int rc; | |
| rc = kvmppc_gse_parse(&gsp, gsb); | |
| if (rc < 0) | |
| return rc; | |
| gse = kvmppc_gsp_lookup(&gsp, KVMPPC_GSID_GPR(0)); | |
| if (gse) | |
| data->a = kvmppc_gse_get_u64(gse); | |
| gse = kvmppc_gsp_lookup(&gsp, KVMPPC_GSID_CR); | |
| if (gse) | |
| data->b = kvmppc_gse_get_u32(gse); | |
| return 0; | |
| } | |
| static struct kvmppc_gs_msg_ops gs_msg_test1_ops = { | |
| .get_size = test1_get_size, | |
| .fill_info = test1_fill_info, | |
| .refresh_info = test1_refresh_info, | |
| }; | |
| static void test_gs_msg(struct kunit *test) | |
| { | |
| struct kvmppc_gs_msg_test1_data test1_data = { | |
| .a = 0xdeadbeef, | |
| .b = 0x1, | |
| }; | |
| struct kvmppc_gs_msg *gsm; | |
| struct kvmppc_gs_buff *gsb; | |
| gsm = kvmppc_gsm_new(&gs_msg_test1_ops, &test1_data, GSM_SEND, | |
| GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsm); | |
| gsb = kvmppc_gsb_new(kvmppc_gsm_size(gsm), 0, 0, GFP_KERNEL); | |
| KUNIT_ASSERT_NOT_ERR_OR_NULL(test, gsb); | |
| kvmppc_gsm_include(gsm, KVMPPC_GSID_PARTITION_TABLE); | |
| kvmppc_gsm_include(gsm, KVMPPC_GSID_PROCESS_TABLE); | |
| kvmppc_gsm_include(gsm, KVMPPC_GSID_RUN_INPUT); | |
| kvmppc_gsm_include(gsm, KVMPPC_GSID_GPR(0)); | |
| kvmppc_gsm_include(gsm, KVMPPC_GSID_CR); | |
| kvmppc_gsm_fill_info(gsm, gsb); | |
| memset(&test1_data, 0, sizeof(test1_data)); | |
| kvmppc_gsm_refresh_info(gsm, gsb); | |
| KUNIT_EXPECT_EQ(test, test1_data.a, 0xdeadbeef); | |
| KUNIT_EXPECT_EQ(test, test1_data.b, 0x1); | |
| kvmppc_gsm_free(gsm); | |
| } | |
| static struct kunit_case guest_state_buffer_testcases[] = { | |
| KUNIT_CASE(test_creating_buffer), | |
| KUNIT_CASE(test_adding_element), | |
| KUNIT_CASE(test_gs_bitmap), | |
| KUNIT_CASE(test_gs_parsing), | |
| KUNIT_CASE(test_gs_msg), | |
| {} | |
| }; | |
| static struct kunit_suite guest_state_buffer_test_suite = { | |
| .name = "guest_state_buffer_test", | |
| .test_cases = guest_state_buffer_testcases, | |
| }; | |
| kunit_test_suites(&guest_state_buffer_test_suite); | |
| MODULE_DESCRIPTION("KUnit tests for Guest State Buffer APIs"); | |
| MODULE_LICENSE("GPL"); |