Skip to contentSkip to conversation

KVM: selftests: add powerpc support

RFC Superseded Ritesh Harjani (IBM) 2 participants 10 messages May 23 – May 27

Subsystems (8)
KVM Kselftest RISC-V KVM arm64 +4 more subsystems

v2 of 7 Superseded Latest is v7 →

lore
Share & tools
lore
#0 lore raw

v2 cover KVM: selftests: add powerpc support

Hi All,

This series primarly adds KVM selftests support for powerpc (64-bit, BookS, radix MMU).

This patch series is originally Nick's work. I have mainly only rebased it on the latest upstream tree. Since the rebase required few changes to all the four patches (Patch 1-4), I have dropped the earlier Acked-by from Michael Ellerman.

Since the last series was posted three years ago [1], I am resetting the version to RFC. This rebase was done as part of a larger effort to improve the selftests infrastructure for Linux on PowerPC tree. Thanks to Harsh and Maddy for their help on this.

Testing Updates:
================
1. Tested this on PowerNV P9 with Radix mode. (all selftests passes)
2. Tested this on LPAR (KVM on PowerVM) case. (all selftests except-1 passes). The failed testcase (kvm_create_max_vcpus) is because H_GUEST_CREATE_VCPU (PAPR HCALL) only supports vcpu_ids 0-2047 i.e. max 2048 vcpus. However, kernel always returns NR_CPUS for the KVM_CAP_MAX_VCPUS extension. So if the LPAR kernel is built using NR_CPUS=8192, then kvm_create_max_vcpus can fail in __vm_vcpu_add(). This failure needs to be handled seperately, mostly as fix in kernel.
3. Tested these selftests changes inside x86 kvm guest - no new failures seen.

RFC v1 -> RFC v2
================
(mostly Sashiko review comments)
1. Fixed x86's stage-2 mmu handling in patch-1 - commit msg has more description
2. Added "cc" into the clobber list for hcalls in patch-3
3. Fixed the size calculation in kvm_arch_vm_post_create in patch-3 for allocating 2 pages for 4K pagesize
4. Added patch-5 which prints vcpu_id in case of an error

[RFCv1]: https://lore.kernel.org/linuxppc-dev/[email protected]/
[1]: https://lore.kernel.org/all/[email protected]/

Nicholas Piggin (4):
KVM: selftests: Move pgd_created check into virt_pgd_alloc
KVM: selftests: Add aligned guest physical page allocator
KVM: PPC: selftests: add support for powerpc
KVM: PPC: selftests: powerpc enable kvm_create_max_vcpus test

Ritesh Harjani (IBM) (1):
KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

 MAINTAINERS                                   |   2 +
 tools/testing/selftests/kvm/Makefile          |   2 +-
 tools/testing/selftests/kvm/Makefile.kvm      |  10 +
 .../testing/selftests/kvm/include/kvm_util.h  |  34 +-
 .../selftests/kvm/include/powerpc/hcall.h     |  17 +
 .../kvm/include/powerpc/kvm_util_arch.h       |  22 +
 .../selftests/kvm/include/powerpc/ppc_asm.h   |  32 ++
 .../selftests/kvm/include/powerpc/processor.h |  38 ++
 .../selftests/kvm/include/powerpc/ucall.h     |  21 +
 .../selftests/kvm/kvm_create_max_vcpus.c      |   9 +
 .../selftests/kvm/lib/arm64/processor.c       |   4 -
 tools/testing/selftests/kvm/lib/guest_modes.c |  20 +-
 tools/testing/selftests/kvm/lib/kvm_util.c    |  44 +-
 .../selftests/kvm/lib/loongarch/processor.c   |   4 -
 .../selftests/kvm/lib/powerpc/handlers.S      |  93 ++++
 .../testing/selftests/kvm/lib/powerpc/hcall.c |  45 ++
 .../selftests/kvm/lib/powerpc/processor.c     | 484 ++++++++++++++++++
 .../testing/selftests/kvm/lib/powerpc/ucall.c |  22 +
 .../selftests/kvm/lib/riscv/processor.c       |   4 -
 .../selftests/kvm/lib/s390/processor.c        |   4 -
 20 files changed, 872 insertions(+), 39 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/hcall.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/ppc_asm.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/processor.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/ucall.h
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/handlers.S
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/hcall.c
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/processor.c
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/ucall.c

--
2.39.5

#1 lore raw

RFC v2 1/5 KVM: selftests: Move pgd_created check into virt_pgd_alloc

From: Nicholas Piggin <[email protected]>

virt_arch_pgd_alloc all do the same test and set pgd_created. Move this into common code -
except for x86's virt_arch_pgd_alloc() -> virt_mmu_init() - because this can also be called from it's tdp_mmu_init() call for setting up it's stage-2 mmu.

[Rebased to latest mainline tree and removed x86 case]
---
 tools/testing/selftests/kvm/include/kvm_util.h        | 5 +++++
 tools/testing/selftests/kvm/lib/arm64/processor.c     | 4 ----
 tools/testing/selftests/kvm/lib/loongarch/processor.c | 4 ----
 tools/testing/selftests/kvm/lib/riscv/processor.c     | 4 ----
 tools/testing/selftests/kvm/lib/s390/processor.c      | 4 ----
 5 files changed, 5 insertions(+), 16 deletions(-)
