Commit 6ccbbc33 authored by Jordan Niethe's avatar Jordan Niethe Committed by Michael Ellerman

KVM: PPC: Add helper library for Guest State Buffers

The PAPR "Nestedv2" guest API introduces the concept of a Guest State
Buffer for communication about L2 guests between L1 and L0 hosts.

In the new API, the L0 manages the L2 on behalf of the L1. This means
that if the L1 needs to change L2 state (e.g. GPRs, SPRs, partition
table...), it must request the L0 perform the modification. If the
nested host needs to read L2 state likewise this request must
go through the L0.

The Guest State Buffer is a Type-Length-Value style data format defined
in the PAPR which assigns all relevant partition state a unique
identity. Unlike a typical TLV format the length is redundant as the
length of each identity is fixed but is included for checking
correctness.

A guest state buffer consists of an element count followed by a stream
of elements, where elements are composed of an ID number, data length,
then the data:

  Header:

   <---4 bytes--->
  +----------------+-----
  | Element Count  | Elements...
  +----------------+-----

  Element:

   <----2 bytes---> <-2 bytes-> <-Length bytes->
  +----------------+-----------+----------------+
  | Guest State ID |  Length   |      Data      |
  +----------------+-----------+----------------+

Guest State IDs have other attributes defined in the PAPR such as
whether they are per thread or per guest, or read-only.

Introduce a library for using guest state buffers. This includes support
for actions such as creating buffers, adding elements to buffers,
reading the value of elements and parsing buffers. This will be used
later by the nestedv2 guest support.
Signed-off-by: default avatarJordan Niethe <jniethe5@gmail.com>
Signed-off-by: default avatarMichael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230914030600.16993-9-jniethe5@gmail.com
parent 6de2e837
......@@ -82,6 +82,18 @@ config MSI_BITMAP_SELFTEST
bool "Run self-tests of the MSI bitmap code"
depends on DEBUG_KERNEL
config GUEST_STATE_BUFFER_TEST
def_tristate n
prompt "Enable Guest State Buffer unit tests"
depends on KUNIT
depends on KVM_BOOK3S_HV_POSSIBLE
default KUNIT_ALL_TESTS
help
The Guest State Buffer is a data format specified in the PAPR.
It is by hcalls to communicate the state of L2 guests between
the L1 and L0 hypervisors. Enable unit tests for the library
used to create and use guest state buffers.
config PPC_IRQ_SOFT_MASK_DEBUG
bool "Include extra checks for powerpc irq soft masking"
depends on PPC64
......
This diff is collapsed.
......@@ -87,8 +87,11 @@ kvm-book3s_64-builtin-objs-$(CONFIG_KVM_BOOK3S_64_HANDLER) += \
book3s_hv_ras.o \
book3s_hv_builtin.o \
book3s_hv_p9_perf.o \
guest-state-buffer.o \
$(kvm-book3s_64-builtin-tm-objs-y) \
$(kvm-book3s_64-builtin-xics-objs-y)
obj-$(CONFIG_GUEST_STATE_BUFFER_TEST) += test-guest-state-buffer.o
endif
kvm-book3s_64-objs-$(CONFIG_KVM_XICS) += \
......
This diff is collapsed.
// 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_GSID_CTRL; 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_LICENSE("GPL");
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment