2022-08-01 20:14:38

by Peter Gonda

[permalink] [raw]
Subject: [V2 00/11] KVM: selftests: Add simple SEV test

This patch series combines the work Michael Roth has done in supporting
SEV guests in selftests and the work Sean Christopherson suggested to
allow ucalls from SEV guests. And the work Sean has sent to consolidate
the ucall boilerplate code. Along with a very simple version of the
SEV selftests Michael originally proposed.

V2
* Dropped RFC tag
* Correctly seperated Sean's ucall patches into 2 as originally
intended.
* Addressed andrew.jones@ in ucall patches.
* Fixed ucall pool usage to work for other archs

V1
* https://lore.kernel.org/all/[email protected]/


Michael Roth (6):
KVM: selftests: move vm_phy_pages_alloc() earlier in file
KVM: selftests: sparsebit: add const where appropriate
KVM: selftests: add hooks for managing encrypted guest memory
KVM: selftests: handle encryption bits in page tables
KVM: selftests: add support for encrypted vm_vaddr_* allocations
KVM: selftests: add library for creating/interacting with SEV guests

Peter Gonda (3):
tools: Add atomic_test_and_set_bit()
KVM: selftests: Make ucall work with encrypted guests
KVM: selftests: Add simple sev vm testing

Sean Christopherson (2):
KVM: selftests: Consolidate common code for popuplating
KVM: selftests: Consolidate boilerplate code in get_ucall()

tools/arch/x86/include/asm/atomic.h | 7 +
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 3 +
.../selftests/kvm/include/kvm_util_base.h | 26 ++
.../testing/selftests/kvm/include/sparsebit.h | 36 +--
.../selftests/kvm/include/ucall_common.h | 15 +-
.../selftests/kvm/include/x86_64/sev.h | 47 +++
.../testing/selftests/kvm/lib/aarch64/ucall.c | 33 +--
tools/testing/selftests/kvm/lib/kvm_util.c | 267 +++++++++++++-----
tools/testing/selftests/kvm/lib/riscv/ucall.c | 40 +--
tools/testing/selftests/kvm/lib/s390x/ucall.c | 37 +--
tools/testing/selftests/kvm/lib/sparsebit.c | 48 ++--
.../testing/selftests/kvm/lib/ucall_common.c | 139 +++++++++
.../selftests/kvm/lib/x86_64/processor.c | 15 +-
tools/testing/selftests/kvm/lib/x86_64/sev.c | 249 ++++++++++++++++
.../testing/selftests/kvm/lib/x86_64/ucall.c | 37 +--
.../selftests/kvm/x86_64/sev_all_boot_test.c | 131 +++++++++
17 files changed, 885 insertions(+), 246 deletions(-)
create mode 100644 tools/testing/selftests/kvm/include/x86_64/sev.h
create mode 100644 tools/testing/selftests/kvm/lib/ucall_common.c
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/sev.c
create mode 100644 tools/testing/selftests/kvm/x86_64/sev_all_boot_test.c

--
2.37.1.455.g008518b4e5-goog



2022-08-01 20:14:39

by Peter Gonda

[permalink] [raw]
Subject: [V2 01/11] KVM: selftests: move vm_phy_pages_alloc() earlier in file

From: Michael Roth <[email protected]>

Subsequent patches will break some of this code out into file-local
helper functions, which will be used by functions like vm_vaddr_alloc(),
which currently are defined earlier in the file, so a forward
declaration would be needed.

Instead, move it earlier in the file, just above vm_vaddr_alloc() and
and friends, which are the main users.

Reviewed-by: Mingwei Zhang <[email protected]>
Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
tools/testing/selftests/kvm/lib/kvm_util.c | 145 ++++++++++-----------
1 file changed, 72 insertions(+), 73 deletions(-)

diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 9889fe0d8919..cb3a5f8a53b7 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1089,6 +1089,78 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
return vcpu;
}