tools/testing/selftests/kvm/include/kvm_util.h +5 -0
index 2ecaaa0e9965..3666a8530f31 100644
@@ -1197,7 +1197,12 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm);
 
 static inline void virt_pgd_alloc(struct kvm_vm *vm)
 {
+	if (vm->mmu.pgd_created)
+		return;
+
 	virt_arch_pgd_alloc(vm);
+
+	vm->mmu.pgd_created = true;
 }
 
 /*
tools/testing/selftests/kvm/lib/arm64/processor.c +0 -4
index 01325bf4d36f..498fbcb0ea16 100644
@@ -112,13 +112,9 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)
 {
 	size_t nr_pages = vm_page_align(vm, ptrs_per_pgd(vm) * 8) / vm->page_size;
 
-	if (vm->mmu.pgd_created)
-		return;
-
 	vm->mmu.pgd = vm_phy_pages_alloc(vm, nr_pages,
 					 KVM_GUEST_PAGE_TABLE_MIN_PADDR,
 					 vm->memslots[MEM_REGION_PT]);
-	vm->mmu.pgd_created = true;
 }
 
 static void _virt_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa,
tools/testing/selftests/kvm/lib/loongarch/processor.c +0 -4
index 64d91fb76522..207055db5f5d 100644
@@ -51,9 +51,6 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)
 	int i;
 	gpa_t child, table;
 
-	if (vm->mmu.pgd_created)
-		return;
-
 	child = table = 0;
 	for (i = 0; i < vm->mmu.pgtable_levels; i++) {
 		invalid_pgtable[i] = child;
@@ -64,7 +61,6 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)
 		child = table;
 	}
 	vm->mmu.pgd = table;
-	vm->mmu.pgd_created = true;
 }
 
 static int virt_pte_none(u64 *ptep, int level)
tools/testing/selftests/kvm/lib/riscv/processor.c +0 -4
index ded5429f3448..75a5d4c46001 100644
@@ -66,13 +66,9 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)
 {
 	size_t nr_pages = vm_page_align(vm, ptrs_per_pte(vm) * 8) / vm->page_size;
 
-	if (vm->mmu.pgd_created)
-		return;
-
 	vm->mmu.pgd = vm_phy_pages_alloc(vm, nr_pages,
 					 KVM_GUEST_PAGE_TABLE_MIN_PADDR,
 					 vm->memslots[MEM_REGION_PT]);
-	vm->mmu.pgd_created = true;
 }
 
 void virt_arch_pg_map(struct kvm_vm *vm, gva_t gva, gpa_t gpa)
tools/testing/selftests/kvm/lib/s390/processor.c +0 -4
index a9adb3782b35..342b7c92463e 100644
@@ -17,16 +17,12 @@ void virt_arch_pgd_alloc(struct kvm_vm *vm)
 	TEST_ASSERT(vm->page_size == PAGE_SIZE, "Unsupported page size: 0x%x",
 		    vm->page_size);
 
-	if (vm->mmu.pgd_created)
-		return;
-
 	gpa = vm_phy_pages_alloc(vm, PAGES_PER_REGION,
 				   KVM_GUEST_PAGE_TABLE_MIN_PADDR,
 				   vm->memslots[MEM_REGION_PT]);
 	memset(addr_gpa2hva(vm, gpa), 0xff, PAGES_PER_REGION * vm->page_size);
 
 	vm->mmu.pgd = gpa;
-	vm->mmu.pgd_created = true;
 }
 
 /*
Signature · 1 line
2.39.5
#2 lore raw

RFC v2 2/5 KVM: selftests: Add aligned guest physical page allocator

In reply to Ritesh Harjani (IBM)

From: Nicholas Piggin <[email protected]>

powerpc will require this to allocate MMU tables in guest memory that are larger than guest base page size.

[Rebased to latest mainline tree]
---
 .../testing/selftests/kvm/include/kvm_util.h  | 20 +++++++++--
 tools/testing/selftests/kvm/lib/kvm_util.c    | 33 +++++++++----------
 2 files changed, 33 insertions(+), 20 deletions(-)
tools/testing/selftests/kvm/include/kvm_util.h +17 -3
index 3666a8530f31..c515c918c2c9 100644
@@ -991,8 +991,8 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing);
 const char *exit_reason_str(unsigned int exit_reason);
 
 gpa_t vm_phy_page_alloc(struct kvm_vm *vm, gpa_t min_gpa, u32 memslot);
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, gpa_t min_gpa,
-			   u32 memslot, bool protected);
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
+			   gpa_t min_gpa, u32 memslot, bool protected);
 gpa_t vm_alloc_page_table(struct kvm_vm *vm);
 
 static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
@@ -1003,10 +1003,24 @@ static inline gpa_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
 	 * protected memory, as the majority of memory for such VMs is
 	 * protected, i.e. using shared memory is effectively opt-in.
 	 */
-	return __vm_phy_pages_alloc(vm, num, min_gpa, memslot,
+	return __vm_phy_pages_alloc(vm, num, 1, min_gpa, memslot,
 				    vm_arch_has_protected_memory(vm));
 }
 