+/*
+ * Physical Contiguous Page Allocator
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ * num - number of pages
+ * paddr_min - Physical address minimum
+ * memslot - Memory region to allocate page from
+ *
+ * Output Args: None
+ *
+ * Return:
+ * Starting physical address
+ *
+ * Within the VM specified by vm, locates a range of available physical
+ * pages at or above paddr_min. If found, the pages are marked as in use
+ * and their base address is returned. A TEST_ASSERT failure occurs if
+ * not enough pages are available at or above paddr_min.
+ */
+vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
+ vm_paddr_t paddr_min, uint32_t memslot)
+{
+ struct userspace_mem_region *region;
+ sparsebit_idx_t pg, base;
+
+ TEST_ASSERT(num > 0, "Must allocate at least one page");
+
+ TEST_ASSERT((paddr_min % vm->page_size) == 0,
+ "Min physical address not divisible by page size.\n paddr_min: 0x%lx page_size: 0x%x",
+ paddr_min, vm->page_size);
+
+ region = memslot2region(vm, memslot);
+ base = pg = paddr_min >> vm->page_shift;
+
+ do {
+ for (; pg < base + num; ++pg) {
+ if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
+ base = pg = sparsebit_next_set(region->unused_phy_pages, pg);
+ break;
+ }
+ }
+ } while (pg && pg != base + num);
+
+ if (pg == 0) {
+ fprintf(stderr,
+ "No guest physical page available, paddr_min: 0x%lx page_size: 0x%x memslot: %u\n",
+ paddr_min, vm->page_size, memslot);
+ fputs("---- vm dump ----\n", stderr);
+ vm_dump(stderr, vm, 2);
+ abort();
+ }
+
+ for (pg = base; pg < base + num; ++pg)
+ sparsebit_clear(region->unused_phy_pages, pg);
+
+ return base * vm->page_size;
+}
+
+vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
+ uint32_t memslot)
+{
+ return vm_phy_pages_alloc(vm, 1, paddr_min, memslot);
+}
+
+/* Arbitrary minimum physical address used for virtual translation tables. */
+#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
+
+vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm)
+{
+ return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
+}
+
/*
* VM Virtual Address Unused Gap
*
@@ -1735,79 +1807,6 @@ const char *exit_reason_str(unsigned int exit_reason)
return "Unknown";
}

-/*
- * Physical Contiguous Page Allocator
- *
- * Input Args:
- * vm - Virtual Machine
- * num - number of pages
- * paddr_min - Physical address minimum
- * memslot - Memory region to allocate page from
- *
- * Output Args: None
- *
- * Return:
- * Starting physical address
- *
- * Within the VM specified by vm, locates a range of available physical
- * pages at or above paddr_min. If found, the pages are marked as in use
- * and their base address is returned. A TEST_ASSERT failure occurs if
- * not enough pages are available at or above paddr_min.
- */
-vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
- vm_paddr_t paddr_min, uint32_t memslot)
-{
- struct userspace_mem_region *region;
- sparsebit_idx_t pg, base;
-
- TEST_ASSERT(num > 0, "Must allocate at least one page");
-
- TEST_ASSERT((paddr_min % vm->page_size) == 0, "Min physical address "
- "not divisible by page size.\n"
- " paddr_min: 0x%lx page_size: 0x%x",
- paddr_min, vm->page_size);
-
- region = memslot2region(vm, memslot);
- base = pg = paddr_min >> vm->page_shift;
-
- do {
- for (; pg < base + num; ++pg) {
- if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
- base = pg = sparsebit_next_set(region->unused_phy_pages, pg);
- break;
- }
- }
- } while (pg && pg != base + num);
-
- if (pg == 0) {
- fprintf(stderr, "No guest physical page available, "
- "paddr_min: 0x%lx page_size: 0x%x memslot: %u\n",
- paddr_min, vm->page_size, memslot);
- fputs("---- vm dump ----\n", stderr);
- vm_dump(stderr, vm, 2);
- abort();
- }
-
- for (pg = base; pg < base + num; ++pg)
- sparsebit_clear(region->unused_phy_pages, pg);
-
- return base * vm->page_size;
-}
-
-vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
- uint32_t memslot)
-{
- return vm_phy_pages_alloc(vm, 1, paddr_min, memslot);
-}
-
-/* Arbitrary minimum physical address used for virtual translation tables. */
-#define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000
-
-vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm)
-{
- return vm_phy_page_alloc(vm, KVM_GUEST_PAGE_TABLE_MIN_PADDR, 0);
-}
-
/*
* Address Guest Virtual to Host Virtual
*
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:14:43

by Peter Gonda

[permalink] [raw]
Subject: [V2 02/11] KVM: selftests: sparsebit: add const where appropriate

From: Michael Roth <[email protected]>

Subsequent patches will introduce an encryption bitmap in kvm_util that
would be useful to allow tests to access in read-only fashion. This
will be done via a const sparsebit*. To avoid warnings or the need to
add casts everywhere, add const to the various sparsebit functions that
are applicable for read-only usage of sparsebit.

Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../testing/selftests/kvm/include/sparsebit.h | 36 +++++++-------
tools/testing/selftests/kvm/lib/sparsebit.c | 48 +++++++++----------
2 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/sparsebit.h b/tools/testing/selftests/kvm/include/sparsebit.h
index 12a9a4b9cead..fb5170d57fcb 100644
--- a/tools/testing/selftests/kvm/include/sparsebit.h
+++ b/tools/testing/selftests/kvm/include/sparsebit.h
@@ -30,26 +30,26 @@ typedef uint64_t sparsebit_num_t;

struct sparsebit *sparsebit_alloc(void);
void sparsebit_free(struct sparsebit **sbitp);
-void sparsebit_copy(struct sparsebit *dstp, struct sparsebit *src);
+void sparsebit_copy(struct sparsebit *dstp, const struct sparsebit *src);

-bool sparsebit_is_set(struct sparsebit *sbit, sparsebit_idx_t idx);
-bool sparsebit_is_set_num(struct sparsebit *sbit,
+bool sparsebit_is_set(const struct sparsebit *sbit, sparsebit_idx_t idx);
+bool sparsebit_is_set_num(const struct sparsebit *sbit,
sparsebit_idx_t idx, sparsebit_num_t num);
-bool sparsebit_is_clear(struct sparsebit *sbit, sparsebit_idx_t idx);
-bool sparsebit_is_clear_num(struct sparsebit *sbit,
+bool sparsebit_is_clear(const struct sparsebit *sbit, sparsebit_idx_t idx);
+bool sparsebit_is_clear_num(const struct sparsebit *sbit,
sparsebit_idx_t idx, sparsebit_num_t num);
-sparsebit_num_t sparsebit_num_set(struct sparsebit *sbit);
-bool sparsebit_any_set(struct sparsebit *sbit);
-bool sparsebit_any_clear(struct sparsebit *sbit);
-bool sparsebit_all_set(struct sparsebit *sbit);
-bool sparsebit_all_clear(struct sparsebit *sbit);
-sparsebit_idx_t sparsebit_first_set(struct sparsebit *sbit);
-sparsebit_idx_t sparsebit_first_clear(struct sparsebit *sbit);
-sparsebit_idx_t sparsebit_next_set(struct sparsebit *sbit, sparsebit_idx_t prev);
-sparsebit_idx_t sparsebit_next_clear(struct sparsebit *sbit, sparsebit_idx_t prev);
-sparsebit_idx_t sparsebit_next_set_num(struct sparsebit *sbit,
+sparsebit_num_t sparsebit_num_set(const struct sparsebit *sbit);
+bool sparsebit_any_set(const struct sparsebit *sbit);
+bool sparsebit_any_clear(const struct sparsebit *sbit);
+bool sparsebit_all_set(const struct sparsebit *sbit);
+bool sparsebit_all_clear(const struct sparsebit *sbit);
+sparsebit_idx_t sparsebit_first_set(const struct sparsebit *sbit);
+sparsebit_idx_t sparsebit_first_clear(const struct sparsebit *sbit);
+sparsebit_idx_t sparsebit_next_set(const struct sparsebit *sbit, sparsebit_idx_t prev);
+sparsebit_idx_t sparsebit_next_clear(const struct sparsebit *sbit, sparsebit_idx_t prev);
+sparsebit_idx_t sparsebit_next_set_num(const struct sparsebit *sbit,
sparsebit_idx_t start, sparsebit_num_t num);
-sparsebit_idx_t sparsebit_next_clear_num(struct sparsebit *sbit,
+sparsebit_idx_t sparsebit_next_clear_num(const struct sparsebit *sbit,
sparsebit_idx_t start, sparsebit_num_t num);

void sparsebit_set(struct sparsebit *sbitp, sparsebit_idx_t idx);
@@ -62,9 +62,9 @@ void sparsebit_clear_num(struct sparsebit *sbitp,
sparsebit_idx_t start, sparsebit_num_t num);
void sparsebit_clear_all(struct sparsebit *sbitp);

-void sparsebit_dump(FILE *stream, struct sparsebit *sbit,
+void sparsebit_dump(FILE *stream, const struct sparsebit *sbit,
unsigned int indent);
-void sparsebit_validate_internal(struct sparsebit *sbit);
+void sparsebit_validate_internal(const struct sparsebit *sbit);

#ifdef __cplusplus
}
diff --git a/tools/testing/selftests/kvm/lib/sparsebit.c b/tools/testing/selftests/kvm/lib/sparsebit.c
index 50e0cf41a7dd..6777a5b1fbd2 100644
--- a/tools/testing/selftests/kvm/lib/sparsebit.c
+++ b/tools/testing/selftests/kvm/lib/sparsebit.c
@@ -202,7 +202,7 @@ static sparsebit_num_t node_num_set(struct node *nodep)
/* Returns a pointer to the node that describes the
* lowest bit index.
*/
-static struct node *node_first(struct sparsebit *s)
+static struct node *node_first(const struct sparsebit *s)
{
struct node *nodep;

@@ -216,7 +216,7 @@ static struct node *node_first(struct sparsebit *s)
* lowest bit index > the index of the node pointed to by np.
* Returns NULL if no node with a higher index exists.
*/
-static struct node *node_next(struct sparsebit *s, struct node *np)
+static struct node *node_next(const struct sparsebit *s, struct node *np)
{
struct node *nodep = np;

@@ -244,7 +244,7 @@ static struct node *node_next(struct sparsebit *s, struct node *np)
* highest index < the index of the node pointed to by np.
* Returns NULL if no node with a lower index exists.
*/
-static struct node *node_prev(struct sparsebit *s, struct node *np)
+static struct node *node_prev(const struct sparsebit *s, struct node *np)
{
struct node *nodep = np;

@@ -273,7 +273,7 @@ static struct node *node_prev(struct sparsebit *s, struct node *np)
* subtree and duplicates the bit settings to the newly allocated nodes.
* Returns the newly allocated copy of subtree.
*/
-static struct node *node_copy_subtree(struct node *subtree)
+static struct node *node_copy_subtree(const struct node *subtree)
{
struct node *root;

@@ -307,7 +307,7 @@ static struct node *node_copy_subtree(struct node *subtree)
* index is within the bits described by the mask bits or the number of
* contiguous bits set after the mask. Returns NULL if there is no such node.
*/
-static struct node *node_find(struct sparsebit *s, sparsebit_idx_t idx)
+static struct node *node_find(const struct sparsebit *s, sparsebit_idx_t idx)
{
struct node *nodep;

@@ -393,7 +393,7 @@ static struct node *node_add(struct sparsebit *s, sparsebit_idx_t idx)
}

/* Returns whether all the bits in the sparsebit array are set. */
-bool sparsebit_all_set(struct sparsebit *s)
+bool sparsebit_all_set(const struct sparsebit *s)
{
/*
* If any nodes there must be at least one bit set. Only case
@@ -776,7 +776,7 @@ static void node_reduce(struct sparsebit *s, struct node *nodep)
/* Returns whether the bit at the index given by idx, within the
* sparsebit array is set or not.
*/
-bool sparsebit_is_set(struct sparsebit *s, sparsebit_idx_t idx)
+bool sparsebit_is_set(const struct sparsebit *s, sparsebit_idx_t idx)
{
struct node *nodep;

@@ -922,7 +922,7 @@ static inline sparsebit_idx_t node_first_clear(struct node *nodep, int start)
* used by test cases after they detect an unexpected condition, as a means
* to capture diagnostic information.
*/
-static void sparsebit_dump_internal(FILE *stream, struct sparsebit *s,
+static void sparsebit_dump_internal(FILE *stream, const struct sparsebit *s,
unsigned int indent)
{
/* Dump the contents of s */
@@ -970,7 +970,7 @@ void sparsebit_free(struct sparsebit **sbitp)
* sparsebit_alloc(). It can though already have bits set, which
* if different from src will be cleared.
*/
-void sparsebit_copy(struct sparsebit *d, struct sparsebit *s)
+void sparsebit_copy(struct sparsebit *d, const struct sparsebit *s)
{
/* First clear any bits already set in the destination */
sparsebit_clear_all(d);
@@ -982,7 +982,7 @@ void sparsebit_copy(struct sparsebit *d, struct sparsebit *s)
}

/* Returns whether num consecutive bits starting at idx are all set. */
-bool sparsebit_is_set_num(struct sparsebit *s,
+bool sparsebit_is_set_num(const struct sparsebit *s,
sparsebit_idx_t idx, sparsebit_num_t num)
{
sparsebit_idx_t next_cleared;
@@ -1006,14 +1006,14 @@ bool sparsebit_is_set_num(struct sparsebit *s,
}

/* Returns whether the bit at the index given by idx. */
-bool sparsebit_is_clear(struct sparsebit *s,
+bool sparsebit_is_clear(const struct sparsebit *s,
sparsebit_idx_t idx)
{
return !sparsebit_is_set(s, idx);
}

/* Returns whether num consecutive bits starting at idx are all cleared. */
-bool sparsebit_is_clear_num(struct sparsebit *s,
+bool sparsebit_is_clear_num(const struct sparsebit *s,
sparsebit_idx_t idx, sparsebit_num_t num)
{
sparsebit_idx_t next_set;
@@ -1042,13 +1042,13 @@ bool sparsebit_is_clear_num(struct sparsebit *s,
* value. Use sparsebit_any_set(), instead of sparsebit_num_set() > 0,
* to determine if the sparsebit array has any bits set.
*/
-sparsebit_num_t sparsebit_num_set(struct sparsebit *s)
+sparsebit_num_t sparsebit_num_set(const struct sparsebit *s)
{
return s->num_set;
}

/* Returns whether any bit is set in the sparsebit array. */
-bool sparsebit_any_set(struct sparsebit *s)
+bool sparsebit_any_set(const struct sparsebit *s)
{
/*
* Nodes only describe set bits. If any nodes then there
@@ -1071,20 +1071,20 @@ bool sparsebit_any_set(struct sparsebit *s)
}

/* Returns whether all the bits in the sparsebit array are cleared. */
-bool sparsebit_all_clear(struct sparsebit *s)
+bool sparsebit_all_clear(const struct sparsebit *s)
{
return !sparsebit_any_set(s);
}

/* Returns whether all the bits in the sparsebit array are set. */
-bool sparsebit_any_clear(struct sparsebit *s)
+bool sparsebit_any_clear(const struct sparsebit *s)
{
return !sparsebit_all_set(s);
}

/* Returns the index of the first set bit. Abort if no bits are set.
*/
-sparsebit_idx_t sparsebit_first_set(struct sparsebit *s)
+sparsebit_idx_t sparsebit_first_set(const struct sparsebit *s)
{
struct node *nodep;

@@ -1098,7 +1098,7 @@ sparsebit_idx_t sparsebit_first_set(struct sparsebit *s)
/* Returns the index of the first cleared bit. Abort if
* no bits are cleared.
*/
-sparsebit_idx_t sparsebit_first_clear(struct sparsebit *s)
+sparsebit_idx_t sparsebit_first_clear(const struct sparsebit *s)
{
struct node *nodep1, *nodep2;

@@ -1152,7 +1152,7 @@ sparsebit_idx_t sparsebit_first_clear(struct sparsebit *s)
/* Returns index of next bit set within s after the index given by prev.
* Returns 0 if there are no bits after prev that are set.
*/
-sparsebit_idx_t sparsebit_next_set(struct sparsebit *s,
+sparsebit_idx_t sparsebit_next_set(const struct sparsebit *s,
sparsebit_idx_t prev)
{
sparsebit_idx_t lowest_possible = prev + 1;
@@ -1245,7 +1245,7 @@ sparsebit_idx_t sparsebit_next_set(struct sparsebit *s,
/* Returns index of next bit cleared within s after the index given by prev.
* Returns 0 if there are no bits after prev that are cleared.
*/
-sparsebit_idx_t sparsebit_next_clear(struct sparsebit *s,
+sparsebit_idx_t sparsebit_next_clear(const struct sparsebit *s,
sparsebit_idx_t prev)
{
sparsebit_idx_t lowest_possible = prev + 1;
@@ -1301,7 +1301,7 @@ sparsebit_idx_t sparsebit_next_clear(struct sparsebit *s,
* and returns the index of the first sequence of num consecutively set
* bits. Returns a value of 0 of no such sequence exists.
*/
-sparsebit_idx_t sparsebit_next_set_num(struct sparsebit *s,
+sparsebit_idx_t sparsebit_next_set_num(const struct sparsebit *s,
sparsebit_idx_t start, sparsebit_num_t num)
{
sparsebit_idx_t idx;
@@ -1336,7 +1336,7 @@ sparsebit_idx_t sparsebit_next_set_num(struct sparsebit *s,
* and returns the index of the first sequence of num consecutively cleared
* bits. Returns a value of 0 of no such sequence exists.
*/
-sparsebit_idx_t sparsebit_next_clear_num(struct sparsebit *s,
+sparsebit_idx_t sparsebit_next_clear_num(const struct sparsebit *s,
sparsebit_idx_t start, sparsebit_num_t num)
{
sparsebit_idx_t idx;
@@ -1584,7 +1584,7 @@ static size_t display_range(FILE *stream, sparsebit_idx_t low,
* contiguous bits. This is done because '-' is used to specify command-line
* options, and sometimes ranges are specified as command-line arguments.
*/
-void sparsebit_dump(FILE *stream, struct sparsebit *s,
+void sparsebit_dump(FILE *stream, const struct sparsebit *s,
unsigned int indent)
{
size_t current_line_len = 0;
@@ -1682,7 +1682,7 @@ void sparsebit_dump(FILE *stream, struct sparsebit *s,
* s. On error, diagnostic information is printed to stderr and
* abort is called.
*/
-void sparsebit_validate_internal(struct sparsebit *s)
+void sparsebit_validate_internal(const struct sparsebit *s)
{
bool error_detected = false;
struct node *nodep, *prev = NULL;
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:14:55

by Peter Gonda

[permalink] [raw]
Subject: [V2 03/11] KVM: selftests: add hooks for managing encrypted guest memory

From: Michael Roth <[email protected]>

VM implementations that make use of encrypted memory need a way to
configure things like the encryption/shared bit position for page
table handling, the default encryption policy for internal allocations
made by the core library, and a way to fetch the list/bitmap of
encrypted pages to do the actual memory encryption. Add an interface to
configure these parameters. Also introduce a sparsebit map to track
allocations/mappings that should be treated as encrypted, and provide
a way for VM implementations to retrieve it to handle operations
related memory encryption.

Reviewed-by: Mingwei Zhang <[email protected]>
Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 17 ++++++
tools/testing/selftests/kvm/lib/kvm_util.c | 52 +++++++++++++++++--
2 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 24fde97f6121..3928351e497e 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -32,6 +32,7 @@ typedef uint64_t vm_vaddr_t; /* Virtual Machine (Guest) virtual address */
struct userspace_mem_region {
struct kvm_userspace_memory_region region;
struct sparsebit *unused_phy_pages;
+ struct sparsebit *encrypted_phy_pages;
int fd;
off_t offset;
void *host_mem;
@@ -64,6 +65,14 @@ struct userspace_mem_regions {
DECLARE_HASHTABLE(slot_hash, 9);
};

+/* Memory encryption policy/configuration. */
+struct vm_memcrypt {
+ bool enabled;
+ int8_t enc_by_default;
+ bool has_enc_bit;
+ int8_t enc_bit;
+};
+
struct kvm_vm {
int mode;
unsigned long type;
@@ -87,6 +96,7 @@ struct kvm_vm {
vm_vaddr_t idt;
vm_vaddr_t handlers;
uint32_t dirty_ring_size;
+ struct vm_memcrypt memcrypt;

/* Cache of information for binary stats interface */
int stats_fd;
@@ -834,4 +844,11 @@ static inline int __vm_disable_nx_huge_pages(struct kvm_vm *vm)
return __vm_enable_cap(vm, KVM_CAP_VM_DISABLE_NX_HUGE_PAGES, 0);
}

+void vm_set_memory_encryption(struct kvm_vm *vm, bool enc_by_default, bool has_enc_bit,
+ uint8_t enc_bit);
+
+const struct sparsebit *vm_get_encrypted_phy_pages(struct kvm_vm *vm, int slot,
+ vm_paddr_t *gpa_start,
+ uint64_t *size);
+
#endif /* SELFTEST_KVM_UTIL_BASE_H */
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index cb3a5f8a53b7..c6b87b411186 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -542,6 +542,7 @@ static void __vm_mem_region_delete(struct kvm_vm *vm,
vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, &region->region);

sparsebit_free(&region->unused_phy_pages);
+ sparsebit_free(&region->encrypted_phy_pages);
ret = munmap(region->mmap_start, region->mmap_size);
TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret));

@@ -882,6 +883,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm,
}

region->unused_phy_pages = sparsebit_alloc();
+ region->encrypted_phy_pages = sparsebit_alloc();
sparsebit_set_num(region->unused_phy_pages,
guest_paddr >> vm->page_shift, npages);
region->region.slot = slot;
@@ -1097,6 +1099,7 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
* num - number of pages
* paddr_min - Physical address minimum
* memslot - Memory region to allocate page from
+ * encrypt - Whether to treat the pages as encrypted
*
* Output Args: None
*
@@ -1108,8 +1111,9 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id)
* and their base address is returned. A TEST_ASSERT failure occurs if
* not enough pages are available at or above paddr_min.
*/
-vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
- vm_paddr_t paddr_min, uint32_t memslot)
+static vm_paddr_t
+_vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, vm_paddr_t paddr_min,
+ uint32_t memslot, bool encrypt)
{
struct userspace_mem_region *region;
sparsebit_idx_t pg, base;
@@ -1141,12 +1145,22 @@ vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
abort();
}

- for (pg = base; pg < base + num; ++pg)
+ for (pg = base; pg < base + num; ++pg) {
sparsebit_clear(region->unused_phy_pages, pg);
+ if (encrypt)
+ sparsebit_set(region->encrypted_phy_pages, pg);
+ }

return base * vm->page_size;
}

+vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
+ vm_paddr_t paddr_min, uint32_t memslot)
+{
+ return _vm_phy_pages_alloc(vm, num, paddr_min, memslot,
+ vm->memcrypt.enc_by_default);
+}
+
vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min,
uint32_t memslot)
{
@@ -1730,6 +1744,10 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent)
region->host_mem);
fprintf(stream, "%*sunused_phy_pages: ", indent + 2, "");
sparsebit_dump(stream, region->unused_phy_pages, 0);
+ if (vm->memcrypt.enabled) {
+ fprintf(stream, "%*sencrypted_phy_pages: ", indent + 2, "");
+ sparsebit_dump(stream, region->encrypted_phy_pages, 0);
+ }
}
fprintf(stream, "%*sMapped Virtual Pages:\n", indent, "");
sparsebit_dump(stream, vm->vpages_mapped, indent + 2);
@@ -1978,3 +1996,31 @@ void __vm_get_stat(struct kvm_vm *vm, const char *stat_name, uint64_t *data,
break;
}
}
+
+void vm_set_memory_encryption(struct kvm_vm *vm, bool enc_by_default, bool has_enc_bit,
+ uint8_t enc_bit)
+{
+ vm->memcrypt.enabled = true;
+ vm->memcrypt.enc_by_default = enc_by_default;
+ vm->memcrypt.has_enc_bit = has_enc_bit;
+ vm->memcrypt.enc_bit = enc_bit;
+}
+
+const struct sparsebit *
+vm_get_encrypted_phy_pages(struct kvm_vm *vm, int slot, vm_paddr_t *gpa_start,
+ uint64_t *size)
+{
+ struct userspace_mem_region *region;
+
+ if (!vm->memcrypt.enabled)
+ return NULL;
+
+ region = memslot2region(vm, slot);
+ if (!region)
+ return NULL;
+
+ *size = region->region.memory_size;
+ *gpa_start = region->region.guest_phys_addr;
+
+ return region->encrypted_phy_pages;
+}
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:15:13

by Peter Gonda

[permalink] [raw]
Subject: [V2 06/11] KVM: selftests: Consolidate common code for popuplating

From: Sean Christopherson <[email protected]>

Make ucall() a common helper that populates struct ucall, and only calls
into arch code to make the actually call out to userspace.

Rename all arch-specific helpers to make it clear they're arch-specific,
and to avoid collisions with common helpers (one more on its way...)

No functional change intended.

Cc: Colton Lewis <[email protected]>
Cc: Andrew Jones <[email protected]>
Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/ucall_common.h | 23 ++++++++++++++++---
.../testing/selftests/kvm/lib/aarch64/ucall.c | 20 ++++------------
tools/testing/selftests/kvm/lib/riscv/ucall.c | 23 ++++---------------
tools/testing/selftests/kvm/lib/s390x/ucall.c | 23 ++++---------------
.../testing/selftests/kvm/lib/ucall_common.c | 20 ++++++++++++++++
.../testing/selftests/kvm/lib/x86_64/ucall.c | 23 ++++---------------
7 files changed, 60 insertions(+), 73 deletions(-)
create mode 100644 tools/testing/selftests/kvm/lib/ucall_common.c

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 690b499c3471..39fc5e8e5594 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -46,6 +46,7 @@ LIBKVM += lib/perf_test_util.c
LIBKVM += lib/rbtree.c
LIBKVM += lib/sparsebit.c
LIBKVM += lib/test_util.c
+LIBKVM += lib/ucall_common.c

LIBKVM_x86_64 += lib/x86_64/apic.c
LIBKVM_x86_64 += lib/x86_64/handlers.S
diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
index ee79d180e07e..5a85f5318bbe 100644
--- a/tools/testing/selftests/kvm/include/ucall_common.h
+++ b/tools/testing/selftests/kvm/include/ucall_common.h
@@ -24,10 +24,27 @@ struct ucall {
uint64_t args[UCALL_MAX_ARGS];
};

-void ucall_init(struct kvm_vm *vm, void *arg);
-void ucall_uninit(struct kvm_vm *vm);
+void ucall_arch_init(struct kvm_vm *vm, void *arg);
+void ucall_arch_uninit(struct kvm_vm *vm);
+void ucall_arch_do_ucall(vm_vaddr_t uc);
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
+
void ucall(uint64_t cmd, int nargs, ...);
-uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
+
+static inline void ucall_init(struct kvm_vm *vm, void *arg)
+{
+ ucall_arch_init(vm, arg);
+}
+
+static inline void ucall_uninit(struct kvm_vm *vm)
+{
+ ucall_arch_uninit(vm);
+}
+
+static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+{
+ return ucall_arch_get_ucall(vcpu, uc);
+}

#define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
index ed237b744690..1c81a6a5c1f2 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
@@ -21,7 +21,7 @@ static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
return true;
}

-void ucall_init(struct kvm_vm *vm, void *arg)
+void ucall_arch_init(struct kvm_vm *vm, void *arg)
{
vm_paddr_t gpa, start, end, step, offset;
unsigned int bits;
@@ -64,30 +64,18 @@ void ucall_init(struct kvm_vm *vm, void *arg)
TEST_FAIL("Can't find a ucall mmio address");
}

-void ucall_uninit(struct kvm_vm *vm)
+void ucall_arch_uninit(struct kvm_vm *vm)
{
ucall_exit_mmio_addr = 0;
sync_global_to_guest(vm, ucall_exit_mmio_addr);
}

-void ucall(uint64_t cmd, int nargs, ...)
+void ucall_arch_do_ucall(vm_vaddr_t uc)
{
- struct ucall uc = {};
- va_list va;
- int i;
-
- WRITE_ONCE(uc.cmd, cmd);
- nargs = min(nargs, UCALL_MAX_ARGS);
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- WRITE_ONCE(uc.args[i], va_arg(va, uint64_t));
- va_end(va);
-
WRITE_ONCE(*ucall_exit_mmio_addr, (vm_vaddr_t)&uc);
}

-uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct kvm_run *run = vcpu->run;
struct ucall ucall = {};
diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
index 087b9740bc8f..b1598f418c1f 100644
--- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
+++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
@@ -10,11 +10,11 @@
#include "kvm_util.h"
#include "processor.h"

-void ucall_init(struct kvm_vm *vm, void *arg)
+void ucall_arch_init(struct kvm_vm *vm, void *arg)
{
}

-void ucall_uninit(struct kvm_vm *vm)
+void ucall_arch_uninit(struct kvm_vm *vm)
{
}

@@ -44,27 +44,14 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
return ret;
}

-void ucall(uint64_t cmd, int nargs, ...)
+void ucall_arch_do_ucall(vm_vaddr_t uc)
{
- struct ucall uc = {
- .cmd = cmd,
- };
- va_list va;
- int i;
-
- nargs = min(nargs, UCALL_MAX_ARGS);
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
- va_end(va);
-
sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,
KVM_RISCV_SELFTESTS_SBI_UCALL,
- (vm_vaddr_t)&uc, 0, 0, 0, 0, 0);
+ uc, 0, 0, 0, 0, 0);
}

-uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct kvm_run *run = vcpu->run;
struct ucall ucall = {};
diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
index 73dc4e21190f..114cb4af295f 100644
--- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
+++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
@@ -6,34 +6,21 @@
*/
#include "kvm_util.h"

-void ucall_init(struct kvm_vm *vm, void *arg)
+void ucall_arch_init(struct kvm_vm *vm, void *arg)
{
}

-void ucall_uninit(struct kvm_vm *vm)
+void ucall_arch_uninit(struct kvm_vm *vm)
{
}

-void ucall(uint64_t cmd, int nargs, ...)
+void ucall_arch_do_ucall(vm_vaddr_t uc)
{
- struct ucall uc = {
- .cmd = cmd,
- };
- va_list va;
- int i;
-
- nargs = min(nargs, UCALL_MAX_ARGS);
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
- va_end(va);
-
/* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */
- asm volatile ("diag 0,%0,0x501" : : "a"(&uc) : "memory");
+ asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
}

-uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct kvm_run *run = vcpu->run;
struct ucall ucall = {};
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
new file mode 100644
index 000000000000..749ffdf23855
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include "kvm_util.h"
+
+void ucall(uint64_t cmd, int nargs, ...)
+{
+ struct ucall uc = {
+ .cmd = cmd,
+ };
+ va_list va;
+ int i;
+
+ nargs = min(nargs, UCALL_MAX_ARGS);
+
+ va_start(va, nargs);
+ for (i = 0; i < nargs; ++i)
+ uc.args[i] = va_arg(va, uint64_t);
+ va_end(va);
+
+ ucall_arch_do_ucall((vm_vaddr_t)&uc);
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
index e5f0f9e0d3ee..9f532dba1003 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
@@ -8,34 +8,21 @@

#define UCALL_PIO_PORT ((uint16_t)0x1000)

-void ucall_init(struct kvm_vm *vm, void *arg)
+void ucall_arch_init(struct kvm_vm *vm, void *arg)
{
}

-void ucall_uninit(struct kvm_vm *vm)
+void ucall_arch_uninit(struct kvm_vm *vm)
{
}

-void ucall(uint64_t cmd, int nargs, ...)
+void ucall_arch_do_ucall(vm_vaddr_t uc)
{
- struct ucall uc = {
- .cmd = cmd,
- };
- va_list va;
- int i;
-
- nargs = min(nargs, UCALL_MAX_ARGS);
-
- va_start(va, nargs);
- for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
- va_end(va);
-
asm volatile("in %[port], %%al"
- : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax", "memory");
+ : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
}

-uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct kvm_run *run = vcpu->run;
struct ucall ucall = {};
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:16:02

by Peter Gonda

[permalink] [raw]
Subject: [V2 07/11] KVM: selftests: Consolidate boilerplate code in get_ucall()

From: Sean Christopherson <[email protected]>

Consolidate the actual copying of a ucall struct from guest=>host into
the common get_ucall(). Return a host virtual address instead of a guest
virtual address even though the addr_gva2hva() part could be moved to
get_ucall() too. Conceptually, get_ucall() is invoked from the host and
should return a host virtual address (and returning NULL for "nothing to
see here" is far superior to returning 0).

Use pointer shenanigans instead of an unnecessary bounce buffer when the
caller of get_ucall() provides a valid pointer.

Signed-off-by: Sean Christopherson <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../selftests/kvm/include/ucall_common.h | 8 ++------
.../testing/selftests/kvm/lib/aarch64/ucall.c | 13 +++----------
tools/testing/selftests/kvm/lib/riscv/ucall.c | 19 +++----------------
tools/testing/selftests/kvm/lib/s390x/ucall.c | 16 +++-------------
.../testing/selftests/kvm/lib/ucall_common.c | 19 +++++++++++++++++++
.../testing/selftests/kvm/lib/x86_64/ucall.c | 16 +++-------------
6 files changed, 33 insertions(+), 58 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
index 5a85f5318bbe..c1bc8e33ef3f 100644
--- a/tools/testing/selftests/kvm/include/ucall_common.h
+++ b/tools/testing/selftests/kvm/include/ucall_common.h
@@ -27,9 +27,10 @@ struct ucall {
void ucall_arch_init(struct kvm_vm *vm, void *arg);
void ucall_arch_uninit(struct kvm_vm *vm);
void ucall_arch_do_ucall(vm_vaddr_t uc);
-uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);

void ucall(uint64_t cmd, int nargs, ...);
+uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);

static inline void ucall_init(struct kvm_vm *vm, void *arg)
{
@@ -41,11 +42,6 @@ static inline void ucall_uninit(struct kvm_vm *vm)
ucall_arch_uninit(vm);
}

-static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
-{
- return ucall_arch_get_ucall(vcpu, uc);
-}
-
#define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
#define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage)
diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
index 1c81a6a5c1f2..d2f099caa9ab 100644
--- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
@@ -78,24 +78,17 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
{
struct kvm_run *run = vcpu->run;
- struct ucall ucall = {};
-
- if (uc)
- memset(uc, 0, sizeof(*uc));

if (run->exit_reason == KVM_EXIT_MMIO &&
run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
- vm_vaddr_t gva;
+ uint64_t ucall_addr;

TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
"Unexpected ucall exit mmio address access");
memcpy(&gva, run->mmio.data, sizeof(gva));
- memcpy(&ucall, addr_gva2hva(vcpu->vm, gva), sizeof(ucall));

- vcpu_run_complete_io(vcpu);
- if (uc)
- memcpy(uc, &ucall, sizeof(ucall));
+ return ucall_addr;
}

- return ucall.cmd;
+ return 0;
}
diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
index b1598f418c1f..3f000d0b705f 100644
--- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
+++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
@@ -51,27 +51,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
uc, 0, 0, 0, 0, 0);
}

-uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
- struct ucall ucall = {};
-
- if (uc)
- memset(uc, 0, sizeof(*uc));

if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
switch (run->riscv_sbi.function_id) {
case KVM_RISCV_SELFTESTS_SBI_UCALL:
- memcpy(&ucall,
- addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]),
- sizeof(ucall));
-
- vcpu_run_complete_io(vcpu);
- if (uc)
- memcpy(uc, &ucall, sizeof(ucall));
-
- break;
+ return vcpu->vm, run->riscv_sbi.args[0];
case KVM_RISCV_SELFTESTS_SBI_UNEXP:
vcpu_dump(stderr, vcpu, 2);
TEST_ASSERT(0, "Unexpected trap taken by guest");
@@ -80,6 +68,5 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
break;
}
}
-
- return ucall.cmd;
+ return 0;
}
diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
index 114cb4af295f..f7a5a7eb4aa8 100644
--- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
+++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
@@ -20,13 +20,9 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
}

-uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
- struct ucall ucall = {};
-
- if (uc)
- memset(uc, 0, sizeof(*uc));

if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
run->s390_sieic.icptcode == 4 &&
@@ -34,13 +30,7 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
(run->s390_sieic.ipb >> 16) == 0x501) {
int reg = run->s390_sieic.ipa & 0xf;

- memcpy(&ucall, addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]),
- sizeof(ucall));
-
- vcpu_run_complete_io(vcpu);
- if (uc)
- memcpy(uc, &ucall, sizeof(ucall));
+ return run->s.regs.gprs[reg];
}
-
- return ucall.cmd;
+ return 0;
}
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
index 749ffdf23855..a060252bab40 100644
--- a/tools/testing/selftests/kvm/lib/ucall_common.c
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -18,3 +18,22 @@ void ucall(uint64_t cmd, int nargs, ...)

ucall_arch_do_ucall((vm_vaddr_t)&uc);
}
+
+uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+{
+ struct ucall ucall;
+ void *addr;
+
+ if (!uc)
+ uc = &ucall;
+
+ addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
+ if (addr) {
+ memcpy(uc, addr, sizeof(*uc));
+ vcpu_run_complete_io(vcpu);
+ } else {
+ memset(uc, 0, sizeof(*uc));
+ }
+
+ return uc->cmd;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
index 9f532dba1003..24746120a593 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
@@ -22,25 +22,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
: : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
}

-uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
+uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
{
struct kvm_run *run = vcpu->run;
- struct ucall ucall = {};
-
- if (uc)
- memset(uc, 0, sizeof(*uc));

if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
struct kvm_regs regs;

vcpu_regs_get(vcpu, &regs);
- memcpy(&ucall, addr_gva2hva(vcpu->vm, (vm_vaddr_t)regs.rdi),
- sizeof(ucall));
-
- vcpu_run_complete_io(vcpu);
- if (uc)
- memcpy(uc, &ucall, sizeof(ucall));
+ return regs.rdi;
}
-
- return ucall.cmd;
+ return 0;
}
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:18

by Peter Gonda

[permalink] [raw]
Subject: [V2 08/11] tools: Add atomic_test_and_set_bit()

atomic_test_and_set_bit() allows for atomic bitmap usage from KVM
selftests.

Signed-off-by: Peter Gonda <[email protected]>
Suggested-by: Sean Christopherson <[email protected]>
---
tools/arch/x86/include/asm/atomic.h | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/tools/arch/x86/include/asm/atomic.h b/tools/arch/x86/include/asm/atomic.h
index 1f5e26aae9fc..01cc27ec4520 100644
--- a/tools/arch/x86/include/asm/atomic.h
+++ b/tools/arch/x86/include/asm/atomic.h
@@ -8,6 +8,7 @@

#define LOCK_PREFIX "\n\tlock; "

+#include <asm/asm.h>
#include <asm/cmpxchg.h>

/*
@@ -70,4 +71,10 @@ static __always_inline int atomic_cmpxchg(atomic_t *v, int old, int new)
return cmpxchg(&v->counter, old, new);
}

+static inline int atomic_test_and_set_bit(long nr, unsigned long *addr)
+{
+ GEN_BINARY_RMWcc(LOCK_PREFIX __ASM_SIZE(bts), *addr, "Ir", nr, "%0", "c");
+
+}
+
#endif /* _TOOLS_LINUX_ASM_X86_ATOMIC_H */
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:20

by Peter Gonda

[permalink] [raw]
Subject: [V2 09/11] KVM: selftests: Make ucall work with encrypted guests

Add support for encrypted, SEV, guests in the ucall framework. If
encryption is enabled set up a pool of ucall structs in the guests'
shared memory region. This was suggested in the thread on "[RFC PATCH
00/10] KVM: selftests: Add support for test-selectable ucall
implementations". Using a listed as suggested there doesn't work well
because the list is setup using HVAs not GVAs so use a bitmap + array
solution instead to get the same pool result.

Suggested-by: Sean Christopherson <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 3 +
.../selftests/kvm/include/ucall_common.h | 14 +--
.../testing/selftests/kvm/lib/ucall_common.c | 112 +++++++++++++++++-
3 files changed, 115 insertions(+), 14 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 8ce9e5be70a3..ad4abc6be1ab 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -102,6 +102,9 @@ struct kvm_vm {
int stats_fd;
struct kvm_stats_header stats_header;
struct kvm_stats_desc *stats_desc;
+
+ bool use_ucall_list;
+ struct list_head ucall_list;
};


diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
index c1bc8e33ef3f..a96220ac6024 100644
--- a/tools/testing/selftests/kvm/include/ucall_common.h
+++ b/tools/testing/selftests/kvm/include/ucall_common.h
@@ -22,6 +22,10 @@ enum {
struct ucall {
uint64_t cmd;
uint64_t args[UCALL_MAX_ARGS];
+
+ /* For encrypted guests. */
+ uint64_t idx;
+ struct ucall *hva;
};

void ucall_arch_init(struct kvm_vm *vm, void *arg);
@@ -32,15 +36,9 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
void ucall(uint64_t cmd, int nargs, ...);
uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);

-static inline void ucall_init(struct kvm_vm *vm, void *arg)
-{
- ucall_arch_init(vm, arg);
-}
+void ucall_init(struct kvm_vm *vm, void *arg);

-static inline void ucall_uninit(struct kvm_vm *vm)
-{
- ucall_arch_uninit(vm);
-}
+void ucall_uninit(struct kvm_vm *vm);

#define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
index a060252bab40..feb0173179ec 100644
--- a/tools/testing/selftests/kvm/lib/ucall_common.c
+++ b/tools/testing/selftests/kvm/lib/ucall_common.c
@@ -1,22 +1,122 @@
// SPDX-License-Identifier: GPL-2.0-only
#include "kvm_util.h"
+#include "linux/types.h"
+#include "linux/bitmap.h"
+#include "linux/atomic.h"
+
+struct ucall_header {
+ DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
+ struct ucall ucalls[KVM_MAX_VCPUS];
+};
+
+static bool use_ucall_list;
+static struct ucall_header *ucall_hdr;
+
+void ucall_init(struct kvm_vm *vm, void *arg)
+{
+ struct ucall *uc;
+ struct ucall_header *hdr;
+ vm_vaddr_t vaddr;
+ int i;
+
+ use_ucall_list = vm->use_ucall_list;
+ sync_global_to_guest(vm, use_ucall_list);
+ if (!use_ucall_list)
+ goto out;
+
+ TEST_ASSERT(!ucall_hdr,
+ "Only a single encrypted guest at a time for ucalls.");
+ vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), vm->page_size);
+ hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
+ memset(hdr, 0, sizeof(*hdr));
+
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ uc = &hdr->ucalls[i];
+ uc->hva = uc;
+ uc->idx = i;
+ }
+
+ ucall_hdr = (struct ucall_header *)vaddr;
+ sync_global_to_guest(vm, ucall_hdr);
+
+out:
+ ucall_arch_init(vm, arg);
+}
+
+void ucall_uninit(struct kvm_vm *vm)
+{
+ use_ucall_list = false;
+ ucall_hdr = NULL;
+
+ ucall_arch_uninit(vm);
+}
+
+static struct ucall *ucall_alloc(void)
+{
+ struct ucall *uc = NULL;
+ int i;
+
+ if (!use_ucall_list)
+ goto out;
+
+ for (i = 0; i < KVM_MAX_VCPUS; ++i) {
+ if (atomic_test_and_set_bit(i, ucall_hdr->in_use))
+ continue;
+
+ uc = &ucall_hdr->ucalls[i];
+ }
+
+out:
+ return uc;
+}
+
+static void ucall_free(struct ucall *uc)
+{
+ if (!use_ucall_list)
+ return;
+
+ clear_bit(uc->idx, ucall_hdr->in_use);
+}
+
+static vm_vaddr_t get_ucall_addr(struct ucall *uc)
+{
+ if (use_ucall_list)
+ return (vm_vaddr_t)uc->hva;
+
+ return (vm_vaddr_t)uc;
+}

void ucall(uint64_t cmd, int nargs, ...)
{
- struct ucall uc = {
- .cmd = cmd,
- };
+ struct ucall *uc;
+ struct ucall tmp;
va_list va;
int i;

+ uc = ucall_alloc();
+ if (!uc)
+ uc = &tmp;
+
+ uc->cmd = cmd;
+
nargs = min(nargs, UCALL_MAX_ARGS);

va_start(va, nargs);
for (i = 0; i < nargs; ++i)
- uc.args[i] = va_arg(va, uint64_t);
+ uc->args[i] = va_arg(va, uint64_t);
va_end(va);

- ucall_arch_do_ucall((vm_vaddr_t)&uc);
+ ucall_arch_do_ucall(get_ucall_addr(uc));
+
+ ucall_free(uc);
+}
+
+static void *get_ucall_hva(struct kvm_vm *vm, uint64_t uc)
+{
+ if (vm->use_ucall_list)
+ return (void *)uc;
+
+ return addr_gva2hva(vm, uc);
}

uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
@@ -27,7 +127,7 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
if (!uc)
uc = &ucall;

- addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
+ addr = get_ucall_hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
if (addr) {
memcpy(uc, addr, sizeof(*uc));
vcpu_run_complete_io(vcpu);
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:24

by Peter Gonda

[permalink] [raw]
Subject: [V2 11/11] KVM: selftests: Add simple sev vm testing

A very simple of booting SEV guests that checks related CPUID bits. This
is a stripped down version of "[PATCH v2 08/13] KVM: selftests: add SEV
boot tests" from Michael but much simpler.

Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
tools/testing/selftests/kvm/.gitignore | 1 +
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/kvm_util_base.h | 2 +
.../selftests/kvm/include/x86_64/sev.h | 3 +
tools/testing/selftests/kvm/lib/x86_64/sev.c | 2 -
.../selftests/kvm/x86_64/sev_all_boot_test.c | 131 ++++++++++++++++++
6 files changed, 138 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/kvm/x86_64/sev_all_boot_test.c

diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore
index d625a3f83780..ca57969a0923 100644
--- a/tools/testing/selftests/kvm/.gitignore
+++ b/tools/testing/selftests/kvm/.gitignore
@@ -33,6 +33,7 @@
/x86_64/pmu_event_filter_test
/x86_64/set_boot_cpu_id
/x86_64/set_sregs_test
+/x86_64/sev_all_boot_test
/x86_64/sev_migrate_tests
/x86_64/smm_test
/x86_64/state_test
diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index b247c4b595af..73b083f93b46 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -122,6 +122,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/tsc_msrs_test
TEST_GEN_PROGS_x86_64 += x86_64/vmx_pmu_caps_test
TEST_GEN_PROGS_x86_64 += x86_64/xen_shinfo_test
TEST_GEN_PROGS_x86_64 += x86_64/xen_vmcall_test
+TEST_GEN_PROGS_x86_64 += x86_64/sev_all_boot_test
TEST_GEN_PROGS_x86_64 += x86_64/sev_migrate_tests
TEST_GEN_PROGS_x86_64 += x86_64/amx_test
TEST_GEN_PROGS_x86_64 += x86_64/max_vcpuid_cap_test
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 1172c4218ccc..3c0ab83c5f69 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -820,6 +820,8 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva);

static inline vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
{
+ TEST_ASSERT(!vm->memcrypt.encrypted,
+ "Encrypted guests have their page tables encrypted so gva2* conversions are not possible.");
return addr_arch_gva2gpa(vm, gva);
}

diff --git a/tools/testing/selftests/kvm/include/x86_64/sev.h b/tools/testing/selftests/kvm/include/x86_64/sev.h
index 2f7f7c741b12..b6552ea1c716 100644
--- a/tools/testing/selftests/kvm/include/x86_64/sev.h
+++ b/tools/testing/selftests/kvm/include/x86_64/sev.h
@@ -22,6 +22,9 @@
#define SEV_POLICY_NO_DBG (1UL << 0)
#define SEV_POLICY_ES (1UL << 2)

+#define CPUID_MEM_ENC_LEAF 0x8000001f
+#define CPUID_EBX_CBIT_MASK 0x3f
+
enum {
SEV_GSTATE_UNINIT = 0,
SEV_GSTATE_LUPDATE,
diff --git a/tools/testing/selftests/kvm/lib/x86_64/sev.c b/tools/testing/selftests/kvm/lib/x86_64/sev.c
index 3abcf50c0b5d..8f9f55c685a7 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/sev.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/sev.c
@@ -13,8 +13,6 @@
#include "sev.h"

#define PAGE_SHIFT 12
-#define CPUID_MEM_ENC_LEAF 0x8000001f
-#define CPUID_EBX_CBIT_MASK 0x3f

struct sev_vm {
struct kvm_vm *vm;
diff --git a/tools/testing/selftests/kvm/x86_64/sev_all_boot_test.c b/tools/testing/selftests/kvm/x86_64/sev_all_boot_test.c
new file mode 100644
index 000000000000..b319d18bdb60
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86_64/sev_all_boot_test.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Basic SEV boot tests.
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+#define _GNU_SOURCE /* for program_invocation_short_name */
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include "test_util.h"
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "svm_util.h"
+#include "linux/psp-sev.h"
+#include "sev.h"
+
+#define VCPU_ID 2
+#define PAGE_STRIDE 32
+
+#define SHARED_PAGES 8192
+#define SHARED_VADDR_MIN 0x1000000
+
+#define PRIVATE_PAGES 2048
+#define PRIVATE_VADDR_MIN (SHARED_VADDR_MIN + SHARED_PAGES * PAGE_SIZE)
+
+#define TOTAL_PAGES (512 + SHARED_PAGES + PRIVATE_PAGES)
+
+#define NR_SYNCS 1
+
+static void guest_run_loop(struct kvm_vcpu *vcpu)
+{
+ struct ucall uc;
+ int i;
+
+ for (i = 0; i <= NR_SYNCS; ++i) {
+ vcpu_run(vcpu);
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_SYNC:
+ continue;
+ case UCALL_DONE:
+ return;
+ case UCALL_ABORT:
+ TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx",
+ (const char *)uc.args[0], __FILE__,
+ uc.args[1], uc.args[2], uc.args[3]);
+ default:
+ TEST_ASSERT(
+ false, "Unexpected exit: %s",
+ exit_reason_str(vcpu->run->exit_reason));
+ }
+ }
+}
+
+static void __attribute__((__flatten__)) guest_sev_code(void)
+{
+ uint32_t eax, ebx, ecx, edx;
+ uint64_t sev_status;
+
+ GUEST_SYNC(1);
+
+ cpuid(CPUID_MEM_ENC_LEAF, &eax, &ebx, &ecx, &edx);
+ GUEST_ASSERT(eax & (1 << 1));
+
+ sev_status = rdmsr(MSR_AMD64_SEV);
+ GUEST_ASSERT((sev_status & 0x1) == 1);
+
+ GUEST_DONE();
+}
+
+static struct sev_vm *setup_test_common(void *guest_code, uint64_t policy,
+ struct kvm_vcpu **vcpu)
+{
+ uint8_t measurement[512];
+ struct sev_vm *sev;
+ struct kvm_vm *vm;
+ int i;
+
+ sev = sev_vm_create(policy, TOTAL_PAGES);
+ if (!sev)
+ return NULL;
+ vm = sev_get_vm(sev);
+
+ /* Set up VCPU and initial guest kernel. */
+ *vcpu = vm_vcpu_add(vm, VCPU_ID, guest_code);
+ kvm_vm_elf_load(vm, program_invocation_name);
+
+ /* Allocations/setup done. Encrypt initial guest payload. */
+ sev_vm_launch(sev);
+
+ /* Dump the initial measurement. A test to actually verify it would be nice. */
+ sev_vm_launch_measure(sev, measurement);
+ pr_info("guest measurement: ");
+ for (i = 0; i < 32; ++i)
+ pr_info("%02x", measurement[i]);
+ pr_info("\n");
+
+ sev_vm_launch_finish(sev);
+
+ return sev;
+}
+
+static void test_sev(void *guest_code, uint64_t policy)
+{
+ struct sev_vm *sev;
+ struct kvm_vcpu *vcpu;
+
+ sev = setup_test_common(guest_code, policy, &vcpu);
+ if (!sev)
+ return;
+
+ /* Guest is ready to run. Do the tests. */
+ guest_run_loop(vcpu);
+
+ pr_info("guest ran successfully\n");
+
+ sev_vm_free(sev);
+}
+
+int main(int argc, char *argv[])
+{
+ /* SEV tests */
+ test_sev(guest_sev_code, SEV_POLICY_NO_DBG);
+ test_sev(guest_sev_code, 0);
+
+ return 0;
+}
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:24

by Peter Gonda

[permalink] [raw]
Subject: [V2 10/11] KVM: selftests: add library for creating/interacting with SEV guests

From: Michael Roth <[email protected]>

Add interfaces to allow tests to create/manage SEV guests. The
additional state associated with these guests is encapsulated in a new
struct sev_vm, which is a light wrapper around struct kvm_vm. These
VMs will use vm_set_memory_encryption() and vm_get_encrypted_phy_pages()
under the covers to configure and sync up with the core kvm_util
library on what should/shouldn't be treated as encrypted memory.

Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
tools/testing/selftests/kvm/Makefile | 1 +
.../selftests/kvm/include/kvm_util_base.h | 1 +
.../selftests/kvm/include/x86_64/sev.h | 44 +++
tools/testing/selftests/kvm/lib/x86_64/sev.c | 251 ++++++++++++++++++
4 files changed, 297 insertions(+)
create mode 100644 tools/testing/selftests/kvm/include/x86_64/sev.h
create mode 100644 tools/testing/selftests/kvm/lib/x86_64/sev.c

diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
index 39fc5e8e5594..b247c4b595af 100644
--- a/tools/testing/selftests/kvm/Makefile
+++ b/tools/testing/selftests/kvm/Makefile
@@ -55,6 +55,7 @@ LIBKVM_x86_64 += lib/x86_64/processor.c
LIBKVM_x86_64 += lib/x86_64/svm.c
LIBKVM_x86_64 += lib/x86_64/ucall.c
LIBKVM_x86_64 += lib/x86_64/vmx.c
+LIBKVM_x86_64 += lib/x86_64/sev.c

LIBKVM_aarch64 += lib/aarch64/gic.c
LIBKVM_aarch64 += lib/aarch64/gic_v3.c
diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index ad4abc6be1ab..1172c4218ccc 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -68,6 +68,7 @@ struct userspace_mem_regions {
/* Memory encryption policy/configuration. */
struct vm_memcrypt {
bool enabled;
+ bool encrypted;
int8_t enc_by_default;
bool has_enc_bit;
int8_t enc_bit;
diff --git a/tools/testing/selftests/kvm/include/x86_64/sev.h b/tools/testing/selftests/kvm/include/x86_64/sev.h
new file mode 100644
index 000000000000..2f7f7c741b12
--- /dev/null
+++ b/tools/testing/selftests/kvm/include/x86_64/sev.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Helpers used for SEV guests
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+#ifndef SELFTEST_KVM_SEV_H
+#define SELFTEST_KVM_SEV_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "kvm_util.h"
+
+/* Makefile might set this separately for user-overrides */
+#ifndef SEV_DEV_PATH
+#define SEV_DEV_PATH "/dev/sev"
+#endif
+
+#define SEV_FW_REQ_VER_MAJOR 0
+#define SEV_FW_REQ_VER_MINOR 17
+
+#define SEV_POLICY_NO_DBG (1UL << 0)
+#define SEV_POLICY_ES (1UL << 2)
+
+enum {
+ SEV_GSTATE_UNINIT = 0,
+ SEV_GSTATE_LUPDATE,
+ SEV_GSTATE_LSECRET,
+ SEV_GSTATE_RUNNING,
+};
+
+struct sev_vm;
+
+void kvm_sev_ioctl(struct sev_vm *sev, int cmd, void *data);
+struct kvm_vm *sev_get_vm(struct sev_vm *sev);
+uint8_t sev_get_enc_bit(struct sev_vm *sev);
+
+struct sev_vm *sev_vm_create(uint32_t policy, uint64_t npages);
+void sev_vm_free(struct sev_vm *sev);
+void sev_vm_launch(struct sev_vm *sev);
+void sev_vm_launch_measure(struct sev_vm *sev, uint8_t *measurement);
+void sev_vm_launch_finish(struct sev_vm *sev);
+
+#endif /* SELFTEST_KVM_SEV_H */
diff --git a/tools/testing/selftests/kvm/lib/x86_64/sev.c b/tools/testing/selftests/kvm/lib/x86_64/sev.c
new file mode 100644
index 000000000000..3abcf50c0b5d
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86_64/sev.c
@@ -0,0 +1,251 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Helpers used for SEV guests
+ *
+ * Copyright (C) 2021 Advanced Micro Devices
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "kvm_util.h"
+#include "linux/psp-sev.h"
+#include "processor.h"
+#include "sev.h"
+
+#define PAGE_SHIFT 12
+#define CPUID_MEM_ENC_LEAF 0x8000001f
+#define CPUID_EBX_CBIT_MASK 0x3f
+
+struct sev_vm {
+ struct kvm_vm *vm;
+ int fd;
+ int enc_bit;
+ uint32_t sev_policy;
+};
+
+/* Common SEV helpers/accessors. */
+
+struct kvm_vm *sev_get_vm(struct sev_vm *sev)
+{
+ return sev->vm;
+}
+
+uint8_t sev_get_enc_bit(struct sev_vm *sev)
+{
+ return sev->enc_bit;
+}
+
+void sev_ioctl(int sev_fd, int cmd, void *data)
+{
+ int ret;
+ struct sev_issue_cmd arg;
+
+ arg.cmd = cmd;
+ arg.data = (unsigned long)data;
+ ret = ioctl(sev_fd, SEV_ISSUE_CMD, &arg);
+ TEST_ASSERT(ret == 0,
+ "SEV ioctl %d failed, error: %d, fw_error: %d",
+ cmd, ret, arg.error);
+}
+
+void kvm_sev_ioctl(struct sev_vm *sev, int cmd, void *data)
+{
+ struct kvm_sev_cmd arg = {0};
+ int ret;
+
+ arg.id = cmd;
+ arg.sev_fd = sev->fd;
+ arg.data = (__u64)data;
+
+ ret = ioctl(sev->vm->fd, KVM_MEMORY_ENCRYPT_OP, &arg);
+ TEST_ASSERT(ret == 0,
+ "SEV KVM ioctl %d failed, rc: %i errno: %i (%s), fw_error: %d",
+ cmd, ret, errno, strerror(errno), arg.error);
+}
+
+/* Local helpers. */
+
+static void
+sev_register_user_region(struct sev_vm *sev, void *hva, uint64_t size)
+{
+ struct kvm_enc_region range = {0};
+ int ret;
+
+ pr_debug("%s: hva: %p, size: %lu\n", __func__, hva, size);
+
+ range.addr = (__u64)hva;
+ range.size = size;
+
+ ret = ioctl(sev->vm->fd, KVM_MEMORY_ENCRYPT_REG_REGION, &range);
+ TEST_ASSERT(ret == 0, "failed to register user range, errno: %i\n", errno);
+}
+
+static void
+sev_encrypt_phy_range(struct sev_vm *sev, vm_paddr_t gpa, uint64_t size)
+{
+ struct kvm_sev_launch_update_data ksev_update_data = {0};
+
+ pr_debug("%s: addr: 0x%lx, size: %lu\n", __func__, gpa, size);
+
+ ksev_update_data.uaddr = (__u64)addr_gpa2hva(sev->vm, gpa);
+ ksev_update_data.len = size;
+
+ kvm_sev_ioctl(sev, KVM_SEV_LAUNCH_UPDATE_DATA, &ksev_update_data);
+}
+
+static void sev_encrypt(struct sev_vm *sev)
+{
+ const struct sparsebit *enc_phy_pages;
+ struct kvm_vm *vm = sev->vm;
+ sparsebit_idx_t pg = 0;
+ vm_paddr_t gpa_start;
+ uint64_t memory_size;
+
+ /* Only memslot 0 supported for now. */
+ enc_phy_pages = vm_get_encrypted_phy_pages(sev->vm, 0, &gpa_start, &memory_size);
+ TEST_ASSERT(enc_phy_pages, "Unable to retrieve encrypted pages bitmap");
+ while (pg < (memory_size / vm->page_size)) {
+ sparsebit_idx_t pg_cnt;
+
+ if (sparsebit_is_clear(enc_phy_pages, pg)) {
+ pg = sparsebit_next_set(enc_phy_pages, pg);
+ if (!pg)
+ break;
+ }
+
+ pg_cnt = sparsebit_next_clear(enc_phy_pages, pg) - pg;
+ if (pg_cnt <= 0)
+ pg_cnt = 1;
+
+ sev_encrypt_phy_range(sev,
+ gpa_start + pg * vm->page_size,
+ pg_cnt * vm->page_size);
+ pg += pg_cnt;
+ }
+
+ sev->vm->memcrypt.encrypted = true;
+}
+
+/* SEV VM implementation. */
+
+static struct sev_vm *sev_vm_alloc(struct kvm_vm *vm)
+{
+ struct sev_user_data_status sev_status = {0};
+ uint32_t eax, ebx, ecx, edx;
+ struct sev_vm *sev;
+ int sev_fd;
+
+ sev_fd = open(SEV_DEV_PATH, O_RDWR);
+ if (sev_fd < 0) {
+ pr_info("Failed to open SEV device, path: %s, error: %d, skipping test.\n",
+ SEV_DEV_PATH, sev_fd);
+ return NULL;
+ }
+
+ sev_ioctl(sev_fd, SEV_PLATFORM_STATUS, &sev_status);
+
+ if (!(sev_status.api_major > SEV_FW_REQ_VER_MAJOR ||
+ (sev_status.api_major == SEV_FW_REQ_VER_MAJOR &&
+ sev_status.api_minor >= SEV_FW_REQ_VER_MINOR))) {
+ pr_info("SEV FW version too old. Have API %d.%d (build: %d), need %d.%d, skipping test.\n",
+ sev_status.api_major, sev_status.api_minor, sev_status.build,
+ SEV_FW_REQ_VER_MAJOR, SEV_FW_REQ_VER_MINOR);
+ return NULL;
+ }
+
+ sev = calloc(1, sizeof(*sev));
+ sev->fd = sev_fd;
+ sev->vm = vm;
+
+ /* Get encryption bit via CPUID. */
+ cpuid(CPUID_MEM_ENC_LEAF, &eax, &ebx, &ecx, &edx);
+ sev->enc_bit = ebx & CPUID_EBX_CBIT_MASK;
+
+ return sev;
+}
+
+void sev_vm_free(struct sev_vm *sev)
+{
+ ucall_uninit(sev->vm);
+ kvm_vm_free(sev->vm);
+ close(sev->fd);
+ free(sev);
+}
+
+struct sev_vm *sev_vm_create(uint32_t policy, uint64_t npages)
+{
+ struct sev_vm *sev;
+ struct kvm_vm *vm;
+
+ /* Need to handle memslots after init, and after setting memcrypt. */
+ vm = vm_create_barebones();
+ sev = sev_vm_alloc(vm);
+ if (!sev)
+ return NULL;
+ sev->sev_policy = policy;
+
+ kvm_sev_ioctl(sev, KVM_SEV_INIT, NULL);
+
+ vm->vpages_mapped = sparsebit_alloc();
+ vm_set_memory_encryption(vm, true, true, sev->enc_bit);
+ pr_info("SEV cbit: %d\n", sev->enc_bit);
+ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, 0, 0, npages, 0);
+ sev_register_user_region(sev, addr_gpa2hva(vm, 0),
+ npages * vm->page_size);
+
+ pr_info("SEV guest created, policy: 0x%x, size: %lu KB\n",
+ sev->sev_policy, npages * vm->page_size / 1024);
+
+ return sev;
+}
+
+void sev_vm_launch(struct sev_vm *sev)
+{
+ struct kvm_sev_launch_start ksev_launch_start = {0};
+ struct kvm_sev_guest_status ksev_status = {0};
+
+ /* Need to use ucall_shared for synchronization. */
+ //ucall_init_ops(sev_get_vm(sev), NULL, &ucall_ops_halt);
+
+ ksev_launch_start.policy = sev->sev_policy;
+ kvm_sev_ioctl(sev, KVM_SEV_LAUNCH_START, &ksev_launch_start);
+ kvm_sev_ioctl(sev, KVM_SEV_GUEST_STATUS, &ksev_status);
+ TEST_ASSERT(ksev_status.policy == sev->sev_policy, "Incorrect guest policy.");
+ TEST_ASSERT(ksev_status.state == SEV_GSTATE_LUPDATE,
+ "Unexpected guest state: %d", ksev_status.state);
+
+ ucall_init(sev->vm, NULL);
+
+ sev_encrypt(sev);
+}
+
+void sev_vm_launch_measure(struct sev_vm *sev, uint8_t *measurement)
+{
+ struct kvm_sev_launch_measure ksev_launch_measure = {0};
+ struct kvm_sev_guest_status ksev_guest_status = {0};
+
+ ksev_launch_measure.len = 256;
+ ksev_launch_measure.uaddr = (__u64)measurement;
+ kvm_sev_ioctl(sev, KVM_SEV_LAUNCH_MEASURE, &ksev_launch_measure);
+
+ /* Measurement causes a state transition, check that. */
+ kvm_sev_ioctl(sev, KVM_SEV_GUEST_STATUS, &ksev_guest_status);
+ TEST_ASSERT(ksev_guest_status.state == SEV_GSTATE_LSECRET,
+ "Unexpected guest state: %d", ksev_guest_status.state);
+}
+
+void sev_vm_launch_finish(struct sev_vm *sev)
+{
+ struct kvm_sev_guest_status ksev_status = {0};
+
+ kvm_sev_ioctl(sev, KVM_SEV_GUEST_STATUS, &ksev_status);
+ TEST_ASSERT(ksev_status.state == SEV_GSTATE_LUPDATE ||
+ ksev_status.state == SEV_GSTATE_LSECRET,
+ "Unexpected guest state: %d", ksev_status.state);
+
+ kvm_sev_ioctl(sev, KVM_SEV_LAUNCH_FINISH, NULL);
+
+ kvm_sev_ioctl(sev, KVM_SEV_GUEST_STATUS, &ksev_status);
+ TEST_ASSERT(ksev_status.state == SEV_GSTATE_RUNNING,
+ "Unexpected guest state: %d", ksev_status.state);
+}
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:29

by Peter Gonda

[permalink] [raw]
Subject: [V2 05/11] KVM: selftests: add support for encrypted vm_vaddr_* allocations

From: Michael Roth <[email protected]>

The default policy for whether to handle allocations as encrypted or
shared pages is currently determined by vm_phy_pages_alloc(), which in
turn uses the policy defined by vm->memcrypt.enc_by_default.

Test programs may wish to allocate shared vaddrs for things like
sharing memory with the guest. Since enc_by_default will be true in the
case of SEV guests (since it's required in order to have the initial
ELF binary and page table become part of the initial guest payload), an
interface is needed to explicitly request shared pages.

Implement this by splitting the common code out from vm_vaddr_alloc()
and introducing a new vm_vaddr_alloc_shared().

Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 1 +
tools/testing/selftests/kvm/lib/kvm_util.c | 21 +++++++++++++++----
2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index de769b3de274..8ce9e5be70a3 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -390,6 +390,7 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa);
void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot);
struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id);
vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
+vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min);
vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages);
vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm);

diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index 87772e23d1b5..4e4b28e4e890 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1262,12 +1262,13 @@ static vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
}

/*
- * VM Virtual Address Allocate
+ * VM Virtual Address Allocate Shared/Encrypted
*
* Input Args:
* vm - Virtual Machine
* sz - Size in bytes
* vaddr_min - Minimum starting virtual address
+ * encrypt - Whether the region should be handled as encrypted
*
* Output Args: None
*
@@ -1280,13 +1281,15 @@ static vm_vaddr_t vm_vaddr_unused_gap(struct kvm_vm *vm, size_t sz,
* a unique set of pages, with the minimum real allocation being at least
* a page.
*/
-vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
+static vm_vaddr_t
+_vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min, bool encrypt)
{
uint64_t pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);

virt_pgd_alloc(vm);
- vm_paddr_t paddr = vm_phy_pages_alloc(vm, pages,
- KVM_UTIL_MIN_PFN * vm->page_size, 0);
+ vm_paddr_t paddr = _vm_phy_pages_alloc(vm, pages,
+ KVM_UTIL_MIN_PFN * vm->page_size,
+ 0, encrypt);

/*
* Find an unused range of virtual page addresses of at least
@@ -1307,6 +1310,16 @@ vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
return vaddr_start;
}

+vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
+{
+ return _vm_vaddr_alloc(vm, sz, vaddr_min, vm->memcrypt.enc_by_default);
+}
+
+vm_vaddr_t vm_vaddr_alloc_shared(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min)
+{
+ return _vm_vaddr_alloc(vm, sz, vaddr_min, false);
+}
+
/*
* VM Virtual Address Allocate Pages
*
--
2.37.1.455.g008518b4e5-goog


2022-08-01 20:17:29

by Peter Gonda

[permalink] [raw]
Subject: [V2 04/11] KVM: selftests: handle encryption bits in page tables

From: Michael Roth <[email protected]>

SEV guests rely on an encyption bit which resides within the range that
current code treats as address bits. Guest code will expect these bits
to be set appropriately in their page tables, whereas the rest of the
kvm_util functions will generally expect these bits to not be present.
Introduce addr_gpa2raw()/addr_raw2gpa() to add/remove these bits, then
use them where appropriate.

Signed-off-by: Michael Roth <[email protected]>
Signed-off-by: Peter Gonda <[email protected]>
---
.../selftests/kvm/include/kvm_util_base.h | 2 +
tools/testing/selftests/kvm/lib/kvm_util.c | 55 ++++++++++++++++++-
.../selftests/kvm/lib/x86_64/processor.c | 15 +++--
3 files changed, 66 insertions(+), 6 deletions(-)

diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
index 3928351e497e..de769b3de274 100644
--- a/tools/testing/selftests/kvm/include/kvm_util_base.h
+++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
@@ -399,6 +399,8 @@ void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa);
void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva);
vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva);
void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa);
+vm_paddr_t addr_raw2gpa(struct kvm_vm *vm, vm_vaddr_t gpa_raw);
+vm_paddr_t addr_gpa2raw(struct kvm_vm *vm, vm_vaddr_t gpa);

void vcpu_run(struct kvm_vcpu *vcpu);
int _vcpu_run(struct kvm_vcpu *vcpu);
diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c
index c6b87b411186..87772e23d1b5 100644
--- a/tools/testing/selftests/kvm/lib/kvm_util.c
+++ b/tools/testing/selftests/kvm/lib/kvm_util.c
@@ -1377,6 +1377,58 @@ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
}
}

+/*
+ * Mask off any special bits from raw GPA
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ * gpa_raw - Raw VM physical address
+ *
+ * Output Args: None
+ *
+ * Return:
+ * GPA with special bits (e.g. shared/encrypted) masked off.
+ */
+vm_paddr_t addr_raw2gpa(struct kvm_vm *vm, vm_paddr_t gpa_raw)
+{
+ if (!vm->memcrypt.has_enc_bit)
+ return gpa_raw;
+
+ return gpa_raw & ~(1ULL << vm->memcrypt.enc_bit);
+}
+
+/*
+ * Add special/encryption bits to a GPA based on encryption bitmap.
+ *
+ * Input Args:
+ * vm - Virtual Machine
+ * gpa - VM physical address
+ *
+ * Output Args: None
+ *
+ * Return:
+ * GPA with special bits (e.g. shared/encrypted) added in.
+ */
+vm_paddr_t addr_gpa2raw(struct kvm_vm *vm, vm_paddr_t gpa)
+{
+ struct userspace_mem_region *region;
+ sparsebit_idx_t pg;
+ vm_paddr_t gpa_raw = gpa;
+
+ TEST_ASSERT(addr_raw2gpa(vm, gpa) == gpa, "Unexpected bits in GPA: %lx",
+ gpa);
+
+ if (!vm->memcrypt.has_enc_bit)
+ return gpa;
+
+ region = userspace_mem_region_find(vm, gpa, gpa);
+ pg = gpa >> vm->page_shift;
+ if (sparsebit_is_set(region->encrypted_phy_pages, pg))
+ gpa_raw |= (1ULL << vm->memcrypt.enc_bit);
+
+ return gpa_raw;
+}
+
/*
* Address VM Physical to Host Virtual
*
@@ -1394,9 +1446,10 @@ void virt_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr,
* address providing the memory to the vm physical address is returned.
* A TEST_ASSERT failure occurs if no region containing gpa exists.
*/
-void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa)
+void *addr_gpa2hva(struct kvm_vm *vm, vm_paddr_t gpa_raw)
{
struct userspace_mem_region *region;
+ vm_paddr_t gpa = addr_raw2gpa(vm, gpa_raw);

region = userspace_mem_region_find(vm, gpa, gpa);
if (!region) {
diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c
index f35626df1dea..55855594d26d 100644
--- a/tools/testing/selftests/kvm/lib/x86_64/processor.c
+++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c
@@ -118,7 +118,7 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)

/* If needed, create page map l4 table. */
if (!vm->pgd_created) {
- vm->pgd = vm_alloc_page_table(vm);
+ vm->pgd = addr_gpa2raw(vm, vm_alloc_page_table(vm));
vm->pgd_created = true;
}
}
@@ -140,13 +140,15 @@ static uint64_t *virt_create_upper_pte(struct kvm_vm *vm,
int target_level)
{
uint64_t *pte = virt_get_pte(vm, pt_pfn, vaddr, current_level);
+ uint64_t paddr_raw = addr_gpa2raw(vm, paddr);

if (!(*pte & PTE_PRESENT_MASK)) {
*pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK;
if (current_level == target_level)
- *pte |= PTE_LARGE_MASK | (paddr & PHYSICAL_PAGE_MASK);
+ *pte |= PTE_LARGE_MASK | (paddr_raw & PHYSICAL_PAGE_MASK);
else
- *pte |= vm_alloc_page_table(vm) & PHYSICAL_PAGE_MASK;
+ *pte |= addr_gpa2raw(vm, vm_alloc_page_table(vm)) & PHYSICAL_PAGE_MASK;
+
} else {
/*
* Entry already present. Assert that the caller doesn't want
@@ -184,6 +186,8 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level)
"Physical address beyond maximum supported,\n"
" paddr: 0x%lx vm->max_gfn: 0x%lx vm->page_size: 0x%x",
paddr, vm->max_gfn, vm->page_size);
+ TEST_ASSERT(addr_raw2gpa(vm, paddr) == paddr,
+ "Unexpected bits in paddr: %lx", paddr);

/*
* Allocate upper level page tables, if not already present. Return
@@ -206,7 +210,8 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level)
pte = virt_get_pte(vm, PTE_GET_PFN(*pde), vaddr, PG_LEVEL_4K);
TEST_ASSERT(!(*pte & PTE_PRESENT_MASK),
"PTE already present for 4k page at vaddr: 0x%lx\n", vaddr);
- *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MASK);
+ *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK |
+ (addr_gpa2raw(vm, paddr) & PHYSICAL_PAGE_MASK);
}

void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr)
@@ -515,7 +520,7 @@ vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
if (!(pte[index[0]] & PTE_PRESENT_MASK))
goto unmapped_gva;

- return (PTE_GET_PFN(pte[index[0]]) * vm->page_size) + (gva & ~PAGE_MASK);
+ return addr_raw2gpa(vm, PTE_GET_PFN(pte[index[0]]) * vm->page_size) + (gva & ~PAGE_MASK);

unmapped_gva:
TEST_FAIL("No mapping for vm virtual address, gva: 0x%lx", gva);
--
2.37.1.455.g008518b4e5-goog


2022-08-02 08:46:57

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 07/11] KVM: selftests: Consolidate boilerplate code in get_ucall()

On Mon, Aug 01, 2022 at 01:11:05PM -0700, Peter Gonda wrote:
> From: Sean Christopherson <[email protected]>
>
> Consolidate the actual copying of a ucall struct from guest=>host into
> the common get_ucall(). Return a host virtual address instead of a guest
> virtual address even though the addr_gva2hva() part could be moved to
> get_ucall() too. Conceptually, get_ucall() is invoked from the host and
> should return a host virtual address (and returning NULL for "nothing to
> see here" is far superior to returning 0).
>
> Use pointer shenanigans instead of an unnecessary bounce buffer when the
> caller of get_ucall() provides a valid pointer.
>
> Signed-off-by: Sean Christopherson <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> .../selftests/kvm/include/ucall_common.h | 8 ++------
> .../testing/selftests/kvm/lib/aarch64/ucall.c | 13 +++----------
> tools/testing/selftests/kvm/lib/riscv/ucall.c | 19 +++----------------
> tools/testing/selftests/kvm/lib/s390x/ucall.c | 16 +++-------------
> .../testing/selftests/kvm/lib/ucall_common.c | 19 +++++++++++++++++++
> .../testing/selftests/kvm/lib/x86_64/ucall.c | 16 +++-------------
> 6 files changed, 33 insertions(+), 58 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> index 5a85f5318bbe..c1bc8e33ef3f 100644
> --- a/tools/testing/selftests/kvm/include/ucall_common.h
> +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> @@ -27,9 +27,10 @@ struct ucall {
> void ucall_arch_init(struct kvm_vm *vm, void *arg);
> void ucall_arch_uninit(struct kvm_vm *vm);
> void ucall_arch_do_ucall(vm_vaddr_t uc);
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);

This should now return a vm_vaddr_t instead of a uint64_t.

>
> void ucall(uint64_t cmd, int nargs, ...);
> +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
>
> static inline void ucall_init(struct kvm_vm *vm, void *arg)
> {
> @@ -41,11 +42,6 @@ static inline void ucall_uninit(struct kvm_vm *vm)
> ucall_arch_uninit(vm);
> }
>
> -static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> -{
> - return ucall_arch_get_ucall(vcpu, uc);
> -}
> -
> #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> #define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage)
> diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> index 1c81a6a5c1f2..d2f099caa9ab 100644
> --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> @@ -78,24 +78,17 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)

Need to drop uc from the parameters.

> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_MMIO &&
> run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
> - vm_vaddr_t gva;
> + uint64_t ucall_addr;
>
> TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
> "Unexpected ucall exit mmio address access");
> memcpy(&gva, run->mmio.data, sizeof(gva));

We just dropped the gva variable, so this can't compile. We don't want
to return a uint64_t anyway, though, so we shouldn't replace gva, we
should return it.

> - memcpy(&ucall, addr_gva2hva(vcpu->vm, gva), sizeof(ucall));
>
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return ucall_addr;
> }
>
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> index b1598f418c1f..3f000d0b705f 100644
> --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> @@ -51,27 +51,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> uc, 0, 0, 0, 0, 0);
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
> run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
> switch (run->riscv_sbi.function_id) {
> case KVM_RISCV_SELFTESTS_SBI_UCALL:
> - memcpy(&ucall,
> - addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> -
> - break;
> + return vcpu->vm, run->riscv_sbi.args[0];

The "vcpu->vm," needs to go away.

> case KVM_RISCV_SELFTESTS_SBI_UNEXP:
> vcpu_dump(stderr, vcpu, 2);
> TEST_ASSERT(0, "Unexpected trap taken by guest");
> @@ -80,6 +68,5 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> break;
> }
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> index 114cb4af295f..f7a5a7eb4aa8 100644
> --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> @@ -20,13 +20,9 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
> run->s390_sieic.icptcode == 4 &&
> @@ -34,13 +30,7 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> (run->s390_sieic.ipb >> 16) == 0x501) {
> int reg = run->s390_sieic.ipa & 0xf;
>
> - memcpy(&ucall, addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return run->s.regs.gprs[reg];
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> index 749ffdf23855..a060252bab40 100644
> --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> @@ -18,3 +18,22 @@ void ucall(uint64_t cmd, int nargs, ...)
>
> ucall_arch_do_ucall((vm_vaddr_t)&uc);
> }
> +
> +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +{
> + struct ucall ucall;
> + void *addr;
> +
> + if (!uc)
> + uc = &ucall;
> +
> + addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> + if (addr) {
> + memcpy(uc, addr, sizeof(*uc));
> + vcpu_run_complete_io(vcpu);
> + } else {
> + memset(uc, 0, sizeof(*uc));
> + }
> +
> + return uc->cmd;
> +}
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> index 9f532dba1003..24746120a593 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> @@ -22,25 +22,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
> struct kvm_regs regs;
>
> vcpu_regs_get(vcpu, &regs);
> - memcpy(&ucall, addr_gva2hva(vcpu->vm, (vm_vaddr_t)regs.rdi),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return regs.rdi;
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> --
> 2.37.1.455.g008518b4e5-goog
>

Thanks,
drew

2022-08-02 10:00:30

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 07/11] KVM: selftests: Consolidate boilerplate code in get_ucall()

On Mon, Aug 01, 2022 at 01:11:05PM -0700, Peter Gonda wrote:
> From: Sean Christopherson <[email protected]>
>
> Consolidate the actual copying of a ucall struct from guest=>host into
> the common get_ucall(). Return a host virtual address instead of a guest
> virtual address even though the addr_gva2hva() part could be moved to
> get_ucall() too. Conceptually, get_ucall() is invoked from the host and
> should return a host virtual address (and returning NULL for "nothing to
> see here" is far superior to returning 0).

The code does not match this description anymore, now that
ucall_arch_get_ucall() returns a gva (as a uint64_t), but the description
is good, the code is wrong. Please restore the spirit of Sean's patch
(particularly because it still says it's from Sean...)

Thanks,
drew

>
> Use pointer shenanigans instead of an unnecessary bounce buffer when the
> caller of get_ucall() provides a valid pointer.
>
> Signed-off-by: Sean Christopherson <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> .../selftests/kvm/include/ucall_common.h | 8 ++------
> .../testing/selftests/kvm/lib/aarch64/ucall.c | 13 +++----------
> tools/testing/selftests/kvm/lib/riscv/ucall.c | 19 +++----------------
> tools/testing/selftests/kvm/lib/s390x/ucall.c | 16 +++-------------
> .../testing/selftests/kvm/lib/ucall_common.c | 19 +++++++++++++++++++
> .../testing/selftests/kvm/lib/x86_64/ucall.c | 16 +++-------------
> 6 files changed, 33 insertions(+), 58 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> index 5a85f5318bbe..c1bc8e33ef3f 100644
> --- a/tools/testing/selftests/kvm/include/ucall_common.h
> +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> @@ -27,9 +27,10 @@ struct ucall {
> void ucall_arch_init(struct kvm_vm *vm, void *arg);
> void ucall_arch_uninit(struct kvm_vm *vm);
> void ucall_arch_do_ucall(vm_vaddr_t uc);
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
>
> void ucall(uint64_t cmd, int nargs, ...);
> +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
>
> static inline void ucall_init(struct kvm_vm *vm, void *arg)
> {
> @@ -41,11 +42,6 @@ static inline void ucall_uninit(struct kvm_vm *vm)
> ucall_arch_uninit(vm);
> }
>
> -static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> -{
> - return ucall_arch_get_ucall(vcpu, uc);
> -}
> -
> #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> #define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage)
> diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> index 1c81a6a5c1f2..d2f099caa9ab 100644
> --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> @@ -78,24 +78,17 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_MMIO &&
> run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
> - vm_vaddr_t gva;
> + uint64_t ucall_addr;
>
> TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
> "Unexpected ucall exit mmio address access");
> memcpy(&gva, run->mmio.data, sizeof(gva));
> - memcpy(&ucall, addr_gva2hva(vcpu->vm, gva), sizeof(ucall));
>
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return ucall_addr;
> }
>
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> index b1598f418c1f..3f000d0b705f 100644
> --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> @@ -51,27 +51,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> uc, 0, 0, 0, 0, 0);
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
> run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
> switch (run->riscv_sbi.function_id) {
> case KVM_RISCV_SELFTESTS_SBI_UCALL:
> - memcpy(&ucall,
> - addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> -
> - break;
> + return vcpu->vm, run->riscv_sbi.args[0];
> case KVM_RISCV_SELFTESTS_SBI_UNEXP:
> vcpu_dump(stderr, vcpu, 2);
> TEST_ASSERT(0, "Unexpected trap taken by guest");
> @@ -80,6 +68,5 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> break;
> }
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> index 114cb4af295f..f7a5a7eb4aa8 100644
> --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> @@ -20,13 +20,9 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
> run->s390_sieic.icptcode == 4 &&
> @@ -34,13 +30,7 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> (run->s390_sieic.ipb >> 16) == 0x501) {
> int reg = run->s390_sieic.ipa & 0xf;
>
> - memcpy(&ucall, addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return run->s.regs.gprs[reg];
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> index 749ffdf23855..a060252bab40 100644
> --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> @@ -18,3 +18,22 @@ void ucall(uint64_t cmd, int nargs, ...)
>
> ucall_arch_do_ucall((vm_vaddr_t)&uc);
> }
> +
> +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +{
> + struct ucall ucall;
> + void *addr;
> +
> + if (!uc)
> + uc = &ucall;
> +
> + addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> + if (addr) {
> + memcpy(uc, addr, sizeof(*uc));
> + vcpu_run_complete_io(vcpu);
> + } else {
> + memset(uc, 0, sizeof(*uc));
> + }
> +
> + return uc->cmd;
> +}
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> index 9f532dba1003..24746120a593 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> @@ -22,25 +22,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
> }
>
> -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> {
> struct kvm_run *run = vcpu->run;
> - struct ucall ucall = {};
> -
> - if (uc)
> - memset(uc, 0, sizeof(*uc));
>
> if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
> struct kvm_regs regs;
>
> vcpu_regs_get(vcpu, &regs);
> - memcpy(&ucall, addr_gva2hva(vcpu->vm, (vm_vaddr_t)regs.rdi),
> - sizeof(ucall));
> -
> - vcpu_run_complete_io(vcpu);
> - if (uc)
> - memcpy(uc, &ucall, sizeof(ucall));
> + return regs.rdi;
> }
> -
> - return ucall.cmd;
> + return 0;
> }
> --
> 2.37.1.455.g008518b4e5-goog
>

2022-08-02 10:12:35

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 06/11] KVM: selftests: Consolidate common code for popuplating

On Mon, Aug 01, 2022 at 01:11:04PM -0700, Peter Gonda wrote:
> From: Sean Christopherson <[email protected]>
>
> Make ucall() a common helper that populates struct ucall, and only calls
> into arch code to make the actually call out to userspace.
>
> Rename all arch-specific helpers to make it clear they're arch-specific,
> and to avoid collisions with common helpers (one more on its way...)
>
> No functional change intended.

But there is...

>
> Cc: Colton Lewis <[email protected]>
> Cc: Andrew Jones <[email protected]>
> Signed-off-by: Sean Christopherson <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> tools/testing/selftests/kvm/Makefile | 1 +
> .../selftests/kvm/include/ucall_common.h | 23 ++++++++++++++++---
> .../testing/selftests/kvm/lib/aarch64/ucall.c | 20 ++++------------
> tools/testing/selftests/kvm/lib/riscv/ucall.c | 23 ++++---------------
> tools/testing/selftests/kvm/lib/s390x/ucall.c | 23 ++++---------------
> .../testing/selftests/kvm/lib/ucall_common.c | 20 ++++++++++++++++
> .../testing/selftests/kvm/lib/x86_64/ucall.c | 23 ++++---------------
> 7 files changed, 60 insertions(+), 73 deletions(-)
> create mode 100644 tools/testing/selftests/kvm/lib/ucall_common.c
>
> diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> index 690b499c3471..39fc5e8e5594 100644
> --- a/tools/testing/selftests/kvm/Makefile
> +++ b/tools/testing/selftests/kvm/Makefile
> @@ -46,6 +46,7 @@ LIBKVM += lib/perf_test_util.c
> LIBKVM += lib/rbtree.c
> LIBKVM += lib/sparsebit.c
> LIBKVM += lib/test_util.c
> +LIBKVM += lib/ucall_common.c
>
> LIBKVM_x86_64 += lib/x86_64/apic.c
> LIBKVM_x86_64 += lib/x86_64/handlers.S
> diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> index ee79d180e07e..5a85f5318bbe 100644
> --- a/tools/testing/selftests/kvm/include/ucall_common.h
> +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> @@ -24,10 +24,27 @@ struct ucall {
> uint64_t args[UCALL_MAX_ARGS];
> };
>
> -void ucall_init(struct kvm_vm *vm, void *arg);
> -void ucall_uninit(struct kvm_vm *vm);
> +void ucall_arch_init(struct kvm_vm *vm, void *arg);
> +void ucall_arch_uninit(struct kvm_vm *vm);
> +void ucall_arch_do_ucall(vm_vaddr_t uc);
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> +
> void ucall(uint64_t cmd, int nargs, ...);
> -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> +
> +static inline void ucall_init(struct kvm_vm *vm, void *arg)
> +{
> + ucall_arch_init(vm, arg);
> +}
> +
> +static inline void ucall_uninit(struct kvm_vm *vm)
> +{
> + ucall_arch_uninit(vm);
> +}
> +
> +static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +{
> + return ucall_arch_get_ucall(vcpu, uc);
> +}
>
> #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> index ed237b744690..1c81a6a5c1f2 100644
> --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> @@ -21,7 +21,7 @@ static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
> return true;
> }
>
> -void ucall_init(struct kvm_vm *vm, void *arg)
> +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> {
> vm_paddr_t gpa, start, end, step, offset;
> unsigned int bits;
> @@ -64,30 +64,18 @@ void ucall_init(struct kvm_vm *vm, void *arg)
> TEST_FAIL("Can't find a ucall mmio address");
> }
>
> -void ucall_uninit(struct kvm_vm *vm)
> +void ucall_arch_uninit(struct kvm_vm *vm)
> {
> ucall_exit_mmio_addr = 0;
> sync_global_to_guest(vm, ucall_exit_mmio_addr);
> }
>
> -void ucall(uint64_t cmd, int nargs, ...)
> +void ucall_arch_do_ucall(vm_vaddr_t uc)
> {
> - struct ucall uc = {};
> - va_list va;
> - int i;
> -
> - WRITE_ONCE(uc.cmd, cmd);
> - nargs = min(nargs, UCALL_MAX_ARGS);
> -
> - va_start(va, nargs);
> - for (i = 0; i < nargs; ++i)
> - WRITE_ONCE(uc.args[i], va_arg(va, uint64_t));
> - va_end(va);
> -

There are WRITE_ONCE's being used here...

> WRITE_ONCE(*ucall_exit_mmio_addr, (vm_vaddr_t)&uc);
> }
>
> -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> {
> struct kvm_run *run = vcpu->run;
> struct ucall ucall = {};
> diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> index 087b9740bc8f..b1598f418c1f 100644
> --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> @@ -10,11 +10,11 @@
> #include "kvm_util.h"
> #include "processor.h"
>
> -void ucall_init(struct kvm_vm *vm, void *arg)
> +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> {
> }
>
> -void ucall_uninit(struct kvm_vm *vm)
> +void ucall_arch_uninit(struct kvm_vm *vm)
> {
> }
>
> @@ -44,27 +44,14 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
> return ret;
> }
>
> -void ucall(uint64_t cmd, int nargs, ...)
> +void ucall_arch_do_ucall(vm_vaddr_t uc)
> {
> - struct ucall uc = {
> - .cmd = cmd,
> - };
> - va_list va;
> - int i;
> -
> - nargs = min(nargs, UCALL_MAX_ARGS);
> -
> - va_start(va, nargs);
> - for (i = 0; i < nargs; ++i)
> - uc.args[i] = va_arg(va, uint64_t);
> - va_end(va);
> -
> sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,
> KVM_RISCV_SELFTESTS_SBI_UCALL,
> - (vm_vaddr_t)&uc, 0, 0, 0, 0, 0);
> + uc, 0, 0, 0, 0, 0);
> }
>
> -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> {
> struct kvm_run *run = vcpu->run;
> struct ucall ucall = {};
> diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> index 73dc4e21190f..114cb4af295f 100644
> --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> @@ -6,34 +6,21 @@
> */
> #include "kvm_util.h"
>
> -void ucall_init(struct kvm_vm *vm, void *arg)
> +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> {
> }
>
> -void ucall_uninit(struct kvm_vm *vm)
> +void ucall_arch_uninit(struct kvm_vm *vm)
> {
> }
>
> -void ucall(uint64_t cmd, int nargs, ...)
> +void ucall_arch_do_ucall(vm_vaddr_t uc)
> {
> - struct ucall uc = {
> - .cmd = cmd,
> - };
> - va_list va;
> - int i;
> -
> - nargs = min(nargs, UCALL_MAX_ARGS);
> -
> - va_start(va, nargs);
> - for (i = 0; i < nargs; ++i)
> - uc.args[i] = va_arg(va, uint64_t);
> - va_end(va);
> -
> /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */
> - asm volatile ("diag 0,%0,0x501" : : "a"(&uc) : "memory");
> + asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
> }
>
> -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> {
> struct kvm_run *run = vcpu->run;
> struct ucall ucall = {};
> diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> new file mode 100644
> index 000000000000..749ffdf23855
> --- /dev/null
> +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> @@ -0,0 +1,20 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +#include "kvm_util.h"
> +
> +void ucall(uint64_t cmd, int nargs, ...)
> +{
> + struct ucall uc = {
> + .cmd = cmd,
> + };
> + va_list va;
> + int i;
> +
> + nargs = min(nargs, UCALL_MAX_ARGS);
> +
> + va_start(va, nargs);
> + for (i = 0; i < nargs; ++i)
> + uc.args[i] = va_arg(va, uint64_t);
> + va_end(va);

...but not here. At least AArch64 needs them, see commit 9e2f6498efbb
("selftests: KVM: Handle compiler optimizations in ucall")

> +
> + ucall_arch_do_ucall((vm_vaddr_t)&uc);
> +}
> diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> index e5f0f9e0d3ee..9f532dba1003 100644
> --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> @@ -8,34 +8,21 @@
>
> #define UCALL_PIO_PORT ((uint16_t)0x1000)
>
> -void ucall_init(struct kvm_vm *vm, void *arg)
> +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> {
> }
>
> -void ucall_uninit(struct kvm_vm *vm)
> +void ucall_arch_uninit(struct kvm_vm *vm)
> {
> }
>
> -void ucall(uint64_t cmd, int nargs, ...)
> +void ucall_arch_do_ucall(vm_vaddr_t uc)
> {
> - struct ucall uc = {
> - .cmd = cmd,
> - };
> - va_list va;
> - int i;
> -
> - nargs = min(nargs, UCALL_MAX_ARGS);
> -
> - va_start(va, nargs);
> - for (i = 0; i < nargs; ++i)
> - uc.args[i] = va_arg(va, uint64_t);
> - va_end(va);
> -
> asm volatile("in %[port], %%al"
> - : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax", "memory");
> + : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
> }
>
> -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> {
> struct kvm_run *run = vcpu->run;
> struct ucall ucall = {};
> --
> 2.37.1.455.g008518b4e5-goog
>

Thanks,
drew

2022-08-02 10:18:55

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 09/11] KVM: selftests: Make ucall work with encrypted guests

On Mon, Aug 01, 2022 at 01:11:07PM -0700, Peter Gonda wrote:
> Add support for encrypted, SEV, guests in the ucall framework. If
> encryption is enabled set up a pool of ucall structs in the guests'
> shared memory region. This was suggested in the thread on "[RFC PATCH
> 00/10] KVM: selftests: Add support for test-selectable ucall
> implementations". Using a listed as suggested there doesn't work well
> because the list is setup using HVAs not GVAs so use a bitmap + array
> solution instead to get the same pool result.
>
> Suggested-by: Sean Christopherson <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> .../selftests/kvm/include/kvm_util_base.h | 3 +
> .../selftests/kvm/include/ucall_common.h | 14 +--
> .../testing/selftests/kvm/lib/ucall_common.c | 112 +++++++++++++++++-
> 3 files changed, 115 insertions(+), 14 deletions(-)
>
> diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
> index 8ce9e5be70a3..ad4abc6be1ab 100644
> --- a/tools/testing/selftests/kvm/include/kvm_util_base.h
> +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
> @@ -102,6 +102,9 @@ struct kvm_vm {
> int stats_fd;
> struct kvm_stats_header stats_header;
> struct kvm_stats_desc *stats_desc;
> +
> + bool use_ucall_list;

use_ucall_pool

> + struct list_head ucall_list;
> };
>
>
> diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> index c1bc8e33ef3f..a96220ac6024 100644
> --- a/tools/testing/selftests/kvm/include/ucall_common.h
> +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> @@ -22,6 +22,10 @@ enum {
> struct ucall {
> uint64_t cmd;
> uint64_t args[UCALL_MAX_ARGS];
> +
> + /* For encrypted guests. */

Please, no 'encrypted' words in ucall files. ucalls shouldn't care about
guest types. Indeed, the summary of this patch could even drop the word
'encrypted'. This patch is adding support for ucall pools, which is
motivated by the need to support encrypted guests (the motivation should
go in the commit message, but otherwise the patch should be ucall specific
and guest type agnostic)

> + uint64_t idx;

We don't need 'idx' because 'hva' will always be at the
idx * sizeof(struct ucall) offset of ucall_hdr->ucalls, which means
we can always calculate it,

static inline size_t uc_pool_idx(struct ucall *uc)
{
return uc->hva - ucall_hdr->ucalls;
}

> + struct ucall *hva;
> };
>
> void ucall_arch_init(struct kvm_vm *vm, void *arg);
> @@ -32,15 +36,9 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
> void ucall(uint64_t cmd, int nargs, ...);
> uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
>
> -static inline void ucall_init(struct kvm_vm *vm, void *arg)
> -{
> - ucall_arch_init(vm, arg);
> -}
> +void ucall_init(struct kvm_vm *vm, void *arg);
>
> -static inline void ucall_uninit(struct kvm_vm *vm)
> -{
> - ucall_arch_uninit(vm);
> -}
> +void ucall_uninit(struct kvm_vm *vm);
>
> #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> index a060252bab40..feb0173179ec 100644
> --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> @@ -1,22 +1,122 @@
> // SPDX-License-Identifier: GPL-2.0-only
> #include "kvm_util.h"
> +#include "linux/types.h"
> +#include "linux/bitmap.h"
> +#include "linux/atomic.h"
> +
> +struct ucall_header {
> + DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
> + struct ucall ucalls[KVM_MAX_VCPUS];
> +};
> +
> +static bool use_ucall_list;

use_ucall_pool

> +static struct ucall_header *ucall_hdr;

ucall_pool

> +
> +void ucall_init(struct kvm_vm *vm, void *arg)
> +{
> + struct ucall *uc;
> + struct ucall_header *hdr;
> + vm_vaddr_t vaddr;
> + int i;
> +
> + use_ucall_list = vm->use_ucall_list;
> + sync_global_to_guest(vm, use_ucall_list);
> + if (!use_ucall_list)
> + goto out;
> +
> + TEST_ASSERT(!ucall_hdr,
> + "Only a single encrypted guest at a time for ucalls.");

single VM at a time

And I'd leave that on a single line. checkpatch allows up to 100 chars and
I'm included to use all 110 chars of it.

> + vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), vm->page_size);
> + hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
> + memset(hdr, 0, sizeof(*hdr));
> +
> + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> + uc = &hdr->ucalls[i];
> + uc->hva = uc;
> + uc->idx = i;
> + }
> +
> + ucall_hdr = (struct ucall_header *)vaddr;
> + sync_global_to_guest(vm, ucall_hdr);
> +
> +out:
> + ucall_arch_init(vm, arg);
> +}
> +
> +void ucall_uninit(struct kvm_vm *vm)
> +{
> + use_ucall_list = false;
> + ucall_hdr = NULL;

It's unlikely we'd ever change the ucall setup on a running VM,
but we should sync these changes to the guest for good measure.

> +
> + ucall_arch_uninit(vm);
> +}
> +
> +static struct ucall *ucall_alloc(void)
> +{
> + struct ucall *uc = NULL;
> + int i;
> +
> + if (!use_ucall_list)
> + goto out;
> +
> + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> + if (atomic_test_and_set_bit(i, ucall_hdr->in_use))
> + continue;
> +
> + uc = &ucall_hdr->ucalls[i];
> + }

This is not what I suggested in the last revision and it's still wrong.
Here, you're still looping through all of the pool and returning the last
one.

What I suggested was

for (i = 0; i < KVM_MAX_VCPUS; ++i) {
if (!atomic_test_and_set_bit(i, ucall_hdr->in_use)) {
uc = &ucall_hdr->ucalls[i];
break;
}
}

Notice how we leave the loop early when we find a free uc.

We should also zero the contents of the uc buffer before returning.

> +
> +out:
> + return uc;
> +}
> +
> +static void ucall_free(struct ucall *uc)
> +{
> + if (!use_ucall_list)
> + return;
> +
> + clear_bit(uc->idx, ucall_hdr->in_use);

This seems to be the one and only use of idx which is another argument for
dropping the variable and just calculating it instead.

> +}
> +
> +static vm_vaddr_t get_ucall_addr(struct ucall *uc)
> +{
> + if (use_ucall_list)
> + return (vm_vaddr_t)uc->hva;

A comment explaining that this hva has already been synchronized
with the guest would be good. Or maybe a different name for the
pointer than hva, one which conveys that it's a pointer that works
as both an hva and gva would be better.


> +
> + return (vm_vaddr_t)uc;
> +}
>
> void ucall(uint64_t cmd, int nargs, ...)
> {
> - struct ucall uc = {
> - .cmd = cmd,
> - };

This zeros all members except cmd.

> + struct ucall *uc;
> + struct ucall tmp;

And this makes tmp full of stack garbage, so in the non uc-pool case we
no longer have an equivalent uc. Please initialize tmp the same way uc
was initialized.

> va_list va;
> int i;
>
> + uc = ucall_alloc();
> + if (!uc)
> + uc = &tmp;
> +
> + uc->cmd = cmd;
> +
> nargs = min(nargs, UCALL_MAX_ARGS);
>
> va_start(va, nargs);
> for (i = 0; i < nargs; ++i)
> - uc.args[i] = va_arg(va, uint64_t);
> + uc->args[i] = va_arg(va, uint64_t);
> va_end(va);
>
> - ucall_arch_do_ucall((vm_vaddr_t)&uc);
> + ucall_arch_do_ucall(get_ucall_addr(uc));

We don't need get_ucall_addr(). Just do the if-else right here

if (use_ucall_list)
ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
else
ucall_arch_do_ucall((vm_vaddr_t)uc);

> +
> + ucall_free(uc);
> +}
> +
> +static void *get_ucall_hva(struct kvm_vm *vm, uint64_t uc)
> +{
> + if (vm->use_ucall_list)
> + return (void *)uc;
> +
> + return addr_gva2hva(vm, uc);
> }
>
> uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> @@ -27,7 +127,7 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> if (!uc)
> uc = &ucall;
>
> - addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> + addr = get_ucall_hva(vcpu->vm, ucall_arch_get_ucall(vcpu));

Wait, I thought we wanted ucall_arch_get_ucall() to return hvas?
It would make more sense if it did, since it's called from the
host.

> if (addr) {
> memcpy(uc, addr, sizeof(*uc));
> vcpu_run_complete_io(vcpu);
> --
> 2.37.1.455.g008518b4e5-goog
>

Thanks,
drew

2022-08-02 10:18:59

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 01/11] KVM: selftests: move vm_phy_pages_alloc() earlier in file

On Mon, Aug 01, 2022 at 01:10:59PM -0700, Peter Gonda wrote:
> From: Michael Roth <[email protected]>
>
> Subsequent patches will break some of this code out into file-local
> helper functions, which will be used by functions like vm_vaddr_alloc(),
> which currently are defined earlier in the file, so a forward
> declaration would be needed.
>
> Instead, move it earlier in the file, just above vm_vaddr_alloc() and
> and friends, which are the main users.
>
> Reviewed-by: Mingwei Zhang <[email protected]>
> Signed-off-by: Michael Roth <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> tools/testing/selftests/kvm/lib/kvm_util.c | 145 ++++++++++-----------
> 1 file changed, 72 insertions(+), 73 deletions(-)
>

Reviewed-by: Andrew Jones <[email protected]>

2022-08-02 10:32:06

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 02/11] KVM: selftests: sparsebit: add const where appropriate