+static inline gpa_t vm_phy_pages_alloc_align(struct kvm_vm *vm, size_t num,
+					     size_t align, gpa_t min_gpa,
+					     u32 memslot)
+{
+	/*
+	 * By default, allocate memory as protected for VMs that support
+	 * protected memory, as the majority of memory for such VMs is
+	 * protected, i.e. using shared memory is effectively opt-in.
+	 */
+	return __vm_phy_pages_alloc(vm, num, align, min_gpa, memslot,
+				    vm_arch_has_protected_memory(vm));
+}
+
+
 /*
  * ____vm_create() does KVM_CREATE_VM and little else.  __vm_create() also
  * loads the test binary into guest memory and creates an IRQ chip (x86 only).
tools/testing/selftests/kvm/lib/kvm_util.c +16 -17
index 2a76eca7029d..cdb004c9ba56 100644
@@ -1442,7 +1442,7 @@ static gva_t ____vm_alloc(struct kvm_vm *vm, size_t sz, gva_t min_gva,
 	u64 pages = (sz >> vm->page_shift) + ((sz % vm->page_size) != 0);
 
 	virt_pgd_alloc(vm);
-	gpa_t gpa = __vm_phy_pages_alloc(vm, pages,
+	gpa_t gpa = __vm_phy_pages_alloc(vm, pages, 1,
 					   KVM_UTIL_MIN_PFN * vm->page_size,
 					   vm->memslots[type], protected);
 
@@ -2021,7 +2021,7 @@ const char *exit_reason_str(unsigned int exit_reason)
  * and their base address is returned. A TEST_ASSERT failure occurs if
  * not enough pages are available at or above min_gpa.
  */
-gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
+gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, size_t align,
 			   gpa_t min_gpa, u32 memslot,
 			   bool protected)
 {
@@ -2039,23 +2039,22 @@ gpa_t __vm_phy_pages_alloc(struct kvm_vm *vm, size_t num,
 	TEST_ASSERT(!protected || region->protected_phy_pages,
 		    "Region doesn't support protected memory");
 
-	base = pg = min_gpa >> 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;
+	base = min_gpa >> vm->page_shift;
+again:
+	base = (base + align - 1) & ~(align - 1);
+	for (pg = base; pg < base + num; ++pg) {
+		if (!sparsebit_is_set(region->unused_phy_pages, pg)) {
+			base = sparsebit_next_set(region->unused_phy_pages, pg);
+			if (!base) {
+				fprintf(stderr, "No guest physical page available, "
+					"min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
+					min_gpa, vm->page_size, memslot);
+				fputs("---- vm dump ----\n", stderr);
+				vm_dump(stderr, vm, 2);
+				abort();
 			}
+			goto again;
 		}
-	} while (pg && pg != base + num);
-
-	if (pg == 0) {
-		fprintf(stderr, "No guest physical page available, "
-			"min_gpa: 0x%lx page_size: 0x%x memslot: %u\n",
-			min_gpa, vm->page_size, memslot);
-		fputs("---- vm dump ----\n", stderr);
-		vm_dump(stderr, vm, 2);
-		abort();
 	}
 
 	for (pg = base; pg < base + num; ++pg) {
Signature · 1 line
2.39.5
#3 lore raw

RFC v2 3/5 KVM: PPC: selftests: add support for powerpc

In reply to Ritesh Harjani (IBM)

From: Nicholas Piggin <[email protected]>

Implement KVM selftests support for powerpc (Book3S-64).

ucalls are implemented with an unsupported PAPR hcall number which will always cause KVM to exit to userspace.

Virtual memory is implemented for the radix MMU, and only a base page size is supported (both 4K and 64K).

Guest interrupts are taken in real-mode, so require a page allocated at gRA 0x0. Interrupt entry is complicated because gVA:gRA is not 1:1 mapped (like the kernel is), so the MMU can not just be switched on and off.

[Rebased to latest mainline tree, add "cc" to clobber list and fix the pagesize calculation in kvm_arch_vm_post_create()]

---
 MAINTAINERS                                   |   2 +
 tools/testing/selftests/kvm/Makefile          |   2 +-
 tools/testing/selftests/kvm/Makefile.kvm      |  10 +
 .../testing/selftests/kvm/include/kvm_util.h  |   9 +
 .../selftests/kvm/include/powerpc/hcall.h     |  17 +
 .../kvm/include/powerpc/kvm_util_arch.h       |  22 +
 .../selftests/kvm/include/powerpc/ppc_asm.h   |  32 ++
 .../selftests/kvm/include/powerpc/processor.h |  38 ++
 .../selftests/kvm/include/powerpc/ucall.h     |  21 +
 tools/testing/selftests/kvm/lib/guest_modes.c |  20 +-
 tools/testing/selftests/kvm/lib/kvm_util.c    |   8 +
 .../selftests/kvm/lib/powerpc/handlers.S      |  93 ++++
 .../testing/selftests/kvm/lib/powerpc/hcall.c |  45 ++
 .../selftests/kvm/lib/powerpc/processor.c     | 484 ++++++++++++++++++
 .../testing/selftests/kvm/lib/powerpc/ucall.c |  22 +
 15 files changed, 822 insertions(+), 3 deletions(-)
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/hcall.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/ppc_asm.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/processor.h
 create mode 100644 tools/testing/selftests/kvm/include/powerpc/ucall.h
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/handlers.S
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/hcall.c
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/processor.c
 create mode 100644 tools/testing/selftests/kvm/lib/powerpc/ucall.c
MAINTAINERS +2 -0
index f1e5e4258e7b..3c876ea1198e 100644
@@ -14118,6 +14118,8 @@ F:	arch/powerpc/include/asm/kvm*
 F:	arch/powerpc/include/uapi/asm/kvm*
 F:	arch/powerpc/kernel/kvm*
 F:	arch/powerpc/kvm/
+F:	tools/testing/selftests/kvm/*/powerpc/
+F:	tools/testing/selftests/kvm/powerpc/
 
 KERNEL VIRTUAL MACHINE FOR RISC-V (KVM/riscv)
 M:	Anup Patel <[email protected]>
tools/testing/selftests/kvm/Makefile +1 -1
index f2b223072b62..03d91f00092f 100644
@@ -3,7 +3,7 @@ top_srcdir = ../../../..
 include $(top_srcdir)/scripts/subarch.include
 ARCH            ?= $(SUBARCH)
 
-ifeq ($(ARCH),$(filter $(ARCH),arm64 s390 riscv x86 x86_64 loongarch))
+ifeq ($(ARCH),$(filter $(ARCH),arm64 s390 riscv x86 x86_64 loongarch powerpc))
 # Top-level selftests allows ARCH=x86_64 :-(
 ifeq ($(ARCH),x86_64)
 	override ARCH := x86
tools/testing/selftests/kvm/Makefile.kvm +10 -0
index 9118a5a51b89..825bea7f851d 100644
@@ -52,6 +52,11 @@ LIBKVM_loongarch += lib/loongarch/processor.c
 LIBKVM_loongarch += lib/loongarch/ucall.c
 LIBKVM_loongarch += lib/loongarch/exception.S
 
+LIBKVM_powerpc += lib/powerpc/handlers.S
+LIBKVM_powerpc += lib/powerpc/processor.c
+LIBKVM_powerpc += lib/powerpc/ucall.c
+LIBKVM_powerpc += lib/powerpc/hcall.c
+
 # Non-compiled test targets
 TEST_PROGS_x86 += x86/nx_huge_pages_test.sh
 
@@ -239,6 +244,11 @@ TEST_GEN_PROGS_loongarch += memslot_perf_test
 TEST_GEN_PROGS_loongarch += set_memory_region_test
 TEST_GEN_PROGS_loongarch += steal_time
 
+TEST_GEN_PROGS_powerpc = $(TEST_GEN_PROGS_COMMON)
+TEST_GEN_PROGS_powerpc += access_tracking_perf_test
+TEST_GEN_PROGS_powerpc += dirty_log_perf_test
+TEST_GEN_PROGS_powerpc += hardware_disable_test
+
 SPLIT_TESTS += arch_timer
 SPLIT_TESTS += get-reg-list
 
tools/testing/selftests/kvm/include/kvm_util.h +9 -0
index c515c918c2c9..10f03a182c8b 100644
@@ -209,6 +209,9 @@ enum vm_guest_mode {
 	VM_MODE_P41V48_4K,
 	VM_MODE_P41V39_4K,
 
+	VM_MODE_P52V52_4K,	/* For powerpc64 */
+	VM_MODE_P52V52_64K,
+
 	NUM_VM_MODES,
 };
 
@@ -268,6 +271,12 @@ extern enum vm_guest_mode vm_mode_default;
 #define MIN_PAGE_SHIFT			12U
 #define ptes_per_page(page_size)	((page_size) / 8)
 
+#elif defined(__powerpc64__)
+
+#define VM_MODE_DEFAULT			vm_mode_default
+#define MIN_PAGE_SHIFT			12U
+#define ptes_per_page(page_size)	((page_size) / 8)
+
 #endif
 
 #define VM_SHAPE_DEFAULT	VM_SHAPE(VM_MODE_DEFAULT)
tools/testing/selftests/kvm/include/powerpc/hcall.h new file +17 -0
new file mode 100644
index 000000000000..4028baa6c5d8
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc hcall defines
+ */
+#ifndef SELFTEST_KVM_HCALL_H
+#define SELFTEST_KVM_HCALL_H
+
+#include <linux/compiler.h>
+
+/* Ucalls use unimplemented PAPR hcall 0 which exits KVM */
+#define H_UCALL	0
+
+int64_t hcall0(uint64_t token);
+int64_t hcall1(uint64_t token, uint64_t arg1);
+int64_t hcall2(uint64_t token, uint64_t arg1, uint64_t arg2);
+
+#endif
tools/testing/selftests/kvm/include/powerpc/kvm_util_arch.h new file +22 -0
new file mode 100644
index 000000000000..5d45c25cd299
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_UTIL_ARCH_H
+#define SELFTEST_KVM_UTIL_ARCH_H
+
+#include <stdint.h>
+
+#include "kvm_util_types.h"
+
+struct kvm_mmu_arch {};
+
+/* Page table fragment cache for guest page tables < page size */
+struct vm_pt_frag_cache {
+	gpa_t page;
+	size_t page_nr_used;
+};
+
+struct kvm_vm_arch {
+	gpa_t prtb; /* process table */
+	struct vm_pt_frag_cache pt_frag_cache[2]; /* 256B and 4KB PT caches */
+};
+
+#endif  /* SELFTEST_KVM_UTIL_ARCH_H */
tools/testing/selftests/kvm/include/powerpc/ppc_asm.h new file +32 -0
new file mode 100644
index 000000000000..b9df64659792
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc asm specific defines
+ */
+#ifndef SELFTEST_KVM_PPC_ASM_H
+#define SELFTEST_KVM_PPC_ASM_H
+
+#define STACK_FRAME_MIN_SIZE	112 /* Could be 32 on ELFv2 */
+#define STACK_REDZONE_SIZE	512
+
+#define INT_FRAME_SIZE		(STACK_FRAME_MIN_SIZE + STACK_REDZONE_SIZE)
+
+#define SPR_SRR0	0x01a
+#define SPR_SRR1	0x01b
+#define SPR_CFAR	0x01c
+
+#define MSR_SF		0x8000000000000000ULL
+#define MSR_HV		0x1000000000000000ULL
+#define MSR_VEC		0x0000000002000000ULL
+#define MSR_VSX		0x0000000000800000ULL
+#define MSR_EE		0x0000000000008000ULL
+#define MSR_PR		0x0000000000004000ULL
+#define MSR_FP		0x0000000000002000ULL
+#define MSR_ME		0x0000000000001000ULL
+#define MSR_IR		0x0000000000000020ULL
+#define MSR_DR		0x0000000000000010ULL
+#define MSR_RI		0x0000000000000002ULL
+#define MSR_LE		0x0000000000000001ULL
+
+#define LPCR_ILE	0x0000000002000000ULL
+
+#endif
tools/testing/selftests/kvm/include/powerpc/processor.h new file +38 -0
new file mode 100644
index 000000000000..cb75b77c33bb
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * powerpc processor specific defines
+ */
+#ifndef SELFTEST_KVM_PROCESSOR_H
+#define SELFTEST_KVM_PROCESSOR_H
+
+#include <linux/compiler.h>
+#include "ppc_asm.h"
+
+extern unsigned char __interrupts_start[];
+extern unsigned char __interrupts_end[];
+
+struct kvm_vm;
+struct kvm_vcpu;
+
+struct ex_regs {
+	uint64_t	gprs[32];
+	uint64_t	nia;
+	uint64_t	msr;
+	uint64_t	cfar;
+	uint64_t	lr;
+	uint64_t	ctr;
+	uint64_t	xer;
+	uint32_t	cr;
+	uint32_t	trap;
+	uint64_t	vaddr; /* vaddr of this struct */
+};
+
+void vm_install_exception_handler(struct kvm_vm *vm, int vector,
+			void (*handler)(struct ex_regs *));
+
+static inline void cpu_relax(void)
+{
+	asm volatile("" ::: "memory");
+}
+
+#endif
tools/testing/selftests/kvm/include/powerpc/ucall.h new file +21 -0
new file mode 100644
index 000000000000..e0dbe91e8848
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef SELFTEST_KVM_UCALL_H
+#define SELFTEST_KVM_UCALL_H
+
+#include "hcall.h"
+
+#define UCALL_EXIT_REASON	KVM_EXIT_PAPR_HCALL
+
+#define UCALL_R4_UCALL	0x5715 /* regular ucall, r5 contains ucall pointer */
+#define UCALL_R4_SIMPLE	0x0000 /* simple exit usable by asm with no ucall data */
+
+static inline void ucall_arch_init(struct kvm_vm *vm, gpa_t mmio_gpa)
+{
+}
+
+static inline void ucall_arch_do_ucall(gva_t uc)
+{
+	hcall2(H_UCALL, UCALL_R4_UCALL, (uintptr_t)(uc));
+}
+
+#endif
tools/testing/selftests/kvm/lib/guest_modes.c +18 -2
index 7a96c43b5704..439766fad693 100644
@@ -4,16 +4,20 @@
  */
 #include "guest_modes.h"
 
-#if defined(__aarch64__) || defined(__riscv)
+#if defined(__aarch64__) || defined(__riscv) || defined(__powerpc64__)
 #include "processor.h"
 enum vm_guest_mode vm_mode_default;
 #endif
 
+#if defined(__powerpc64__)
+#include <unistd.h>
+#endif
+
 struct guest_mode guest_modes[NUM_VM_MODES];
 
 void guest_modes_append_default(void)
 {
-#if !defined(__aarch64__) && !defined(__riscv)
+#if !defined(__aarch64__) && !defined(__riscv) && !defined(__powerpc64__)
 	guest_mode_append(VM_MODE_DEFAULT, true);
 #endif
 
@@ -108,6 +112,18 @@ void guest_modes_append_default(void)
 		TEST_ASSERT(vm_mode_default != NUM_VM_MODES, "No supported mode!");
 	}
 #endif
+#ifdef __powerpc64__
+	{
+		TEST_REQUIRE(kvm_has_cap(KVM_CAP_PPC_MMU_RADIX));
+		/* Radix guest EA and RA are 52-bit on POWER9 and POWER10 */
+		if (sysconf(_SC_PAGESIZE) == 4096)
+			vm_mode_default = VM_MODE_P52V52_4K;
+		else
+			vm_mode_default = VM_MODE_P52V52_64K;
+		guest_mode_append(VM_MODE_P52V52_4K, true);
+		guest_mode_append(VM_MODE_P52V52_64K, true);
+	}
+#endif
 }
 
 void for_each_guest_mode(void (*func)(enum vm_guest_mode, void *), void *arg)
tools/testing/selftests/kvm/lib/kvm_util.c +8 -0
index cdb004c9ba56..0dc67c1502cf 100644
@@ -218,6 +218,8 @@ const char *vm_guest_mode_string(u32 i)
 		[VM_MODE_P41V57_4K]	= "PA-bits:41,  VA-bits:57,  4K pages",
 		[VM_MODE_P41V48_4K]	= "PA-bits:41,  VA-bits:48,  4K pages",
 		[VM_MODE_P41V39_4K]	= "PA-bits:41,  VA-bits:39,  4K pages",
+		[VM_MODE_P52V52_4K]	= "PA-bits:52,  VA-bits:52,  4K pages",
+		[VM_MODE_P52V52_64K]	= "PA-bits:52,  VA-bits:52, 64K pages",
 	};
 	_Static_assert(sizeof(strings)/sizeof(char *) == NUM_VM_MODES,
 		       "Missing new mode strings?");