On Mon, Aug 01, 2022 at 01:11:00PM -0700, Peter Gonda wrote:
> From: Michael Roth <[email protected]>
>
> Subsequent patches will introduce an encryption bitmap in kvm_util that
> would be useful to allow tests to access in read-only fashion. This
> will be done via a const sparsebit*. To avoid warnings or the need to
> add casts everywhere, add const to the various sparsebit functions that
> are applicable for read-only usage of sparsebit.
>
> Signed-off-by: Michael Roth <[email protected]>
> Signed-off-by: Peter Gonda <[email protected]>
> ---
> .../testing/selftests/kvm/include/sparsebit.h | 36 +++++++-------
> tools/testing/selftests/kvm/lib/sparsebit.c | 48 +++++++++----------
> 2 files changed, 42 insertions(+), 42 deletions(-)
>

Reviewed-by: Andrew Jones <[email protected]>

2022-08-02 14:42:53

by Peter Gonda

[permalink] [raw]
Subject: Re: [V2 09/11] KVM: selftests: Make ucall work with encrypted guests

On Tue, Aug 2, 2022 at 3:49 AM Andrew Jones <[email protected]> wrote:
>
> On Mon, Aug 01, 2022 at 01:11:07PM -0700, Peter Gonda wrote:
> > Add support for encrypted, SEV, guests in the ucall framework. If
> > encryption is enabled set up a pool of ucall structs in the guests'
> > shared memory region. This was suggested in the thread on "[RFC PATCH
> > 00/10] KVM: selftests: Add support for test-selectable ucall
> > implementations". Using a listed as suggested there doesn't work well
> > because the list is setup using HVAs not GVAs so use a bitmap + array
> > solution instead to get the same pool result.
> >
> > Suggested-by: Sean Christopherson <[email protected]>
> > Signed-off-by: Peter Gonda <[email protected]>
> > ---
> > .../selftests/kvm/include/kvm_util_base.h | 3 +
> > .../selftests/kvm/include/ucall_common.h | 14 +--
> > .../testing/selftests/kvm/lib/ucall_common.c | 112 +++++++++++++++++-
> > 3 files changed, 115 insertions(+), 14 deletions(-)
> >
> > diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > index 8ce9e5be70a3..ad4abc6be1ab 100644
> > --- a/tools/testing/selftests/kvm/include/kvm_util_base.h
> > +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > @@ -102,6 +102,9 @@ struct kvm_vm {
> > int stats_fd;
> > struct kvm_stats_header stats_header;
> > struct kvm_stats_desc *stats_desc;
> > +
> > + bool use_ucall_list;
>
> use_ucall_pool
>
> > + struct list_head ucall_list;
> > };