@@ -254,6 +256,8 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = {
 	[VM_MODE_P41V57_4K]	= { 41, 57,  0x1000, 12 },
 	[VM_MODE_P41V48_4K]	= { 41, 48,  0x1000, 12 },
 	[VM_MODE_P41V39_4K]	= { 41, 39,  0x1000, 12 },
+	[VM_MODE_P52V52_4K]	= { 52, 52,  0x1000, 12 },
+	[VM_MODE_P52V52_64K]	= { 52, 52, 0x10000, 16 },
 };
 _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES,
 	       "Missing new mode params?");
@@ -371,6 +375,10 @@ struct kvm_vm *____vm_create(struct vm_shape shape)
 	case VM_MODE_P41V39_4K:
 		vm->mmu.pgtable_levels = 3;
 		break;
+	case VM_MODE_P52V52_4K:
+	case VM_MODE_P52V52_64K:
+		vm->mmu.pgtable_levels = 4;
+		break;
 	default:
 		TEST_FAIL("Unknown guest mode: 0x%x", vm->mode);
 	}
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +93 -0

93 lines not on this page · raw message

tools/testing/selftests/kvm/lib/powerpc/hcall.c new file +45 -0

45 lines not on this page · raw message

tools/testing/selftests/kvm/lib/powerpc/processor.c new file +484 -0