Will do. I also need to remove this |ucall_list| member.

> >
> >
> > diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> > index c1bc8e33ef3f..a96220ac6024 100644
> > --- a/tools/testing/selftests/kvm/include/ucall_common.h
> > +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> > @@ -22,6 +22,10 @@ enum {
> > struct ucall {
> > uint64_t cmd;
> > uint64_t args[UCALL_MAX_ARGS];
> > +
> > + /* For encrypted guests. */
>
> Please, no 'encrypted' words in ucall files. ucalls shouldn't care about
> guest types. Indeed, the summary of this patch could even drop the word
> 'encrypted'. This patch is adding support for ucall pools, which is
> motivated by the need to support encrypted guests (the motivation should
> go in the commit message, but otherwise the patch should be ucall specific
> and guest type agnostic)
>
> > + uint64_t idx;
>
> We don't need 'idx' because 'hva' will always be at the
> idx * sizeof(struct ucall) offset of ucall_hdr->ucalls, which means
> we can always calculate it,
>
> static inline size_t uc_pool_idx(struct ucall *uc)
> {
> return uc->hva - ucall_hdr->ucalls;
> }

Good call, I didn't think of that.

>
> > + struct ucall *hva;
> > };
> >
> > void ucall_arch_init(struct kvm_vm *vm, void *arg);
> > @@ -32,15 +36,9 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
> > void ucall(uint64_t cmd, int nargs, ...);
> > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> >
> > -static inline void ucall_init(struct kvm_vm *vm, void *arg)
> > -{
> > - ucall_arch_init(vm, arg);
> > -}
> > +void ucall_init(struct kvm_vm *vm, void *arg);
> >
> > -static inline void ucall_uninit(struct kvm_vm *vm)
> > -{
> > - ucall_arch_uninit(vm);
> > -}
> > +void ucall_uninit(struct kvm_vm *vm);
> >
> > #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> > ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> > diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> > index a060252bab40..feb0173179ec 100644
> > --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> > +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> > @@ -1,22 +1,122 @@
> > // SPDX-License-Identifier: GPL-2.0-only
> > #include "kvm_util.h"
> > +#include "linux/types.h"
> > +#include "linux/bitmap.h"
> > +#include "linux/atomic.h"
> > +
> > +struct ucall_header {
> > + DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
> > + struct ucall ucalls[KVM_MAX_VCPUS];
> > +};
> > +
> > +static bool use_ucall_list;
>
> use_ucall_pool
>
> > +static struct ucall_header *ucall_hdr;
>
> ucall_pool

Will update naming here.

>
> > +
> > +void ucall_init(struct kvm_vm *vm, void *arg)
> > +{
> > + struct ucall *uc;
> > + struct ucall_header *hdr;
> > + vm_vaddr_t vaddr;
> > + int i;
> > +
> > + use_ucall_list = vm->use_ucall_list;
> > + sync_global_to_guest(vm, use_ucall_list);
> > + if (!use_ucall_list)
> > + goto out;
> > +
> > + TEST_ASSERT(!ucall_hdr,
> > + "Only a single encrypted guest at a time for ucalls.");
>
> single VM at a time
>
> And I'd leave that on a single line. checkpatch allows up to 100 chars and
> I'm included to use all 110 chars of it.

Sounds good.

>
> > + vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), vm->page_size);
> > + hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
> > + memset(hdr, 0, sizeof(*hdr));
> > +
> > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > + uc = &hdr->ucalls[i];
> > + uc->hva = uc;
> > + uc->idx = i;
> > + }
> > +
> > + ucall_hdr = (struct ucall_header *)vaddr;
> > + sync_global_to_guest(vm, ucall_hdr);
> > +
> > +out:
> > + ucall_arch_init(vm, arg);
> > +}
> > +
> > +void ucall_uninit(struct kvm_vm *vm)
> > +{
> > + use_ucall_list = false;
> > + ucall_hdr = NULL;
>
> It's unlikely we'd ever change the ucall setup on a running VM,
> but we should sync these changes to the guest for good measure.

Hmm I'll need to have some notion of encrypted guests here then. Since
the guests page tables also get encrypted we can no longer get the
gva2gpa translations so sync_global_to_guest() cannot be done due to
the addr_ga2hva(). So is it OK if this call references encryption like
below?

use_ucall_list = false;
ucall_hdr = NULL;
if (!vm->memencrypt.encrypted) {
sync_global_to_guest(vm, use_ucall_list);
sync_global_to_guest(vm, ucall_hdr);
}

>
> > +
> > + ucall_arch_uninit(vm);
> > +}
> > +
> > +static struct ucall *ucall_alloc(void)
> > +{
> > + struct ucall *uc = NULL;
> > + int i;
> > +
> > + if (!use_ucall_list)
> > + goto out;
> > +
> > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > + if (atomic_test_and_set_bit(i, ucall_hdr->in_use))
> > + continue;
> > +
> > + uc = &ucall_hdr->ucalls[i];
> > + }
>
> This is not what I suggested in the last revision and it's still wrong.
> Here, you're still looping through all of the pool and returning the last
> one.
>
> What I suggested was
>
> for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> if (!atomic_test_and_set_bit(i, ucall_hdr->in_use)) {
> uc = &ucall_hdr->ucalls[i];
> break;
> }
> }
>
> Notice how we leave the loop early when we find a free uc.

Ah my mistake, I didn't get this fix into this series

>
> We should also zero the contents of the uc buffer before returning.

Ack will do.

>
> > +
> > +out:
> > + return uc;
> > +}
> > +
> > +static void ucall_free(struct ucall *uc)
> > +{
> > + if (!use_ucall_list)
> > + return;
> > +
> > + clear_bit(uc->idx, ucall_hdr->in_use);
>
> This seems to be the one and only use of idx which is another argument for
> dropping the variable and just calculating it instead.

Will do.

>
> > +}
> > +
> > +static vm_vaddr_t get_ucall_addr(struct ucall *uc)
> > +{
> > + if (use_ucall_list)
> > + return (vm_vaddr_t)uc->hva;
>
> A comment explaining that this hva has already been synchronized
> with the guest would be good. Or maybe a different name for the
> pointer than hva, one which conveys that it's a pointer that works
> as both an hva and gva would be better.

I don't think this pointer does work as a gva though since its created
by just taking &uc during host execution. I can add a comment that
mentions this is a hva is already translated for the host and not for
guest use other than communicating with the host. I thought 'hva' was
a good name here since it is literally the hva of this ucall struct,
what name would you prefer?

>
>
> > +
> > + return (vm_vaddr_t)uc;
> > +}
> >
> > void ucall(uint64_t cmd, int nargs, ...)
> > {
> > - struct ucall uc = {
> > - .cmd = cmd,
> > - };
>
> This zeros all members except cmd.
>
> > + struct ucall *uc;
> > + struct ucall tmp;
>
> And this makes tmp full of stack garbage, so in the non uc-pool case we
> no longer have an equivalent uc. Please initialize tmp the same way uc
> was initialized.

Ack, I'll get this fixed.

>
> > va_list va;
> > int i;
> >
> > + uc = ucall_alloc();
> > + if (!uc)
> > + uc = &tmp;
> > +
> > + uc->cmd = cmd;
> > +
> > nargs = min(nargs, UCALL_MAX_ARGS);
> >
> > va_start(va, nargs);
> > for (i = 0; i < nargs; ++i)
> > - uc.args[i] = va_arg(va, uint64_t);
> > + uc->args[i] = va_arg(va, uint64_t);
> > va_end(va);
> >
> > - ucall_arch_do_ucall((vm_vaddr_t)&uc);
> > + ucall_arch_do_ucall(get_ucall_addr(uc));
>
> We don't need get_ucall_addr(). Just do the if-else right here
>
> if (use_ucall_list)
> ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
> else
> ucall_arch_do_ucall((vm_vaddr_t)uc);

Will do.

>
> > +
> > + ucall_free(uc);
> > +}
> > +
> > +static void *get_ucall_hva(struct kvm_vm *vm, uint64_t uc)
> > +{
> > + if (vm->use_ucall_list)
> > + return (void *)uc;
> > +
> > + return addr_gva2hva(vm, uc);
> > }
> >
> > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > @@ -27,7 +127,7 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > if (!uc)
> > uc = &ucall;
> >
> > - addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> > + addr = get_ucall_hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
>
> Wait, I thought we wanted ucall_arch_get_ucall() to return hvas?
> It would make more sense if it did, since it's called from the
> host.

As you noted in [V2 07/11] I updated ucall_arch_get_ucall() to return
the gva. I figured this was just more boilerplate which could be
consolidated given all archs right now just use the addr_gva2hva()
helper to do this translation. This change also allowed for the
"use_ucall_pool" to be completely contained in ucall_common.c instead
of spilling into each arch specific file. If there is a preference to
have the arch specific calls translate from gva to hva I can do that,
I think I'll need to have them all check if "use_ucall_pool" is set
though. Thoughts?

We can still update ucall_arch_get_ucall() to return a pointer type so
that we can return NULL instead of 0.