484 lines not on this page · raw message

tools/testing/selftests/kvm/lib/powerpc/ucall.c new file +22 -0
new file mode 100644
index 000000000000..3481a7a0b850
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ucall support. A ucall is a "hypercall to host userspace".
+ */
+#include "kvm_util.h"
+#include "ucall_common.h"
+#include "hcall.h"
+
+void *ucall_arch_get_ucall(struct kvm_vcpu *vcpu)
+{
+	struct kvm_run *run = vcpu->run;
+
+	if (run->exit_reason == UCALL_EXIT_REASON &&
+	    run->papr_hcall.nr == H_UCALL) {
+		struct kvm_regs regs;
+
+		vcpu_regs_get(vcpu, &regs);
+		if (regs.gpr[4] == UCALL_R4_UCALL)
+			return (void *)regs.gpr[5];
+	}
+	return NULL;
+}
Signature · 1 line
2.39.5
#4 lore raw

Re: [RFC v2 3/5] KVM: PPC: selftests: add support for powerpc

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] Guest registers r1 (stack pointer) and r2 (TOC pointer) are not saved to or restored from…

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] Guest registers r1 (stack pointer) and r2 (TOC pointer) are not saved to or restored from ex_regs during exception handling.
- [Medium] Overly restrictive PTE assertions in addr_arch_gva2gpa() prevent address translation for non-RWX pages. --

commit b672003494da996fee181637da999ccf8c28f3e8
Author: Nicholas Piggin <[email protected]>
Subject: KVM: PPC: selftests: add support for powerpc

This commit implements KVM selftests support for powerpc (Book3S-64). It includes virtual memory support for the radix MMU, ucall implementation via PAPR hcalls, and real-mode guest interrupt handling.

Quoted 4 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +0 -0
new file mode 100644

[ ... ]

Quoted 13 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +13 -0
+virt_handle_interrupt:
+	stdu	%r1,-INT_FRAME_SIZE(%r1)
+	mr	%r3,%r31
+	bl	route_interrupt
+	ld	%r4,(32*8)(%r31) /* NIA */
+	ld	%r5,(33*8)(%r31) /* MSR */
+	ld	%r6,(35*8)(%r31) /* LR */
+	ld	%r7,(36*8)(%r31) /* CTR */
+	ld	%r8,(37*8)(%r31) /* XER */
+	lwz	%r9,(38*8)(%r31) /* CR */
+	mtspr	SPR_SRR0,%r4
+	mtspr	SPR_SRR1,%r5
+	mtlr	%r6

tools/testing/selftests/kvm/lib/powerpc/handlers.S line 58

Quoted 10 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +10 -0
+	mtctr	%r7
+	mtxer	%r8
+	mtcr	%r9
+reg=4
+	ld	%r0,(0*8)(%r31)
+	ld	%r3,(3*8)(%r31)
+.rept 28
+	ld	reg,(reg*8)(%r31)
+	reg=reg+1
+.endr

[Severity: Medium]
Are r1 (stack pointer) and r2 (TOC pointer) missing from the restore sequence?

Since they aren't restored here, tests cannot modify the stack pointer during exception handling.

[ ... ]

tools/testing/selftests/kvm/lib/powerpc/handlers.S line 70

Quoted 6 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +6 -0
+handle_interrupt:
+reg=4
+.rept 28
+	std	reg,(reg*8)(%r13)
+	reg=reg+1
+.endr

[Severity: Medium]
Does this sequence skip saving r1 and r2 into ex_regs?

If they are skipped, the C handlers receive uninitialized values for these registers in ex_regs->gprs[1] and gprs[2], meaning they cannot inspect the stack pointer or TOC pointer at the time of the exception.

Quoted 4 lines
tools/testing/selftests/kvm/lib/powerpc/processor.c new file +0 -0
new file mode 100644

[ ... ]

Quoted 2 lines
tools/testing/selftests/kvm/lib/powerpc/processor.c new file +2 -0
+gpa_t addr_arch_gva2gpa(struct kvm_vm *vm, gva_t gva)
+{

[ ... ]

tools/testing/selftests/kvm/lib/powerpc/processor.c line 254

Quoted 11 lines
tools/testing/selftests/kvm/lib/powerpc/processor.c new file +11 -0
+	ptep = virt_get_pte(vm, pt, gva, level, NULL);
+	pte = be64_to_cpu(*ptep);
+
+	TEST_ASSERT(pte,
+		"PTE not present at level: %u gva: 0x%lx pte:0x%lx\n",
+		level, gva, pte);
+
+	TEST_ASSERT((pte & PTE_VALID) && (pte & PTE_LEAF) &&
+		    (pte & PTE_READ) && (pte & PTE_RW) && (pte & PTE_EXEC),
+		    "PTE not valid at level: %u gva: 0x%lx pte:0x%lx\n",
+		    level, gva, pte);

[Severity: Medium]
Is it possible for tests to map memory without write or execute permissions (e.g., read-only or non-executable pages)?

If so, this assertion will falsely trigger and abort the test during virtual to physical address translation. Should this check only verify PTE_VALID and PTE_LEAF to support testing diverse page permissions?

Quoted 3 lines
tools/testing/selftests/kvm/lib/powerpc/processor.c new file +3 -0
+
+	return (pte & PTE_PAGE_MASK) + (gva & (vm->page_size - 1));
+}
Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=3
#5 lore raw

Re: [RFC v2 3/5] KVM: PPC: selftests: add support for powerpc

Quoted 32 lines · [email protected] writes:
Quoted 4 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +0 -0
new file mode 100644

[ ... ]

Quoted 13 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +13 -0
+virt_handle_interrupt:
+	stdu	%r1,-INT_FRAME_SIZE(%r1)
+	mr	%r3,%r31
+	bl	route_interrupt
+	ld	%r4,(32*8)(%r31) /* NIA */
+	ld	%r5,(33*8)(%r31) /* MSR */
+	ld	%r6,(35*8)(%r31) /* LR */
+	ld	%r7,(36*8)(%r31) /* CTR */
+	ld	%r8,(37*8)(%r31) /* XER */
+	lwz	%r9,(38*8)(%r31) /* CR */
+	mtspr	SPR_SRR0,%r4
+	mtspr	SPR_SRR1,%r5
+	mtlr	%r6

tools/testing/selftests/kvm/lib/powerpc/handlers.S line 58

Quoted 10 lines
tools/testing/selftests/kvm/lib/powerpc/handlers.S new file +10 -0
+	mtctr	%r7
+	mtxer	%r8
+	mtcr	%r9
+reg=4
+	ld	%r0,(0*8)(%r31)
+	ld	%r3,(3*8)(%r31)
+.rept 28
+	ld	reg,(reg*8)(%r31)
+	reg=reg+1
+.endr

[Severity: Medium]
Are r1 (stack pointer) and r2 (TOC pointer) missing from the restore sequence?

Quoted 3 lines

Since they aren't restored here, tests cannot modify the stack pointer during exception handling.

The guest exception handling carefully create and destroy the stack frame during exception handling for running C code.
Also LIBKVM is directly linked in as object files directly into each test binary, so the call to rout_interrupt() is the same TOC call.

So it's not missing, but we really don't need save/restore of r1 & r2 here.

Quoted 9 lines

[Severity: Medium]
Is it possible for tests to map memory without write or execute permissions (e.g., read-only or non-executable pages)?

If so, this assertion will falsely trigger and abort the test during virtual to physical address translation. Should this check only verify PTE_VALID and PTE_LEAF to support testing diverse page permissions?

Yup, that is something which can be relaxed I guess. Will do that in v3.

-ritesh

#6 lore raw

RFC v2 4/5 KVM: PPC: selftests: powerpc enable kvm_create_max_vcpus test

In reply to Ritesh Harjani (IBM)

From: Nicholas Piggin <[email protected]>

powerpc's maximum permitted vCPU ID depends on the VM's SMT mode, and the maximum reported by KVM_CAP_MAX_VCPU_ID exceeds a simple non-SMT VM's limit.

The powerpc KVM selftest port uses non-SMT VMs, so add a workaround to the kvm_create_max_vcpus test case to limit vCPU IDs to KVM_CAP_MAX_VCPUS on powerpc.

[Rebased to latest mainline tree]
---
 tools/testing/selftests/kvm/kvm_create_max_vcpus.c | 9 +++++++++
 1 file changed, 9 insertions(+)
tools/testing/selftests/kvm/kvm_create_max_vcpus.c +9 -0
index c5310736ed06..a82c13d6cdf5 100644
@@ -56,6 +56,15 @@ int main(int argc, char *argv[])
 		    "KVM_MAX_VCPU_IDS (%d) must be at least as large as KVM_MAX_VCPUS (%d).",
 		    kvm_max_vcpu_id, kvm_max_vcpus);
 
+#ifdef __powerpc64__
+	/*
+	 * powerpc has a particular format for the vcpu ID that depends on
+	 * the guest SMT mode, and the max ID cap is too large for non-SMT
+	 * modes, where the maximum ID is the same as the maximum vCPUs.
+	 */
+	kvm_max_vcpu_id = kvm_max_vcpus;
+#endif
+
 	test_vcpu_creation(0, kvm_max_vcpus);
 
 	if (kvm_max_vcpu_id > kvm_max_vcpus)

--
2.39.5

#7 lore raw

RFC v2 5/5 KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

In reply to Ritesh Harjani (IBM)