>
> > if (addr) {
> > memcpy(uc, addr, sizeof(*uc));
> > vcpu_run_complete_io(vcpu);
> > --
> > 2.37.1.455.g008518b4e5-goog
> >
>
> Thanks,
> drew

2022-08-02 14:43:37

by Andrew Jones

[permalink] [raw]
Subject: Re: [V2 09/11] KVM: selftests: Make ucall work with encrypted guests

On Tue, Aug 02, 2022 at 07:51:40AM -0600, Peter Gonda wrote:
> On Tue, Aug 2, 2022 at 3:49 AM Andrew Jones <[email protected]> wrote:
> >
> > On Mon, Aug 01, 2022 at 01:11:07PM -0700, Peter Gonda wrote:
> > > Add support for encrypted, SEV, guests in the ucall framework. If
> > > encryption is enabled set up a pool of ucall structs in the guests'
> > > shared memory region. This was suggested in the thread on "[RFC PATCH
> > > 00/10] KVM: selftests: Add support for test-selectable ucall
> > > implementations". Using a listed as suggested there doesn't work well
> > > because the list is setup using HVAs not GVAs so use a bitmap + array
> > > solution instead to get the same pool result.
> > >
> > > Suggested-by: Sean Christopherson <[email protected]>
> > > Signed-off-by: Peter Gonda <[email protected]>
> > > ---
> > > .../selftests/kvm/include/kvm_util_base.h | 3 +
> > > .../selftests/kvm/include/ucall_common.h | 14 +--
> > > .../testing/selftests/kvm/lib/ucall_common.c | 112 +++++++++++++++++-
> > > 3 files changed, 115 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > index 8ce9e5be70a3..ad4abc6be1ab 100644
> > > --- a/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > @@ -102,6 +102,9 @@ struct kvm_vm {
> > > int stats_fd;
> > > struct kvm_stats_header stats_header;
> > > struct kvm_stats_desc *stats_desc;
> > > +
> > > + bool use_ucall_list;
> >
> > use_ucall_pool
> >
> > > + struct list_head ucall_list;
> > > };
>
> Will do. I also need to remove this |ucall_list| member.
>
> > >
> > >
> > > diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> > > index c1bc8e33ef3f..a96220ac6024 100644
> > > --- a/tools/testing/selftests/kvm/include/ucall_common.h
> > > +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> > > @@ -22,6 +22,10 @@ enum {
> > > struct ucall {
> > > uint64_t cmd;
> > > uint64_t args[UCALL_MAX_ARGS];
> > > +
> > > + /* For encrypted guests. */
> >
> > Please, no 'encrypted' words in ucall files. ucalls shouldn't care about
> > guest types. Indeed, the summary of this patch could even drop the word
> > 'encrypted'. This patch is adding support for ucall pools, which is
> > motivated by the need to support encrypted guests (the motivation should
> > go in the commit message, but otherwise the patch should be ucall specific
> > and guest type agnostic)
> >
> > > + uint64_t idx;
> >
> > We don't need 'idx' because 'hva' will always be at the
> > idx * sizeof(struct ucall) offset of ucall_hdr->ucalls, which means
> > we can always calculate it,
> >
> > static inline size_t uc_pool_idx(struct ucall *uc)
> > {
> > return uc->hva - ucall_hdr->ucalls;
> > }
>
> Good call, I didn't think of that.
>
> >
> > > + struct ucall *hva;
> > > };
> > >
> > > void ucall_arch_init(struct kvm_vm *vm, void *arg);
> > > @@ -32,15 +36,9 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
> > > void ucall(uint64_t cmd, int nargs, ...);
> > > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> > >
> > > -static inline void ucall_init(struct kvm_vm *vm, void *arg)
> > > -{
> > > - ucall_arch_init(vm, arg);
> > > -}
> > > +void ucall_init(struct kvm_vm *vm, void *arg);
> > >
> > > -static inline void ucall_uninit(struct kvm_vm *vm)
> > > -{
> > > - ucall_arch_uninit(vm);
> > > -}
> > > +void ucall_uninit(struct kvm_vm *vm);
> > >
> > > #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> > > ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> > > diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> > > index a060252bab40..feb0173179ec 100644
> > > --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> > > +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> > > @@ -1,22 +1,122 @@
> > > // SPDX-License-Identifier: GPL-2.0-only
> > > #include "kvm_util.h"
> > > +#include "linux/types.h"
> > > +#include "linux/bitmap.h"
> > > +#include "linux/atomic.h"
> > > +
> > > +struct ucall_header {
> > > + DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
> > > + struct ucall ucalls[KVM_MAX_VCPUS];
> > > +};
> > > +
> > > +static bool use_ucall_list;
> >
> > use_ucall_pool
> >
> > > +static struct ucall_header *ucall_hdr;
> >
> > ucall_pool
>
> Will update naming here.
>
> >
> > > +
> > > +void ucall_init(struct kvm_vm *vm, void *arg)
> > > +{
> > > + struct ucall *uc;
> > > + struct ucall_header *hdr;
> > > + vm_vaddr_t vaddr;
> > > + int i;
> > > +
> > > + use_ucall_list = vm->use_ucall_list;
> > > + sync_global_to_guest(vm, use_ucall_list);
> > > + if (!use_ucall_list)
> > > + goto out;
> > > +
> > > + TEST_ASSERT(!ucall_hdr,
> > > + "Only a single encrypted guest at a time for ucalls.");
> >
> > single VM at a time
> >
> > And I'd leave that on a single line. checkpatch allows up to 100 chars and
> > I'm included to use all 110 chars of it.
>
> Sounds good.
>
> >
> > > + vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), vm->page_size);
> > > + hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
> > > + memset(hdr, 0, sizeof(*hdr));
> > > +
> > > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > > + uc = &hdr->ucalls[i];
> > > + uc->hva = uc;
> > > + uc->idx = i;
> > > + }
> > > +
> > > + ucall_hdr = (struct ucall_header *)vaddr;
> > > + sync_global_to_guest(vm, ucall_hdr);
> > > +
> > > +out:
> > > + ucall_arch_init(vm, arg);
> > > +}
> > > +
> > > +void ucall_uninit(struct kvm_vm *vm)
> > > +{
> > > + use_ucall_list = false;
> > > + ucall_hdr = NULL;
> >
> > It's unlikely we'd ever change the ucall setup on a running VM,
> > but we should sync these changes to the guest for good measure.
>
> Hmm I'll need to have some notion of encrypted guests here then. Since
> the guests page tables also get encrypted we can no longer get the
> gva2gpa translations so sync_global_to_guest() cannot be done due to
> the addr_ga2hva(). So is it OK if this call references encryption like
> below?
>
> use_ucall_list = false;
> ucall_hdr = NULL;
> if (!vm->memencrypt.encrypted) {
> sync_global_to_guest(vm, use_ucall_list);
> sync_global_to_guest(vm, ucall_hdr);
> }

Yes, I guess that's the best we can do.

If sync_global_to_guest() becomes useless once a guest is encrypted then
maybe it should assert !vm->memencrypt.encrypted.

>
> >
> > > +
> > > + ucall_arch_uninit(vm);
> > > +}
> > > +
> > > +static struct ucall *ucall_alloc(void)
> > > +{
> > > + struct ucall *uc = NULL;
> > > + int i;
> > > +
> > > + if (!use_ucall_list)
> > > + goto out;
> > > +
> > > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > > + if (atomic_test_and_set_bit(i, ucall_hdr->in_use))
> > > + continue;
> > > +
> > > + uc = &ucall_hdr->ucalls[i];
> > > + }
> >
> > This is not what I suggested in the last revision and it's still wrong.
> > Here, you're still looping through all of the pool and returning the last
> > one.
> >
> > What I suggested was
> >
> > for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > if (!atomic_test_and_set_bit(i, ucall_hdr->in_use)) {
> > uc = &ucall_hdr->ucalls[i];
> > break;
> > }
> > }
> >
> > Notice how we leave the loop early when we find a free uc.
>
> Ah my mistake, I didn't get this fix into this series
>
> >
> > We should also zero the contents of the uc buffer before returning.
>
> Ack will do.
>
> >
> > > +
> > > +out:
> > > + return uc;
> > > +}
> > > +
> > > +static void ucall_free(struct ucall *uc)
> > > +{
> > > + if (!use_ucall_list)
> > > + return;
> > > +
> > > + clear_bit(uc->idx, ucall_hdr->in_use);
> >
> > This seems to be the one and only use of idx which is another argument for
> > dropping the variable and just calculating it instead.
>
> Will do.
>
> >
> > > +}
> > > +
> > > +static vm_vaddr_t get_ucall_addr(struct ucall *uc)
> > > +{
> > > + if (use_ucall_list)
> > > + return (vm_vaddr_t)uc->hva;
> >
> > A comment explaining that this hva has already been synchronized
> > with the guest would be good. Or maybe a different name for the
> > pointer than hva, one which conveys that it's a pointer that works
> > as both an hva and gva would be better.
>
> I don't think this pointer does work as a gva though since its created
> by just taking &uc during host execution. I can add a comment that
> mentions this is a hva is already translated for the host and not for
> guest use other than communicating with the host. I thought 'hva' was
> a good name here since it is literally the hva of this ucall struct,
> what name would you prefer?

My mistake, I forgot how we initialized uc->hva. 'hva' is a good name.

>
> >
> >
> > > +
> > > + return (vm_vaddr_t)uc;
> > > +}
> > >
> > > void ucall(uint64_t cmd, int nargs, ...)
> > > {
> > > - struct ucall uc = {
> > > - .cmd = cmd,
> > > - };
> >
> > This zeros all members except cmd.
> >
> > > + struct ucall *uc;
> > > + struct ucall tmp;
> >
> > And this makes tmp full of stack garbage, so in the non uc-pool case we
> > no longer have an equivalent uc. Please initialize tmp the same way uc
> > was initialized.
>
> Ack, I'll get this fixed.
>
> >
> > > va_list va;
> > > int i;
> > >
> > > + uc = ucall_alloc();
> > > + if (!uc)
> > > + uc = &tmp;
> > > +
> > > + uc->cmd = cmd;
> > > +
> > > nargs = min(nargs, UCALL_MAX_ARGS);
> > >
> > > va_start(va, nargs);
> > > for (i = 0; i < nargs; ++i)
> > > - uc.args[i] = va_arg(va, uint64_t);
> > > + uc->args[i] = va_arg(va, uint64_t);
> > > va_end(va);
> > >
> > > - ucall_arch_do_ucall((vm_vaddr_t)&uc);
> > > + ucall_arch_do_ucall(get_ucall_addr(uc));
> >
> > We don't need get_ucall_addr(). Just do the if-else right here
> >
> > if (use_ucall_list)
> > ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
> > else
> > ucall_arch_do_ucall((vm_vaddr_t)uc);
>
> Will do.
>
> >
> > > +
> > > + ucall_free(uc);
> > > +}
> > > +
> > > +static void *get_ucall_hva(struct kvm_vm *vm, uint64_t uc)
> > > +{
> > > + if (vm->use_ucall_list)
> > > + return (void *)uc;
> > > +
> > > + return addr_gva2hva(vm, uc);
> > > }
> > >
> > > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > > @@ -27,7 +127,7 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > > if (!uc)
> > > uc = &ucall;
> > >
> > > - addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> > > + addr = get_ucall_hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> >
> > Wait, I thought we wanted ucall_arch_get_ucall() to return hvas?
> > It would make more sense if it did, since it's called from the
> > host.
>
> As you noted in [V2 07/11] I updated ucall_arch_get_ucall() to return
> the gva. I figured this was just more boilerplate which could be
> consolidated given all archs right now just use the addr_gva2hva()
> helper to do this translation. This change also allowed for the
> "use_ucall_pool" to be completely contained in ucall_common.c instead
> of spilling into each arch specific file. If there is a preference to
> have the arch specific calls translate from gva to hva I can do that,
> I think I'll need to have them all check if "use_ucall_pool" is set
> though. Thoughts?

I just checked what I wrote for the last version of this patch,
"[RFC V1 08/10] KVM: selftests: Make ucall work with encrypted guests".
It was

> ...we're only updating x86's ucall_arch_get_ucall() to return gvas.
> What about the other architectures? Anyway, I'd rather we don't
> change ucall_arch_get_ucall() to return gvas. They should continue
> returning hvas and any trickery needed to translate a pool uc to
> an hva should be put inside ucall_arch_get_ucall().