Print the vcpu_id and errno when the KVM_CREATE_VCPU ioctl fails, for debug purposes.
This helped in debugging an issue with KVM on PowerVM, where KVM_CREATE_VCPU only supports max 2048 vcpus, because the PAPR_HCALL H_GUEST_CREATE_VCPU ("Documentation/arch/powerpc/kvm-nested.rst") supports only up to 0-2047 vcpu_id. However KVM_CAP_MAX_VCPUS capability extension always reports max_vcpus as NR_CPUS of the host.

---
 tools/testing/selftests/kvm/lib/kvm_util.c | 3 +++
 1 file changed, 3 insertions(+)
tools/testing/selftests/kvm/lib/kvm_util.c +3 -0
index 0dc67c1502cf..6a0ee6803623 100644
@@ -1354,6 +1354,9 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
 	vcpu->vm = vm;
 	vcpu->id = vcpu_id;
 	vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id);
+	if (vcpu->fd < 0)
+		pr_info("Failed KVM_CREATE_VCPU for vcpu_id %u with errno %d\n",
+			vcpu_id, errno);
 	TEST_ASSERT_VM_VCPU_IOCTL(vcpu->fd >= 0, KVM_CREATE_VCPU, vcpu->fd, vm);
 
 	TEST_ASSERT(vcpu_mmap_sz() >= sizeof(*vcpu->run), "vcpu mmap size "

--
2.39.5

#8 lore raw

Re: [RFC v2 5/5] KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] The added `pr_info()` call clobbers the global `errno` variable before it can be read by the…

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The added pr_info() call clobbers the global errno variable before it can be read by the TEST_ASSERT_VM_VCPU_IOCTL() macro. --

commit 9022e7c111e4121ff5a4ed4a90dcbfd2df0a62cd
Author: Ritesh Harjani (IBM) <[email protected]>

KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

This commit adds a debug print statement to display the vcpu_id and errno when the KVM_CREATE_VCPU ioctl fails.

tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) · line 1360

Quoted 12 lines
tools/testing/selftests/kvm/lib/kvm_util.c +3 -0
index 0dc67c1502cfb..6a0ee68036236 100644
@@ -1354,6 +1354,9 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
 	vcpu->vm = vm;
 	vcpu->id = vcpu_id;
 	vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id);
+	if (vcpu->fd < 0)
+		pr_info("Failed KVM_CREATE_VCPU for vcpu_id %u with errno %d\n",
+			vcpu_id, errno);
 	TEST_ASSERT_VM_VCPU_IOCTL(vcpu->fd >= 0, KVM_CREATE_VCPU, vcpu->fd, vm);

[Severity: Medium]
Does this pr_info() call clobber errno before it can be evaluated by TEST_ASSERT_VM_VCPU_IOCTL()?

Since pr_info() uses standard C library print functions, it can modify errno internally even on success. TEST_ASSERT_VM_VCPU_IOCTL() captures errno to detect specific failure modes and to format the assertion error message.

Could this cause the test framework to print an incorrect error code or bypass checks like dead VM detection when KVM_CREATE_VCPU fails?

Signature · 1 line
Sashiko AI review · https://sashiko.dev/#/patchset/[email protected]?part=5
#9 lore raw

Re: [RFC v2 5/5] KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

Quoted 29 lines · [email protected] writes:

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] The added pr_info() call clobbers the global errno variable before it can be read by the TEST_ASSERT_VM_VCPU_IOCTL() macro. --

commit 9022e7c111e4121ff5a4ed4a90dcbfd2df0a62cd
Author: Ritesh Harjani (IBM) <[email protected]>

KVM: selftests: Print the vcpu_id when KVM_CREATE_VCPU ioctl fails

This commit adds a debug print statement to display the vcpu_id and errno when the KVM_CREATE_VCPU ioctl fails.

tools/testing/selftests/kvm/lib/kvm_util.c struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id) · line 1360

Quoted 12 lines
tools/testing/selftests/kvm/lib/kvm_util.c +3 -0
index 0dc67c1502cfb..6a0ee68036236 100644
@@ -1354,6 +1354,9 @@ struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, u32 vcpu_id)
 	vcpu->vm = vm;
 	vcpu->id = vcpu_id;
 	vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id);
+	if (vcpu->fd < 0)
+		pr_info("Failed KVM_CREATE_VCPU for vcpu_id %u with errno %d\n",
+			vcpu_id, errno);
 	TEST_ASSERT_VM_VCPU_IOCTL(vcpu->fd >= 0, KVM_CREATE_VCPU, vcpu->fd, vm);

[Severity: Medium]
Does this pr_info() call clobber errno before it can be evaluated by TEST_ASSERT_VM_VCPU_IOCTL()?

Quoted 7 lines

Since pr_info() uses standard C library print functions, it can modify errno internally even on success. TEST_ASSERT_VM_VCPU_IOCTL() captures errno to detect specific failure modes and to format the assertion error message.

Could this cause the test framework to print an incorrect error code or bypass checks like dead VM detection when KVM_CREATE_VCPU fails?

yup, I will fix this in v3.. Something like this -

+ if (vcpu->fd < 0) {
+ int saved_errno = errno;
+ pr_info("Failed KVM_CREATE_VCPU for vcpu_id %u with errno %d\n",
+ vcpu_id, saved_errno);
+ errno = saved_errno;
+ }

Also I noticed that all other KVM selftests have moved from

uint64_t -> u64
uint32_t -> u32
uint8_t -> u8
int64_t -> s64
...

So, I will add separate patches on top of the current patch similar to how it was done earlier to convert powerpc kvm selftests as well - to keep the review simpler w/o mixing too many changes in v3.

-ritesh