(It's comforting to see I was consistent, at least wrt the last review.)

So, I would keep returning hvas and put the 'if use_ucall_pool' inside
the ucall_arch_get_ucall() functions.

Thanks,
drew

>
> We can still update ucall_arch_get_ucall() to return a pointer type so
> that we can return NULL instead of 0.
>
> >
> > > if (addr) {
> > > memcpy(uc, addr, sizeof(*uc));
> > > vcpu_run_complete_io(vcpu);
> > > --
> > > 2.37.1.455.g008518b4e5-goog
> > >
> >
> > Thanks,
> > drew

2022-08-02 14:47:24

by Peter Gonda

[permalink] [raw]
Subject: Re: [V2 09/11] KVM: selftests: Make ucall work with encrypted guests

()

On Tue, Aug 2, 2022 at 8:26 AM Andrew Jones <[email protected]> wrote:
>
> On Tue, Aug 02, 2022 at 07:51:40AM -0600, Peter Gonda wrote:
> > On Tue, Aug 2, 2022 at 3:49 AM Andrew Jones <[email protected]> wrote:
> > >
> > > On Mon, Aug 01, 2022 at 01:11:07PM -0700, Peter Gonda wrote:
> > > > Add support for encrypted, SEV, guests in the ucall framework. If
> > > > encryption is enabled set up a pool of ucall structs in the guests'
> > > > shared memory region. This was suggested in the thread on "[RFC PATCH
> > > > 00/10] KVM: selftests: Add support for test-selectable ucall
> > > > implementations". Using a listed as suggested there doesn't work well
> > > > because the list is setup using HVAs not GVAs so use a bitmap + array
> > > > solution instead to get the same pool result.
> > > >
> > > > Suggested-by: Sean Christopherson <[email protected]>
> > > > Signed-off-by: Peter Gonda <[email protected]>
> > > > ---
> > > > .../selftests/kvm/include/kvm_util_base.h | 3 +
> > > > .../selftests/kvm/include/ucall_common.h | 14 +--
> > > > .../testing/selftests/kvm/lib/ucall_common.c | 112 +++++++++++++++++-
> > > > 3 files changed, 115 insertions(+), 14 deletions(-)
> > > >
> > > > diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > > index 8ce9e5be70a3..ad4abc6be1ab 100644
> > > > --- a/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > > +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h
> > > > @@ -102,6 +102,9 @@ struct kvm_vm {
> > > > int stats_fd;
> > > > struct kvm_stats_header stats_header;
> > > > struct kvm_stats_desc *stats_desc;
> > > > +
> > > > + bool use_ucall_list;
> > >
> > > use_ucall_pool
> > >
> > > > + struct list_head ucall_list;
> > > > };
> >
> > Will do. I also need to remove this |ucall_list| member.
> >
> > > >
> > > >
> > > > diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> > > > index c1bc8e33ef3f..a96220ac6024 100644
> > > > --- a/tools/testing/selftests/kvm/include/ucall_common.h
> > > > +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> > > > @@ -22,6 +22,10 @@ enum {
> > > > struct ucall {
> > > > uint64_t cmd;
> > > > uint64_t args[UCALL_MAX_ARGS];
> > > > +
> > > > + /* For encrypted guests. */
> > >
> > > Please, no 'encrypted' words in ucall files. ucalls shouldn't care about
> > > guest types. Indeed, the summary of this patch could even drop the word
> > > 'encrypted'. This patch is adding support for ucall pools, which is
> > > motivated by the need to support encrypted guests (the motivation should
> > > go in the commit message, but otherwise the patch should be ucall specific
> > > and guest type agnostic)
> > >
> > > > + uint64_t idx;
> > >
> > > We don't need 'idx' because 'hva' will always be at the
> > > idx * sizeof(struct ucall) offset of ucall_hdr->ucalls, which means
> > > we can always calculate it,
> > >
> > > static inline size_t uc_pool_idx(struct ucall *uc)
> > > {
> > > return uc->hva - ucall_hdr->ucalls;
> > > }
> >
> > Good call, I didn't think of that.
> >
> > >
> > > > + struct ucall *hva;
> > > > };
> > > >
> > > > void ucall_arch_init(struct kvm_vm *vm, void *arg);
> > > > @@ -32,15 +36,9 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
> > > > void ucall(uint64_t cmd, int nargs, ...);
> > > > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> > > >
> > > > -static inline void ucall_init(struct kvm_vm *vm, void *arg)
> > > > -{
> > > > - ucall_arch_init(vm, arg);
> > > > -}
> > > > +void ucall_init(struct kvm_vm *vm, void *arg);
> > > >
> > > > -static inline void ucall_uninit(struct kvm_vm *vm)
> > > > -{
> > > > - ucall_arch_uninit(vm);
> > > > -}
> > > > +void ucall_uninit(struct kvm_vm *vm);
> > > >
> > > > #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> > > > ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> > > > diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> > > > index a060252bab40..feb0173179ec 100644
> > > > --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> > > > +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> > > > @@ -1,22 +1,122 @@
> > > > // SPDX-License-Identifier: GPL-2.0-only
> > > > #include "kvm_util.h"
> > > > +#include "linux/types.h"
> > > > +#include "linux/bitmap.h"
> > > > +#include "linux/atomic.h"
> > > > +
> > > > +struct ucall_header {
> > > > + DECLARE_BITMAP(in_use, KVM_MAX_VCPUS);
> > > > + struct ucall ucalls[KVM_MAX_VCPUS];
> > > > +};
> > > > +
> > > > +static bool use_ucall_list;
> > >
> > > use_ucall_pool
> > >
> > > > +static struct ucall_header *ucall_hdr;
> > >
> > > ucall_pool
> >
> > Will update naming here.
> >
> > >
> > > > +
> > > > +void ucall_init(struct kvm_vm *vm, void *arg)
> > > > +{
> > > > + struct ucall *uc;
> > > > + struct ucall_header *hdr;
> > > > + vm_vaddr_t vaddr;
> > > > + int i;
> > > > +
> > > > + use_ucall_list = vm->use_ucall_list;
> > > > + sync_global_to_guest(vm, use_ucall_list);
> > > > + if (!use_ucall_list)
> > > > + goto out;
> > > > +
> > > > + TEST_ASSERT(!ucall_hdr,
> > > > + "Only a single encrypted guest at a time for ucalls.");
> > >
> > > single VM at a time
> > >
> > > And I'd leave that on a single line. checkpatch allows up to 100 chars and
> > > I'm included to use all 110 chars of it.
> >
> > Sounds good.
> >
> > >
> > > > + vaddr = vm_vaddr_alloc_shared(vm, sizeof(*hdr), vm->page_size);
> > > > + hdr = (struct ucall_header *)addr_gva2hva(vm, vaddr);
> > > > + memset(hdr, 0, sizeof(*hdr));
> > > > +
> > > > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > > > + uc = &hdr->ucalls[i];
> > > > + uc->hva = uc;
> > > > + uc->idx = i;
> > > > + }
> > > > +
> > > > + ucall_hdr = (struct ucall_header *)vaddr;
> > > > + sync_global_to_guest(vm, ucall_hdr);
> > > > +
> > > > +out:
> > > > + ucall_arch_init(vm, arg);
> > > > +}
> > > > +
> > > > +void ucall_uninit(struct kvm_vm *vm)
> > > > +{
> > > > + use_ucall_list = false;
> > > > + ucall_hdr = NULL;
> > >
> > > It's unlikely we'd ever change the ucall setup on a running VM,
> > > but we should sync these changes to the guest for good measure.
> >
> > Hmm I'll need to have some notion of encrypted guests here then. Since
> > the guests page tables also get encrypted we can no longer get the
> > gva2gpa translations so sync_global_to_guest() cannot be done due to
> > the addr_ga2hva(). So is it OK if this call references encryption like
> > below?
> >
> > use_ucall_list = false;
> > ucall_hdr = NULL;
> > if (!vm->memencrypt.encrypted) {
> > sync_global_to_guest(vm, use_ucall_list);
> > sync_global_to_guest(vm, ucall_hdr);
> > }
>
> Yes, I guess that's the best we can do.
>
> If sync_global_to_guest() becomes useless once a guest is encrypted then
> maybe it should assert !vm->memencrypt.encrypted.

In the last patch I have edited addr_gva2gpa like below to assert.
This should cover sync_global_to_guest() and other similar cases.

static inline vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva)
{
+ TEST_ASSERT(!vm->memcrypt.encrypted,
+ "Encrypted guests have their page tables encrypted
so gva2* conversions are not possible.");
return addr_arch_gva2gpa(vm, gva);
}

Speaking of that though I should move that to 10/11 instead of 11/11.

>
> >
> > >
> > > > +
> > > > + ucall_arch_uninit(vm);
> > > > +}
> > > > +
> > > > +static struct ucall *ucall_alloc(void)
> > > > +{
> > > > + struct ucall *uc = NULL;
> > > > + int i;
> > > > +
> > > > + if (!use_ucall_list)
> > > > + goto out;
> > > > +
> > > > + for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > > > + if (atomic_test_and_set_bit(i, ucall_hdr->in_use))
> > > > + continue;
> > > > +
> > > > + uc = &ucall_hdr->ucalls[i];
> > > > + }
> > >
> > > This is not what I suggested in the last revision and it's still wrong.
> > > Here, you're still looping through all of the pool and returning the last
> > > one.
> > >
> > > What I suggested was
> > >
> > > for (i = 0; i < KVM_MAX_VCPUS; ++i) {
> > > if (!atomic_test_and_set_bit(i, ucall_hdr->in_use)) {
> > > uc = &ucall_hdr->ucalls[i];
> > > break;
> > > }
> > > }
> > >
> > > Notice how we leave the loop early when we find a free uc.
> >
> > Ah my mistake, I didn't get this fix into this series
> >
> > >
> > > We should also zero the contents of the uc buffer before returning.
> >
> > Ack will do.
> >
> > >
> > > > +
> > > > +out:
> > > > + return uc;
> > > > +}
> > > > +
> > > > +static void ucall_free(struct ucall *uc)
> > > > +{
> > > > + if (!use_ucall_list)
> > > > + return;
> > > > +
> > > > + clear_bit(uc->idx, ucall_hdr->in_use);
> > >
> > > This seems to be the one and only use of idx which is another argument for
> > > dropping the variable and just calculating it instead.
> >
> > Will do.
> >
> > >
> > > > +}
> > > > +
> > > > +static vm_vaddr_t get_ucall_addr(struct ucall *uc)
> > > > +{
> > > > + if (use_ucall_list)
> > > > + return (vm_vaddr_t)uc->hva;
> > >
> > > A comment explaining that this hva has already been synchronized
> > > with the guest would be good. Or maybe a different name for the
> > > pointer than hva, one which conveys that it's a pointer that works
> > > as both an hva and gva would be better.
> >
> > I don't think this pointer does work as a gva though since its created
> > by just taking &uc during host execution. I can add a comment that
> > mentions this is a hva is already translated for the host and not for
> > guest use other than communicating with the host. I thought 'hva' was
> > a good name here since it is literally the hva of this ucall struct,
> > what name would you prefer?
>
> My mistake, I forgot how we initialized uc->hva. 'hva' is a good name.
>
> >
> > >
> > >
> > > > +
> > > > + return (vm_vaddr_t)uc;
> > > > +}
> > > >
> > > > void ucall(uint64_t cmd, int nargs, ...)
> > > > {
> > > > - struct ucall uc = {
> > > > - .cmd = cmd,
> > > > - };
> > >
> > > This zeros all members except cmd.
> > >
> > > > + struct ucall *uc;
> > > > + struct ucall tmp;
> > >
> > > And this makes tmp full of stack garbage, so in the non uc-pool case we
> > > no longer have an equivalent uc. Please initialize tmp the same way uc
> > > was initialized.
> >
> > Ack, I'll get this fixed.
> >
> > >
> > > > va_list va;
> > > > int i;
> > > >
> > > > + uc = ucall_alloc();
> > > > + if (!uc)
> > > > + uc = &tmp;
> > > > +
> > > > + uc->cmd = cmd;
> > > > +
> > > > nargs = min(nargs, UCALL_MAX_ARGS);
> > > >
> > > > va_start(va, nargs);
> > > > for (i = 0; i < nargs; ++i)
> > > > - uc.args[i] = va_arg(va, uint64_t);
> > > > + uc->args[i] = va_arg(va, uint64_t);
> > > > va_end(va);
> > > >
> > > > - ucall_arch_do_ucall((vm_vaddr_t)&uc);
> > > > + ucall_arch_do_ucall(get_ucall_addr(uc));
> > >
> > > We don't need get_ucall_addr(). Just do the if-else right here
> > >
> > > if (use_ucall_list)
> > > ucall_arch_do_ucall((vm_vaddr_t)uc->hva);
> > > else
> > > ucall_arch_do_ucall((vm_vaddr_t)uc);
> >
> > Will do.
> >
> > >
> > > > +
> > > > + ucall_free(uc);
> > > > +}
> > > > +
> > > > +static void *get_ucall_hva(struct kvm_vm *vm, uint64_t uc)
> > > > +{
> > > > + if (vm->use_ucall_list)
> > > > + return (void *)uc;
> > > > +
> > > > + return addr_gva2hva(vm, uc);
> > > > }
> > > >
> > > > uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > > > @@ -27,7 +127,7 @@ uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > > > if (!uc)
> > > > uc = &ucall;
> > > >
> > > > - addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> > > > + addr = get_ucall_hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> > >
> > > Wait, I thought we wanted ucall_arch_get_ucall() to return hvas?
> > > It would make more sense if it did, since it's called from the
> > > host.
> >
> > As you noted in [V2 07/11] I updated ucall_arch_get_ucall() to return
> > the gva. I figured this was just more boilerplate which could be
> > consolidated given all archs right now just use the addr_gva2hva()
> > helper to do this translation. This change also allowed for the
> > "use_ucall_pool" to be completely contained in ucall_common.c instead
> > of spilling into each arch specific file. If there is a preference to
> > have the arch specific calls translate from gva to hva I can do that,
> > I think I'll need to have them all check if "use_ucall_pool" is set
> > though. Thoughts?
>
> I just checked what I wrote for the last version of this patch,
> "[RFC V1 08/10] KVM: selftests: Make ucall work with encrypted guests".
> It was
>
> > ...we're only updating x86's ucall_arch_get_ucall() to return gvas.
> > What about the other architectures? Anyway, I'd rather we don't
> > change ucall_arch_get_ucall() to return gvas. They should continue
> > returning hvas and any trickery needed to translate a pool uc to
> > an hva should be put inside ucall_arch_get_ucall().
>
> (It's comforting to see I was consistent, at least wrt the last review.)
>
> So, I would keep returning hvas and put the 'if use_ucall_pool' inside
> the ucall_arch_get_ucall() functions.

Makes sense. I'll have ucall_arch_get_ucall() return hvas and deal
with the use_pool trickery. Sorry for not following that for this
version I had just thought consolidating that logic would be cleaner.

Thanks for thorough review Drew!

>
> Thanks,
> drew
>
> >
> > We can still update ucall_arch_get_ucall() to return a pointer type so
> > that we can return NULL instead of 0.
> >
> > >
> > > > if (addr) {
> > > > memcpy(uc, addr, sizeof(*uc));
> > > > vcpu_run_complete_io(vcpu);
> > > > --
> > > > 2.37.1.455.g008518b4e5-goog
> > > >
> > >
> > > Thanks,
> > > drew

2022-08-02 14:50:17

by Peter Gonda

[permalink] [raw]
Subject: Re: [V2 06/11] KVM: selftests: Consolidate common code for popuplating

On Tue, Aug 2, 2022 at 4:09 AM Andrew Jones <[email protected]> wrote:
>
> On Mon, Aug 01, 2022 at 01:11:04PM -0700, Peter Gonda wrote:
> > From: Sean Christopherson <[email protected]>
> >
> > Make ucall() a common helper that populates struct ucall, and only calls
> > into arch code to make the actually call out to userspace.
> >
> > Rename all arch-specific helpers to make it clear they're arch-specific,
> > and to avoid collisions with common helpers (one more on its way...)
> >
> > No functional change intended.
>
> But there is...

Yea my mistake. Looking at 9e2f6498efbbc it appears this optimization
could happen on all the other archs given the same reasoning in the
description "perhaps due to no immediate readers".

What do you think about dropping the "No functional change intended"
from this patch and moving the WRITE_ONCE() for the ops in the common
ucall() code?

>
> >
> > Cc: Colton Lewis <[email protected]>
> > Cc: Andrew Jones <[email protected]>
> > Signed-off-by: Sean Christopherson <[email protected]>
> > Signed-off-by: Peter Gonda <[email protected]>
> > ---
> > tools/testing/selftests/kvm/Makefile | 1 +
> > .../selftests/kvm/include/ucall_common.h | 23 ++++++++++++++++---
> > .../testing/selftests/kvm/lib/aarch64/ucall.c | 20 ++++------------
> > tools/testing/selftests/kvm/lib/riscv/ucall.c | 23 ++++---------------
> > tools/testing/selftests/kvm/lib/s390x/ucall.c | 23 ++++---------------
> > .../testing/selftests/kvm/lib/ucall_common.c | 20 ++++++++++++++++
> > .../testing/selftests/kvm/lib/x86_64/ucall.c | 23 ++++---------------
> > 7 files changed, 60 insertions(+), 73 deletions(-)
> > create mode 100644 tools/testing/selftests/kvm/lib/ucall_common.c
> >
> > diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile
> > index 690b499c3471..39fc5e8e5594 100644
> > --- a/tools/testing/selftests/kvm/Makefile
> > +++ b/tools/testing/selftests/kvm/Makefile
> > @@ -46,6 +46,7 @@ LIBKVM += lib/perf_test_util.c
> > LIBKVM += lib/rbtree.c
> > LIBKVM += lib/sparsebit.c
> > LIBKVM += lib/test_util.c
> > +LIBKVM += lib/ucall_common.c
> >
> > LIBKVM_x86_64 += lib/x86_64/apic.c
> > LIBKVM_x86_64 += lib/x86_64/handlers.S
> > diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> > index ee79d180e07e..5a85f5318bbe 100644
> > --- a/tools/testing/selftests/kvm/include/ucall_common.h
> > +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> > @@ -24,10 +24,27 @@ struct ucall {
> > uint64_t args[UCALL_MAX_ARGS];
> > };
> >
> > -void ucall_init(struct kvm_vm *vm, void *arg);
> > -void ucall_uninit(struct kvm_vm *vm);
> > +void ucall_arch_init(struct kvm_vm *vm, void *arg);
> > +void ucall_arch_uninit(struct kvm_vm *vm);
> > +void ucall_arch_do_ucall(vm_vaddr_t uc);
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> > +
> > void ucall(uint64_t cmd, int nargs, ...);
> > -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> > +
> > +static inline void ucall_init(struct kvm_vm *vm, void *arg)
> > +{
> > + ucall_arch_init(vm, arg);
> > +}
> > +
> > +static inline void ucall_uninit(struct kvm_vm *vm)
> > +{
> > + ucall_arch_uninit(vm);
> > +}
> > +
> > +static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +{
> > + return ucall_arch_get_ucall(vcpu, uc);
> > +}
> >
> > #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> > ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> > diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > index ed237b744690..1c81a6a5c1f2 100644
> > --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > @@ -21,7 +21,7 @@ static bool ucall_mmio_init(struct kvm_vm *vm, vm_paddr_t gpa)
> > return true;
> > }
> >
> > -void ucall_init(struct kvm_vm *vm, void *arg)
> > +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> > {
> > vm_paddr_t gpa, start, end, step, offset;
> > unsigned int bits;
> > @@ -64,30 +64,18 @@ void ucall_init(struct kvm_vm *vm, void *arg)
> > TEST_FAIL("Can't find a ucall mmio address");
> > }
> >
> > -void ucall_uninit(struct kvm_vm *vm)
> > +void ucall_arch_uninit(struct kvm_vm *vm)
> > {
> > ucall_exit_mmio_addr = 0;
> > sync_global_to_guest(vm, ucall_exit_mmio_addr);
> > }
> >
> > -void ucall(uint64_t cmd, int nargs, ...)
> > +void ucall_arch_do_ucall(vm_vaddr_t uc)
> > {
> > - struct ucall uc = {};
> > - va_list va;
> > - int i;
> > -
> > - WRITE_ONCE(uc.cmd, cmd);
> > - nargs = min(nargs, UCALL_MAX_ARGS);
> > -
> > - va_start(va, nargs);
> > - for (i = 0; i < nargs; ++i)
> > - WRITE_ONCE(uc.args[i], va_arg(va, uint64_t));
> > - va_end(va);
> > -
>
> There are WRITE_ONCE's being used here...
>
> > WRITE_ONCE(*ucall_exit_mmio_addr, (vm_vaddr_t)&uc);
> > }
> >
> > -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > {
> > struct kvm_run *run = vcpu->run;
> > struct ucall ucall = {};
> > diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > index 087b9740bc8f..b1598f418c1f 100644
> > --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > @@ -10,11 +10,11 @@
> > #include "kvm_util.h"
> > #include "processor.h"
> >
> > -void ucall_init(struct kvm_vm *vm, void *arg)
> > +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> > {
> > }
> >
> > -void ucall_uninit(struct kvm_vm *vm)
> > +void ucall_arch_uninit(struct kvm_vm *vm)
> > {
> > }
> >
> > @@ -44,27 +44,14 @@ struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
> > return ret;
> > }
> >
> > -void ucall(uint64_t cmd, int nargs, ...)
> > +void ucall_arch_do_ucall(vm_vaddr_t uc)
> > {
> > - struct ucall uc = {
> > - .cmd = cmd,
> > - };
> > - va_list va;
> > - int i;
> > -
> > - nargs = min(nargs, UCALL_MAX_ARGS);
> > -
> > - va_start(va, nargs);
> > - for (i = 0; i < nargs; ++i)
> > - uc.args[i] = va_arg(va, uint64_t);
> > - va_end(va);
> > -
> > sbi_ecall(KVM_RISCV_SELFTESTS_SBI_EXT,
> > KVM_RISCV_SELFTESTS_SBI_UCALL,
> > - (vm_vaddr_t)&uc, 0, 0, 0, 0, 0);
> > + uc, 0, 0, 0, 0, 0);
> > }
> >
> > -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > {
> > struct kvm_run *run = vcpu->run;
> > struct ucall ucall = {};
> > diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > index 73dc4e21190f..114cb4af295f 100644
> > --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > @@ -6,34 +6,21 @@
> > */
> > #include "kvm_util.h"
> >
> > -void ucall_init(struct kvm_vm *vm, void *arg)
> > +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> > {
> > }
> >
> > -void ucall_uninit(struct kvm_vm *vm)
> > +void ucall_arch_uninit(struct kvm_vm *vm)
> > {
> > }
> >
> > -void ucall(uint64_t cmd, int nargs, ...)
> > +void ucall_arch_do_ucall(vm_vaddr_t uc)
> > {
> > - struct ucall uc = {
> > - .cmd = cmd,
> > - };
> > - va_list va;
> > - int i;
> > -
> > - nargs = min(nargs, UCALL_MAX_ARGS);
> > -
> > - va_start(va, nargs);
> > - for (i = 0; i < nargs; ++i)
> > - uc.args[i] = va_arg(va, uint64_t);
> > - va_end(va);
> > -
> > /* Exit via DIAGNOSE 0x501 (normally used for breakpoints) */
> > - asm volatile ("diag 0,%0,0x501" : : "a"(&uc) : "memory");
> > + asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
> > }
> >
> > -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > {
> > struct kvm_run *run = vcpu->run;
> > struct ucall ucall = {};
> > diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> > new file mode 100644
> > index 000000000000..749ffdf23855
> > --- /dev/null
> > +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> > @@ -0,0 +1,20 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +#include "kvm_util.h"
> > +
> > +void ucall(uint64_t cmd, int nargs, ...)
> > +{
> > + struct ucall uc = {
> > + .cmd = cmd,
> > + };
> > + va_list va;
> > + int i;
> > +
> > + nargs = min(nargs, UCALL_MAX_ARGS);
> > +
> > + va_start(va, nargs);
> > + for (i = 0; i < nargs; ++i)
> > + uc.args[i] = va_arg(va, uint64_t);
> > + va_end(va);
>
> ...but not here. At least AArch64 needs them, see commit 9e2f6498efbb
> ("selftests: KVM: Handle compiler optimizations in ucall")
>
> > +
> > + ucall_arch_do_ucall((vm_vaddr_t)&uc);
> > +}
> > diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > index e5f0f9e0d3ee..9f532dba1003 100644
> > --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > @@ -8,34 +8,21 @@
> >
> > #define UCALL_PIO_PORT ((uint16_t)0x1000)
> >
> > -void ucall_init(struct kvm_vm *vm, void *arg)
> > +void ucall_arch_init(struct kvm_vm *vm, void *arg)
> > {
> > }
> >
> > -void ucall_uninit(struct kvm_vm *vm)
> > +void ucall_arch_uninit(struct kvm_vm *vm)
> > {
> > }
> >
> > -void ucall(uint64_t cmd, int nargs, ...)
> > +void ucall_arch_do_ucall(vm_vaddr_t uc)
> > {
> > - struct ucall uc = {
> > - .cmd = cmd,
> > - };
> > - va_list va;
> > - int i;
> > -
> > - nargs = min(nargs, UCALL_MAX_ARGS);
> > -
> > - va_start(va, nargs);
> > - for (i = 0; i < nargs; ++i)
> > - uc.args[i] = va_arg(va, uint64_t);
> > - va_end(va);
> > -
> > asm volatile("in %[port], %%al"
> > - : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax", "memory");
> > + : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
> > }
> >
> > -uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > {
> > struct kvm_run *run = vcpu->run;
> > struct ucall ucall = {};
> > --
> > 2.37.1.455.g008518b4e5-goog
> >
>
> Thanks,
> drew

2022-08-02 15:30:14

by Peter Gonda

[permalink] [raw]
Subject: Re: [V2 07/11] KVM: selftests: Consolidate boilerplate code in get_ucall()

On Tue, Aug 2, 2022 at 3:54 AM Andrew Jones <[email protected]> wrote:
>
> On Mon, Aug 01, 2022 at 01:11:05PM -0700, Peter Gonda wrote:
> > From: Sean Christopherson <[email protected]>
> >
> > Consolidate the actual copying of a ucall struct from guest=>host into
> > the common get_ucall(). Return a host virtual address instead of a guest
> > virtual address even though the addr_gva2hva() part could be moved to
> > get_ucall() too. Conceptually, get_ucall() is invoked from the host and
> > should return a host virtual address (and returning NULL for "nothing to
> > see here" is far superior to returning 0).
>
> The code does not match this description anymore, now that
> ucall_arch_get_ucall() returns a gva (as a uint64_t), but the description
> is good, the code is wrong. Please restore the spirit of Sean's patch
> (particularly because it still says it's from Sean...)

As discussed in the encrypted ucall patch I'll get this fixed up to
return gva from ucall_arch_get_ucall() and return a vm_addr_t.

>
> Thanks,
> drew
>
> >
> > Use pointer shenanigans instead of an unnecessary bounce buffer when the
> > caller of get_ucall() provides a valid pointer.
> >
> > Signed-off-by: Sean Christopherson <[email protected]>
> > Signed-off-by: Peter Gonda <[email protected]>
> > ---
> > .../selftests/kvm/include/ucall_common.h | 8 ++------
> > .../testing/selftests/kvm/lib/aarch64/ucall.c | 13 +++----------
> > tools/testing/selftests/kvm/lib/riscv/ucall.c | 19 +++----------------
> > tools/testing/selftests/kvm/lib/s390x/ucall.c | 16 +++-------------
> > .../testing/selftests/kvm/lib/ucall_common.c | 19 +++++++++++++++++++
> > .../testing/selftests/kvm/lib/x86_64/ucall.c | 16 +++-------------
> > 6 files changed, 33 insertions(+), 58 deletions(-)
> >
> > diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h
> > index 5a85f5318bbe..c1bc8e33ef3f 100644
> > --- a/tools/testing/selftests/kvm/include/ucall_common.h
> > +++ b/tools/testing/selftests/kvm/include/ucall_common.h
> > @@ -27,9 +27,10 @@ struct ucall {
> > void ucall_arch_init(struct kvm_vm *vm, void *arg);
> > void ucall_arch_uninit(struct kvm_vm *vm);
> > void ucall_arch_do_ucall(vm_vaddr_t uc);
> > -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu);
> >
> > void ucall(uint64_t cmd, int nargs, ...);
> > +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc);
> >
> > static inline void ucall_init(struct kvm_vm *vm, void *arg)
> > {
> > @@ -41,11 +42,6 @@ static inline void ucall_uninit(struct kvm_vm *vm)
> > ucall_arch_uninit(vm);
> > }
> >
> > -static inline uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > -{
> > - return ucall_arch_get_ucall(vcpu, uc);
> > -}
> > -
> > #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \
> > ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4)
> > #define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage)
> > diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > index 1c81a6a5c1f2..d2f099caa9ab 100644
> > --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c
> > @@ -78,24 +78,17 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> > uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > {
> > struct kvm_run *run = vcpu->run;
> > - struct ucall ucall = {};
> > -
> > - if (uc)
> > - memset(uc, 0, sizeof(*uc));
> >
> > if (run->exit_reason == KVM_EXIT_MMIO &&
> > run->mmio.phys_addr == (uint64_t)ucall_exit_mmio_addr) {
> > - vm_vaddr_t gva;
> > + uint64_t ucall_addr;
> >
> > TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8,
> > "Unexpected ucall exit mmio address access");
> > memcpy(&gva, run->mmio.data, sizeof(gva));
> > - memcpy(&ucall, addr_gva2hva(vcpu->vm, gva), sizeof(ucall));
> >
> > - vcpu_run_complete_io(vcpu);
> > - if (uc)
> > - memcpy(uc, &ucall, sizeof(ucall));
> > + return ucall_addr;
> > }
> >
> > - return ucall.cmd;
> > + return 0;
> > }
> > diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > index b1598f418c1f..3f000d0b705f 100644
> > --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c
> > @@ -51,27 +51,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> > uc, 0, 0, 0, 0, 0);
> > }
> >
> > -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> > {
> > struct kvm_run *run = vcpu->run;
> > - struct ucall ucall = {};
> > -
> > - if (uc)
> > - memset(uc, 0, sizeof(*uc));
> >
> > if (run->exit_reason == KVM_EXIT_RISCV_SBI &&
> > run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) {
> > switch (run->riscv_sbi.function_id) {
> > case KVM_RISCV_SELFTESTS_SBI_UCALL:
> > - memcpy(&ucall,
> > - addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]),
> > - sizeof(ucall));
> > -
> > - vcpu_run_complete_io(vcpu);
> > - if (uc)
> > - memcpy(uc, &ucall, sizeof(ucall));
> > -
> > - break;
> > + return vcpu->vm, run->riscv_sbi.args[0];
> > case KVM_RISCV_SELFTESTS_SBI_UNEXP:
> > vcpu_dump(stderr, vcpu, 2);
> > TEST_ASSERT(0, "Unexpected trap taken by guest");
> > @@ -80,6 +68,5 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > break;
> > }
> > }
> > -
> > - return ucall.cmd;
> > + return 0;
> > }
> > diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > index 114cb4af295f..f7a5a7eb4aa8 100644
> > --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c
> > @@ -20,13 +20,9 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> > asm volatile ("diag 0,%0,0x501" : : "a"(uc) : "memory");
> > }
> >
> > -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> > {
> > struct kvm_run *run = vcpu->run;
> > - struct ucall ucall = {};
> > -
> > - if (uc)
> > - memset(uc, 0, sizeof(*uc));
> >
> > if (run->exit_reason == KVM_EXIT_S390_SIEIC &&
> > run->s390_sieic.icptcode == 4 &&
> > @@ -34,13 +30,7 @@ uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > (run->s390_sieic.ipb >> 16) == 0x501) {
> > int reg = run->s390_sieic.ipa & 0xf;
> >
> > - memcpy(&ucall, addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]),
> > - sizeof(ucall));
> > -
> > - vcpu_run_complete_io(vcpu);
> > - if (uc)
> > - memcpy(uc, &ucall, sizeof(ucall));
> > + return run->s.regs.gprs[reg];
> > }
> > -
> > - return ucall.cmd;
> > + return 0;
> > }
> > diff --git a/tools/testing/selftests/kvm/lib/ucall_common.c b/tools/testing/selftests/kvm/lib/ucall_common.c
> > index 749ffdf23855..a060252bab40 100644
> > --- a/tools/testing/selftests/kvm/lib/ucall_common.c
> > +++ b/tools/testing/selftests/kvm/lib/ucall_common.c
> > @@ -18,3 +18,22 @@ void ucall(uint64_t cmd, int nargs, ...)
> >
> > ucall_arch_do_ucall((vm_vaddr_t)&uc);
> > }
> > +
> > +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +{
> > + struct ucall ucall;
> > + void *addr;
> > +
> > + if (!uc)
> > + uc = &ucall;
> > +
> > + addr = addr_gva2hva(vcpu->vm, ucall_arch_get_ucall(vcpu));
> > + if (addr) {
> > + memcpy(uc, addr, sizeof(*uc));
> > + vcpu_run_complete_io(vcpu);
> > + } else {
> > + memset(uc, 0, sizeof(*uc));
> > + }
> > +
> > + return uc->cmd;
> > +}
> > diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > index 9f532dba1003..24746120a593 100644
> > --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c
> > @@ -22,25 +22,15 @@ void ucall_arch_do_ucall(vm_vaddr_t uc)
> > : : [port] "d" (UCALL_PIO_PORT), "D" (uc) : "rax", "memory");
> > }
> >
> > -uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc)
> > +uint64_t ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
> > {
> > struct kvm_run *run = vcpu->run;
> > - struct ucall ucall = {};
> > -
> > - if (uc)
> > - memset(uc, 0, sizeof(*uc));
> >
> > if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) {
> > struct kvm_regs regs;
> >
> > vcpu_regs_get(vcpu, &regs);
> > - memcpy(&ucall, addr_gva2hva(vcpu->vm, (vm_vaddr_t)regs.rdi),
> > - sizeof(ucall));
> > -
> > - vcpu_run_complete_io(vcpu);
> > - if (uc)
> > - memcpy(uc, &ucall, sizeof(ucall));
> > + return regs.rdi;
> > }
> > -
> > - return ucall.cmd;
> > + return 0;
> > }
> > --
> > 2.37.1.455.g008518b4e5-goog
> >