linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [kvm-unit-tests PATCH 00/32] powerpc improvements
@ 2024-02-26 10:11 Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts Nicholas Piggin
                   ` (31 more replies)
  0 siblings, 32 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This is the same familiar set of powerpc patches, it's taken a
while to get them merged. And unfortunately they keep growing
with fixes and new tests.

This goes on top of the previous migration and other core
patches. Full tree can be found here.

https://gitlab.com/npiggin/kvm-unit-tests/-/tree/powerpc?ref_type=heads

I have tried to test all combinations of KVM and TCG for P8/9/10
and powernv. There are a small number of failures, some due to
bugs in TCG, QEMU, and KVM. I don't want to drop such test cases
but xfail doesn't quite work because it will start failing if we
fix implementation to now work AFAIKS. If there is concern about
that, then maybe we could add a known-failure type of report that
can document the reason and not fail the entire group of tests.

Since last posting, this adds several fixes to the start of the
series, fixes a bunch of bugs in the SPRs and other tests that
Joel and Thomas raised.  Tidied up the new new SMP support and
fixed a couple of issues there. Added MMU, usermode support,
add atomics, timebase, PMU tests, and removes the ppc64
subdirectories.

Thanks,
Nick

Nicholas Piggin (32):
  powerpc: Fix KVM caps on POWER9 hosts
  powerpc: Fix pseries getchar return value
  powerpc: Fix stack backtrace termination
  powerpc: interrupt stack backtracing
  powerpc: Cleanup SPR and MSR definitions
  powerpc/sprs: Specify SPRs with data rather than code
  powerpc/sprs: Don't fail changed SPRs that are used by the test
    harness
  powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing
  scripts: allow machine option to be specified in unittests.cfg
  scripts: Accommodate powerpc powernv machine differences
  powerpc: Support powernv machine with QEMU TCG
  powerpc: Fix emulator illegal instruction test for powernv
  powerpc/sprs: Test hypervisor registers on powernv machine
  powerpc: general interrupt tests
  powerpc: Add rtas stop-self support
  powerpc: Remove broken SMP exception stack setup
  arch-run: Fix handling multiple exit status messages
  powerpc: add SMP and IPI support
  powerpc: Permit ACCEL=tcg,thread=single
  powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is
    running
  powerpc: Add atomics tests
  powerpc: Add timebase tests
  powerpc: Add MMU support
  common/sieve: Use vmalloc.h for setup_mmu definition
  common/sieve: Support machines without MMU
  powerpc: Add sieve.c common test
  powerpc: add usermode support
  powerpc: add pmu tests
  configure: Fail on unknown arch
  configure: Make arch_libdir a first-class entity
  powerpc: Remove remnants of ppc64 directory and build structure
  powerpc: gitlab CI update

 .gitlab-ci.yml                           |  16 +-
 MAINTAINERS                              |   1 -
 Makefile                                 |   2 +-
 common/sieve.c                           |  15 +-
 configure                                |  69 ++-
 lib/libcflat.h                           |   2 -
 lib/{ppc64 => powerpc}/asm-offsets.c     |   7 +
 lib/{ppc64 => powerpc}/asm/asm-offsets.h |   0
 lib/powerpc/asm/atomic.h                 |   6 +
 lib/powerpc/asm/barrier.h                |  12 +
 lib/{ppc64 => powerpc}/asm/bitops.h      |   4 +-
 lib/powerpc/asm/hcall.h                  |   6 +
 lib/{ppc64 => powerpc}/asm/io.h          |   4 +-
 lib/powerpc/asm/mmu.h                    |  10 +
 lib/powerpc/asm/opal.h                   |  22 +
 lib/powerpc/asm/page.h                   |  66 +++
 lib/powerpc/asm/pgtable-hwdef.h          |  67 +++
 lib/powerpc/asm/pgtable.h                | 126 +++++
 lib/powerpc/asm/ppc_asm.h                |   8 +-
 lib/powerpc/asm/processor.h              |  68 ++-
 lib/{ppc64 => powerpc}/asm/ptrace.h      |  22 +-
 lib/powerpc/asm/reg.h                    |  72 +++
 lib/powerpc/asm/rtas.h                   |   2 +
 lib/powerpc/asm/setup.h                  |   3 +-
 lib/powerpc/asm/smp.h                    |  50 +-
 lib/powerpc/asm/spinlock.h               |  11 +
 lib/powerpc/asm/stack.h                  |   3 +
 lib/powerpc/asm/time.h                   |   1 +
 lib/{ppc64 => powerpc}/asm/vpa.h         |   0
 lib/powerpc/hcall.c                      |   6 +-
 lib/powerpc/io.c                         |  41 +-
 lib/powerpc/io.h                         |   6 +
 lib/powerpc/mmu.c                        | 275 +++++++++
 lib/powerpc/opal-calls.S                 |  50 ++
 lib/powerpc/opal.c                       |  76 +++
 lib/powerpc/processor.c                  |  91 ++-
 lib/powerpc/rtas.c                       |  81 ++-
 lib/powerpc/setup.c                      | 159 +++++-
 lib/powerpc/smp.c                        | 287 ++++++++--
 lib/powerpc/spinlock.c                   |  32 ++
 lib/powerpc/stack.c                      |  55 ++
 lib/ppc64/.gitignore                     |   1 -
 lib/ppc64/asm/barrier.h                  |   9 -
 lib/ppc64/asm/handlers.h                 |   1 -
 lib/ppc64/asm/hcall.h                    |   1 -
 lib/ppc64/asm/memory_areas.h             |   6 -
 lib/ppc64/asm/page.h                     |   1 -
 lib/ppc64/asm/ppc_asm.h                  |   1 -
 lib/ppc64/asm/processor.h                |   1 -
 lib/ppc64/asm/rtas.h                     |   1 -
 lib/ppc64/asm/setup.h                    |   1 -
 lib/ppc64/asm/smp.h                      |   1 -
 lib/ppc64/asm/spinlock.h                 |   6 -
 lib/ppc64/asm/stack.h                    |   8 -
 lib/s390x/io.c                           |   1 +
 lib/s390x/uv.h                           |   1 +
 lib/vmalloc.c                            |   7 +
 lib/vmalloc.h                            |   2 +
 lib/x86/vm.h                             |   1 +
 powerpc/Makefile                         | 111 +++-
 powerpc/Makefile.common                  |  86 ---
 powerpc/Makefile.ppc64                   |  27 -
 powerpc/atomics.c                        | 373 +++++++++++++
 powerpc/cstart64.S                       |  78 ++-
 powerpc/emulator.c                       |  21 +-
 powerpc/interrupts.c                     | 517 +++++++++++++++++
 powerpc/pmu.c                            | 337 +++++++++++
 powerpc/run                              |  42 +-
 powerpc/selftest.c                       |   4 +-
 powerpc/sieve.c                          |   1 +
 powerpc/smp.c                            | 349 ++++++++++++
 powerpc/sprs.c                           | 678 ++++++++++++++++-------
 powerpc/timebase.c                       | 330 +++++++++++
 powerpc/tm.c                             |   4 +-
 powerpc/unittests.cfg                    |  63 ++-
 s390x/mvpg.c                             |   1 +
 s390x/selftest.c                         |   1 +
 scripts/arch-run.bash                    |   2 +-
 scripts/common.bash                      |   8 +-
 scripts/runtime.bash                     |  20 +-
 x86/pmu.c                                |   1 +
 x86/pmu_lbr.c                            |   1 +
 x86/vmexit.c                             |   1 +
 x86/vmware_backdoors.c                   |   1 +
 84 files changed, 4400 insertions(+), 541 deletions(-)
 rename lib/{ppc64 => powerpc}/asm-offsets.c (94%)
 rename lib/{ppc64 => powerpc}/asm/asm-offsets.h (100%)
 create mode 100644 lib/powerpc/asm/atomic.h
 create mode 100644 lib/powerpc/asm/barrier.h
 rename lib/{ppc64 => powerpc}/asm/bitops.h (69%)
 rename lib/{ppc64 => powerpc}/asm/io.h (50%)
 create mode 100644 lib/powerpc/asm/mmu.h
 create mode 100644 lib/powerpc/asm/opal.h
 create mode 100644 lib/powerpc/asm/page.h
 create mode 100644 lib/powerpc/asm/pgtable-hwdef.h
 create mode 100644 lib/powerpc/asm/pgtable.h
 rename lib/{ppc64 => powerpc}/asm/ptrace.h (59%)
 create mode 100644 lib/powerpc/asm/reg.h
 create mode 100644 lib/powerpc/asm/spinlock.h
 rename lib/{ppc64 => powerpc}/asm/vpa.h (100%)
 create mode 100644 lib/powerpc/mmu.c
 create mode 100644 lib/powerpc/opal-calls.S
 create mode 100644 lib/powerpc/opal.c
 create mode 100644 lib/powerpc/spinlock.c
 create mode 100644 lib/powerpc/stack.c
 delete mode 100644 lib/ppc64/.gitignore
 delete mode 100644 lib/ppc64/asm/barrier.h
 delete mode 100644 lib/ppc64/asm/handlers.h
 delete mode 100644 lib/ppc64/asm/hcall.h
 delete mode 100644 lib/ppc64/asm/memory_areas.h
 delete mode 100644 lib/ppc64/asm/page.h
 delete mode 100644 lib/ppc64/asm/ppc_asm.h
 delete mode 100644 lib/ppc64/asm/processor.h
 delete mode 100644 lib/ppc64/asm/rtas.h
 delete mode 100644 lib/ppc64/asm/setup.h
 delete mode 100644 lib/ppc64/asm/smp.h
 delete mode 100644 lib/ppc64/asm/spinlock.h
 delete mode 100644 lib/ppc64/asm/stack.h
 delete mode 100644 powerpc/Makefile.common
 delete mode 100644 powerpc/Makefile.ppc64
 create mode 100644 powerpc/atomics.c
 create mode 100644 powerpc/interrupts.c
 create mode 100644 powerpc/pmu.c
 create mode 120000 powerpc/sieve.c
 create mode 100644 powerpc/smp.c
 create mode 100644 powerpc/timebase.c

-- 
2.42.0


^ permalink raw reply	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-26 11:33   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value Nicholas Piggin
                   ` (30 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

KVM does not like to run on POWER9 hosts without cap-ccf-assist=off.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/run | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/powerpc/run b/powerpc/run
index e469f1eb3..5cdb94194 100755
--- a/powerpc/run
+++ b/powerpc/run
@@ -24,6 +24,8 @@ M+=",accel=$ACCEL$ACCEL_PROPS"
 
 if [[ "$ACCEL" == "tcg" ]] ; then
 	M+=",cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off"
+elif [[ "$ACCEL" == "kvm" ]] ; then
+	M+=",cap-ccf-assist=off"
 fi
 
 command="$qemu -nodefaults $M -bios $FIRMWARE"
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-26 11:35   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination Nicholas Piggin
                   ` (29 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

getchar() didn't get the shift value correct and never returned the
first character. This never really mattered since it was only ever
used for press-a-key-to-continue prompts. but it tripped me up when
debugging a QEMU console output problem.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/hcall.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/powerpc/hcall.c b/lib/powerpc/hcall.c
index 711cb1b0f..b4d39ac65 100644
--- a/lib/powerpc/hcall.c
+++ b/lib/powerpc/hcall.c
@@ -43,5 +43,5 @@ int __getchar(void)
 	asm volatile (" sc 1 "  : "+r"(r3), "+r"(r4), "=r"(r5)
 				: "r"(r3),  "r"(r4));
 
-	return r3 == H_SUCCESS && r4 > 0 ? r5 >> 48 : -1;
+	return r3 == H_SUCCESS && r4 > 0 ? r5 >> 56 : -1;
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-27  8:50   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing Nicholas Piggin
                   ` (28 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

The backtrace handler terminates when it sees a NULL caller address,
but the powerpc stack setup does not keep such a NULL caller frame
at the start of the stack.

This happens to work on pseries because the memory at 0 is mapped and
it contains 0 at the location of the return address pointer if it
were a stack frame. But this is fragile, and does not work with powernv
where address 0 contains firmware instructions.

Use the existing dummy frame on stack as the NULL caller, and create a
new frame on stack for the entry code.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/cstart64.S | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
index e18ae9a22..14ab0c6c8 100644
--- a/powerpc/cstart64.S
+++ b/powerpc/cstart64.S
@@ -46,8 +46,16 @@ start:
 	add	r1, r1, r31
 	add	r2, r2, r31
 
+	/* Zero backpointers in initial stack frame so backtrace() stops */
+	li	r0,0
+	std	r0,0(r1)
+	std	r0,16(r1)
+
+	/* Create entry frame */
+	stdu	r1,-INT_FRAME_SIZE(r1)
+
 	/* save DTB pointer */
-	std	r3, 56(r1)
+	SAVE_GPR(3,r1)
 
 	/*
 	 * Call relocate. relocate is C code, but careful to not use
@@ -101,7 +109,7 @@ start:
 	stw	r4, 0(r3)
 
 	/* complete setup */
-1:	ld	r3, 56(r1)
+1:	REST_GPR(3, r1)
 	bl	setup
 
 	/* run the test */
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (2 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-28 11:46   ` Andrew Jones
  2024-03-01  9:53   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions Nicholas Piggin
                   ` (27 subsequent siblings)
  31 siblings, 2 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Add support for backtracing across interrupt stacks, and
add interrupt frame backtrace for unhandled interrupts.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/processor.c |  4 ++-
 lib/ppc64/asm/stack.h   |  3 +++
 lib/ppc64/stack.c       | 55 +++++++++++++++++++++++++++++++++++++++++
 powerpc/Makefile.ppc64  |  1 +
 powerpc/cstart64.S      |  7 ++++--
 5 files changed, 67 insertions(+), 3 deletions(-)
 create mode 100644 lib/ppc64/stack.c

diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
index ad0d95666..114584024 100644
--- a/lib/powerpc/processor.c
+++ b/lib/powerpc/processor.c
@@ -51,7 +51,9 @@ void do_handle_exception(struct pt_regs *regs)
 		return;
 	}
 
-	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
+	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
+			regs->trap, regs->nip, regs->msr);
+	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
 	abort();
 }
 
diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
index 9734bbb8f..94fd1021c 100644
--- a/lib/ppc64/asm/stack.h
+++ b/lib/ppc64/asm/stack.h
@@ -5,4 +5,7 @@
 #error Do not directly include <asm/stack.h>. Just use <stack.h>.
 #endif
 
+#define HAVE_ARCH_BACKTRACE
+#define HAVE_ARCH_BACKTRACE_FRAME
+
 #endif
diff --git a/lib/ppc64/stack.c b/lib/ppc64/stack.c
new file mode 100644
index 000000000..fcb7fa860
--- /dev/null
+++ b/lib/ppc64/stack.c
@@ -0,0 +1,55 @@
+#include <libcflat.h>
+#include <asm/ptrace.h>
+#include <stack.h>
+
+extern char exception_stack_marker[];
+
+int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
+{
+	static int walking;
+	int depth = 0;
+	const unsigned long *bp = (unsigned long *)frame;
+	void *return_addr;
+
+	asm volatile("" ::: "lr"); /* Force it to save LR */
+
+	if (walking) {
+		printf("RECURSIVE STACK WALK!!!\n");
+		return 0;
+	}
+	walking = 1;
+
+	bp = (unsigned long *)bp[0];
+	return_addr = (void *)bp[2];
+
+	for (depth = 0; bp && depth < max_depth; depth++) {
+		return_addrs[depth] = return_addr;
+		if (return_addrs[depth] == 0)
+			break;
+		if (return_addrs[depth] == exception_stack_marker) {
+			struct pt_regs *regs;
+
+			regs = (void *)bp + STACK_FRAME_OVERHEAD;
+			bp = (unsigned long *)bp[0];
+			/* Represent interrupt frame with vector number */
+			return_addr = (void *)regs->trap;
+			if (depth + 1 < max_depth) {
+				depth++;
+				return_addrs[depth] = return_addr;
+				return_addr = (void *)regs->nip;
+			}
+		} else {
+			bp = (unsigned long *)bp[0];
+			return_addr = (void *)bp[2];
+		}
+	}
+
+	walking = 0;
+	return depth;
+}
+
+int backtrace(const void **return_addrs, int max_depth)
+{
+	return backtrace_frame(__builtin_frame_address(0), return_addrs,
+			       max_depth);
+}
diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
index b0ed2b104..eb682c226 100644
--- a/powerpc/Makefile.ppc64
+++ b/powerpc/Makefile.ppc64
@@ -17,6 +17,7 @@ cstart.o = $(TEST_DIR)/cstart64.o
 reloc.o  = $(TEST_DIR)/reloc64.o
 
 OBJDIRS += lib/ppc64
+cflatobjs += lib/ppc64/stack.o
 
 # ppc64 specific tests
 tests = $(TEST_DIR)/spapr_vpa.elf
diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
index 14ab0c6c8..278af84a6 100644
--- a/powerpc/cstart64.S
+++ b/powerpc/cstart64.S
@@ -188,6 +188,7 @@ call_handler:
 	.endr
 	mfsprg1	r0
 	std	r0,GPR1(r1)
+	std	r0,0(r1)
 
 	/* lr, xer, ccr */
 
@@ -206,12 +207,12 @@ call_handler:
 	subi	r31, r31, 0b - start_text
 	ld	r2, (p_toc_text - start_text)(r31)
 
-	/* FIXME: build stack frame */
-
 	/* call generic handler */
 
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	bl	do_handle_exception
+	.global exception_stack_marker
+exception_stack_marker:
 
 	/* restore context */
 
@@ -321,6 +322,7 @@ handler_trampoline:
 	/* nip and msr */
 	mfsrr0	r0
 	std	r0, _NIP(r1)
+	std	r0, INT_FRAME_SIZE+16(r1)
 
 	mfsrr1	r0
 	std	r0, _MSR(r1)
@@ -337,6 +339,7 @@ handler_htrampoline:
 	/* nip and msr */
 	mfspr	r0, SPR_HSRR0
 	std	r0, _NIP(r1)
+	std	r0, INT_FRAME_SIZE+16(r1)
 
 	mfspr	r0, SPR_HSRR1
 	std	r0, _MSR(r1)
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (3 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-03-01 11:11   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 06/32] powerpc/sprs: Specify SPRs with data rather than code Nicholas Piggin
                   ` (26 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Move SPR and MSR defines out of ppc_asm.h and processor.h and into a
new include, asm/reg.h.

Add a define for the PVR SPR and various processor versions, and replace
the open coded numbers in the sprs.c test case.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/ppc_asm.h   |  8 +-------
 lib/powerpc/asm/processor.h |  7 +------
 lib/powerpc/asm/reg.h       | 30 ++++++++++++++++++++++++++++++
 lib/powerpc/asm/time.h      |  1 +
 lib/ppc64/asm/reg.h         |  1 +
 powerpc/sprs.c              | 21 ++++++++++-----------
 6 files changed, 44 insertions(+), 24 deletions(-)
 create mode 100644 lib/powerpc/asm/reg.h
 create mode 100644 lib/ppc64/asm/reg.h

diff --git a/lib/powerpc/asm/ppc_asm.h b/lib/powerpc/asm/ppc_asm.h
index 46b4be009..52a42dfbe 100644
--- a/lib/powerpc/asm/ppc_asm.h
+++ b/lib/powerpc/asm/ppc_asm.h
@@ -2,6 +2,7 @@
 #define _ASMPOWERPC_PPC_ASM_H
 
 #include <asm/asm-offsets.h>
+#include <asm/reg.h>
 
 #define SAVE_GPR(n, base)	std	n,GPR0+8*(n)(base)
 #define REST_GPR(n, base)	ld	n,GPR0+8*(n)(base)
@@ -35,11 +36,4 @@
 
 #endif /* __BYTE_ORDER__ */
 
-#define SPR_HSRR0	0x13A
-#define SPR_HSRR1	0x13B
-
-/* Machine State Register definitions: */
-#define MSR_EE_BIT	15			/* External Interrupts Enable */
-#define MSR_SF_BIT	63			/* 64-bit mode */
-
 #endif /* _ASMPOWERPC_PPC_ASM_H */
diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index fe1052939..e415f9235 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -3,18 +3,13 @@
 
 #include <libcflat.h>
 #include <asm/ptrace.h>
+#include <asm/reg.h>
 
 #ifndef __ASSEMBLY__
 void handle_exception(int trap, void (*func)(struct pt_regs *, void *), void *);
 void do_handle_exception(struct pt_regs *regs);
 #endif /* __ASSEMBLY__ */
 
-#define SPR_TB		0x10c
-#define SPR_SPRG0	0x110
-#define SPR_SPRG1	0x111
-#define SPR_SPRG2	0x112
-#define SPR_SPRG3	0x113
-
 static inline uint64_t mfspr(int nr)
 {
 	uint64_t ret;
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
new file mode 100644
index 000000000..6810c1d82
--- /dev/null
+++ b/lib/powerpc/asm/reg.h
@@ -0,0 +1,30 @@
+#ifndef _ASMPOWERPC_REG_H
+#define _ASMPOWERPC_REG_H
+
+#include <linux/const.h>
+
+#define UL(x) _AC(x, UL)
+
+#define SPR_TB		0x10c
+#define SPR_SPRG0	0x110
+#define SPR_SPRG1	0x111
+#define SPR_SPRG2	0x112
+#define SPR_SPRG3	0x113
+#define SPR_PVR		0x11f
+#define   PVR_VERSION_MASK	UL(0xffff0000)
+#define   PVR_VER_970		UL(0x00390000)
+#define   PVR_VER_970FX		UL(0x003c0000)
+#define   PVR_VER_970MP		UL(0x00440000)
+#define   PVR_VER_POWER8E	UL(0x004b0000)
+#define   PVR_VER_POWER8NVL	UL(0x004c0000)
+#define   PVR_VER_POWER8	UL(0x004d0000)
+#define   PVR_VER_POWER9	UL(0x004e0000)
+#define   PVR_VER_POWER10	UL(0x00800000)
+#define SPR_HSRR0	0x13a
+#define SPR_HSRR1	0x13b
+
+/* Machine State Register definitions: */
+#define MSR_EE_BIT	15			/* External Interrupts Enable */
+#define MSR_SF_BIT	63			/* 64-bit mode */
+
+#endif
diff --git a/lib/powerpc/asm/time.h b/lib/powerpc/asm/time.h
index 72fcb1bd0..a1f072989 100644
--- a/lib/powerpc/asm/time.h
+++ b/lib/powerpc/asm/time.h
@@ -3,6 +3,7 @@
 
 #include <libcflat.h>
 #include <asm/processor.h>
+#include <asm/reg.h>
 
 static inline uint64_t get_tb(void)
 {
diff --git a/lib/ppc64/asm/reg.h b/lib/ppc64/asm/reg.h
new file mode 100644
index 000000000..bc407b555
--- /dev/null
+++ b/lib/ppc64/asm/reg.h
@@ -0,0 +1 @@
+#include "../../powerpc/asm/reg.h"
diff --git a/powerpc/sprs.c b/powerpc/sprs.c
index 285976488..a19d80a1a 100644
--- a/powerpc/sprs.c
+++ b/powerpc/sprs.c
@@ -23,6 +23,7 @@
 #include <util.h>
 #include <migrate.h>
 #include <alloc.h>
+#include <asm/ppc_asm.h>
 #include <asm/handlers.h>
 #include <asm/hcall.h>
 #include <asm/processor.h>
@@ -120,25 +121,23 @@ static void set_sprs_book3s_31(uint64_t val)
 
 static void set_sprs(uint64_t val)
 {
-	uint32_t pvr = mfspr(287);	/* Processor Version Register */
-
 	set_sprs_common(val);
 
-	switch (pvr >> 16) {
-	case 0x39:			/* PPC970 */
-	case 0x3C:			/* PPC970FX */
-	case 0x44:			/* PPC970MP */
+	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
+	case PVR_VER_970:
+	case PVR_VER_970FX:
+	case PVR_VER_970MP:
 		set_sprs_book3s_201(val);
 		break;
-	case 0x4b:			/* POWER8E */
-	case 0x4c:			/* POWER8NVL */
-	case 0x4d:			/* POWER8 */
+	case PVR_VER_POWER8E:
+	case PVR_VER_POWER8NVL:
+	case PVR_VER_POWER8:
 		set_sprs_book3s_207(val);
 		break;
-	case 0x4e:			/* POWER9 */
+	case PVR_VER_POWER9:
 		set_sprs_book3s_300(val);
 		break;
-	case 0x80:                      /* POWER10 */
+	case PVR_VER_POWER10:
 		set_sprs_book3s_31(val);
 		break;
 	default:
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 06/32] powerpc/sprs: Specify SPRs with data rather than code
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (4 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness Nicholas Piggin
                   ` (25 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

A significant rework that builds an array of 'struct spr', where each
element describes an SPR. This makes various metadata about the SPR
like name and access type easier to carry and use.

Hypervisor privileged registers are described despite not being used
at the moment for completeness, but also the code might one day be
reused for a hypervisor-privileged test.

Acked-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/reg.h |   2 +
 powerpc/sprs.c        | 647 +++++++++++++++++++++++++++++-------------
 2 files changed, 457 insertions(+), 192 deletions(-)

diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 6810c1d82..1f991288e 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -5,6 +5,8 @@
 
 #define UL(x) _AC(x, UL)
 
+#define SPR_SRR0	0x01a
+#define SPR_SRR1	0x01b
 #define SPR_TB		0x10c
 #define SPR_SPRG0	0x110
 #define SPR_SPRG1	0x111
diff --git a/powerpc/sprs.c b/powerpc/sprs.c
index a19d80a1a..8253ea971 100644
--- a/powerpc/sprs.c
+++ b/powerpc/sprs.c
@@ -30,229 +30,458 @@
 #include <asm/time.h>
 #include <asm/barrier.h>
 
-uint64_t before[1024], after[1024];
-
-/* Common SPRs for all PowerPC CPUs */
-static void set_sprs_common(uint64_t val)
+/* "Indirect" mfspr/mtspr which accept a non-constant spr number */
+static uint64_t __mfspr(unsigned spr)
 {
-	mtspr(9, val);		/* CTR */
-	// mtspr(273, val);	/* SPRG1 */  /* Used by our exception handler */
-	mtspr(274, val);	/* SPRG2 */
-	mtspr(275, val);	/* SPRG3 */
+	uint64_t tmp;
+	uint64_t ret;
+
+	asm volatile(
+"	bcl	20, 31, 1f		\n"
+"1:	mflr	%0			\n"
+"	addi	%0, %0, (2f-1b)		\n"
+"	add	%0, %0, %2		\n"
+"	mtctr	%0			\n"
+"	bctr				\n"
+"2:					\n"
+".LSPR=0				\n"
+".rept 1024				\n"
+"	mfspr	%1, .LSPR		\n"
+"	b	3f			\n"
+"	.LSPR=.LSPR+1			\n"
+".endr					\n"
+"3:					\n"
+	: "=&r"(tmp),
+	  "=r"(ret)
+	: "r"(spr*8) /* 8 bytes per 'mfspr ; b' block */
+	: "lr", "ctr");
+
+	return ret;
 }
 
-/* SPRs from PowerPC Operating Environment Architecture, Book III, Vers. 2.01 */
-static void set_sprs_book3s_201(uint64_t val)
+static void __mtspr(unsigned spr, uint64_t val)
 {
-	mtspr(18, val);		/* DSISR */
-	mtspr(19, val);		/* DAR */
-	mtspr(152, val);	/* CTRL */
-	mtspr(256, val);	/* VRSAVE */
-	mtspr(786, val);	/* MMCRA */
-	mtspr(795, val);	/* MMCR0 */
-	mtspr(798, val);	/* MMCR1 */
+	uint64_t tmp;
+
+	asm volatile(
+"	bcl	20, 31, 1f		\n"
+"1:	mflr	%0			\n"
+"	addi	%0, %0, (2f-1b)		\n"
+"	add	%0, %0, %2		\n"
+"	mtctr	%0			\n"
+"	bctr				\n"
+"2:					\n"
+".LSPR=0				\n"
+".rept 1024				\n"
+"	mtspr	.LSPR, %1		\n"
+"	b	3f			\n"
+"	.LSPR=.LSPR+1			\n"
+".endr					\n"
+"3:					\n"
+	: "=&r"(tmp)
+	: "r"(val),
+	  "r"(spr*8) /* 8 bytes per 'mfspr ; b' block */
+	: "lr", "ctr", "xer");
 }
 
+static uint64_t before[1024], after[1024];
+
+#define SPR_PR_READ	0x0001
+#define SPR_PR_WRITE	0x0002
+#define SPR_OS_READ	0x0010
+#define SPR_OS_WRITE	0x0020
+#define SPR_HV_READ	0x0100
+#define SPR_HV_WRITE	0x0200
+
+#define RW		0x333
+#define RO		0x111
+#define WO		0x222
+#define OS_RW		0x330
+#define OS_RO		0x110
+#define OS_WO		0x220
+#define HV_RW		0x300
+#define HV_RO		0x100
+#define HV_WO		0x200
+
+#define SPR_ASYNC	0x1000	/* May be updated asynchronously */
+#define SPR_INT		0x2000	/* May be updated by synchronous interrupt */
+#define SPR_HARNESS	0x4000	/* Test harness uses the register */
+
+struct spr {
+	const char	*name;
+	uint8_t		width;
+	uint16_t	access;
+	uint16_t	type;
+};
+
+/* SPRs common denominator back to PowerPC Operating Environment Architecture */
+static const struct spr sprs_common[1024] = {
+  [1] = { "XER",	64,	RW,		SPR_HARNESS, }, /* Used by compiler */
+  [8] = { "LR", 	64,	RW,		SPR_HARNESS, }, /* Compiler, mfspr/mtspr */
+  [9] = { "CTR",	64,	RW,		SPR_HARNESS, }, /* Compiler, mfspr/mtspr */
+ [18] = { "DSISR",	32,	OS_RW,		SPR_INT, },
+ [19] = { "DAR",	64,	OS_RW,		SPR_INT, },
+ [26] = { "SRR0",	64,	OS_RW,		SPR_INT, },
+ [27] = { "SRR1",	64,	OS_RW,		SPR_INT, },
+[268] = { "TB",		64,	RO	,	SPR_ASYNC, },
+[269] = { "TBU",	32,	RO,		SPR_ASYNC, },
+[272] = { "SPRG0",	64,	OS_RW,		SPR_HARNESS, }, /* Interrupt stacr */
+[273] = { "SPRG1",	64,	OS_RW,		SPR_HARNESS, }, /* Interrupt Scratch */
+[274] = { "SPRG2",	64,	OS_RW, },
+[275] = { "SPRG3",	64,	OS_RW, },
+[287] = { "PVR",	32,	OS_RO, },
+};
+
+/* SPRs from PowerPC Operating Environment Architecture, Book III, Vers. 2.01 */
+static const struct spr sprs_201[1024] = {
+ [22] = { "DEC",	32,	OS_RW,		SPR_ASYNC, },
+ [25] = { "SDR1",	64,	HV_RW | OS_RO, },
+ [29] = { "ACCR",	64,	OS_RW, },
+[136] = { "CTRL",	32,	RO, },
+[152] = { "CTRL",	32,	OS_WO, },
+[259] = { "SPRG3",	64,	RO, },
+/* ASR, EAR omitted */
+[284] = { "TBL",	32,	HV_WO, },
+[285] = { "TBU",	32,	HV_WO, },
+[310] = { "HDEC",	32,	HV_RW,		SPR_ASYNC, },
+[1013]= { "DABR",	64,	HV_RW | OS_RO, },
+[1023]= { "PIR",	32,	OS_RO,		SPR_ASYNC, }, /* Can't be virtualised, appears to be async */
+};
+
+static const struct spr sprs_970_pmu[1024] = {
+/* POWER4+ PMU, should confirm with PPC970 */
+[770] = { "MMCRA",	64,	RO, },
+[771] = { "PMC1",	32,	RO, },
+[772] = { "PMC2",	32,	RO, },
+[773] = { "PMC3",	32,	RO, },
+[774] = { "PMC4",	32,	RO, },
+[775] = { "PMC5",	32,	RO, },
+[776] = { "PMC6",	32,	RO, },
+[777] = { "PMC7",	32,	RO, },
+[778] = { "PMC8",	32,	RO, },
+[779] = { "MMCR0",	64,	RO, },
+[780] = { "SIAR",	64,	RO, },
+[781] = { "SDAR",	64,	RO, },
+[782] = { "MMCR1",	64,	RO, },
+[786] = { "MMCRA",	64,	OS_RW, },
+[787] = { "PMC1",	32,	OS_RW, },
+[788] = { "PMC2",	32,	OS_RW, },
+[789] = { "PMC3",	32,	OS_RW, },
+[790] = { "PMC4",	32,	OS_RW, },
+[791] = { "PMC5",	32,	OS_RW, },
+[792] = { "PMC6",	32,	OS_RW, },
+[793] = { "PMC7",	32,	OS_RW, },
+[794] = { "PMC8",	32,	OS_RW, },
+[795] = { "MMCR0",	64,	OS_RW, },
+[796] = { "SIAR",	64,	OS_RW, },
+[797] = { "SDAR",	64,	OS_RW, },
+[798] = { "MMCR1",	64,	OS_RW, },
+};
+
+/* These are common SPRs from 2.07S onward (POWER CPUs that support KVM HV) */
+static const struct spr sprs_power_common[1024] = {
+  [3] = { "DSCR",	64,	RW, },
+ [13] = { "AMR",	64,	RW, },
+ [17] = { "DSCR",	64,	OS_RW, },
+ [28] = { "CFAR",	64,	OS_RW,		SPR_ASYNC, }, /* Effectively async */
+ [29] = { "AMR",	64,	OS_RW, },
+ [61] = { "IAMR",	64,	OS_RW, },
+[136] = { "CTRL",	32,	RO, },
+[152] = { "CTRL",	32,	OS_WO, },
+[153] = { "FSCR",	64,	OS_RW, },
+[157] = { "UAMOR",	64,	OS_RW, },
+[159] = { "PSPB",	32,	OS_RW, },
+[176] = { "DPDES",	64,	HV_RW | OS_RO, },
+[180] = { "DAWR0",	64,	HV_RW, },
+[186] = { "RPR",	64,	HV_RW, },
+[187] = { "CIABR",	64,	HV_RW, },
+[188] = { "DAWRX0",	32,	HV_RW, },
+[190] = { "HFSCR",	64,	HV_RW, },
+[256] = { "VRSAVE",	32,	RW, },
+[259] = { "SPRG3",	64,	RO, },
+[284] = { "TBL",	32,	HV_WO, },
+[285] = { "TBU",	32,	HV_WO, },
+[286] = { "TBU40",	64,	HV_WO, },
+[304] = { "HSPRG0",	64,	HV_RW, },
+[305] = { "HSPRG1",	64,	HV_RW, },
+[306] = { "HDSISR",	32,	HV_RW,		SPR_INT, },
+[307] = { "HDAR",	64,	HV_RW,		SPR_INT, },
+[308] = { "SPURR",	64,	HV_RW | OS_RO,	SPR_ASYNC, },
+[309] = { "PURR",	64,	HV_RW | OS_RO,	SPR_ASYNC, },
+[313] = { "HRMOR",	64,	HV_RW, },
+[314] = { "HSRR0",	64,	HV_RW,		SPR_INT, },
+[315] = { "HSRR1",	64,	HV_RW,		SPR_INT, },
+[318] = { "LPCR",	64,	HV_RW, },
+[319] = { "LPIDR",	32,	HV_RW, },
+[336] = { "HMER",	64,	HV_RW, },
+[337] = { "HMEER",	64,	HV_RW, },
+[338] = { "PCR",	64,	HV_RW, },
+[349] = { "AMOR",	64,	HV_RW, },
+[446] = { "TIR",	64,	OS_RO, },
+[800] = { "BESCRS",	64,	RW, },
+[801] = { "BESCRSU",	32,	RW, },
+[802] = { "BESCRR",	64,	RW, },
+[803] = { "BESCRRU",	32,	RW, },
+[804] = { "EBBHR",	64,	RW, },
+[805] = { "EBBRR",	64,	RW, },
+[806] = { "BESCR",	64,	RW, },
+[815] = { "TAR",	64,	RW, },
+[848] = { "IC",		64,	HV_RW | OS_RO,	SPR_ASYNC, },
+[849] = { "VTB",	64,	HV_RW | OS_RO,	SPR_ASYNC, },
+[896] = { "PPR",	64,	RW, },
+[898] = { "PPR32",	32,	RW, },
+[1023]= { "PIR",	32,	OS_RO,		SPR_ASYNC, }, /* Can't be virtualised, appears to be async */
+};
+
+static const struct spr sprs_tm[1024] = {
+#if 0
+	/* XXX: leave these out until enabling TM facility (and more testing) */
+[128] = { "TFHAR",	64,	RW, },
+[129] = { "TFIAR",	64,	RW, },
+[130] = { "TEXASR",	64,	RW, },
+[131] = { "TEXASRU",	32,	RW, },
+#endif
+};
+
 /* SPRs from PowerISA 2.07 Book III-S */
-static void set_sprs_book3s_207(uint64_t val)
-{
-	mtspr(3, val);		/* DSCR */
-	mtspr(13, val);		/* AMR */
-	mtspr(17, val);		/* DSCR */
-	mtspr(18, val);		/* DSISR */
-	mtspr(19, val);		/* DAR */
-	mtspr(29, val);		/* AMR */
-	mtspr(61, val);		/* IAMR */
-	// mtspr(152, val);	/* CTRL */  /* TODO: Needs a fix in KVM */
-	mtspr(153, val);	/* FSCR */
-	mtspr(157, val);	/* UAMOR */
-	mtspr(159, val);	/* PSPB */
-	mtspr(256, val);	/* VRSAVE */
-	// mtspr(272, val);	/* SPRG0 */ /* Used by our exception handler */
-	mtspr(769, val);	/* MMCR2 */
-	mtspr(770, val);	/* MMCRA */
-	mtspr(771, val);	/* PMC1 */
-	mtspr(772, val);	/* PMC2 */
-	mtspr(773, val);	/* PMC3 */
-	mtspr(774, val);	/* PMC4 */
-	mtspr(775, val);	/* PMC5 */
-	mtspr(776, val);	/* PMC6 */
-	mtspr(779, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070);	/* MMCR0 */
-	mtspr(784, val);	/* SIER */
-	mtspr(785, val);	/* MMCR2 */
-	mtspr(786, val);	/* MMCRA */
-	mtspr(787, val);	/* PMC1 */
-	mtspr(788, val);	/* PMC2 */
-	mtspr(789, val);	/* PMC3 */
-	mtspr(790, val);	/* PMC4 */
-	mtspr(791, val);	/* PMC5 */
-	mtspr(792, val);	/* PMC6 */
-	mtspr(795, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070);	/* MMCR0 */
-	mtspr(796, val);	/* SIAR */
-	mtspr(797, val);	/* SDAR */
-	mtspr(798, val);	/* MMCR1 */
-	mtspr(800, val);	/* BESCRS */
-	mtspr(801, val);	/* BESCCRSU */
-	mtspr(802, val);	/* BESCRR */
-	mtspr(803, val);	/* BESCRRU */
-	mtspr(804, val);	/* EBBHR */
-	mtspr(805, val);	/* EBBRR */
-	mtspr(806, val);	/* BESCR */
-	mtspr(815, val);	/* TAR */
-}
+static const struct spr sprs_207[1024] = {
+ [22] = { "DEC",	32,	OS_RW,		SPR_ASYNC, },
+ [25] = { "SDR1",	64,	HV_RW, },
+[177] = { "DHDES",	64,	HV_RW, },
+[283] = { "CIR",	32,	OS_RO, },
+[310] = { "HDEC",	32,	HV_RW,		SPR_ASYNC, },
+[312] = { "RMOR",	64,	HV_RW, },
+[339] = { "HEIR",	32,	HV_RW,		SPR_INT, },
+};
 
 /* SPRs from PowerISA 3.00 Book III */
-static void set_sprs_book3s_300(uint64_t val)
-{
-	set_sprs_book3s_207(val);
-	mtspr(48, val);		/* PIDR */
-	mtspr(144, val);	/* TIDR */
-	mtspr(823, val);	/* PSSCR */
-}
+static const struct spr sprs_300[1024] = {
+ [22] = { "DEC",	64,	OS_RW,		SPR_ASYNC, },
+ [48] = { "PIDR",	32,	OS_RW, },
+[144] = { "TIDR",	64,	OS_RW, },
+[283] = { "CIR",	32,	OS_RO, },
+[310] = { "HDEC",	64,	HV_RW,		SPR_ASYNC, },
+[339] = { "HEIR",	32,	HV_RW,		SPR_INT, },
+[464] = { "PTCR",	64,	HV_RW, },
+[816] = { "ASDR",	64,	HV_RW,		SPR_INT, },
+[823] = { "PSSCR",	64,	OS_RW, },
+[855] = { "PSSCR",	64,	HV_RW, },
+};
 
-/* SPRs from Power ISA Version 3.1B */
-static void set_sprs_book3s_31(uint64_t val)
-{
-	set_sprs_book3s_207(val);
-	mtspr(48, val);		/* PIDR */
-	/* 3.1 removes TIDR */
-	mtspr(823, val);	/* PSSCR */
-}
+/* SPRs from PowerISA 3.1B Book III */
+static const struct spr sprs_31[1024] = {
+ [22] = { "DEC",	64,	OS_RW,		SPR_ASYNC, },
+ [48] = { "PIDR",	32,	OS_RW, },
+[181] = { "DAWR1",	64,	HV_RW, },
+[189] = { "DAWRX1",	32,	HV_RW, },
+[310] = { "HDEC",	64,	HV_RW,		SPR_ASYNC, },
+[339] = { "HEIR",	64,	HV_RW,		SPR_INT, },
+[455] = { "HDEXCR",	32,	RO, },
+[464] = { "PTCR",	64,	HV_RW, },
+[468] = { "HASHKEYR",	64,	OS_RW, },
+[469] = { "HASHPKEYR",	64,	HV_RW, },
+[471] = { "HDEXCR",	64,	HV_RW, },
+[812] = { "DEXCR",	32,	RO, },
+[816] = { "ASDR",	64,	HV_RW,		SPR_INT, },
+[823] = { "PSSCR",	64,	OS_RW, },
+[828] = { "DEXCR",	64,	OS_RW, },
+[855] = { "PSSCR",	64,	HV_RW, },
+};
 
-static void set_sprs(uint64_t val)
+/* SPRs POWER9, POWER10 User Manual */
+static const struct spr sprs_power9_10[1024] = {
+[276] = { "SPRC",	64,	HV_RW, },
+[277] = { "SPRD",	64,	HV_RW, },
+[317] = { "TFMR",	64,	HV_RW, },
+[799] = { "IMC",	64,	HV_RW, },
+[850] = { "LDBAR",	64,	HV_RO, },
+[851] = { "MMCRC",	32,	HV_RW, },
+[853] = { "PMSR",	32,	HV_RO, },
+[861] = { "L2QOSR",	64,	HV_WO, },
+[881] = { "TRIG1",	64,	OS_WO, },
+[882] = { "TRIG2",	64,	OS_WO, },
+[884] = { "PMCR",	64,	HV_RW, },
+[885] = { "RWMR",	64,	HV_RW, },
+[895] = { "WORT",	64,	OS_RW, }, /* UM says 18-bits! */
+[921] = { "TSCR",	32,	HV_RW, },
+[922] = { "TTR",	64,	HV_RW, },
+[1006]= { "TRACE",	64,	WO, },
+[1008]= { "HID",	64,	HV_RW, },
+};
+
+/* This covers POWER8 and POWER9 PMUs */
+static const struct spr sprs_power_common_pmu[1024] = {
+[768] = { "SIER",	64,	RO, },
+[769] = { "MMCR2",	64,	RW, },
+[770] = { "MMCRA",	64,	RW, },
+[771] = { "PMC1",	32,	RW, },
+[772] = { "PMC2",	32,	RW, },
+[773] = { "PMC3",	32,	RW, },
+[774] = { "PMC4",	32,	RW, },
+[775] = { "PMC5",	32,	RW, },
+[776] = { "PMC6",	32,	RW, },
+[779] = { "MMCR0",	64,	RW, },
+[780] = { "SIAR",	64,	RO, },
+[781] = { "SDAR",	64,	RO, },
+[782] = { "MMCR1",	64,	RO, },
+[784] = { "SIER",	64,	OS_RW, },
+[785] = { "MMCR2",	64,	OS_RW, },
+[786] = { "MMCRA",	64,	OS_RW, },
+[787] = { "PMC1",	32,	OS_RW, },
+[788] = { "PMC2",	32,	OS_RW, },
+[789] = { "PMC3",	32,	OS_RW, },
+[790] = { "PMC4",	32,	OS_RW, },
+[791] = { "PMC5",	32,	OS_RW, },
+[792] = { "PMC6",	32,	OS_RW, },
+[795] = { "MMCR0",	64,	OS_RW, },
+[796] = { "SIAR",	64,	OS_RW, },
+[797] = { "SDAR",	64,	OS_RW, },
+[798] = { "MMCR1",	64,	OS_RW, },
+};
+
+static const struct spr sprs_power10_pmu[1024] = {
+[736] = { "SIER2",	64,	RO, },
+[737] = { "SIER3",	64,	RO, },
+[738] = { "MMCR3",	64,	RO, },
+[752] = { "SIER2",	64,	OS_RW, },
+[753] = { "SIER3",	64,	OS_RW, },
+[754] = { "MMCR3",	64,	OS_RW, },
+};
+
+static struct spr sprs[1024];
+
+static void setup_sprs(void)
 {
-	set_sprs_common(val);
+	int i;
+
+	for (i = 0; i < 1024; i++) {
+		if (sprs_common[i].name) {
+			memcpy(&sprs[i], &sprs_common[i], sizeof(struct spr));
+		}
+	}
 
 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
 	case PVR_VER_970:
 	case PVR_VER_970FX:
 	case PVR_VER_970MP:
-		set_sprs_book3s_201(val);
+		for (i = 0; i < 1024; i++) {
+			if (sprs_201[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_201[i], sizeof(struct spr));
+			}
+			if (sprs_970_pmu[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr));
+			}
+		}
 		break;
+
 	case PVR_VER_POWER8E:
 	case PVR_VER_POWER8NVL:
 	case PVR_VER_POWER8:
-		set_sprs_book3s_207(val);
+		for (i = 0; i < 1024; i++) {
+			if (sprs_power_common[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr));
+			}
+			if (sprs_207[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_207[i], sizeof(struct spr));
+			}
+			if (sprs_tm[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_tm[i], sizeof(struct spr));
+			}
+			if (sprs_power_common_pmu[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr));
+			}
+		}
 		break;
+
 	case PVR_VER_POWER9:
-		set_sprs_book3s_300(val);
+		for (i = 0; i < 1024; i++) {
+			if (sprs_power_common[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr));
+			}
+			if (sprs_300[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_300[i], sizeof(struct spr));
+			}
+			if (sprs_tm[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_tm[i], sizeof(struct spr));
+			}
+			if (sprs_power9_10[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power9_10[i], sizeof(struct spr));
+			}
+			if (sprs_power_common_pmu[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr));
+			}
+		}
 		break;
+
 	case PVR_VER_POWER10:
-		set_sprs_book3s_31(val);
+		for (i = 0; i < 1024; i++) {
+			if (sprs_power_common[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common[i], sizeof(struct spr));
+			}
+			if (sprs_31[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_31[i], sizeof(struct spr));
+			}
+			if (sprs_power9_10[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power9_10[i], sizeof(struct spr));
+			}
+			if (sprs_power_common_pmu[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power_common_pmu[i], sizeof(struct spr));
+			}
+			if (sprs_power10_pmu[i].name) {
+				assert(!sprs[i].name);
+				memcpy(&sprs[i], &sprs_power10_pmu[i], sizeof(struct spr));
+			}
+		}
 		break;
+
 	default:
-		puts("Warning: Unknown processor version!\n");
+		memcpy(sprs, sprs_common, sizeof(sprs));
+		puts("Warning: Unknown processor version, falling back to common SPRs!\n");
+		break;
 	}
 }
 
-static void get_sprs_common(uint64_t *v)
-{
-	v[9] = mfspr(9);	/* CTR */
-	// v[273] = mfspr(273);	/* SPRG1 */ /* Used by our exception handler */
-	v[274] = mfspr(274);	/* SPRG2 */
-	v[275] = mfspr(275);	/* SPRG3 */
-}
-
-static void get_sprs_book3s_201(uint64_t *v)
-{
-	v[18] = mfspr(18);	/* DSISR */
-	v[19] = mfspr(19);	/* DAR */
-	v[136] = mfspr(136);	/* CTRL */
-	v[256] = mfspr(256);	/* VRSAVE */
-	v[786] = mfspr(786);	/* MMCRA */
-	v[795] = mfspr(795);	/* MMCR0 */
-	v[798] = mfspr(798);	/* MMCR1 */
-}
-
-static void get_sprs_book3s_207(uint64_t *v)
-{
-	v[3] = mfspr(3);	/* DSCR */
-	v[13] = mfspr(13);	/* AMR */
-	v[17] = mfspr(17);	/* DSCR */
-	v[18] = mfspr(18);	/* DSISR */
-	v[19] = mfspr(19);	/* DAR */
-	v[29] = mfspr(29);	/* AMR */
-	v[61] = mfspr(61);	/* IAMR */
-	// v[136] = mfspr(136);	/* CTRL */  /* TODO: Needs a fix in KVM */
-	v[153] = mfspr(153);	/* FSCR */
-	v[157] = mfspr(157);	/* UAMOR */
-	v[159] = mfspr(159);	/* PSPB */
-	v[256] = mfspr(256);	/* VRSAVE */
-	v[259] = mfspr(259);	/* SPRG3 (read only) */
-	// v[272] = mfspr(272);	/* SPRG0 */  /* Used by our exception handler */
-	v[769] = mfspr(769);	/* MMCR2 */
-	v[770] = mfspr(770);	/* MMCRA */
-	v[771] = mfspr(771);	/* PMC1 */
-	v[772] = mfspr(772);	/* PMC2 */
-	v[773] = mfspr(773);	/* PMC3 */
-	v[774] = mfspr(774);	/* PMC4 */
-	v[775] = mfspr(775);	/* PMC5 */
-	v[776] = mfspr(776);	/* PMC6 */
-	v[779] = mfspr(779);	/* MMCR0 */
-	v[780] = mfspr(780);	/* SIAR (read only) */
-	v[781] = mfspr(781);	/* SDAR (read only) */
-	v[782] = mfspr(782);	/* MMCR1 (read only) */
-	v[784] = mfspr(784);	/* SIER */
-	v[785] = mfspr(785);	/* MMCR2 */
-	v[786] = mfspr(786);	/* MMCRA */
-	v[787] = mfspr(787);	/* PMC1 */
-	v[788] = mfspr(788);	/* PMC2 */
-	v[789] = mfspr(789);	/* PMC3 */
-	v[790] = mfspr(790);	/* PMC4 */
-	v[791] = mfspr(791);	/* PMC5 */
-	v[792] = mfspr(792);	/* PMC6 */
-	v[795] = mfspr(795);	/* MMCR0 */
-	v[796] = mfspr(796);	/* SIAR */
-	v[797] = mfspr(797);	/* SDAR */
-	v[798] = mfspr(798);	/* MMCR1 */
-	v[800] = mfspr(800);	/* BESCRS */
-	v[801] = mfspr(801);	/* BESCCRSU */
-	v[802] = mfspr(802);	/* BESCRR */
-	v[803] = mfspr(803);	/* BESCRRU */
-	v[804] = mfspr(804);	/* EBBHR */
-	v[805] = mfspr(805);	/* EBBRR */
-	v[806] = mfspr(806);	/* BESCR */
-	v[815] = mfspr(815);	/* TAR */
-}
-
-static void get_sprs_book3s_300(uint64_t *v)
+static void get_sprs(uint64_t *v)
 {
-	get_sprs_book3s_207(v);
-	v[48] = mfspr(48);	/* PIDR */
-	v[144] = mfspr(144);	/* TIDR */
-	v[823] = mfspr(823);	/* PSSCR */
-}
+	int i;
 
-static void get_sprs_book3s_31(uint64_t *v)
-{
-	get_sprs_book3s_207(v);
-	v[48] = mfspr(48);	/* PIDR */
-	v[823] = mfspr(823);	/* PSSCR */
+	for (i = 0; i < 1024; i++) {
+		if (!(sprs[i].access & SPR_OS_READ))
+			continue;
+		v[i] = __mfspr(i);
+	}
 }
 
-static void get_sprs(uint64_t *v)
+static void set_sprs(uint64_t val)
 {
-	uint32_t pvr = mfspr(287);	/* Processor Version Register */
-
-	get_sprs_common(v);
+	int i;
 
-	switch (pvr >> 16) {
-	case 0x39:			/* PPC970 */
-	case 0x3C:			/* PPC970FX */
-	case 0x44:			/* PPC970MP */
-		get_sprs_book3s_201(v);
-		break;
-	case 0x4b:			/* POWER8E */
-	case 0x4c:			/* POWER8NVL */
-	case 0x4d:			/* POWER8 */
-		get_sprs_book3s_207(v);
-		break;
-	case 0x4e:			/* POWER9 */
-		get_sprs_book3s_300(v);
-		break;
-	case 0x80:                      /* POWER10 */
-		get_sprs_book3s_31(v);
-		break;
+	for (i = 0; i < 1024; i++) {
+		if (!(sprs[i].access & SPR_OS_WRITE))
+			continue;
+		if (sprs[i].type & SPR_HARNESS)
+			continue;
+		if (!strcmp(sprs[i].name, "MMCR0")) {
+			/* XXX: could use a comment or better abstraction! */
+			__mtspr(i, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070);
+		} else {
+			__mtspr(i, val);
+		}
 	}
 }
 
@@ -289,7 +518,9 @@ int main(int argc, char **argv)
 		}
 	}
 
-	printf("Settings SPRs to %#lx...\n", pat);
+	setup_sprs();
+
+	printf("Setting SPRs to 0x%lx...\n", pat);
 	set_sprs(pat);
 
 	memset(before, 0, sizeof(before));
@@ -299,18 +530,50 @@ int main(int argc, char **argv)
 
 	if (pause) {
 		migrate_once();
+		/* Reload regs changed by getchar/putchar hcalls */
+		before[SPR_SRR0] = mfspr(SPR_SRR0);
+		before[SPR_SRR1] = mfspr(SPR_SRR1);
+
+		/* WORT seems to go to 0 after KVM switch, perhaps CPU idle */
+		if (sprs[895].name)
+			before[895] = mfspr(895);
 	} else {
 		msleep(2000);
+
+		/* Reload regs changed by dec interrupt */
+		before[SPR_SRR0] = mfspr(SPR_SRR0);
+		before[SPR_SRR1] = mfspr(SPR_SRR1);
+		before[SPR_SPRG1] = mfspr(SPR_SPRG1);
+
+		/* WORT seems to go to 0 after KVM switch, perhaps CPU idle */
+		if (sprs[895].name)
+			before[895] = mfspr(895);
 	}
 
 	get_sprs(after);
 
 	puts("Checking SPRs...\n");
 	for (i = 0; i < 1024; i++) {
-		if (before[i] != 0 || after[i] != 0)
-			report(before[i] == after[i],
-			       "SPR %d:\t%#018lx <==> %#018lx", i, before[i],
-			       after[i]);
+		bool pass = true;
+
+		if (!(sprs[i].access & SPR_OS_READ))
+			continue;
+
+		if (sprs[i].width == 32) {
+			if (before[i] >> 32)
+				pass = false;
+		}
+		if (!(sprs[i].type & SPR_ASYNC) && (before[i] != after[i]))
+			pass = false;
+
+		if (sprs[i].width == 32 && !(before[i] >> 32) && !(after[i] >> 32))
+			report(pass, "%-10s(%4d):\t        0x%08lx <==>         0x%08lx",
+				sprs[i].name, i,
+				before[i], after[i]);
+		else
+			report(pass, "%-10s(%4d):\t0x%016lx <==> 0x%016lx",
+				sprs[i].name, i,
+				before[i], after[i]);
 	}
 
 	return report_summary();
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (5 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 06/32] powerpc/sprs: Specify SPRs with data rather than code Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-03-01 11:15   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing Nicholas Piggin
                   ` (24 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

SPRs annotated with SPR_HARNESS can change between consecutive reads
because the test harness code has changed them. Avoid failing the
test in this case.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/sprs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/powerpc/sprs.c b/powerpc/sprs.c
index 8253ea971..44edd0d7b 100644
--- a/powerpc/sprs.c
+++ b/powerpc/sprs.c
@@ -563,7 +563,7 @@ int main(int argc, char **argv)
 			if (before[i] >> 32)
 				pass = false;
 		}
-		if (!(sprs[i].type & SPR_ASYNC) && (before[i] != after[i]))
+		if (!(sprs[i].type & (SPR_HARNESS|SPR_ASYNC)) && (before[i] != after[i]))
 			pass = false;
 
 		if (sprs[i].width == 32 && !(before[i] >> 32) && !(after[i] >> 32))
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (6 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-03-01 11:25   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg Nicholas Piggin
                   ` (23 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Storing certain values in MMCR0 can cause PMU interrupts when msleep
enables MSR[EE], and this crashes the test. Freeze the PMU counters
and clear any PMU exception before calling msleep.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/reg.h |  4 ++++
 powerpc/sprs.c        | 17 +++++++++++------
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 1f991288e..c80b32059 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -24,6 +24,10 @@
 #define   PVR_VER_POWER10	UL(0x00800000)
 #define SPR_HSRR0	0x13a
 #define SPR_HSRR1	0x13b
+#define SPR_MMCR0	0x31b
+#define   MMCR0_FC		UL(0x80000000)
+#define   MMCR0_PMAE		UL(0x04000000)
+#define   MMCR0_PMAO		UL(0x00000080)
 
 /* Machine State Register definitions: */
 #define MSR_EE_BIT	15			/* External Interrupts Enable */
diff --git a/powerpc/sprs.c b/powerpc/sprs.c
index 44edd0d7b..cb1d6c980 100644
--- a/powerpc/sprs.c
+++ b/powerpc/sprs.c
@@ -476,12 +476,7 @@ static void set_sprs(uint64_t val)
 			continue;
 		if (sprs[i].type & SPR_HARNESS)
 			continue;
-		if (!strcmp(sprs[i].name, "MMCR0")) {
-			/* XXX: could use a comment or better abstraction! */
-			__mtspr(i, (val & 0xfffffffffbab3fffULL) | 0xfa0b2070);
-		} else {
-			__mtspr(i, val);
-		}
+		__mtspr(i, val);
 	}
 }
 
@@ -538,6 +533,16 @@ int main(int argc, char **argv)
 		if (sprs[895].name)
 			before[895] = mfspr(895);
 	} else {
+		/*
+		 * msleep will enable MSR[EE] and take a decrementer
+		 * interrupt. Must account for changed registers and
+		 * prevent taking unhandled interrupts.
+		 */
+		/* Prevent PMU interrupt */
+		mtspr(SPR_MMCR0, (mfspr(SPR_MMCR0) | MMCR0_FC) &
+					~(MMCR0_PMAO | MMCR0_PMAE));
+		before[SPR_MMCR0] = mfspr(SPR_MMCR0);
+		before[779] = mfspr(SPR_MMCR0);
 		msleep(2000);
 
 		/* Reload regs changed by dec interrupt */
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (7 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-28 11:47   ` Andrew Jones
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences Nicholas Piggin
                   ` (22 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This allows different machines with different requirements to be
supported by run_tests.sh, similarly to how different accelerators
are handled.

Acked-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 scripts/common.bash  |  8 ++++++--
 scripts/runtime.bash | 16 ++++++++++++----
 2 files changed, 18 insertions(+), 6 deletions(-)

diff --git a/scripts/common.bash b/scripts/common.bash
index b9413d683..ee1dd8659 100644
--- a/scripts/common.bash
+++ b/scripts/common.bash
@@ -10,6 +10,7 @@ function for_each_unittest()
 	local opts
 	local groups
 	local arch
+	local machine
 	local check
 	local accel
 	local timeout
@@ -21,7 +22,7 @@ function for_each_unittest()
 		if [[ "$line" =~ ^\[(.*)\]$ ]]; then
 			rematch=${BASH_REMATCH[1]}
 			if [ -n "${testname}" ]; then
-				$(arch_cmd) "$cmd" "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch" "$check" "$accel" "$timeout"
+				$(arch_cmd) "$cmd" "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch" "$machine" "$check" "$accel" "$timeout"
 			fi
 			testname=$rematch
 			smp=1
@@ -29,6 +30,7 @@ function for_each_unittest()
 			opts=""
 			groups=""
 			arch=""
+			machine=""
 			check=""
 			accel=""
 			timeout=""
@@ -58,6 +60,8 @@ function for_each_unittest()
 			groups=${BASH_REMATCH[1]}
 		elif [[ $line =~ ^arch\ *=\ *(.*)$ ]]; then
 			arch=${BASH_REMATCH[1]}
+		elif [[ $line =~ ^machine\ *=\ *(.*)$ ]]; then
+			machine=${BASH_REMATCH[1]}
 		elif [[ $line =~ ^check\ *=\ *(.*)$ ]]; then
 			check=${BASH_REMATCH[1]}
 		elif [[ $line =~ ^accel\ *=\ *(.*)$ ]]; then
@@ -67,7 +71,7 @@ function for_each_unittest()
 		fi
 	done
 	if [ -n "${testname}" ]; then
-		$(arch_cmd) "$cmd" "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch" "$check" "$accel" "$timeout"
+		$(arch_cmd) "$cmd" "$testname" "$groups" "$smp" "$kernel" "$opts" "$arch" "$machine" "$check" "$accel" "$timeout"
 	fi
 	exec {fd}<&-
 }
diff --git a/scripts/runtime.bash b/scripts/runtime.bash
index c73fb0240..8f9672d0d 100644
--- a/scripts/runtime.bash
+++ b/scripts/runtime.bash
@@ -30,7 +30,7 @@ premature_failure()
 get_cmdline()
 {
     local kernel=$1
-    echo "TESTNAME=$testname TIMEOUT=$timeout ACCEL=$accel $RUNTIME_arch_run $kernel -smp $smp $opts"
+    echo "TESTNAME=$testname TIMEOUT=$timeout MACHINE=$machine ACCEL=$accel $RUNTIME_arch_run $kernel -smp $smp $opts"
 }
 
 skip_nodefault()
@@ -78,9 +78,10 @@ function run()
     local kernel="$4"
     local opts="$5"
     local arch="$6"
-    local check="${CHECK:-$7}"
-    local accel="$8"
-    local timeout="${9:-$TIMEOUT}" # unittests.cfg overrides the default
+    local machine="$7"
+    local check="${CHECK:-$8}"
+    local accel="$9"
+    local timeout="${10:-$TIMEOUT}" # unittests.cfg overrides the default
 
     if [ "${CONFIG_EFI}" == "y" ]; then
         kernel=${kernel/%.flat/.efi}
@@ -114,6 +115,13 @@ function run()
         return 2
     fi
 
+    if [ -n "$machine" ] && [ -n "$MACHINE" ] && [ "$machine" != "$MACHINE" ]; then
+        print_result "SKIP" $testname "" "$machine only"
+        return 2
+    elif [ -n "$MACHINE" ]; then
+        machine="$MACHINE"
+    fi
+
     if [ -n "$accel" ] && [ -n "$ACCEL" ] && [ "$accel" != "$ACCEL" ]; then
         print_result "SKIP" $testname "" "$accel only, but ACCEL=$ACCEL"
         return 2
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (8 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-28 11:52   ` Andrew Jones
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 11/32] powerpc: Support powernv machine with QEMU TCG Nicholas Piggin
                   ` (21 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

The QEMU powerpc powernv machine has minor differences that must be
accommodated for in output parsing:

- Summary parsing must search more lines of output for the summary
  line, to accommodate OPAL message on shutdown.
- Premature failure testing must tolerate case differences in kernel
  load error message.

Acked-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 scripts/runtime.bash | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/runtime.bash b/scripts/runtime.bash
index 8f9672d0d..bb32c0d10 100644
--- a/scripts/runtime.bash
+++ b/scripts/runtime.bash
@@ -9,7 +9,7 @@ FAIL() { echo -ne "\e[31mFAIL\e[0m"; }
 extract_summary()
 {
     local cr=$'\r'
-    tail -3 | grep '^SUMMARY: ' | sed 's/^SUMMARY: /(/;s/'"$cr"'\{0,1\}$/)/'
+    tail -5 | grep '^SUMMARY: ' | sed 's/^SUMMARY: /(/;s/'"$cr"'\{0,1\}$/)/'
 }
 
 # We assume that QEMU is going to work if it tried to load the kernel
@@ -18,7 +18,7 @@ premature_failure()
     local log="$(eval "$(get_cmdline _NO_FILE_4Uhere_)" 2>&1)"
 
     echo "$log" | grep "_NO_FILE_4Uhere_" |
-        grep -q -e "could not \(load\|open\) kernel" -e "error loading" &&
+        grep -q -e "[Cc]ould not \(load\|open\) kernel" -e "error loading" &&
         return 1
 
     RUNTIME_log_stderr <<< "$log"
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 11/32] powerpc: Support powernv machine with QEMU TCG
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (9 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv Nicholas Piggin
                   ` (20 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev, Cédric Le Goater

Add support for QEMU's powernv machine. This uses standard firmware
(skiboot) rather than a minimal firmware shim.

Reviewed-by: Cédric Le Goater <clg@kaod.org>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h | 23 +++++++++++
 lib/powerpc/asm/reg.h       |  4 ++
 lib/powerpc/hcall.c         |  4 +-
 lib/powerpc/io.c            | 27 ++++++++++++-
 lib/powerpc/io.h            |  6 +++
 lib/powerpc/processor.c     | 37 ++++++++++++++++++
 lib/powerpc/setup.c         | 14 +++++--
 lib/ppc64/asm/opal.h        | 15 ++++++++
 lib/ppc64/opal-calls.S      | 50 ++++++++++++++++++++++++
 lib/ppc64/opal.c            | 76 +++++++++++++++++++++++++++++++++++++
 powerpc/Makefile.ppc64      |  2 +
 powerpc/cstart64.S          |  7 ++++
 powerpc/run                 | 42 ++++++++++++++++----
 powerpc/unittests.cfg       | 10 ++++-
 14 files changed, 301 insertions(+), 16 deletions(-)
 create mode 100644 lib/ppc64/asm/opal.h
 create mode 100644 lib/ppc64/opal-calls.S
 create mode 100644 lib/ppc64/opal.c

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index e415f9235..9d8061962 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -10,6 +10,8 @@ void handle_exception(int trap, void (*func)(struct pt_regs *, void *), void *);
 void do_handle_exception(struct pt_regs *regs);
 #endif /* __ASSEMBLY__ */
 
+extern bool cpu_has_hv;
+
 static inline uint64_t mfspr(int nr)
 {
 	uint64_t ret;
@@ -38,4 +40,25 @@ static inline void mtmsr(uint64_t msr)
 	asm volatile ("mtmsrd %[msr]" :: [msr] "r" (msr) : "memory");
 }
 
+/*
+ * This returns true on PowerNV / OPAL machines which run in hypervisor
+ * mode. False on pseries / PAPR machines that run in guest mode.
+ */
+static inline bool machine_is_powernv(void)
+{
+	return cpu_has_hv;
+}
+
+/*
+ * This returns true on pseries / PAPR / KVM machines which run under a
+ * hypervisor or QEMU pseries machine. False for PowerNV / OPAL.
+ */
+static inline bool machine_is_pseries(void)
+{
+	return !machine_is_powernv();
+}
+
+void enable_mcheck(void);
+void disable_mcheck(void);
+
 #endif /* _ASMPOWERPC_PROCESSOR_H_ */
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index c80b32059..782e75527 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -30,7 +30,11 @@
 #define   MMCR0_PMAO		UL(0x00000080)
 
 /* Machine State Register definitions: */
+#define MSR_LE_BIT	0
 #define MSR_EE_BIT	15			/* External Interrupts Enable */
+#define MSR_HV_BIT	60			/* Hypervisor mode */
 #define MSR_SF_BIT	63			/* 64-bit mode */
 
+#define MSR_ME		UL(0x1000)
+
 #endif
diff --git a/lib/powerpc/hcall.c b/lib/powerpc/hcall.c
index b4d39ac65..45f201315 100644
--- a/lib/powerpc/hcall.c
+++ b/lib/powerpc/hcall.c
@@ -25,7 +25,7 @@ int hcall_have_broken_sc1(void)
 	return r3 == (unsigned long)H_PRIVILEGE;
 }
 
-void putchar(int c)
+void papr_putchar(int c)
 {
 	unsigned long vty = 0;		/* 0 == default */
 	unsigned long nr_chars = 1;
@@ -34,7 +34,7 @@ void putchar(int c)
 	hcall(H_PUT_TERM_CHAR, vty, nr_chars, chars);
 }
 
-int __getchar(void)
+int __papr_getchar(void)
 {
 	register unsigned long r3 asm("r3") = H_GET_TERM_CHAR;
 	register unsigned long r4 asm("r4") = 0; /* 0 == default vty */
diff --git a/lib/powerpc/io.c b/lib/powerpc/io.c
index a381688bc..ab7bb843c 100644
--- a/lib/powerpc/io.c
+++ b/lib/powerpc/io.c
@@ -9,13 +9,33 @@
 #include <asm/spinlock.h>
 #include <asm/rtas.h>
 #include <asm/setup.h>
+#include <asm/processor.h>
 #include "io.h"
 
 static struct spinlock print_lock;
 
+void putchar(int c)
+{
+	if (machine_is_powernv())
+		opal_putchar(c);
+	else
+		papr_putchar(c);
+}
+
+int __getchar(void)
+{
+	if (machine_is_powernv())
+		return __opal_getchar();
+	else
+		return __papr_getchar();
+}
+
 void io_init(void)
 {
-	rtas_init();
+	if (machine_is_powernv())
+		assert(!opal_init());
+	else
+		rtas_init();
 }
 
 void puts(const char *s)
@@ -38,7 +58,10 @@ void exit(int code)
 // FIXME: change this print-exit/rtas-poweroff to chr_testdev_exit(),
 //        maybe by plugging chr-testdev into a spapr-vty.
 	printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
-	rtas_power_off();
+	if (machine_is_powernv())
+		opal_power_off();
+	else
+		rtas_power_off();
 	halt(code);
 	__builtin_unreachable();
 }
diff --git a/lib/powerpc/io.h b/lib/powerpc/io.h
index d4f21ba15..943bf142b 100644
--- a/lib/powerpc/io.h
+++ b/lib/powerpc/io.h
@@ -8,6 +8,12 @@
 #define _POWERPC_IO_H_
 
 extern void io_init(void);
+extern int opal_init(void);
+extern void opal_power_off(void);
 extern void putchar(int c);
+extern void opal_putchar(int c);
+extern void papr_putchar(int c);
+extern int __opal_getchar(void);
+extern int __papr_getchar(void);
 
 #endif
diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
index 114584024..1b4bb0d61 100644
--- a/lib/powerpc/processor.c
+++ b/lib/powerpc/processor.c
@@ -84,6 +84,16 @@ void sleep_tb(uint64_t cycles)
 {
 	uint64_t start, end, now;
 
+	if (!machine_is_pseries()) {
+		/*
+		 * P9/10 Could use 'stop' to sleep here which would be
+		 * interesting.  stop with ESL=0 should be simple enough, ESL=1
+		 * would require SRESET based wakeup which is more involved.
+		 */
+		delay(cycles);
+		return;
+	}
+
 	start = now = get_tb();
 	end = start + cycles;
 
@@ -120,3 +130,30 @@ void usleep(uint64_t us)
 {
 	sleep_tb((us * tb_hz) / 1000000);
 }
+
+static void rfid_msr(uint64_t msr)
+{
+	uint64_t tmp;
+
+	asm volatile(
+		"mtsrr1	%1		\n\
+		 bl	0f		\n\
+		 0:			\n\
+		 mflr	%0		\n\
+		 addi	%0,%0,1f-0b	\n\
+		 mtsrr0	%0		\n\
+		 rfid			\n\
+		 1:			\n"
+		: "=r"(tmp) : "r"(msr) : "lr");
+}
+
+void enable_mcheck(void)
+{
+	/* This is a no-op on pseries */
+	rfid_msr(mfmsr() | MSR_ME);
+}
+
+void disable_mcheck(void)
+{
+	rfid_msr(mfmsr() & ~MSR_ME);
+}
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index d98f66fae..89e5157f2 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -19,6 +19,7 @@
 #include <asm/setup.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
+#include <asm/processor.h>
 #include <asm/hcall.h>
 #include "io.h"
 
@@ -85,6 +86,8 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
 	}
 }
 
+bool cpu_has_hv;
+
 static void cpu_init(void)
 {
 	struct cpu_set_params params;
@@ -98,12 +101,13 @@ static void cpu_init(void)
 	tb_hz = params.tb_hz;
 
 	/* Interrupt Endianness */
-
+	if (machine_is_pseries()) {
 #if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-        hcall(H_SET_MODE, 1, 4, 0, 0);
+		hcall(H_SET_MODE, 1, 4, 0, 0);
 #else
-        hcall(H_SET_MODE, 0, 4, 0, 0);
+		hcall(H_SET_MODE, 0, 4, 0, 0);
 #endif
+	}
 }
 
 static void mem_init(phys_addr_t freemem_start)
@@ -159,6 +163,10 @@ void setup(const void *fdt)
 	u32 fdt_size;
 	int ret;
 
+	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
+
+	enable_mcheck();
+
 	/*
 	 * Before calling mem_init we need to move the fdt and initrd
 	 * to safe locations. We move them to construct the memory
diff --git a/lib/ppc64/asm/opal.h b/lib/ppc64/asm/opal.h
new file mode 100644
index 000000000..de64e2c8d
--- /dev/null
+++ b/lib/ppc64/asm/opal.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef _ASMPPC64_OPAL_H_
+#define _ASMPPC64_OPAL_H_
+
+#define OPAL_SUCCESS				0
+
+#define OPAL_CONSOLE_WRITE			1
+#define OPAL_CONSOLE_READ			2
+#define OPAL_CEC_POWER_DOWN			5
+#define OPAL_POLL_EVENTS			10
+#define OPAL_REINIT_CPUS			70
+# define OPAL_REINIT_CPUS_HILE_BE		(1 << 0)
+# define OPAL_REINIT_CPUS_HILE_LE		(1 << 1)
+
+#endif
diff --git a/lib/ppc64/opal-calls.S b/lib/ppc64/opal-calls.S
new file mode 100644
index 000000000..8cb4c3e91
--- /dev/null
+++ b/lib/ppc64/opal-calls.S
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2016 IBM Corporation.
+ */
+
+#include <asm/ppc_asm.h>
+
+	.text
+	.globl opal_call
+opal_call:
+	mr	r0,r3
+	mr	r3,r4
+	mr	r4,r5
+	mr	r5,r6
+	mr	r6,r7
+	mflr	r11
+	std	r11,16(r1)
+	mfcr	r12
+	stw	r12,8(r1)
+	std	r2,-8(r1) /* use redzone */
+
+	/* Set opal return address */
+	LOAD_REG_ADDR(r11, opal_return)
+	mtlr	r11
+	mfmsr	r12
+	std	r12,-16(r1) /* use redzone */
+
+	/* switch to BE when we enter OPAL */
+	li	r11,(1 << MSR_LE_BIT)
+	ori	r11,r11,(1 << MSR_EE_BIT)
+	andc	r12,r12,r11
+	mtspr	SPR_HSRR1,r12
+
+	/* load the opal call entry point and base */
+	LOAD_REG_ADDR(r11, opal)
+	ld	r12,8(r11)
+	ld	r2,0(r11)
+	mtspr	SPR_HSRR0,r12
+	hrfid
+
+opal_return:
+	FIXUP_ENDIAN
+	ld	r12,-16(r1) /* use redzone */
+	mtmsrd	r12
+	ld	r2,-8(r1) /* use redzone */
+	lwz	r11,8(r1);
+	ld	r12,16(r1)
+	mtcr	r11;
+	mtlr	r12
+	blr
diff --git a/lib/ppc64/opal.c b/lib/ppc64/opal.c
new file mode 100644
index 000000000..63fe42ae6
--- /dev/null
+++ b/lib/ppc64/opal.c
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * OPAL call helpers
+ */
+#include <asm/opal.h>
+#include <libcflat.h>
+#include <libfdt/libfdt.h>
+#include <devicetree.h>
+#include <asm/io.h>
+#include "../powerpc/io.h"
+
+struct opal {
+	uint64_t base;
+	uint64_t entry;
+} opal;
+
+extern int64_t opal_call(int64_t token, int64_t arg1, int64_t arg2, int64_t arg3);
+
+int opal_init(void)
+{
+	const struct fdt_property *prop;
+	int node, len;
+
+	node = fdt_path_offset(dt_fdt(), "/ibm,opal");
+	if (node < 0)
+		return -1;
+
+	prop = fdt_get_property(dt_fdt(), node, "opal-base-address", &len);
+	if (!prop)
+		return -1;
+	opal.base = fdt64_to_cpu(*(uint64_t *)prop->data);
+
+	prop = fdt_get_property(dt_fdt(), node, "opal-entry-address", &len);
+	if (!prop)
+		return -1;
+	opal.entry = fdt64_to_cpu(*(uint64_t *)prop->data);
+
+#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+	if (opal_call(OPAL_REINIT_CPUS, OPAL_REINIT_CPUS_HILE_LE, 0, 0) != OPAL_SUCCESS)
+		return -1;
+#endif
+
+	return 0;
+}
+
+extern void opal_power_off(void)
+{
+	opal_call(OPAL_CEC_POWER_DOWN, 0, 0, 0);
+	while (true)
+		opal_call(OPAL_POLL_EVENTS, 0, 0, 0);
+}
+
+void opal_putchar(int c)
+{
+	unsigned long vty = 0;		/* 0 == default */
+	unsigned long nr_chars = cpu_to_be64(1);
+	char ch = c;
+
+	opal_call(OPAL_CONSOLE_WRITE, (int64_t)vty, (int64_t)&nr_chars, (int64_t)&ch);
+}
+
+int __opal_getchar(void)
+{
+	unsigned long vty = 0;		/* 0 == default */
+	unsigned long nr_chars = cpu_to_be64(1);
+	char ch;
+	int rc;
+
+	rc = opal_call(OPAL_CONSOLE_READ, (int64_t)vty, (int64_t)&nr_chars, (int64_t)&ch);
+	if (rc != OPAL_SUCCESS)
+		return -1;
+	if (nr_chars == 0)
+		return -1;
+
+	return ch;
+}
diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
index eb682c226..a18a9628f 100644
--- a/powerpc/Makefile.ppc64
+++ b/powerpc/Makefile.ppc64
@@ -18,6 +18,8 @@ reloc.o  = $(TEST_DIR)/reloc64.o
 
 OBJDIRS += lib/ppc64
 cflatobjs += lib/ppc64/stack.o
+cflatobjs += lib/ppc64/opal.o
+cflatobjs += lib/ppc64/opal-calls.o
 
 # ppc64 specific tests
 tests = $(TEST_DIR)/spapr_vpa.elf
diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
index 278af84a6..5e091e325 100644
--- a/powerpc/cstart64.S
+++ b/powerpc/cstart64.S
@@ -100,6 +100,13 @@ start:
 	sync
 	isync
 
+	/* powernv machine does not check broken_sc1 */
+	mfmsr	r3
+	li	r4,1
+	sldi	r4,r4,MSR_HV_BIT
+	and.	r3,r3,r4
+	bne	1f
+
 	/* patch sc1 if needed */
 	bl	hcall_have_broken_sc1
 	cmpwi	r3, 0
diff --git a/powerpc/run b/powerpc/run
index 5cdb94194..172f32a46 100755
--- a/powerpc/run
+++ b/powerpc/run
@@ -1,5 +1,14 @@
 #!/usr/bin/env bash
 
+get_qemu_machine ()
+{
+	if [ "$MACHINE" ]; then
+		echo $MACHINE
+	else
+		echo pseries
+	fi
+}
+
 if [ -z "$KUT_STANDALONE" ]; then
 	if [ ! -f config.mak ]; then
 		echo "run ./configure && make first. See ./configure -h"
@@ -11,24 +20,41 @@ fi
 
 set_qemu_accelerator || exit $?
 
+MACHINE=$(get_qemu_machine) ||
+	exit $?
+
+if [[ "$MACHINE" == "powernv"* ]] && [ "$ACCEL" = "kvm" ]; then
+	echo "PowerNV machine does not support KVM. ACCEL=tcg must be specified."
+	exit 2
+fi
+
 qemu=$(search_qemu_binary) ||
 	exit $?
 
-if ! $qemu -machine '?' 2>&1 | grep 'pseries' > /dev/null; then
-	echo "$qemu doesn't support pSeries ('-machine pseries'). Exiting."
+if ! $qemu -machine '?' 2>&1 | grep $MACHINE > /dev/null; then
+	echo "$qemu doesn't support '-machine $MACHINE'. Exiting."
 	exit 2
 fi
 
-M='-machine pseries'
+M="-machine $MACHINE"
 M+=",accel=$ACCEL$ACCEL_PROPS"
+B=""
+D=""
+
+if [[ "$MACHINE" == "pseries"* ]] ; then
+	if [[ "$ACCEL" == "tcg" ]] ; then
+		M+=",cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off"
+	elif [[ "$ACCEL" == "kvm" ]] ; then
+		M+=",cap-ccf-assist=off"
+	fi
+	B+="-bios $FIRMWARE"
+fi
 
-if [[ "$ACCEL" == "tcg" ]] ; then
-	M+=",cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off"
-elif [[ "$ACCEL" == "kvm" ]] ; then
-	M+=",cap-ccf-assist=off"
+if [[ "$MACHINE" == "powernv"* ]] ; then
+	D+="-device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10"
 fi
 
-command="$qemu -nodefaults $M -bios $FIRMWARE"
+command="$qemu -nodefaults $M $B $D"
 command+=" -display none -serial stdio -kernel"
 command="$(migration_cmd) $(timeout_cmd) $command"
 
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index fadd8dde6..41bb8a327 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -33,11 +33,12 @@
 [selftest-setup]
 file = selftest.elf
 smp = 2
-extra_params = -m 256 -append 'setup smp=2 mem=256'
+extra_params = -m 1g -append 'setup smp=2 mem=1024'
 groups = selftest
 
 [selftest-migration]
 file = selftest-migration.elf
+machine = pseries
 groups = selftest migration
 
 [selftest-migration-skip]
@@ -55,24 +56,29 @@ groups = migration
 
 [spapr_hcall]
 file = spapr_hcall.elf
+machine = pseries
 
 [spapr_vpa]
 file = spapr_vpa.elf
+machine = pseries
 
 [rtas-get-time-of-day]
 file = rtas.elf
+machine = pseries
 timeout = 5
 extra_params = -append "get-time-of-day date=$(date +%s)"
 groups = rtas
 
 [rtas-get-time-of-day-base]
 file = rtas.elf
+machine = pseries
 timeout = 5
 extra_params = -rtc base="2006-06-17" -append "get-time-of-day date=$(date --date="2006-06-17 UTC" +%s)"
 groups = rtas
 
 [rtas-set-time-of-day]
 file = rtas.elf
+machine = pseries
 extra_params = -append "set-time-of-day"
 timeout = 5
 groups = rtas
@@ -82,6 +88,7 @@ file = emulator.elf
 
 [h_cede_tm]
 file = tm.elf
+machine = pseries
 accel = kvm
 smp = 2,threads=2
 extra_params = -machine cap-htm=on -append "h_cede_tm"
@@ -92,5 +99,6 @@ file = sprs.elf
 
 [sprs-migration]
 file = sprs.elf
+machine = pseries
 extra_params = -append '-w'
 groups = migration
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (10 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 11/32] powerpc: Support powernv machine with QEMU TCG Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-03-01 11:50   ` Thomas Huth
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 13/32] powerpc/sprs: Test hypervisor registers on powernv machine Nicholas Piggin
                   ` (19 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Illegal instructions cause 0xe40 (HEAI) interrupts rather
than program interrupts.

Acked-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |  1 +
 lib/powerpc/setup.c         | 13 +++++++++++++
 powerpc/emulator.c          | 21 ++++++++++++++++++++-
 3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index 9d8061962..cf1b9d8ff 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -11,6 +11,7 @@ void do_handle_exception(struct pt_regs *regs);
 #endif /* __ASSEMBLY__ */
 
 extern bool cpu_has_hv;
+extern bool cpu_has_heai;
 
 static inline uint64_t mfspr(int nr)
 {
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 89e5157f2..3c81aee9e 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -87,6 +87,7 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
 }
 
 bool cpu_has_hv;
+bool cpu_has_heai;
 
 static void cpu_init(void)
 {
@@ -108,6 +109,18 @@ static void cpu_init(void)
 		hcall(H_SET_MODE, 0, 4, 0, 0);
 #endif
 	}
+
+	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
+	case PVR_VER_POWER10:
+	case PVR_VER_POWER9:
+	case PVR_VER_POWER8E:
+	case PVR_VER_POWER8NVL:
+	case PVR_VER_POWER8:
+		cpu_has_heai = true;
+		break;
+	default:
+		break;
+	}
 }
 
 static void mem_init(phys_addr_t freemem_start)
diff --git a/powerpc/emulator.c b/powerpc/emulator.c
index 39dd59645..c9b17f742 100644
--- a/powerpc/emulator.c
+++ b/powerpc/emulator.c
@@ -31,6 +31,20 @@ static void program_check_handler(struct pt_regs *regs, void *opaque)
 	regs->nip += 4;
 }
 
+static void heai_handler(struct pt_regs *regs, void *opaque)
+{
+	int *data = opaque;
+
+	if (verbose) {
+		printf("Detected invalid instruction %#018lx: %08x\n",
+		       regs->nip, *(uint32_t*)regs->nip);
+	}
+
+	*data = 8; /* Illegal instruction */
+
+	regs->nip += 4;
+}
+
 static void alignment_handler(struct pt_regs *regs, void *opaque)
 {
 	int *data = opaque;
@@ -362,7 +376,12 @@ int main(int argc, char **argv)
 {
 	int i;
 
-	handle_exception(0x700, program_check_handler, (void *)&is_invalid);
+	if (cpu_has_heai) {
+		handle_exception(0xe40, heai_handler, (void *)&is_invalid);
+		handle_exception(0x700, program_check_handler, (void *)&is_invalid);
+	} else {
+		handle_exception(0x700, program_check_handler, (void *)&is_invalid);
+	}
 	handle_exception(0x600, alignment_handler, (void *)&alignment);
 
 	for (i = 1; i < argc; i++) {
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 13/32] powerpc/sprs: Test hypervisor registers on powernv machine
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (11 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv Nicholas Piggin
@ 2024-02-26 10:11 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests Nicholas Piggin
                   ` (18 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:11 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This enables HV privilege registers to be tested with the powernv
machine.

Acked-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/sprs.c | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)

diff --git a/powerpc/sprs.c b/powerpc/sprs.c
index cb1d6c980..0a82418d6 100644
--- a/powerpc/sprs.c
+++ b/powerpc/sprs.c
@@ -199,16 +199,16 @@ static const struct spr sprs_power_common[1024] = {
 [190] = { "HFSCR",	64,	HV_RW, },
 [256] = { "VRSAVE",	32,	RW, },
 [259] = { "SPRG3",	64,	RO, },
-[284] = { "TBL",	32,	HV_WO, },
-[285] = { "TBU",	32,	HV_WO, },
-[286] = { "TBU40",	64,	HV_WO, },
+[284] = { "TBL",	32,	HV_WO, }, /* Things can go a bit wonky with */
+[285] = { "TBU",	32,	HV_WO, }, /* Timebase changing. Should save */
+[286] = { "TBU40",	64,	HV_WO, }, /* and restore it. */
 [304] = { "HSPRG0",	64,	HV_RW, },
 [305] = { "HSPRG1",	64,	HV_RW, },
 [306] = { "HDSISR",	32,	HV_RW,		SPR_INT, },
 [307] = { "HDAR",	64,	HV_RW,		SPR_INT, },
 [308] = { "SPURR",	64,	HV_RW | OS_RO,	SPR_ASYNC, },
 [309] = { "PURR",	64,	HV_RW | OS_RO,	SPR_ASYNC, },
-[313] = { "HRMOR",	64,	HV_RW, },
+[313] = { "HRMOR",	64,	HV_RW,		SPR_HARNESS, }, /* Harness can't cope with HRMOR changing */
 [314] = { "HSRR0",	64,	HV_RW,		SPR_INT, },
 [315] = { "HSRR1",	64,	HV_RW,		SPR_INT, },
 [318] = { "LPCR",	64,	HV_RW, },
@@ -306,7 +306,7 @@ static const struct spr sprs_power9_10[1024] = {
 [921] = { "TSCR",	32,	HV_RW, },
 [922] = { "TTR",	64,	HV_RW, },
 [1006]= { "TRACE",	64,	WO, },
-[1008]= { "HID",	64,	HV_RW, },
+[1008]= { "HID",	64,	HV_RW,		SPR_HARNESS, }, /* HILE would be unhelpful to change */
 };
 
 /* This covers POWER8 and POWER9 PMUs */
@@ -350,6 +350,22 @@ static const struct spr sprs_power10_pmu[1024] = {
 
 static struct spr sprs[1024];
 
+static bool spr_read_perms(int spr)
+{
+	if (cpu_has_hv)
+		return !!(sprs[spr].access & SPR_HV_READ);
+	else
+		return !!(sprs[spr].access & SPR_OS_READ);
+}
+
+static bool spr_write_perms(int spr)
+{
+	if (cpu_has_hv)
+		return !!(sprs[spr].access & SPR_HV_WRITE);
+	else
+		return !!(sprs[spr].access & SPR_OS_WRITE);
+}
+
 static void setup_sprs(void)
 {
 	int i;
@@ -461,7 +477,7 @@ static void get_sprs(uint64_t *v)
 	int i;
 
 	for (i = 0; i < 1024; i++) {
-		if (!(sprs[i].access & SPR_OS_READ))
+		if (!spr_read_perms(i))
 			continue;
 		v[i] = __mfspr(i);
 	}
@@ -472,8 +488,9 @@ static void set_sprs(uint64_t val)
 	int i;
 
 	for (i = 0; i < 1024; i++) {
-		if (!(sprs[i].access & SPR_OS_WRITE))
+		if (!spr_write_perms(i))
 			continue;
+
 		if (sprs[i].type & SPR_HARNESS)
 			continue;
 		__mtspr(i, val);
@@ -561,7 +578,7 @@ int main(int argc, char **argv)
 	for (i = 0; i < 1024; i++) {
 		bool pass = true;
 
-		if (!(sprs[i].access & SPR_OS_READ))
+		if (!spr_read_perms(i))
 			continue;
 
 		if (sprs[i].width == 32) {
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (12 preceding siblings ...)
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 13/32] powerpc/sprs: Test hypervisor registers on powernv machine Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-03-01 12:41   ` Thomas Huth
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 15/32] powerpc: Add rtas stop-self support Nicholas Piggin
                   ` (17 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Add basic testing of various kinds of interrupts, machine check,
page fault, illegal, decrementer, trace, syscall, etc.

This has a known failure on QEMU TCG pseries machines where MSR[ME]
can be incorrectly set to 0.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |   4 +
 lib/powerpc/asm/reg.h       |  17 ++
 lib/powerpc/setup.c         |  11 +
 lib/ppc64/asm/ptrace.h      |  16 ++
 powerpc/Makefile.common     |   3 +-
 powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg       |   3 +
 7 files changed, 468 insertions(+), 1 deletion(-)
 create mode 100644 powerpc/interrupts.c

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index cf1b9d8ff..eed37d1f4 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -11,7 +11,11 @@ void do_handle_exception(struct pt_regs *regs);
 #endif /* __ASSEMBLY__ */
 
 extern bool cpu_has_hv;
+extern bool cpu_has_power_mce;
+extern bool cpu_has_siar;
 extern bool cpu_has_heai;
+extern bool cpu_has_prefix;
+extern bool cpu_has_sc_lev;
 
 static inline uint64_t mfspr(int nr)
 {
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 782e75527..d6097f48f 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -5,8 +5,15 @@
 
 #define UL(x) _AC(x, UL)
 
+#define SPR_DSISR	0x012
+#define SPR_DAR		0x013
+#define SPR_DEC		0x016
 #define SPR_SRR0	0x01a
 #define SPR_SRR1	0x01b
+#define   SRR1_PREFIX		UL(0x20000000)
+#define SPR_FSCR	0x099
+#define   FSCR_PREFIX		UL(0x2000)
+#define SPR_HFSCR	0x0be
 #define SPR_TB		0x10c
 #define SPR_SPRG0	0x110
 #define SPR_SPRG1	0x111
@@ -22,12 +29,17 @@
 #define   PVR_VER_POWER8	UL(0x004d0000)
 #define   PVR_VER_POWER9	UL(0x004e0000)
 #define   PVR_VER_POWER10	UL(0x00800000)
+#define SPR_HDEC	0x136
 #define SPR_HSRR0	0x13a
 #define SPR_HSRR1	0x13b
+#define SPR_LPCR	0x13e
+#define   LPCR_HDICE		UL(0x1)
+#define SPR_HEIR	0x153
 #define SPR_MMCR0	0x31b
 #define   MMCR0_FC		UL(0x80000000)
 #define   MMCR0_PMAE		UL(0x04000000)
 #define   MMCR0_PMAO		UL(0x00000080)
+#define SPR_SIAR	0x31c
 
 /* Machine State Register definitions: */
 #define MSR_LE_BIT	0
@@ -35,6 +47,11 @@
 #define MSR_HV_BIT	60			/* Hypervisor mode */
 #define MSR_SF_BIT	63			/* 64-bit mode */
 
+#define MSR_DR		UL(0x0010)
+#define MSR_IR		UL(0x0020)
+#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
+#define MSR_SE		UL(0x0400)		/* Single Step Enable */
+#define MSR_EE		UL(0x8000)
 #define MSR_ME		UL(0x1000)
 
 #endif
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 3c81aee9e..9b665f59c 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -87,7 +87,11 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
 }
 
 bool cpu_has_hv;
+bool cpu_has_power_mce; /* POWER CPU machine checks */
+bool cpu_has_siar;
 bool cpu_has_heai;
+bool cpu_has_prefix;
+bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
 
 static void cpu_init(void)
 {
@@ -112,15 +116,22 @@ static void cpu_init(void)
 
 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
 	case PVR_VER_POWER10:
+		cpu_has_prefix = true;
+		cpu_has_sc_lev = true;
 	case PVR_VER_POWER9:
 	case PVR_VER_POWER8E:
 	case PVR_VER_POWER8NVL:
 	case PVR_VER_POWER8:
+		cpu_has_power_mce = true;
 		cpu_has_heai = true;
+		cpu_has_siar = true;
 		break;
 	default:
 		break;
 	}
+
+	if (!cpu_has_hv) /* HEIR is HV register */
+		cpu_has_heai = false;
 }
 
 static void mem_init(phys_addr_t freemem_start)
diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
index 12de7499b..db263a59e 100644
--- a/lib/ppc64/asm/ptrace.h
+++ b/lib/ppc64/asm/ptrace.h
@@ -5,6 +5,9 @@
 #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
 
 #ifndef __ASSEMBLY__
+
+#include <asm/reg.h>
+
 struct pt_regs {
 	unsigned long gpr[32];
 	unsigned long nip;
@@ -17,6 +20,19 @@ struct pt_regs {
 	unsigned long _pad; /* stack must be 16-byte aligned */
 };
 
+static inline bool regs_is_prefix(volatile struct pt_regs *regs)
+{
+	return regs->msr & SRR1_PREFIX;
+}
+
+static inline void regs_advance_insn(struct pt_regs *regs)
+{
+	if (regs_is_prefix(regs))
+		regs->nip += 8;
+	else
+		regs->nip += 4;
+}
+
 #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
 				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
 
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 1e181da69..68165fc25 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -12,7 +12,8 @@ tests-common = \
 	$(TEST_DIR)/rtas.elf \
 	$(TEST_DIR)/emulator.elf \
 	$(TEST_DIR)/tm.elf \
-	$(TEST_DIR)/sprs.elf
+	$(TEST_DIR)/sprs.elf \
+	$(TEST_DIR)/interrupts.elf
 
 tests-all = $(tests-common) $(tests)
 all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
new file mode 100644
index 000000000..442f8c569
--- /dev/null
+++ b/powerpc/interrupts.c
@@ -0,0 +1,415 @@
+/*
+ * Test interrupts
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include <libcflat.h>
+#include <util.h>
+#include <migrate.h>
+#include <alloc.h>
+#include <asm/setup.h>
+#include <asm/handlers.h>
+#include <asm/hcall.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/barrier.h>
+
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+
+static void mce_handler(struct pt_regs *regs, void *opaque)
+{
+	bool *is_fetch = opaque;
+
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	if (*is_fetch)
+		regs->nip = regs->link;
+	else
+		regs_advance_insn(regs);
+}
+
+static void fault_handler(struct pt_regs *regs, void *opaque)
+{
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	if (regs->trap == 0x400 || regs->trap == 0x480)
+		regs->nip = regs->link;
+	else
+		regs_advance_insn(regs);
+}
+
+static void test_mce(void)
+{
+	unsigned long addr = -4ULL;
+	uint8_t tmp;
+	bool is_fetch;
+
+	report_prefix_push("mce");
+
+	handle_exception(0x200, mce_handler, &is_fetch);
+	handle_exception(0x300, fault_handler, NULL);
+	handle_exception(0x380, fault_handler, NULL);
+	handle_exception(0x400, fault_handler, NULL);
+	handle_exception(0x480, fault_handler, NULL);
+
+	if (machine_is_powernv()) {
+		enable_mcheck();
+	} else {
+		report(mfmsr() & MSR_ME, "pseries machine has MSR[ME]=1");
+		if (!(mfmsr() & MSR_ME)) { /* try to fix it */
+			enable_mcheck();
+		}
+		if (mfmsr() & MSR_ME) {
+			disable_mcheck();
+			report(mfmsr() & MSR_ME, "pseries is unable to change MSR[ME]");
+			if (!(mfmsr() & MSR_ME)) { /* try to fix it */
+				enable_mcheck();
+			}
+		}
+	}
+
+	is_fetch = false;
+	asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
+
+	report(got_interrupt, "MCE on access to invalid real address");
+	if (got_interrupt) {
+		report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly");
+		if (cpu_has_power_mce)
+			report(recorded_regs.msr & (1ULL << 21), "d-side MCE sets SRR1[42]");
+		got_interrupt = false;
+	}
+
+	is_fetch = true;
+	asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
+	report(got_interrupt, "MCE on fetch from invalid real address");
+	if (got_interrupt) {
+		report(recorded_regs.nip == addr, "MCE sets SRR0 correctly");
+		if (cpu_has_power_mce)
+			report(!(recorded_regs.msr & (1ULL << 21)), "i-side MCE clears SRR1[42]");
+		got_interrupt = false;
+	}
+
+	handle_exception(0x200, NULL, NULL);
+	handle_exception(0x300, NULL, NULL);
+	handle_exception(0x380, NULL, NULL);
+	handle_exception(0x400, NULL, NULL);
+	handle_exception(0x480, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void dseg_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+	regs->msr &= ~MSR_DR;
+}
+
+static void test_dseg(void)
+{
+	uint64_t msr, tmp;
+
+	report_prefix_push("data segment");
+
+	/* Some HV start in radix mode and need 0x300 */
+	handle_exception(0x300, &dseg_handler, NULL);
+	handle_exception(0x380, &dseg_handler, NULL);
+
+	asm volatile(
+"		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0		\n \
+		lbz	%1,0(0)		"
+		: "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory");
+
+	report(got_interrupt, "interrupt on NULL dereference");
+	got_interrupt = false;
+
+	handle_exception(0x300, NULL, NULL);
+	handle_exception(0x380, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void dec_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->msr &= ~MSR_EE;
+}
+
+static void test_dec(void)
+{
+	uint64_t msr;
+	uint64_t tb;
+
+	report_prefix_push("decrementer");
+
+	handle_exception(0x900, &dec_handler, NULL);
+
+	asm volatile(
+"		mtdec	%1		\n \
+		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "r"(10000), "i"(MSR_EE): "memory");
+
+	tb = get_tb();
+	while (!got_interrupt) {
+		if (get_tb() - tb > tb_hz * 5)
+			break; /* timeout 5s */
+	}
+
+	report(got_interrupt, "interrupt on decrementer underflow");
+	got_interrupt = false;
+
+	handle_exception(0x900, NULL, NULL);
+
+	if (!machine_is_powernv())
+		goto done; /* Skip HV tests */
+
+	handle_exception(0x980, &dec_handler, NULL);
+
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE);
+	asm volatile(
+"		mtspr	0x136,%1	\n \
+		mtdec	%3		\n \
+		mfmsr	%0		\n \
+		ori	%0,%0,%2	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "r"(10000), "i"(MSR_EE), "r"(0x7fffffff): "memory");
+
+	tb = get_tb();
+	while (!got_interrupt) {
+		if (get_tb() - tb > tb_hz * 5)
+			break; /* timeout 5s */
+	}
+
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE);
+
+	report(got_interrupt, "interrupt on hdecrementer underflow");
+	got_interrupt = false;
+
+	handle_exception(0x980, NULL, NULL);
+
+done:
+	report_prefix_pop();
+}
+
+
+static volatile uint64_t recorded_heir;
+
+static void heai_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+	if (cpu_has_heai)
+		recorded_heir = mfspr(SPR_HEIR);
+}
+
+static void program_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+}
+
+/*
+ * This tests invalid instruction handling. powernv (HV) should take an
+ * HEAI interrupt with the HEIR SPR set to the instruction image. pseries
+ * (guest) should take a program interrupt. CPUs which support prefix
+ * should report prefix instruction in (H)SRR1[34].
+ */
+static void test_illegal(void)
+{
+	report_prefix_push("illegal instruction");
+
+	if (machine_is_powernv()) {
+		handle_exception(0xe40, &heai_handler, NULL);
+	} else {
+		handle_exception(0x700, &program_handler, NULL);
+	}
+
+	asm volatile(".long 0x12345678" ::: "memory");
+	report(got_interrupt, "interrupt on invalid instruction");
+	got_interrupt = false;
+	if (cpu_has_heai)
+		report(recorded_heir == 0x12345678, "HEIR: 0x%08lx", recorded_heir);
+	report(!regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit clear");
+
+	if (cpu_has_prefix) {
+		asm volatile(".balign 8 ; .long 0x04000123; .long 0x00badc0d");
+		report(got_interrupt, "interrupt on invalid prefix instruction");
+		got_interrupt = false;
+		if (cpu_has_heai)
+			report(recorded_heir == 0x0400012300badc0d, "HEIR: 0x%08lx", recorded_heir);
+		report(regs_is_prefix(&recorded_regs), "(H)SRR1 prefix bit set");
+	}
+
+	handle_exception(0xe40, NULL, NULL);
+	handle_exception(0x700, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+static void sc_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+}
+
+static void test_sc(void)
+{
+	report_prefix_push("syscall");
+
+	handle_exception(0xc00, &sc_handler, NULL);
+
+	asm volatile("sc 0" ::: "memory");
+
+	report(got_interrupt, "interrupt on sc 0 instruction");
+	got_interrupt = false;
+	if (cpu_has_sc_lev)
+		report(((recorded_regs.msr >> 20) & 0x3) == 0, "SRR1 set LEV=0");
+	if (machine_is_powernv()) {
+		asm volatile("sc 1" ::: "memory");
+
+		report(got_interrupt, "interrupt on sc 1 instruction");
+		got_interrupt = false;
+		if (cpu_has_sc_lev)
+			report(((recorded_regs.msr >> 20) & 0x3) == 1, "SRR1 set LEV=1");
+	}
+
+	handle_exception(0xc00, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+
+static void trace_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->msr &= ~(MSR_SE | MSR_BE);
+}
+
+static void program_trace_handler(struct pt_regs *regs, void *data)
+{
+	regs->msr &= ~(MSR_SE | MSR_BE);
+	regs->nip += 4;
+}
+
+extern char trace_insn[];
+extern char trace_insn2[];
+extern char trace_insn3[];
+extern char trace_rfid[];
+
+static void test_trace(void)
+{
+	unsigned long msr;
+
+	report_prefix_push("trace");
+
+	handle_exception(0xd00, &trace_handler, NULL);
+
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn		\n"
+	"trace_insn:			\n"
+	"	nop			\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on single step");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn + 4,
+			"single step interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn,
+			"single step recorded SIAR at the correct address");
+
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn2		\n"
+	"trace_insn2:			\n"
+	"	b	1f		\n"
+	"	nop			\n"
+	"1:				\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on single step branch");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn2 + 8,
+			"single step interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn2,
+			"single step recorded SIAR at the correct address");
+
+	msr = mfmsr() | MSR_BE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	".global trace_insn3		\n"
+	"trace_insn3:			\n"
+	"	nop			\n"
+	"	b	1f		\n"
+	"	nop			\n"
+	"1:				\n"
+	: : "r"(msr) : "memory");
+
+	report(got_interrupt, "interrupt on branch trace");
+	got_interrupt = false;
+	report(recorded_regs.nip == (unsigned long)trace_insn3 + 12,
+			"branch trace interrupt at the correct address");
+	if (cpu_has_siar)
+		report(mfspr(SPR_SIAR) == (unsigned long)trace_insn3 + 4,
+			"branch trace recorded SIAR at the correct address");
+
+	handle_exception(0x700, &program_trace_handler, NULL);
+	msr = mfmsr() | MSR_SE;
+	asm volatile(
+	"	mtmsr	%0		\n"
+	"	trap			\n"
+	: : "r"(msr) : "memory");
+
+	report(!got_interrupt, "no interrupt on single step trap");
+	got_interrupt = false;
+	handle_exception(0x700, NULL, NULL);
+
+	msr = mfmsr() | MSR_SE;
+	mtspr(SPR_SRR0, (unsigned long)trace_rfid);
+	mtspr(SPR_SRR1, mfmsr());
+	asm volatile(
+	"	mtmsr	%0		\n"
+	"	rfid			\n"
+	".global trace_rfid		\n"
+	"trace_rfid:			\n"
+	: : "r"(msr) : "memory");
+
+	report(!got_interrupt, "no interrupt on single step rfid");
+	got_interrupt = false;
+	handle_exception(0xd00, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+
+int main(int argc, char **argv)
+{
+	report_prefix_push("interrupts");
+
+	if (cpu_has_power_mce)
+		test_mce();
+	test_dseg();
+	test_illegal();
+	test_dec();
+	test_sc();
+	test_trace();
+
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 41bb8a327..93c54f52a 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -86,6 +86,9 @@ groups = rtas
 [emulator]
 file = emulator.elf
 
+[interrupts]
+file = interrupts.elf
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 15/32] powerpc: Add rtas stop-self support
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (13 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 16/32] powerpc: Remove broken SMP exception stack setup Nicholas Piggin
                   ` (16 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

In preparation for improved SMP support, add stop-self support to the
harness. This is non-trivial because it requires an unlocked rtas
call: a CPU can't be holding a spin lock when it goes offline or it
will deadlock other CPUs. rtas permits stop-self to be called without
serialising all other rtas operations.

Reviewed-by: Thomas Huth <thuth@redhat.com>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/rtas.h |  2 ++
 lib/powerpc/rtas.c     | 78 +++++++++++++++++++++++++++++++++---------
 2 files changed, 64 insertions(+), 16 deletions(-)

diff --git a/lib/powerpc/asm/rtas.h b/lib/powerpc/asm/rtas.h
index 6fb407a18..364bf9355 100644
--- a/lib/powerpc/asm/rtas.h
+++ b/lib/powerpc/asm/rtas.h
@@ -23,8 +23,10 @@ struct rtas_args {
 extern void rtas_init(void);
 extern int rtas_token(const char *service, uint32_t *token);
 extern int rtas_call(int token, int nargs, int nret, int *outputs, ...);
+extern int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...);
 
 extern void rtas_power_off(void);
+extern void rtas_stop_self(void);
 #endif /* __ASSEMBLY__ */
 
 #define RTAS_MSR_MASK 0xfffffffffffffffe
diff --git a/lib/powerpc/rtas.c b/lib/powerpc/rtas.c
index 41c0a243e..b477a38e0 100644
--- a/lib/powerpc/rtas.c
+++ b/lib/powerpc/rtas.c
@@ -87,40 +87,86 @@ int rtas_token(const char *service, uint32_t *token)
 	return 0;
 }
 
-int rtas_call(int token, int nargs, int nret, int *outputs, ...)
+static void __rtas_call(struct rtas_args *args)
 {
-	va_list list;
-	int ret, i;
+	enter_rtas(__pa(args));
+}
 
-	spin_lock(&rtas_lock);
+static int rtas_call_unlocked_va(struct rtas_args *args,
+			  int token, int nargs, int nret, int *outputs,
+			  va_list list)
+{
+	int ret, i;
 
-	rtas_args.token = cpu_to_be32(token);
-	rtas_args.nargs = cpu_to_be32(nargs);
-	rtas_args.nret = cpu_to_be32(nret);
-	rtas_args.rets = &rtas_args.args[nargs];
+	args->token = cpu_to_be32(token);
+	args->nargs = cpu_to_be32(nargs);
+	args->nret = cpu_to_be32(nret);
+	args->rets = &args->args[nargs];
 
-	va_start(list, outputs);
 	for (i = 0; i < nargs; ++i)
-		rtas_args.args[i] = cpu_to_be32(va_arg(list, u32));
-	va_end(list);
+		args->args[i] = cpu_to_be32(va_arg(list, u32));
 
 	for (i = 0; i < nret; ++i)
-		rtas_args.rets[i] = 0;
+		args->rets[i] = 0;
 
-	enter_rtas(__pa(&rtas_args));
+	__rtas_call(args);
 
 	if (nret > 1 && outputs != NULL)
 		for (i = 0; i < nret - 1; ++i)
-			outputs[i] = be32_to_cpu(rtas_args.rets[i + 1]);
+			outputs[i] = be32_to_cpu(args->rets[i + 1]);
+
+	ret = nret > 0 ? be32_to_cpu(args->rets[0]) : 0;
+
+	return ret;
+}
+
+int rtas_call_unlocked(struct rtas_args *args, int token, int nargs, int nret, int *outputs, ...)
+{
+	va_list list;
+	int ret;
 
-	ret = nret > 0 ? be32_to_cpu(rtas_args.rets[0]) : 0;
+	va_start(list, outputs);
+	ret = rtas_call_unlocked_va(args, token, nargs, nret, outputs, list);
+	va_end(list);
+
+	return ret;
+}
+
+int rtas_call(int token, int nargs, int nret, int *outputs, ...)
+{
+	va_list list;
+	int ret;
+
+	spin_lock(&rtas_lock);
+
+	va_start(list, outputs);
+	ret = rtas_call_unlocked_va(&rtas_args, token, nargs, nret, outputs, list);
+	va_end(list);
 
 	spin_unlock(&rtas_lock);
+
 	return ret;
 }
 
+void rtas_stop_self(void)
+{
+	struct rtas_args args;
+	uint32_t token;
+	int ret;
+
+	ret = rtas_token("stop-self", &token);
+	if (ret) {
+		puts("RTAS stop-self not available\n");
+		return;
+	}
+
+	ret = rtas_call_unlocked(&args, token, 0, 1, NULL);
+	printf("RTAS stop-self returned %d\n", ret);
+}
+
 void rtas_power_off(void)
 {
+	struct rtas_args args;
 	uint32_t token;
 	int ret;
 
@@ -130,6 +176,6 @@ void rtas_power_off(void)
 		return;
 	}
 
-	ret = rtas_call(token, 2, 1, NULL, -1, -1);
+	ret = rtas_call_unlocked(&args, token, 2, 1, NULL, -1, -1);
 	printf("RTAS power-off returned %d\n", ret);
 }
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 16/32] powerpc: Remove broken SMP exception stack setup
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (14 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 15/32] powerpc: Add rtas stop-self support Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages Nicholas Piggin
                   ` (15 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

The exception stack setup does not work correctly for SMP, because
it is the boot processor that calls cpu_set() which sets SPRG2 to
the exception stack, not the target CPU itself. So secondaries
never got their SPRG2 set to a valid exception stack.

Remove the SMP code and just set an exception stack for the boot
processor. Make the stack 64kB while we're here, to match the
size of the regular stack.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/setup.c | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 9b665f59c..496af40f8 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -42,10 +42,6 @@ struct cpu_set_params {
 	uint64_t tb_hz;
 };
 
-#define EXCEPTION_STACK_SIZE	(32*1024) /* 32kB */
-
-static char exception_stack[NR_CPUS][EXCEPTION_STACK_SIZE];
-
 static void cpu_set(int fdtnode, u64 regval, void *info)
 {
 	static bool read_common_info = false;
@@ -56,10 +52,6 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
 
 	cpus[cpu] = regval;
 
-	/* set exception stack address for this CPU (in SPGR0) */
-	asm volatile ("mtsprg0 %[addr]" ::
-		      [addr] "r" (exception_stack[cpu + 1]));
-
 	if (!read_common_info) {
 		const struct fdt_property *prop;
 		u32 *data;
@@ -180,6 +172,10 @@ static void mem_init(phys_addr_t freemem_start)
 					 ? __icache_bytes : __dcache_bytes);
 }
 
+#define EXCEPTION_STACK_SIZE	SZ_64K
+
+static char boot_exception_stack[EXCEPTION_STACK_SIZE];
+
 void setup(const void *fdt)
 {
 	void *freemem = &stacktop;
@@ -189,6 +185,10 @@ void setup(const void *fdt)
 
 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
 
+	/* set exception stack address for this CPU (in SPGR0) */
+	asm volatile ("mtsprg0 %[addr]" ::
+		      [addr] "r" (boot_exception_stack));
+
 	enable_mcheck();
 
 	/*
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (15 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 16/32] powerpc: Remove broken SMP exception stack setup Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 11:51   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 18/32] powerpc: add SMP and IPI support Nicholas Piggin
                   ` (14 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

In SMP tests, it's possible for multiple CPUs to print an exit
message if they abort concurrently, confusing the harness:

  EXIT: STATUS=127

  EXIT: STATUS=127
  scripts/arch-run.bash: line 85: [: too many arguments
  scripts/arch-run.bash: line 93: return: too many arguments

lib/arch code should probably serialise this to prevent it, but
at the moment not all do. So make the parser handle this by
just looking at the first EXIT.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <thuth@redhat.com>
Cc: Andrew Jones <andrew.jones@linux.dev>
Cc: kvm@vger.kernel.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 scripts/arch-run.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash
index 5c7e72036..4af670f1c 100644
--- a/scripts/arch-run.bash
+++ b/scripts/arch-run.bash
@@ -79,7 +79,7 @@ run_qemu_status ()
 	exec {stdout}>&-
 
 	if [ $ret -eq 1 ]; then
-		testret=$(grep '^EXIT: ' <<<"$lines" | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/')
+		testret=$(grep '^EXIT: ' <<<"$lines" | head -n1 | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/')
 		if [ "$testret" ]; then
 			if [ $testret -eq 1 ]; then
 				ret=0
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 18/32] powerpc: add SMP and IPI support
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (16 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 19/32] powerpc: Permit ACCEL=tcg,thread=single Nicholas Piggin
                   ` (13 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

powerpc SMP support is very primitive and does not set up a first-class
runtime environment for secondary CPUs.

This reworks SMP support, and provides a complete C and harness
environment for the secondaries, including interrupt handling, as well
as IPI support.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |  23 +++
 lib/powerpc/asm/reg.h       |   1 +
 lib/powerpc/asm/setup.h     |   2 -
 lib/powerpc/asm/smp.h       |  46 +++--
 lib/powerpc/io.c            |  15 +-
 lib/powerpc/processor.c     |   7 +-
 lib/powerpc/setup.c         |  90 +++++++---
 lib/powerpc/smp.c           | 282 +++++++++++++++++++++++++----
 lib/ppc64/asm-offsets.c     |   7 +
 lib/ppc64/asm/atomic.h      |   6 +
 lib/ppc64/asm/barrier.h     |   3 +
 lib/ppc64/asm/opal.h        |   7 +
 powerpc/Makefile.common     |   1 +
 powerpc/cstart64.S          |  52 +++++-
 powerpc/selftest.c          |   4 +-
 powerpc/smp.c               | 349 ++++++++++++++++++++++++++++++++++++
 powerpc/tm.c                |   4 +-
 powerpc/unittests.cfg       |   8 +
 18 files changed, 822 insertions(+), 85 deletions(-)
 create mode 100644 lib/ppc64/asm/atomic.h
 create mode 100644 powerpc/smp.c

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index eed37d1f4..a3859b5d4 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -16,6 +16,7 @@ extern bool cpu_has_siar;
 extern bool cpu_has_heai;
 extern bool cpu_has_prefix;
 extern bool cpu_has_sc_lev;
+extern bool cpu_has_pause_short;
 
 static inline uint64_t mfspr(int nr)
 {
@@ -45,6 +46,28 @@ static inline void mtmsr(uint64_t msr)
 	asm volatile ("mtmsrd %[msr]" :: [msr] "r" (msr) : "memory");
 }
 
+static inline void local_irq_enable(void)
+{
+	unsigned long msr;
+
+	asm volatile(
+"		mfmsr	%0		\n \
+		ori	%0,%0,%1	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "i"(MSR_EE): "memory");
+}
+
+static inline void local_irq_disable(void)
+{
+	unsigned long msr;
+
+	asm volatile(
+"		mfmsr	%0		\n \
+		andc	%0,%0,%1	\n \
+		mtmsrd	%0,1		"
+		: "=r"(msr) : "r"(MSR_EE): "memory");
+}
+
 /*
  * This returns true on PowerNV / OPAL machines which run in hypervisor
  * mode. False on pseries / PAPR machines that run in guest mode.
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index d6097f48f..d2ca964c4 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -19,6 +19,7 @@
 #define SPR_SPRG1	0x111
 #define SPR_SPRG2	0x112
 #define SPR_SPRG3	0x113
+#define SPR_TBU40	0x11e
 #define SPR_PVR		0x11f
 #define   PVR_VERSION_MASK	UL(0xffff0000)
 #define   PVR_VER_970		UL(0x00390000)
diff --git a/lib/powerpc/asm/setup.h b/lib/powerpc/asm/setup.h
index cc7cf5e25..9ca318ce6 100644
--- a/lib/powerpc/asm/setup.h
+++ b/lib/powerpc/asm/setup.h
@@ -8,8 +8,6 @@
 #include <libcflat.h>
 
 #define NR_CPUS			8	/* arbitrarily set for now */
-extern u32 cpus[NR_CPUS];
-extern int nr_cpus;
 
 extern uint64_t tb_hz;
 
diff --git a/lib/powerpc/asm/smp.h b/lib/powerpc/asm/smp.h
index 21940b4bc..4519e5436 100644
--- a/lib/powerpc/asm/smp.h
+++ b/lib/powerpc/asm/smp.h
@@ -2,21 +2,45 @@
 #define _ASMPOWERPC_SMP_H_
 
 #include <libcflat.h>
+#include <asm/processor.h>
 
-extern int nr_threads;
+typedef void (*secondary_entry_fn)(int cpu_id);
 
-struct start_threads {
-	int nr_threads;
-	int nr_started;
-};
+struct cpu {
+	unsigned long server_no;
+	unsigned long stack;
+	unsigned long exception_stack;
+	secondary_entry_fn entry;
+} __attribute__((packed)); /* used by asm */
 
-typedef void (*secondary_entry_fn)(void);
+extern int nr_cpus_present;
+extern int nr_cpus_online;
+extern struct cpu cpus[];
 
-extern void halt(void);
+register struct cpu *__current_cpu asm("r13");
+static inline struct cpu *current_cpu(void)
+{
+	return __current_cpu;
+}
 
-extern int start_thread(int cpu_id, secondary_entry_fn entry, uint32_t r3);
-extern struct start_threads start_cpu(int cpu_node, secondary_entry_fn entry,
-				      uint32_t r3);
-extern bool start_all_cpus(secondary_entry_fn entry, uint32_t r3);
+static inline int smp_processor_id(void)
+{
+	return current_cpu()->server_no;
+}
+
+void cpu_init(struct cpu *cpu, int cpu_id);
+
+extern void halt(int cpu_id);
+
+extern bool start_all_cpus(secondary_entry_fn entry);
+extern void stop_all_cpus(void);
+
+struct pt_regs;
+void register_ipi(void (*fn)(struct pt_regs *, void *), void *data);
+void unregister_ipi(void);
+void cpu_init_ipis(void);
+void local_ipi_enable(void);
+void local_ipi_disable(void);
+void send_ipi(int cpu_id);
 
 #endif /* _ASMPOWERPC_SMP_H_ */
diff --git a/lib/powerpc/io.c b/lib/powerpc/io.c
index ab7bb843c..cb7f2f050 100644
--- a/lib/powerpc/io.c
+++ b/lib/powerpc/io.c
@@ -10,6 +10,7 @@
 #include <asm/rtas.h>
 #include <asm/setup.h>
 #include <asm/processor.h>
+#include <asm/atomic.h>
 #include "io.h"
 
 static struct spinlock print_lock;
@@ -55,13 +56,17 @@ extern void halt(int code);
 
 void exit(int code)
 {
+	static int exited = 0;
+
 // FIXME: change this print-exit/rtas-poweroff to chr_testdev_exit(),
 //        maybe by plugging chr-testdev into a spapr-vty.
-	printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
-	if (machine_is_powernv())
-		opal_power_off();
-	else
-		rtas_power_off();
+	if (atomic_fetch_inc(&exited) == 0) {
+		printf("\nEXIT: STATUS=%d\n", ((code) << 1) | 1);
+		if (machine_is_powernv())
+			opal_power_off();
+		else
+			rtas_power_off();
+	}
 	halt(code);
 	__builtin_unreachable();
 }
diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
index 1b4bb0d61..a6ce3c905 100644
--- a/lib/powerpc/processor.c
+++ b/lib/powerpc/processor.c
@@ -13,6 +13,7 @@
 #include <asm/barrier.h>
 #include <asm/hcall.h>
 #include <asm/handlers.h>
+#include <asm/smp.h>
 
 static struct {
 	void (*func)(struct pt_regs *, void *data);
@@ -44,6 +45,8 @@ void do_handle_exception(struct pt_regs *regs)
 {
 	unsigned char v;
 
+	__current_cpu = (struct cpu *)mfspr(SPR_SPRG0);
+
 	v = regs->trap >> 5;
 
 	if (v < 128 && handlers[v].func) {
@@ -51,8 +54,8 @@ void do_handle_exception(struct pt_regs *regs)
 		return;
 	}
 
-	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
-			regs->trap, regs->nip, regs->msr);
+	printf("Unhandled CPU%d exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
+		smp_processor_id(), regs->trap, regs->nip, regs->msr);
 	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
 	abort();
 }
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 496af40f8..16f009152 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -2,7 +2,7 @@
  * Initialize machine setup information and I/O.
  *
  * After running setup() unit tests may query how many cpus they have
- * (nr_cpus), how much memory they have (PHYSICAL_END - PHYSICAL_START),
+ * (nr_cpus_present), how much memory they have (PHYSICAL_END - PHYSICAL_START),
  * may use dynamic memory allocation (malloc, etc.), printf, and exit.
  * Finally, argc and argv are also ready to be passed to main().
  *
@@ -17,6 +17,7 @@
 #include <alloc_phys.h>
 #include <argv.h>
 #include <asm/setup.h>
+#include <asm/smp.h>
 #include <asm/page.h>
 #include <asm/ptrace.h>
 #include <asm/processor.h>
@@ -28,8 +29,8 @@ extern unsigned long stacktop;
 char *initrd;
 u32 initrd_size;
 
-u32 cpus[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
-int nr_cpus;
+u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
+int nr_cpus_present;
 uint64_t tb_hz;
 
 struct mem_region mem_regions[NR_MEM_REGIONS];
@@ -44,13 +45,32 @@ struct cpu_set_params {
 
 static void cpu_set(int fdtnode, u64 regval, void *info)
 {
+	const struct fdt_property *prop;
+	u32 *threads;
 	static bool read_common_info = false;
 	struct cpu_set_params *params = info;
-	int cpu = nr_cpus++;
-
-	assert_msg(cpu < NR_CPUS, "Number cpus exceeds maximum supported (%d).", NR_CPUS);
-
-	cpus[cpu] = regval;
+	int nr_threads;
+	int len, i;
+
+	/* Get the id array of threads on this node */
+	prop = fdt_get_property(dt_fdt(), fdtnode,
+				"ibm,ppc-interrupt-server#s", &len);
+	assert(prop);
+
+	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
+	threads = (u32 *)prop->data; /* Array of valid ids */
+
+	for (i = 0; i < nr_threads; i++) {
+		if (nr_cpus_present >= NR_CPUS) {
+			static bool warned = false;
+			if (!warned) {
+				printf("Warning: Number of present CPUs exceeds maximum supported (%d).\n", NR_CPUS);
+				warned = true;
+			}
+			break;
+		}
+		cpu_to_hwid[nr_cpus_present++] = fdt32_to_cpu(threads[i]);
+	}
 
 	if (!read_common_info) {
 		const struct fdt_property *prop;
@@ -84,32 +104,25 @@ bool cpu_has_siar;
 bool cpu_has_heai;
 bool cpu_has_prefix;
 bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
+bool cpu_has_pause_short;
 
-static void cpu_init(void)
+static void cpu_init_params(void)
 {
 	struct cpu_set_params params;
 	int ret;
 
-	nr_cpus = 0;
+	nr_cpus_present = 0;
 	ret = dt_for_each_cpu_node(cpu_set, &params);
 	assert(ret == 0);
 	__icache_bytes = params.icache_bytes;
 	__dcache_bytes = params.dcache_bytes;
 	tb_hz = params.tb_hz;
 
-	/* Interrupt Endianness */
-	if (machine_is_pseries()) {
-#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-		hcall(H_SET_MODE, 1, 4, 0, 0);
-#else
-		hcall(H_SET_MODE, 0, 4, 0, 0);
-#endif
-	}
-
 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
 	case PVR_VER_POWER10:
 		cpu_has_prefix = true;
 		cpu_has_sc_lev = true;
+		cpu_has_pause_short = true;
 	case PVR_VER_POWER9:
 	case PVR_VER_POWER8E:
 	case PVR_VER_POWER8NVL:
@@ -175,19 +188,37 @@ static void mem_init(phys_addr_t freemem_start)
 #define EXCEPTION_STACK_SIZE	SZ_64K
 
 static char boot_exception_stack[EXCEPTION_STACK_SIZE];
+struct cpu cpus[NR_CPUS];
+
+void cpu_init(struct cpu *cpu, int cpu_id)
+{
+	cpu->server_no = cpu_id;
+
+	cpu->stack = (unsigned long)memalign(SZ_4K, SZ_64K);
+	cpu->stack += SZ_64K - 64;
+	cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K);
+	cpu->exception_stack += SZ_64K - 64;
+}
 
 void setup(const void *fdt)
 {
 	void *freemem = &stacktop;
 	const char *bootargs, *tmp;
+	struct cpu *cpu;
 	u32 fdt_size;
 	int ret;
 
 	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
 
-	/* set exception stack address for this CPU (in SPGR0) */
-	asm volatile ("mtsprg0 %[addr]" ::
-		      [addr] "r" (boot_exception_stack));
+	memset(cpus, 0xff, sizeof(cpus));
+
+	cpu = &cpus[0];
+	cpu->server_no = fdt_boot_cpuid_phys(fdt);
+	cpu->exception_stack = (unsigned long)boot_exception_stack;
+	cpu->exception_stack += SZ_64K - 64;
+
+	mtspr(SPR_SPRG0, (unsigned long)cpu);
+	__current_cpu = cpu;
 
 	enable_mcheck();
 
@@ -230,8 +261,19 @@ void setup(const void *fdt)
 
 	assert(STACK_INT_FRAME_SIZE % 16 == 0);
 
-	/* call init functions */
-	cpu_init();
+	/* set parameters from dt */
+	cpu_init_params();
+
+	/* Interrupt Endianness */
+	if (machine_is_pseries()) {
+#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+		hcall(H_SET_MODE, 1, 4, 0, 0);
+#else
+		hcall(H_SET_MODE, 0, 4, 0, 0);
+#endif
+	}
+
+	cpu_init_ipis();
 
 	/* cpu_init must be called before mem_init */
 	mem_init(PAGE_ALIGN((unsigned long)freemem));
diff --git a/lib/powerpc/smp.c b/lib/powerpc/smp.c
index 3e211eba8..a3bf85d44 100644
--- a/lib/powerpc/smp.c
+++ b/lib/powerpc/smp.c
@@ -6,58 +6,253 @@
  * This work is licensed under the terms of the GNU LGPL, version 2.
  */
 
+#include <alloc.h>
 #include <devicetree.h>
+#include <asm/atomic.h>
+#include <asm/barrier.h>
+#include <asm/processor.h>
 #include <asm/time.h>
 #include <asm/setup.h>
+#include <asm/opal.h>
+#include <asm/hcall.h>
 #include <asm/rtas.h>
 #include <asm/smp.h>
 
-int nr_threads;
-
 struct secondary_entry_data {
 	secondary_entry_fn entry;
-	uint64_t r3;
-	int nr_started;
 };
 
+int nr_cpus_online = 1;
+
+static void stop_self(int cpu_id)
+{
+	if (machine_is_powernv()) {
+		if (opal_call(OPAL_RETURN_CPU, 0, 0, 0) != OPAL_SUCCESS) {
+			printf("OPAL_RETURN_CPU failed\n");
+		}
+	} else {
+		rtas_stop_self();
+	}
+
+	printf("failed to stop cpu %d\n", cpu_id);
+	assert(0);
+}
+
+void main_secondary(struct cpu *cpu);
+void main_secondary(struct cpu *cpu)
+{
+	mtspr(SPR_SPRG0, (unsigned long)cpu);
+	__current_cpu = cpu;
+
+	enable_mcheck();
+
+	cpu_init_ipis();
+
+	atomic_fetch_inc(&nr_cpus_online);
+
+	cpu->entry(cpu->server_no);
+
+	mb();
+	atomic_fetch_dec(&nr_cpus_online);
+
+	stop_self(cpu->server_no);
+}
+
+enum OpalThreadStatus {
+        OPAL_THREAD_INACTIVE = 0x0,
+        OPAL_THREAD_STARTED = 0x1,
+        OPAL_THREAD_UNAVAILABLE = 0x2 /* opal-v3 */
+};
+
+#define H_EOI		0x64
+#define H_CPPR		0x68
+#define H_IPI		0x6c
+#define H_XIRR		0x74
+
+static void (*ipi_fn)(struct pt_regs *regs, void *data);
+
+static void dbell_handler(struct pt_regs *regs, void *data)
+{
+	/* sync */
+	ipi_fn(regs, data);
+}
+
+static void extint_handler(struct pt_regs *regs, void *data)
+{
+	int32_t xirr;
+	int32_t xisr;
+	int64_t rc;
+
+	asm volatile("mr r3,%1 ; sc 1 ; mr %0,r4" : "=r"(xirr) : "r"(H_XIRR));
+	xisr = xirr & 0xffffff;
+
+	if (xisr == 2) { /* IPI */
+		rc = hcall(H_IPI, smp_processor_id(), 0xff);
+		assert(rc == H_SUCCESS);
+	}
+
+	xirr |= (5 << 24);
+	rc = hcall(H_EOI, xirr);
+	assert(rc == H_SUCCESS);
+
+	/* lower IPI */
+	ipi_fn(regs, data);
+}
+
+void cpu_init_ipis(void)
+{
+	if (machine_is_powernv()) {
+		/* skiboot can leave some messages set */
+		unsigned long rb = (5 << (63-36));
+		asm volatile("msgclr	%0" :: "r"(rb) : "memory");
+	}
+}
+
+void local_ipi_enable(void)
+{
+	if (machine_is_pseries()) {
+		hcall(H_CPPR, 5);
+	}
+}
+
+void local_ipi_disable(void)
+{
+	if (machine_is_pseries()) {
+		hcall(H_CPPR, 0);
+	}
+}
+
+void register_ipi(void (*fn)(struct pt_regs *, void *), void *data)
+{
+	ipi_fn = fn;
+	if (machine_is_powernv()) {
+		handle_exception(0xe80, &dbell_handler, data);
+	} else {
+		handle_exception(0x500, &extint_handler, data);
+	}
+}
+
+void unregister_ipi(void)
+{
+	if (machine_is_powernv()) {
+		handle_exception(0xe80, NULL, NULL);
+	} else {
+		handle_exception(0x500, NULL, NULL);
+	}
+}
+
+void send_ipi(int cpu_id)
+{
+	if (machine_is_powernv()) {
+		unsigned long rb = (5 << (63-36)) | cpu_id;
+		asm volatile("lwsync" ::: "memory");
+		asm volatile("msgsnd	%0" :: "r"(rb) : "memory");
+	} else {
+		hcall(H_IPI, cpu_id, 4);
+	}
+}
+
+static int nr_started = 1;
+
+extern void start_secondary(uint64_t server_no); /* asm entry point */
+
+static bool cpu_is_running(int cpu_id)
+{
+	if (machine_is_powernv()) {
+		int64_t ret;
+		uint8_t status;
+
+		ret = opal_call(OPAL_QUERY_CPU_STATUS, cpu_id, (unsigned long)&status, 0);
+		if (ret != OPAL_SUCCESS) {
+			printf("OPAL_QUERY_CPU_STATUS failed for cpu %d\n", cpu_id);
+			return false;
+		}
+		return (status != OPAL_THREAD_INACTIVE);
+	} else {
+		uint32_t query_token;
+		int outputs[1], ret;
+
+		ret = rtas_token("query-cpu-stopped-state", &query_token);
+		if (ret != 0) {
+			printf("rtas token query-cpu-stopped-state failed\n");
+			return false;
+		}
+
+		ret = rtas_call(query_token, 1, 2, outputs, cpu_id);
+		if (ret) {
+			printf("query-cpu-stopped-state failed for cpu %d\n", cpu_id);
+			return ret;
+		}
+		if (outputs[0]) /* cpu not in stopped state */
+			return true;
+		return false;
+	}
+}
+
 /*
  * Start stopped thread cpu_id at entry
  * Returns:	<0 on failure to start stopped cpu
  *		0  on success
  *		>0 on cpu not in stopped state
  */
-int start_thread(int cpu_id, secondary_entry_fn entry, uint32_t r3)
+static int start_thread(int cpu_id, secondary_entry_fn entry)
 {
-	uint32_t query_token, start_token;
-	int outputs[1], ret;
+	struct cpu *cpu;
+	uint64_t tb;
 
-	ret = rtas_token("query-cpu-stopped-state", &query_token);
-	assert(ret == 0);
-	ret = rtas_token("start-cpu", &start_token);
-	assert(ret == 0);
+	if (nr_started >= NR_CPUS) {
+		/* Reached limit */
+		return -1;
+	}
+
+	if (cpu_id == smp_processor_id()) {
+		/* Boot CPU already started */
+		return -1;
+	}
 
-	ret = rtas_call(query_token, 1, 2, outputs, cpu_id);
-	if (ret) {
-		printf("query-cpu-stopped-state failed for cpu %d\n", cpu_id);
-	} else if (!outputs[0]) { /* cpu in stopped state */
-		ret = rtas_call(start_token, 3, 1, NULL, cpu_id, entry, r3);
-		if (ret)
+	tb = get_tb();
+	while (cpu_is_running(cpu_id)) {
+		if (get_tb() - tb > 3*tb_hz) {
+			printf("Unable to start running CPU:%d\n", cpu_id);
+			return 1;
+		}
+	}
+
+	cpu = &cpus[nr_started];
+	nr_started++;
+
+	cpu_init(cpu, cpu_id);
+	cpu->entry = entry;
+
+	if (machine_is_powernv()) {
+		if (opal_call(OPAL_START_CPU, cpu_id, (unsigned long)start_secondary, 0) != OPAL_SUCCESS) {
 			printf("failed to start cpu %d\n", cpu_id);
-	} else { /* cpu not in stopped state */
-		ret = outputs[0];
+			return -1;
+		}
+	} else {
+		uint32_t start_token;
+		int ret;
+
+		ret = rtas_token("start-cpu", &start_token);
+		assert(ret == 0);
+
+		ret = rtas_call(start_token, 3, 1, NULL, cpu_id, start_secondary, cpu_id);
+		if (ret) {
+			printf("failed to start cpu %d\n", cpu_id);
+			return ret;
+		}
 	}
 
-	return ret;
+	return 0;
 }
 
 /*
  * Start all stopped threads (vcpus) on cpu_node
  * Returns: Number of stopped cpus which were successfully started
  */
-struct start_threads start_cpu(int cpu_node, secondary_entry_fn entry,
-			       uint32_t r3)
+static void start_core(int cpu_node, secondary_entry_fn entry)
 {
-	int len, i, nr_threads, nr_started = 0;
+	int len, i, nr_threads;
 	const struct fdt_property *prop;
 	u32 *threads;
 
@@ -67,23 +262,18 @@ struct start_threads start_cpu(int cpu_node, secondary_entry_fn entry,
 	assert(prop);
 
 	nr_threads = len >> 2; /* Divide by 4 since 4 bytes per thread */
-	threads = (u32 *)prop->data; /* Array of valid ids */
 
-	for (i = 0; i < nr_threads; i++) {
-		if (!start_thread(fdt32_to_cpu(threads[i]), entry, r3))
-			nr_started++;
-	}
+	threads = (u32 *)prop->data; /* Array of valid ids */
 
-	return (struct start_threads) { nr_threads, nr_started };
+	for (i = 0; i < nr_threads; i++)
+		start_thread(fdt32_to_cpu(threads[i]), entry);
 }
 
 static void start_each_secondary(int fdtnode, u64 regval __unused, void *info)
 {
 	struct secondary_entry_data *datap = info;
-	struct start_threads ret = start_cpu(fdtnode, datap->entry, datap->r3);
 
-	nr_threads += ret.nr_threads;
-	datap->nr_started += ret.nr_started;
+	start_core(fdtnode, datap->entry);
 }
 
 /*
@@ -92,14 +282,34 @@ static void start_each_secondary(int fdtnode, u64 regval __unused, void *info)
  * Returns:	TRUE on success
  *		FALSE on failure
  */
-bool start_all_cpus(secondary_entry_fn entry, uint32_t r3)
+bool start_all_cpus(secondary_entry_fn entry)
 {
-	struct secondary_entry_data data = { entry, r3,	0 };
+	struct secondary_entry_data data = { entry };
+	uint64_t tb;
 	int ret;
 
+	assert(nr_cpus_online == 1);
+	assert(nr_started == 1);
 	ret = dt_for_each_cpu_node(start_each_secondary, &data);
 	assert(ret == 0);
+	assert(nr_started == nr_cpus_present);
 
-	/* We expect that we come in with one thread already started */
-	return data.nr_started == nr_threads - 1;
+	tb = get_tb();
+	while (nr_cpus_online < nr_cpus_present) {
+		if (get_tb() - tb > 3*tb_hz) {
+			printf("failed to start all secondaries\n");
+			assert(0);
+		}
+		cpu_relax();
+	}
+
+	return 1;
+}
+
+void stop_all_cpus(void)
+{
+	while (nr_cpus_online > 1)
+		cpu_relax();
+	mb();
+	nr_started = 1;
 }
diff --git a/lib/ppc64/asm-offsets.c b/lib/ppc64/asm-offsets.c
index 7843a20b4..0ac2c9a75 100644
--- a/lib/ppc64/asm-offsets.c
+++ b/lib/ppc64/asm-offsets.c
@@ -6,6 +6,8 @@
 #include <libcflat.h>
 #include <kbuild.h>
 #include <asm/ptrace.h>
+#include <asm/setup.h>
+#include <asm/smp.h>
 
 int main(void)
 {
@@ -50,5 +52,10 @@ int main(void)
 	DEFINE(_XER, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, xer));
 	DEFINE(_CCR, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, ccr));
 	DEFINE(_TRAP, STACK_FRAME_OVERHEAD+offsetof(struct pt_regs, trap));
+
+	DEFINE(SIZEOF_STRUCT_CPU, sizeof(struct cpu));
+	DEFINE(EXCEPTION_STACK, offsetof(struct cpu, exception_stack));
+	DEFINE(MAX_CPUS, NR_CPUS);
+
 	return 0;
 }
diff --git a/lib/ppc64/asm/atomic.h b/lib/ppc64/asm/atomic.h
new file mode 100644
index 000000000..0f461b9ce
--- /dev/null
+++ b/lib/ppc64/asm/atomic.h
@@ -0,0 +1,6 @@
+#ifndef _POWERPC_ATOMIC_H_
+#define _POWERPC_ATOMIC_H_
+
+#include "asm-generic/atomic.h"
+
+#endif /* _POWERPC_ATOMIC_H_ */
diff --git a/lib/ppc64/asm/barrier.h b/lib/ppc64/asm/barrier.h
index 76f6efa78..475434b6a 100644
--- a/lib/ppc64/asm/barrier.h
+++ b/lib/ppc64/asm/barrier.h
@@ -1,6 +1,9 @@
 #ifndef _ASMPPC64_BARRIER_H_
 #define _ASMPPC64_BARRIER_H_
 
+#define cpu_relax() asm volatile("or 1,1,1 ; or 2,2,2" ::: "memory")
+#define pause_short() asm volatile(".long 0x7c40003c" ::: "memory")
+
 #define mb() asm volatile("sync":::"memory")
 #define rmb() asm volatile("sync":::"memory")
 #define wmb() asm volatile("sync":::"memory")
diff --git a/lib/ppc64/asm/opal.h b/lib/ppc64/asm/opal.h
index de64e2c8d..6c3e9ffe2 100644
--- a/lib/ppc64/asm/opal.h
+++ b/lib/ppc64/asm/opal.h
@@ -2,14 +2,21 @@
 #ifndef _ASMPPC64_OPAL_H_
 #define _ASMPPC64_OPAL_H_
 
+#include <stdint.h>
+
 #define OPAL_SUCCESS				0
 
 #define OPAL_CONSOLE_WRITE			1
 #define OPAL_CONSOLE_READ			2
 #define OPAL_CEC_POWER_DOWN			5
 #define OPAL_POLL_EVENTS			10
+#define OPAL_START_CPU				41
+#define OPAL_QUERY_CPU_STATUS			42
+#define OPAL_RETURN_CPU				69
 #define OPAL_REINIT_CPUS			70
 # define OPAL_REINIT_CPUS_HILE_BE		(1 << 0)
 # define OPAL_REINIT_CPUS_HILE_LE		(1 << 1)
 
+int64_t opal_call(int64_t token, int64_t arg1, int64_t arg2, int64_t arg3);
+
 #endif
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 68165fc25..744dfc1f7 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -12,6 +12,7 @@ tests-common = \
 	$(TEST_DIR)/rtas.elf \
 	$(TEST_DIR)/emulator.elf \
 	$(TEST_DIR)/tm.elf \
+	$(TEST_DIR)/smp.elf \
 	$(TEST_DIR)/sprs.elf \
 	$(TEST_DIR)/interrupts.elf
 
diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
index 5e091e325..dcc147749 100644
--- a/powerpc/cstart64.S
+++ b/powerpc/cstart64.S
@@ -128,6 +128,52 @@ start:
 	bl	exit
 	b	halt
 
+/*
+ * start_secondary is the secondary entry point. r3 contains the cpu id
+ */
+.globl start_secondary
+start_secondary:
+	FIXUP_ENDIAN
+	/* Switch to 64-bit mode */
+	mfmsr	r1
+	li	r2,1
+	sldi	r2,r2,MSR_SF_BIT
+	or	r1,r1,r2
+	mtmsrd	r1
+
+	bl	0f
+0:	mflr	r31
+	subi	r31, r31, 0b - start	/* QEMU's kernel load address */
+
+	ld	r2, (p_toc - start)(r31)
+
+	LOAD_REG_ADDR(r9, cpus)
+	li	r8,0
+	li	r7,0
+1:	ldx	r6,r9,r7
+	cmpd	r6,r3
+	beq	2f
+	addi	r7,r7,SIZEOF_STRUCT_CPU
+	addi	r8,r8,1
+	cmpdi	r8,MAX_CPUS
+	bne	1b
+	b	.
+
+2:	add	r3,r9,r7
+	ld	r1,8(r3)
+
+	/* Zero backpointers in initial stack frame so backtrace() stops */
+	li	r0,0
+	std	r0,0(r1)
+	std	r0,16(r1)
+
+	/* Create entry frame */
+	stdu	r1,-INT_FRAME_SIZE(r1)
+
+	bl	main_secondary
+	bl	exit
+	b	halt
+
 .align 3
 p_stack:	.llong  stackptr
 p_toc:		.llong  tocptr
@@ -260,7 +306,8 @@ exception_stack_marker:
 	. = \vec
 
 	mtsprg1	r1	/* save r1 */
-	mfsprg0	r1	/* get exception stack address */
+	mfsprg0	r1	/* get struct cpu address */
+	ld	r1,EXCEPTION_STACK(r1)	/* get exception stack address */
 	subi	r1,r1, INT_FRAME_SIZE
 
 	/* save r0 and ctr to call generic handler */
@@ -276,7 +323,8 @@ exception_stack_marker:
 	. = \vec
 
 	mtsprg1	r1	/* save r1 */
-	mfsprg0	r1	/* get exception stack address */
+	mfsprg0	r1	/* get struct cpu address */
+	ld	r1,EXCEPTION_STACK(r1)	/* get exception stack address */
 	subi	r1,r1, INT_FRAME_SIZE
 
 	/* save r0 and ctr to call generic handler */
diff --git a/powerpc/selftest.c b/powerpc/selftest.c
index 7acff7104..8d1a2c767 100644
--- a/powerpc/selftest.c
+++ b/powerpc/selftest.c
@@ -8,6 +8,7 @@
 #include <libcflat.h>
 #include <util.h>
 #include <asm/setup.h>
+#include <asm/smp.h>
 
 static void check_setup(int argc, char **argv)
 {
@@ -34,7 +35,8 @@ static void check_setup(int argc, char **argv)
 
 		} else if (strcmp(argv[i], "smp") == 0) {
 
-			report(nr_cpus == (int)val, "nr_cpus = %d", nr_cpus);
+			report(nr_cpus_present == (int)val,
+				"nr_cpus_present = %d", nr_cpus_present);
 			++nr_tests;
 		}
 
diff --git a/powerpc/smp.c b/powerpc/smp.c
new file mode 100644
index 000000000..530a9398e
--- /dev/null
+++ b/powerpc/smp.c
@@ -0,0 +1,349 @@
+/*
+ * SMP and IPI Tests
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include <libcflat.h>
+#include <asm/atomic.h>
+#include <asm/barrier.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/smp.h>
+#include <asm/setup.h>
+#include <asm/ppc_asm.h>
+#include <devicetree.h>
+
+static volatile bool start_test_running = true;
+static volatile int nr_cpus_started;
+
+static void start_fn(int cpu_id)
+{
+	atomic_fetch_inc(&nr_cpus_started);
+	while (start_test_running)
+		cpu_relax();
+	atomic_fetch_dec(&nr_cpus_started);
+}
+
+static void test_start_cpus(int argc, char **argv)
+{
+	uint64_t tb;
+
+	if (argc > 2)
+		report_abort("Unsupported argument: '%s'", argv[2]);
+
+	nr_cpus_started = 1;
+	if (!start_all_cpus(start_fn))
+		report_abort("Failed to start secondary cpus");
+
+	tb = get_tb();
+	while (nr_cpus_started < nr_cpus_present) {
+		cpu_relax();
+		if (get_tb() - tb > tb_hz * 5)
+			report_abort("Failed to start all secondaries");
+	}
+
+	if (nr_cpus_started != nr_cpus_online)
+		report_abort("Started CPUs does not match online");
+
+	barrier();
+	start_test_running = false;
+	barrier();
+
+	tb = get_tb();
+	while (nr_cpus_started > 1) {
+		cpu_relax();
+		if (get_tb() - tb > tb_hz * 5)
+			report_abort("Failed to stop all secondaries");
+	}
+
+	stop_all_cpus();
+
+	report(true, "start cpus");
+}
+
+static volatile int nr_cpus_ipi = 0;
+
+static void ipi_handler(struct pt_regs *regs, void *data)
+{
+	atomic_fetch_inc(&nr_cpus_ipi);
+}
+
+static volatile bool ipi_test_running = true;
+
+static void ipi_fn(int cpu_id)
+{
+	local_ipi_enable();
+
+	mtspr(SPR_DEC, 0x7fffffff);
+	local_irq_enable();
+	while (ipi_test_running)
+		cpu_relax();
+	local_irq_disable();
+
+	local_ipi_disable();
+}
+
+static void test_ipi_cpus(int argc, char **argv)
+{
+	uint64_t tb;
+	int i;
+
+	if (argc > 2)
+		report_abort("Unsupported argument: '%s'", argv[2]);
+
+	if (nr_cpus_present < 2) {
+		report_skip("Requires SMP (2 or more CPUs)");
+		return;
+	}
+
+	register_ipi(ipi_handler, NULL);
+
+	if (!start_all_cpus(ipi_fn))
+		report_abort("Failed to start secondary cpus");
+
+	for (i = 1; i < nr_cpus_online; i++)
+		send_ipi(cpus[i].server_no);
+
+	tb = get_tb();
+	while (nr_cpus_ipi < nr_cpus_online - 1) {
+		cpu_relax();
+		if (get_tb() - tb > tb_hz * 5)
+			report_abort("Secondaries failed to respond to IPIs");
+	}
+
+	send_ipi(cpus[1].server_no);
+
+	tb = get_tb();
+	while (nr_cpus_ipi < nr_cpus_online) {
+		cpu_relax();
+		if (get_tb() - tb > tb_hz * 5)
+			report_abort("Secondaries failed to respond to IPIs");
+	}
+
+	ipi_test_running = false;
+
+	stop_all_cpus();
+
+	assert(nr_cpus_ipi == nr_cpus_present);
+
+	unregister_ipi();
+
+	report(true, "IPI cpus");
+}
+
+static uint64_t time;
+static bool time_went_backward;
+
+static void check_and_record_time(void)
+{
+	uint64_t tb;
+	uint64_t t;
+	uint64_t old;
+
+	t = time;
+again:
+	barrier();
+	tb = get_tb();
+	asm volatile("1: ldarx %0,0,%1 ; cmpd %0,%2 ; bne 2f ; stdcx. %3,0,%1 ; bne- 1b; 2:" : "=&r"(old) : "r"(&time), "r"(t), "r"(tb) : "memory", "cr0");
+	assert(tb >= t);
+	if (old != t) {
+		t = old;
+		goto again;
+	}
+	if (old > tb)
+		time_went_backward = true;
+}
+
+static void update_time(int64_t tb_offset)
+{
+	uint64_t new_tb;
+
+	new_tb = get_tb() + tb_offset;
+	mtspr(SPR_TBU40, new_tb);
+	if ((get_tb() & 0xFFFFFF) < (new_tb & 0xFFFFFF)) {
+		new_tb += 0x1000000;
+		mtspr(SPR_TBU40, new_tb);
+	}
+}
+
+static void time_sync_fn(int cpu_id)
+{
+	uint64_t start = get_tb();
+
+	while (!time_went_backward && get_tb() - start < tb_hz*2) {
+		check_and_record_time();
+		cpu_relax();
+	}
+
+	while (!time_went_backward && get_tb() - start < tb_hz*2) {
+		check_and_record_time();
+		udelay(1);
+	}
+
+	if (machine_is_powernv()) {
+		while (!time_went_backward && get_tb() - start < tb_hz*2) {
+			check_and_record_time();
+			update_time(0x1234000000);
+			cpu_relax();
+			update_time(-0x1234000000);
+		}
+	}
+}
+
+static void test_time_sync(int argc, char **argv)
+{
+	if (argc > 2)
+		report_abort("Unsupported argument: '%s'", argv[2]);
+
+	if (nr_cpus_present < 2) {
+		report_skip("Requires SMP (2 or more CPUs)");
+		return;
+	}
+
+	time_went_backward = false;
+
+	if (!start_all_cpus(time_sync_fn))
+		report_abort("Failed to start secondary cpus");
+
+	time_sync_fn(-1);
+
+	stop_all_cpus();
+
+	report(!time_went_backward, "time sync");
+}
+
+static volatile bool relax_test_running = true;
+
+static int relax_loop_count[NR_CPUS];
+
+static void relax_fn(int cpu_id)
+{
+	volatile int i = 0;
+
+	while (relax_test_running) {
+		cpu_relax();
+		i++;
+	}
+
+	relax_loop_count[cpu_id] = i;
+}
+
+#define ITERS 1000000
+
+static void test_relax(int argc, char **argv)
+{
+	volatile int i;
+	int count;
+
+	if (argc > 2)
+		report_abort("Unsupported argument: '%s'", argv[2]);
+
+	if (nr_cpus_present < 2) {
+		report_skip("Requires SMP (2 or more CPUs)");
+		return;
+	}
+
+	if (!start_all_cpus(relax_fn))
+		report_abort("Failed to start secondary cpus");
+
+	for (i = 0; i < ITERS; i++)
+		;
+
+	relax_test_running = false;
+
+	stop_all_cpus();
+
+	count = 0;
+	for (i = 0; i < NR_CPUS; i++)
+		count += relax_loop_count[i];
+	if (count == 0)
+		count = 1;
+
+	report(true, "busy-loops on CPU:%d vs cpu_relax-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count);
+}
+
+static volatile bool pause_test_running = true;
+
+static int pause_loop_count[NR_CPUS];
+
+static void pause_fn(int cpu_id)
+{
+	volatile int i = 0;
+
+	while (pause_test_running) {
+		pause_short();
+		i++;
+	}
+
+	pause_loop_count[cpu_id] = i;
+}
+
+#define ITERS 1000000
+
+static void test_pause(int argc, char **argv)
+{
+	volatile int i;
+	int count;
+
+	if (argc > 2)
+		report_abort("Unsupported argument: '%s'", argv[2]);
+
+	if (!cpu_has_pause_short)
+		return;
+
+	if (nr_cpus_present < 2) {
+		report_skip("Requires SMP (2 or more CPUs)");
+		return;
+	}
+
+	if (!start_all_cpus(pause_fn))
+		report_abort("Failed to start secondary cpus");
+
+	for (i = 0; i < ITERS; i++)
+		;
+
+	pause_test_running = false;
+
+	stop_all_cpus();
+
+	count = 0;
+	for (i = 0; i < NR_CPUS; i++)
+		count += pause_loop_count[i];
+
+	report(true, "busy-loops on CPU:%d vs pause_short-loops on others %ld%%", smp_processor_id(), (long)ITERS * 100 / count);
+}
+
+struct {
+	const char *name;
+	void (*func)(int argc, char **argv);
+} hctests[] = {
+	{ "start_cpus", test_start_cpus },
+	{ "ipi_cpus", test_ipi_cpus },
+	{ "time_sync", test_time_sync },
+	{ "cpu_relax", test_relax },
+	{ "pause", test_pause },
+	{ NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+	bool all;
+	int i;
+
+	all = argc == 1 || !strcmp(argv[1], "all");
+
+	report_prefix_push("smp");
+
+	for (i = 0; hctests[i].name != NULL; i++) {
+		if (all || strcmp(argv[1], hctests[i].name) == 0) {
+			report_prefix_push(hctests[i].name);
+			hctests[i].func(argc, argv);
+			report_prefix_pop();
+		}
+	}
+
+	report_prefix_pop();
+	return report_summary();
+}
diff --git a/powerpc/tm.c b/powerpc/tm.c
index 6b1ceeb6e..efbcf1f12 100644
--- a/powerpc/tm.c
+++ b/powerpc/tm.c
@@ -89,7 +89,7 @@ static void test_h_cede_tm(int argc, char **argv)
 	if (argc > 2)
 		report_abort("Unsupported argument: '%s'", argv[2]);
 
-	if (!start_all_cpus(halt, 0))
+	if (!start_all_cpus(halt))
 		report_abort("Failed to start secondary cpus");
 
 	if (!enable_tm())
@@ -133,7 +133,7 @@ int main(int argc, char **argv)
 		report_skip("TM is not available");
 		goto done;
 	}
-	report(cpus_with_tm == nr_cpus,
+	report(cpus_with_tm == nr_cpus_present,
 	       "TM available in all 'ibm,pa-features' properties");
 
 	all = argc == 1 || !strcmp(argv[1], "all");
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 93c54f52a..97a549c0d 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -89,6 +89,14 @@ file = emulator.elf
 [interrupts]
 file = interrupts.elf
 
+[smp]
+file = smp.elf
+smp = 2
+
+[smp-smt]
+file = smp.elf
+smp = 8,threads=4
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 19/32] powerpc: Permit ACCEL=tcg,thread=single
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (17 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 18/32] powerpc: add SMP and IPI support Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 20/32] powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is running Nicholas Piggin
                   ` (12 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Modify run script to permit single vs mttcg threading, add a
thread=single smp case to unittests.cfg.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/run           | 4 ++--
 powerpc/unittests.cfg | 6 ++++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/powerpc/run b/powerpc/run
index 172f32a46..27abf1ef6 100755
--- a/powerpc/run
+++ b/powerpc/run
@@ -36,8 +36,8 @@ if ! $qemu -machine '?' 2>&1 | grep $MACHINE > /dev/null; then
 	exit 2
 fi
 
+A="-accel $ACCEL$ACCEL_PROPS"
 M="-machine $MACHINE"
-M+=",accel=$ACCEL$ACCEL_PROPS"
 B=""
 D=""
 
@@ -54,7 +54,7 @@ if [[ "$MACHINE" == "powernv"* ]] ; then
 	D+="-device ipmi-bmc-sim,id=bmc0 -device isa-ipmi-bt,bmc=bmc0,irq=10"
 fi
 
-command="$qemu -nodefaults $M $B $D"
+command="$qemu -nodefaults $A $M $B $D"
 command+=" -display none -serial stdio -kernel"
 command="$(migration_cmd) $(timeout_cmd) $command"
 
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 97a549c0d..915b6a482 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -97,6 +97,12 @@ smp = 2
 file = smp.elf
 smp = 8,threads=4
 
+# mttcg is the default most places, so add a thread=single test
+[smp-thread-single]
+file = smp.elf
+smp = 8,threads=4
+accel = tcg,thread=single
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 20/32] powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is running
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (18 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 19/32] powerpc: Permit ACCEL=tcg,thread=single Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 21/32] powerpc: Add atomics tests Nicholas Piggin
                   ` (11 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

The test harness uses spinlocks if they are implemented with larx/stcx.
it can prevent some test scenarios such as testing migration of a
reservation.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/smp.h    |  1 +
 lib/powerpc/smp.c        |  5 +++++
 lib/powerpc/spinlock.c   | 28 ++++++++++++++++++++++++++++
 lib/ppc64/asm/spinlock.h |  7 ++++++-
 powerpc/Makefile.common  |  1 +
 5 files changed, 41 insertions(+), 1 deletion(-)
 create mode 100644 lib/powerpc/spinlock.c

diff --git a/lib/powerpc/asm/smp.h b/lib/powerpc/asm/smp.h
index 4519e5436..6ef3ae521 100644
--- a/lib/powerpc/asm/smp.h
+++ b/lib/powerpc/asm/smp.h
@@ -15,6 +15,7 @@ struct cpu {
 
 extern int nr_cpus_present;
 extern int nr_cpus_online;
+extern bool multithreaded;
 extern struct cpu cpus[];
 
 register struct cpu *__current_cpu asm("r13");
diff --git a/lib/powerpc/smp.c b/lib/powerpc/smp.c
index a3bf85d44..f3b2a3faf 100644
--- a/lib/powerpc/smp.c
+++ b/lib/powerpc/smp.c
@@ -276,6 +276,8 @@ static void start_each_secondary(int fdtnode, u64 regval __unused, void *info)
 	start_core(fdtnode, datap->entry);
 }
 
+bool multithreaded = false;
+
 /*
  * Start all stopped cpus on the guest at entry with register 3 set to r3
  * We expect that we come in with only one thread currently started
@@ -290,6 +292,7 @@ bool start_all_cpus(secondary_entry_fn entry)
 
 	assert(nr_cpus_online == 1);
 	assert(nr_started == 1);
+	multithreaded = true;
 	ret = dt_for_each_cpu_node(start_each_secondary, &data);
 	assert(ret == 0);
 	assert(nr_started == nr_cpus_present);
@@ -308,8 +311,10 @@ bool start_all_cpus(secondary_entry_fn entry)
 
 void stop_all_cpus(void)
 {
+	assert(multithreaded);
 	while (nr_cpus_online > 1)
 		cpu_relax();
 	mb();
 	nr_started = 1;
+	multithreaded = false;
 }
diff --git a/lib/powerpc/spinlock.c b/lib/powerpc/spinlock.c
new file mode 100644
index 000000000..238549f12
--- /dev/null
+++ b/lib/powerpc/spinlock.c
@@ -0,0 +1,28 @@
+#include <asm/spinlock.h>
+#include <asm/smp.h>
+
+/*
+ * Skip the atomic when single-threaded, which helps avoid larx/stcx. in
+ * the harness when testing tricky larx/stcx. sequences (e.g., migration
+ * vs reservation).
+ */
+void spin_lock(struct spinlock *lock)
+{
+	if (!multithreaded) {
+		assert(lock->v == 0);
+		lock->v = 1;
+	} else {
+		while (__sync_lock_test_and_set(&lock->v, 1))
+			;
+	}
+}
+
+void spin_unlock(struct spinlock *lock)
+{
+	assert(lock->v == 1);
+	if (!multithreaded) {
+		lock->v = 0;
+	} else {
+		__sync_lock_release(&lock->v);
+	}
+}
diff --git a/lib/ppc64/asm/spinlock.h b/lib/ppc64/asm/spinlock.h
index f59eed191..b952386da 100644
--- a/lib/ppc64/asm/spinlock.h
+++ b/lib/ppc64/asm/spinlock.h
@@ -1,6 +1,11 @@
 #ifndef _ASMPPC64_SPINLOCK_H_
 #define _ASMPPC64_SPINLOCK_H_
 
-#include <asm-generic/spinlock.h>
+struct spinlock {
+	unsigned int v;
+};
+
+void spin_lock(struct spinlock *lock);
+void spin_unlock(struct spinlock *lock);
 
 #endif /* _ASMPPC64_SPINLOCK_H_ */
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 744dfc1f7..02af54b83 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -48,6 +48,7 @@ cflatobjs += lib/powerpc/rtas.o
 cflatobjs += lib/powerpc/processor.o
 cflatobjs += lib/powerpc/handlers.o
 cflatobjs += lib/powerpc/smp.o
+cflatobjs += lib/powerpc/spinlock.o
 
 OBJDIRS += lib/powerpc
 
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 21/32] powerpc: Add atomics tests
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (19 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 20/32] powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is running Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 22/32] powerpc: Add timebase tests Nicholas Piggin
                   ` (10 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/Makefile.common |   1 +
 powerpc/atomics.c       | 373 ++++++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg   |   9 +
 3 files changed, 383 insertions(+)
 create mode 100644 powerpc/atomics.c

diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 02af54b83..b6f9b3b85 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -11,6 +11,7 @@ tests-common = \
 	$(TEST_DIR)/spapr_hcall.elf \
 	$(TEST_DIR)/rtas.elf \
 	$(TEST_DIR)/emulator.elf \
+	$(TEST_DIR)/atomics.elf \
 	$(TEST_DIR)/tm.elf \
 	$(TEST_DIR)/smp.elf \
 	$(TEST_DIR)/sprs.elf \
diff --git a/powerpc/atomics.c b/powerpc/atomics.c
new file mode 100644
index 000000000..d79e70eaa
--- /dev/null
+++ b/powerpc/atomics.c
@@ -0,0 +1,373 @@
+/*
+ * Test some powerpc instructions
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ */
+#include <stdint.h>
+#include <libcflat.h>
+#include <migrate.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/atomic.h>
+#include <asm/setup.h>
+#include <asm/barrier.h>
+#include <asm/smp.h>
+
+static bool do_migrate;
+static bool do_record;
+
+#define RSV_SIZE 128
+
+static uint8_t granule[RSV_SIZE] __attribute((__aligned__(RSV_SIZE)));
+
+static void spin_lock(unsigned int *lock)
+{
+	unsigned int old;
+
+	asm volatile ("1:"
+		      "lwarx	%0,0,%2;"
+		      "cmpwi	%0,0;"
+		      "bne	1b;"
+		      "stwcx.	%1,0,%2;"
+		      "bne-	1b;"
+		      "lwsync;"
+		      : "=&r"(old) : "r"(1), "r"(lock) : "cr0", "memory");
+}
+
+static void spin_unlock(unsigned int *lock)
+{
+	asm volatile("lwsync;"
+		     "stw	%1,%0;"
+		     : "+m"(*lock) : "r"(0) : "memory");
+}
+
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+
+static void interrupt_handler(struct pt_regs *regs, void *opaque)
+{
+	assert(!got_interrupt);
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs_advance_insn(regs);
+}
+
+static void test_lwarx_stwcx(int argc, char *argv[])
+{
+	unsigned int *var = (unsigned int *)granule;
+	unsigned int old;
+	unsigned int result;
+
+	*var = 0;
+	asm volatile ("1:"
+		      "lwarx	%0,0,%2;"
+		      "stwcx.	%1,0,%2;"
+		      "bne-	1b;"
+		      : "=&r"(old) : "r"(1), "r"(var) : "cr0", "memory");
+	report(old == 0 && *var == 1, "simple update");
+
+	*var = 0;
+	asm volatile ("li	%0,0;"
+		      "stwcx.	%1,0,%2;"
+		      "stwcx.	%1,0,%2;"
+		      "bne-	1f;"
+		      "li	%0,1;"
+		      "1:"
+		      : "=&r"(result)
+		      : "r"(1), "r"(var) : "cr0", "memory");
+	report(result == 0 && *var == 0, "failed stwcx. (no reservation)");
+
+	*var = 0;
+	asm volatile ("li	%0,0;"
+		      "lwarx	%1,0,%4;"
+		      "stw	%3,0(%4);"
+		      "stwcx.	%2,0,%4;"
+		      "bne-	1f;"
+		      "li	%0,1;"
+		      "1:"
+		      : "=&r"(result), "=&r"(old)
+		      : "r"(1), "r"(2), "r"(var) : "cr0", "memory");
+	/* This is implementation specific, so don't fail */
+	if (result == 0 && *var == 2)
+		report(true, "failed stwcx. (intervening store)");
+	else
+		report(true, "succeeded stwcx. (intervening store)");
+
+	handle_exception(0x600, interrupt_handler, NULL);
+	handle_exception(0x700, interrupt_handler, NULL);
+
+	/* Implementations may not necessarily invoke the alignment interrupt */
+	old = 10;
+	*var = 0;
+	asm volatile (
+		      "lwarx	%0,0,%1;"
+		      : "+&r"(old) : "r"((char *)var + 1));
+	report(old == 10 && got_interrupt && recorded_regs.trap == 0x600, "unaligned lwarx causes fault");
+	got_interrupt = false;
+
+	/*
+	 * Unaligned stwcx. is more difficult to test, at least under QEMU,
+	 * the store does not proceed if there is no matching reservation, so
+	 * the alignment handler does not get invoked. This is okay according
+	 * to the Power ISA (unalignment does not necessarily invoke the
+	 * alignment interrupt). But POWER CPUs do cause alignment interrupt.
+	 */
+	*var = 0;
+	asm volatile (
+		      "lwarx	%0,0,%2;"
+		      "stwcx.	%1,0,%3;"
+		      : "=&r"(old) : "r"(1), "r"(var), "r"((char *)var+1) : "cr0", "memory");
+	report(old == 0 && *var == 0 && got_interrupt && recorded_regs.trap == 0x600, "unaligned stwcx. causes fault");
+	got_interrupt = false;
+
+	handle_exception(0x600, NULL, NULL);
+
+}
+
+static void test_lqarx_stqcx(int argc, char *argv[])
+{
+	union {
+		__int128_t var;
+		struct {
+#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+			unsigned long var1;
+			unsigned long var2;
+#else
+			unsigned long var2;
+			unsigned long var1;
+#endif
+		};
+	} var __attribute__((aligned(16)));
+	register unsigned long new1 asm("r8");
+	register unsigned long new2 asm("r9");
+	register unsigned long old1 asm("r10");
+	register unsigned long old2 asm("r11");
+	unsigned int result;
+
+	var.var1 = 1;
+	var.var2 = 2;
+
+	(void)new2;
+	(void)old2;
+
+	old1 = 0;
+	old2 = 0;
+	new1 = 3;
+	new2 = 4;
+	asm volatile ("1:"
+		      "lqarx	%0,0,%4;"
+		      "stqcx.	%2,0,%4;"
+		      "bne-	1b;"
+		      : "=&r"(old1), "=&r"(old2)
+		      : "r"(new1), "r"(new2), "r"(&var)
+		      : "cr0", "memory");
+
+	report(old1 == 2 && old2 == 1 && var.var1 == 4 && var.var2 == 3,
+			"simple update");
+
+	var.var1 = 1;
+	var.var2 = 2;
+	new1 = 3;
+	new2 = 4;
+	asm volatile ("li	%0,0;"
+		      "stqcx.	%1,0,%3;"
+		      "stqcx.	%1,0,%3;"
+		      "bne-	1f;"
+		      "li	%0,1;"
+		      "1:"
+		      : "=&r"(result)
+		      : "r"(new1), "r"(new2), "r"(&var)
+		      : "cr0", "memory");
+	report(result == 0 && var.var1 == 1 && var.var2 == 2,
+			"failed stqcx. (no reservation)");
+
+	var.var1 = 1;
+	var.var2 = 2;
+	new1 = 3;
+	new2 = 4;
+	asm volatile ("li	%0,0;"
+		      "lqarx	%1,0,%6;"
+		      "std	%5,0(%6);"
+		      "stqcx.	%3,0,%6;"
+		      "bne-	1f;"
+		      "li	%0,1;"
+		      "1:"
+		      : "=&r"(result), "=&r"(old1), "=&r"(old2)
+		      : "r"(new1), "r"(new2), "r"(0), "r"(&var)
+		      : "cr0", "memory");
+	/* This is implementation specific, so don't fail */
+	if (result == 0 && (var.var1 == 0 || var.var2 == 0))
+		report(true, "failed stqcx. (intervening store)");
+	else
+		report(true, "succeeded stqcx. (intervening store)");
+}
+
+static void test_migrate_reserve(int argc, char *argv[])
+{
+	unsigned int *var = (unsigned int *)granule;
+	unsigned int old;
+	int i;
+	int succeed = 0;
+
+	if (!do_migrate)
+		return;
+
+	for (i = 0; i < 10; i++) {
+		*var = 0x12345;
+		asm volatile ("lwarx	%0,0,%1" : "=&r"(old) : "r"(var) : "memory");
+		migrate_quiet();
+		asm volatile ("stwcx.	%0,0,%1" : : "r"(0xf00d), "r"(var) : "cr0", "memory");
+		if (*var == 0xf00d)
+			succeed++;
+	}
+
+	if (do_record) {
+		/*
+		 * Running under TCG record-replay, reservations must not
+		 * be lost by migration
+		 */
+		report(succeed > 0, "migrated reservation is not lost");
+	} else {
+		report(succeed == 0, "migrated reservation is lost");
+	}
+
+	report_prefix_pop();
+}
+
+#define ITERS 10000000
+static int test_counter = 0;
+static void test_inc_perf(int argc, char *argv[])
+{
+	int i;
+	uint64_t tb1, tb2;
+
+	tb1 = get_tb();
+	for (i = 0; i < ITERS; i++)
+		__atomic_fetch_add(&test_counter, 1, __ATOMIC_RELAXED);
+	tb2 = get_tb();
+	report(true, "atomic add takes %ldns", (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
+
+	tb1 = get_tb();
+	for (i = 0; i < ITERS; i++)
+		__atomic_fetch_add(&test_counter, 1, __ATOMIC_SEQ_CST);
+	tb2 = get_tb();
+	report(true, "sequentially conssistent atomic add takes %ldns", (tb2 - tb1) * 1000000000 / ITERS / tb_hz);
+}
+
+static long smp_inc_counter = 0;
+static int smp_inc_started;
+
+static void smp_inc_fn(int cpu_id)
+{
+	long i;
+
+	atomic_fetch_inc(&smp_inc_started);
+	while (smp_inc_started < nr_cpus_present)
+		cpu_relax();
+
+	for (i = 0; i < ITERS; i++)
+		atomic_fetch_inc(&smp_inc_counter);
+	atomic_fetch_dec(&smp_inc_started);
+}
+
+static void test_smp_inc(int argc, char **argv)
+{
+	if (nr_cpus_present < 2)
+		return;
+
+	if (!start_all_cpus(smp_inc_fn))
+		report_abort("Failed to start secondary cpus");
+
+	while (smp_inc_started < nr_cpus_present - 1)
+		cpu_relax();
+	smp_inc_fn(smp_processor_id());
+	while (smp_inc_started > 0)
+		cpu_relax();
+
+	stop_all_cpus();
+
+	report(smp_inc_counter == nr_cpus_present * ITERS, "counter lost no increments");
+}
+
+static long smp_lock_counter __attribute__((aligned(128))) = 0;
+static unsigned int smp_lock __attribute__((aligned(128)));
+static int smp_lock_started;
+
+static void smp_lock_fn(int cpu_id)
+{
+	long i;
+
+	atomic_fetch_inc(&smp_lock_started);
+	while (smp_lock_started < nr_cpus_present)
+		cpu_relax();
+
+	for (i = 0; i < ITERS; i++) {
+		spin_lock(&smp_lock);
+		smp_lock_counter++;
+		spin_unlock(&smp_lock);
+	}
+	atomic_fetch_dec(&smp_lock_started);
+}
+
+static void test_smp_lock(int argc, char **argv)
+{
+	if (nr_cpus_present < 2)
+		return;
+
+	if (!start_all_cpus(smp_lock_fn))
+		report_abort("Failed to start secondary cpus");
+
+	while (smp_lock_started < nr_cpus_present - 1)
+		cpu_relax();
+	smp_lock_fn(smp_processor_id());
+	while (smp_lock_started > 0)
+		cpu_relax();
+
+	stop_all_cpus();
+
+	report(smp_lock_counter == nr_cpus_present * ITERS, "counter lost no increments");
+}
+
+struct {
+	const char *name;
+	void (*func)(int argc, char **argv);
+} hctests[] = {
+	{ "lwarx/stwcx", test_lwarx_stwcx },
+	{ "lqarx/stqcx", test_lqarx_stqcx },
+	{ "migration", test_migrate_reserve },
+	{ "performance", test_inc_perf },
+	{ "SMP-atomic", test_smp_inc },
+	{ "SMP-lock", test_smp_lock },
+	{ NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+	int i;
+	int all;
+
+	all = argc == 1 || !strcmp(argv[1], "all");
+
+	for (i = 1; i < argc; i++) {
+		if (strcmp(argv[i], "-r") == 0) {
+			do_record = true;
+		}
+		if (strcmp(argv[i], "-m") == 0) {
+			do_migrate = true;
+		}
+	}
+
+	report_prefix_push("atomics");
+
+	for (i = 0; hctests[i].name != NULL; i++) {
+		if (all || strcmp(argv[1], hctests[i].name) == 0) {
+			report_prefix_push(hctests[i].name);
+			hctests[i].func(argc, argv);
+			report_prefix_pop();
+		}
+	}
+
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 915b6a482..f8d43c220 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -103,6 +103,15 @@ file = smp.elf
 smp = 8,threads=4
 accel = tcg,thread=single
 
+[atomics]
+file = atomics.elf
+
+[atomics-migration]
+file = atomics.elf
+machine = pseries
+extra_params = -append "'migration -m'"
+groups = migration
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 22/32] powerpc: Add timebase tests
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (20 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 21/32] powerpc: Add atomics tests Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support Nicholas Piggin
                   ` (9 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This has a known failure on QEMU TCG machines where the decrementer
interrupt is not lowered when the DEC wraps from -ve to +ve.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/reg.h   |   1 +
 powerpc/Makefile.common |   1 +
 powerpc/timebase.c      | 330 ++++++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg   |   8 +
 4 files changed, 340 insertions(+)
 create mode 100644 powerpc/timebase.c

diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index d2ca964c4..12f9e8ac6 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -35,6 +35,7 @@
 #define SPR_HSRR1	0x13b
 #define SPR_LPCR	0x13e
 #define   LPCR_HDICE		UL(0x1)
+#define   LPCR_LD		UL(0x20000)
 #define SPR_HEIR	0x153
 #define SPR_MMCR0	0x31b
 #define   MMCR0_FC		UL(0x80000000)
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index b6f9b3b85..1348f658b 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -15,6 +15,7 @@ tests-common = \
 	$(TEST_DIR)/tm.elf \
 	$(TEST_DIR)/smp.elf \
 	$(TEST_DIR)/sprs.elf \
+	$(TEST_DIR)/timebase.elf \
 	$(TEST_DIR)/interrupts.elf
 
 tests-all = $(tests-common) $(tests)
diff --git a/powerpc/timebase.c b/powerpc/timebase.c
new file mode 100644
index 000000000..6d8d54cb1
--- /dev/null
+++ b/powerpc/timebase.c
@@ -0,0 +1,330 @@
+/*
+ * Test Timebase
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ *
+ * This contains tests of timebase facility, TB, DEC, etc.
+ */
+#include <libcflat.h>
+#include <util.h>
+#include <migrate.h>
+#include <alloc.h>
+#include <asm/handlers.h>
+#include <devicetree.h>
+#include <asm/hcall.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/barrier.h>
+
+static int dec_bits = 0;
+
+static void cpu_dec_bits(int fdtnode, u64 regval __unused, void *arg __unused)
+{
+	const struct fdt_property *prop;
+	int plen;
+
+	prop = fdt_get_property(dt_fdt(), fdtnode, "ibm,dec-bits", &plen);
+	if (!prop) {
+		dec_bits = 32;
+		return;
+	}
+
+	/* Sanity check for the property layout (first two bytes are header) */
+	assert(plen == 4);
+
+	dec_bits = fdt32_to_cpu(*(uint32_t *)prop->data);
+}
+
+/* Check amount of CPUs nodes that have the TM flag */
+static int find_dec_bits(void)
+{
+	int ret;
+
+	ret = dt_for_each_cpu_node(cpu_dec_bits, NULL);
+	if (ret < 0)
+		return ret;
+
+	return dec_bits;
+}
+
+
+static bool do_migrate = false;
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+
+static uint64_t dec_max;
+static uint64_t dec_min;
+
+static void test_tb(int argc, char **argv)
+{
+	uint64_t tb;
+
+	tb = get_tb();
+	if (do_migrate)
+		migrate();
+	report(get_tb() >= tb, "timebase is incrementing");
+}
+
+static void dec_stop_handler(struct pt_regs *regs, void *data)
+{
+	mtspr(SPR_DEC, dec_max);
+}
+
+static void dec_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->msr &= ~MSR_EE;
+}
+
+static void test_dec(int argc, char **argv)
+{
+	uint64_t tb1, tb2, dec;
+	int i;
+
+	handle_exception(0x900, &dec_handler, NULL);
+
+	for (i = 0; i < 100; i++) {
+		tb1 = get_tb();
+		mtspr(SPR_DEC, dec_max);
+		dec = mfspr(SPR_DEC);
+		tb2 = get_tb();
+		if (tb2 - tb1 < dec_max - dec)
+			break;
+	}
+	report(tb2 - tb1 >= dec_max - dec, "decrementer remains within TB after mtDEC");
+
+	tb1 = get_tb();
+	mtspr(SPR_DEC, dec_max);
+	mdelay(1000);
+	dec = mfspr(SPR_DEC);
+	tb2 = get_tb();
+	report(tb2 - tb1 >= dec_max - dec, "decrementer remains within TB after 1s");
+
+	mtspr(SPR_DEC, dec_max);
+	local_irq_enable();
+	local_irq_disable();
+	if (mfspr(SPR_DEC) <= dec_max) {
+		report(!got_interrupt, "no interrupt on decrementer positive");
+	}
+	got_interrupt = false;
+
+	mtspr(SPR_DEC, 1);
+	mdelay(100); /* Give the timer a chance to run */
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "interrupt on decrementer underflow");
+	got_interrupt = false;
+
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "interrupt on decrementer still underflown");
+	got_interrupt = false;
+
+	mtspr(SPR_DEC, 0);
+	mdelay(100); /* Give the timer a chance to run */
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "DEC deal with set to 0");
+	got_interrupt = false;
+
+	/* Test for level-triggered decrementer */
+	mtspr(SPR_DEC, -1ULL);
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "interrupt on decrementer write MSB");
+	got_interrupt = false;
+
+	mtspr(SPR_DEC, dec_max);
+	local_irq_enable();
+	if (do_migrate)
+		migrate();
+	mtspr(SPR_DEC, -1);
+	local_irq_disable();
+	report(got_interrupt, "interrupt on decrementer write MSB with irqs on");
+	got_interrupt = false;
+
+	mtspr(SPR_DEC, dec_min + 1);
+	mdelay(100);
+	local_irq_enable();
+	local_irq_disable();
+	report(!got_interrupt, "no interrupt after wrap to positive");
+	got_interrupt = false;
+
+	handle_exception(0x900, NULL, NULL);
+}
+
+static void test_hdec(int argc, char **argv)
+{
+	uint64_t tb1, tb2, hdec;
+
+	if (!machine_is_powernv()) {
+		report_skip("skipping on !powernv machine");
+		return;
+	}
+
+	handle_exception(0x900, &dec_stop_handler, NULL);
+	handle_exception(0x980, &dec_handler, NULL);
+
+	mtspr(SPR_HDEC, dec_max);
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_HDICE);
+
+	tb1 = get_tb();
+	mtspr(SPR_HDEC, dec_max);
+	hdec = mfspr(SPR_HDEC);
+	tb2 = get_tb();
+	report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB");
+
+	tb1 = get_tb();
+	mtspr(SPR_HDEC, dec_max);
+	mdelay(1000);
+	hdec = mfspr(SPR_HDEC);
+	tb2 = get_tb();
+	report(tb2 - tb1 >= dec_max - hdec, "hdecrementer remains within TB after 1s");
+
+	mtspr(SPR_HDEC, dec_max);
+	local_irq_enable();
+	local_irq_disable();
+	if (mfspr(SPR_HDEC) <= dec_max) {
+		report(!got_interrupt, "no interrupt on decrementer positive");
+	}
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, 1);
+	mdelay(100); /* Give the timer a chance to run */
+	if (do_migrate)
+		migrate();
+	/* HDEC is edge triggered so ensure it still fires */
+	mtspr(SPR_HDEC, dec_max);
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "interrupt on hdecrementer underflow");
+	got_interrupt = false;
+
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(!got_interrupt, "no interrupt on hdecrementer still underflown");
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, -1ULL);
+	if (do_migrate)
+		migrate();
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "no interrupt on hdecrementer underflown write MSB");
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, 0);
+	mdelay(100); /* Give the timer a chance to run */
+	if (do_migrate)
+		migrate();
+	/* HDEC is edge triggered so ensure it still fires */
+	mtspr(SPR_HDEC, dec_max);
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "HDEC deal with set to 0");
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, dec_max);
+	local_irq_enable();
+	if (do_migrate)
+		migrate();
+	mtspr(SPR_HDEC, -1ULL);
+	local_irq_disable();
+	report(got_interrupt, "interrupt on hdecrementer write MSB with irqs on");
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, dec_max);
+	got_interrupt = false;
+	mtspr(SPR_HDEC, dec_min + 1);
+	if (do_migrate)
+		migrate();
+	mdelay(100);
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "got interrupt after wrap to positive");
+	got_interrupt = false;
+
+	mtspr(SPR_HDEC, -1ULL);
+	local_irq_enable();
+	local_irq_disable();
+	got_interrupt = false;
+	mtspr(SPR_HDEC, dec_min + 1000000);
+	if (do_migrate)
+		migrate();
+	mdelay(100);
+	mtspr(SPR_HDEC, -1ULL);
+	local_irq_enable();
+	local_irq_disable();
+	report(got_interrupt, "edge re-armed after wrap to positive");
+	got_interrupt = false;
+
+	mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_HDICE);
+
+	handle_exception(0x900, NULL, NULL);
+	handle_exception(0x980, NULL, NULL);
+}
+
+struct {
+	const char *name;
+	void (*func)(int argc, char **argv);
+} hctests[] = {
+	{ "tb", test_tb },
+	{ "dec", test_dec },
+	{ "hdec", test_hdec },
+	{ NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+	bool all;
+	int i;
+
+	all = argc == 1 || !strcmp(argv[1], "all");
+
+	for (i = 1; i < argc; i++) {
+		if (!strcmp(argv[i], "-w")) {
+			do_migrate = true;
+			if (!all && argc == 2)
+				all = true;
+		}
+	}
+
+	find_dec_bits();
+	dec_max = (1ULL << (dec_bits - 1)) - 1;
+	dec_min = (1ULL << (dec_bits - 1));
+
+	if (machine_is_powernv() && dec_bits > 32) {
+		mtspr(SPR_LPCR, mfspr(SPR_LPCR) | LPCR_LD);
+	}
+
+	report_prefix_push("timebase");
+
+	for (i = 0; hctests[i].name != NULL; i++) {
+		if (all || strcmp(argv[1], hctests[i].name) == 0) {
+			report_prefix_push(hctests[i].name);
+			hctests[i].func(argc, argv);
+			report_prefix_pop();
+		}
+	}
+
+	report_prefix_pop();
+
+	if (machine_is_powernv() && dec_bits > 32) {
+		mtspr(SPR_LPCR, mfspr(SPR_LPCR) & ~LPCR_LD);
+	}
+
+	return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index f8d43c220..3ebdf9dd3 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -112,6 +112,14 @@ machine = pseries
 extra_params = -append "'migration -m'"
 groups = migration
 
+[timebase]
+file = timebase.elf
+
+[timebase-icount]
+file = timebase.elf
+accel = tcg
+extra_params = -icount shift=5
+
 [h_cede_tm]
 file = tm.elf
 machine = pseries
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (21 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 22/32] powerpc: Add timebase tests Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:01   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition Nicholas Piggin
                   ` (8 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Add support for radix MMU, 4kB and 64kB pages.

This also adds MMU interrupt test cases, and runs the interrupts
test entirely with MMU enabled if it is available (aside from
machine check tests).

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 configure                     |  39 +++--
 lib/powerpc/asm/hcall.h       |   6 +
 lib/powerpc/asm/processor.h   |   1 +
 lib/powerpc/asm/reg.h         |   3 +
 lib/powerpc/asm/smp.h         |   2 +
 lib/powerpc/processor.c       |   9 ++
 lib/powerpc/setup.c           |   4 +
 lib/ppc64/asm/mmu.h           |  11 ++
 lib/ppc64/asm/page.h          |  67 ++++++++-
 lib/ppc64/asm/pgtable-hwdef.h |  67 +++++++++
 lib/ppc64/asm/pgtable.h       | 126 ++++++++++++++++
 lib/ppc64/mmu.c               | 273 ++++++++++++++++++++++++++++++++++
 lib/ppc64/opal-calls.S        |   4 +-
 powerpc/Makefile.common       |   2 +
 powerpc/Makefile.ppc64        |   1 +
 powerpc/interrupts.c          |  96 ++++++++++--
 16 files changed, 684 insertions(+), 27 deletions(-)
 create mode 100644 lib/ppc64/asm/mmu.h
 create mode 100644 lib/ppc64/asm/pgtable-hwdef.h
 create mode 100644 lib/ppc64/asm/pgtable.h
 create mode 100644 lib/ppc64/mmu.c

diff --git a/configure b/configure
index 05e6702ea..6907ccbbb 100755
--- a/configure
+++ b/configure
@@ -222,29 +222,35 @@ fi
 if [ -z "$page_size" ]; then
     if [ "$efi" = 'y' ] && [ "$arch" = "arm64" ]; then
         page_size="4096"
-    elif [ "$arch" = "arm64" ]; then
+    elif [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then
         page_size="65536"
     elif [ "$arch" = "arm" ]; then
         page_size="4096"
     fi
 else
-    if [ "$arch" != "arm64" ]; then
-        echo "--page-size is not supported for $arch"
-        usage
-    fi
-
     if [ "${page_size: -1}" = "K" ] || [ "${page_size: -1}" = "k" ]; then
         page_size=$(( ${page_size%?} * 1024 ))
     fi
-    if [ "$page_size" != "4096" ] && [ "$page_size" != "16384" ] &&
-           [ "$page_size" != "65536" ]; then
-        echo "arm64 doesn't support page size of $page_size"
+
+    if [ "$arch" = "arm64" ]; then
+        if [ "$page_size" != "4096" ] && [ "$page_size" != "16384" ] &&
+               [ "$page_size" != "65536" ]; then
+            echo "arm64 doesn't support page size of $page_size"
+            usage
+        fi
+        if [ "$efi" = 'y' ] && [ "$page_size" != "4096" ]; then
+            echo "efi must use 4K pages"
+            exit 1
+        fi
+    elif [ "$arch" = "ppc64" ]; then
+        if [ "$page_size" != "4096" ] && [ "$page_size" != "65536" ]; then
+            echo "ppc64 doesn't support page size of $page_size"
+            usage
+        fi
+    else
+        echo "--page-size is not supported for $arch"
         usage
     fi
-    if [ "$efi" = 'y' ] && [ "$page_size" != "4096" ]; then
-        echo "efi must use 4K pages"
-        exit 1
-    fi
 fi
 
 [ -z "$processor" ] && processor="$arch"
@@ -444,6 +450,13 @@ cat <<EOF >> lib/config.h
 
 #define CONFIG_UART_EARLY_BASE ${arm_uart_early_addr}
 #define CONFIG_ERRATA_FORCE ${errata_force}
+
+EOF
+fi
+
+if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then
+cat <<EOF >> lib/config.h
+
 #define CONFIG_PAGE_SIZE _AC(${page_size}, UL)
 
 EOF
diff --git a/lib/powerpc/asm/hcall.h b/lib/powerpc/asm/hcall.h
index e0f5009e3..3b44dd204 100644
--- a/lib/powerpc/asm/hcall.h
+++ b/lib/powerpc/asm/hcall.h
@@ -24,6 +24,12 @@
 #define H_PUT_TERM_CHAR		0x58
 #define H_RANDOM		0x300
 #define H_SET_MODE		0x31C
+#define H_REGISTER_PROCESS_TABLE	0x37C
+
+#define PTBL_NEW		0x18
+#define PTBL_UNREGISTER		0x10
+#define PTBL_RADIX		0x04
+#define PTBL_GTSE		0x01
 
 #define KVMPPC_HCALL_BASE	0xf000
 #define KVMPPC_H_RTAS		(KVMPPC_HCALL_BASE + 0x0)
diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index a3859b5d4..d348239c5 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -14,6 +14,7 @@ extern bool cpu_has_hv;
 extern bool cpu_has_power_mce;
 extern bool cpu_has_siar;
 extern bool cpu_has_heai;
+extern bool cpu_has_radix;
 extern bool cpu_has_prefix;
 extern bool cpu_has_sc_lev;
 extern bool cpu_has_pause_short;
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 12f9e8ac6..b2fab4313 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -11,6 +11,7 @@
 #define SPR_SRR0	0x01a
 #define SPR_SRR1	0x01b
 #define   SRR1_PREFIX		UL(0x20000000)
+#define SPR_PIDR	0x030
 #define SPR_FSCR	0x099
 #define   FSCR_PREFIX		UL(0x2000)
 #define SPR_HFSCR	0x0be
@@ -36,7 +37,9 @@
 #define SPR_LPCR	0x13e
 #define   LPCR_HDICE		UL(0x1)
 #define   LPCR_LD		UL(0x20000)
+#define SPR_LPIDR	0x13f
 #define SPR_HEIR	0x153
+#define SPR_PTCR	0x1d0
 #define SPR_MMCR0	0x31b
 #define   MMCR0_FC		UL(0x80000000)
 #define   MMCR0_PMAE		UL(0x04000000)
diff --git a/lib/powerpc/asm/smp.h b/lib/powerpc/asm/smp.h
index 6ef3ae521..820c05e9e 100644
--- a/lib/powerpc/asm/smp.h
+++ b/lib/powerpc/asm/smp.h
@@ -3,6 +3,7 @@
 
 #include <libcflat.h>
 #include <asm/processor.h>
+#include <asm/page.h>
 
 typedef void (*secondary_entry_fn)(int cpu_id);
 
@@ -11,6 +12,7 @@ struct cpu {
 	unsigned long stack;
 	unsigned long exception_stack;
 	secondary_entry_fn entry;
+	pgd_t *pgtable;
 } __attribute__((packed)); /* used by asm */
 
 extern int nr_cpus_present;
diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
index a6ce3c905..09f6bb9d8 100644
--- a/lib/powerpc/processor.c
+++ b/lib/powerpc/processor.c
@@ -13,6 +13,7 @@
 #include <asm/barrier.h>
 #include <asm/hcall.h>
 #include <asm/handlers.h>
+#include <asm/mmu.h>
 #include <asm/smp.h>
 
 static struct {
@@ -47,6 +48,14 @@ void do_handle_exception(struct pt_regs *regs)
 
 	__current_cpu = (struct cpu *)mfspr(SPR_SPRG0);
 
+	/*
+	 * We run with AIL=0, so interrupts taken with MMU disabled.
+	 * Enable here.
+	 */
+	assert(!(mfmsr() & (MSR_IR|MSR_DR)));
+	if (mmu_enabled())
+		mtmsr(mfmsr() | (MSR_IR|MSR_DR));
+
 	v = regs->trap >> 5;
 
 	if (v < 128 && handlers[v].func) {
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 16f009152..ba659cc2b 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -102,6 +102,7 @@ bool cpu_has_hv;
 bool cpu_has_power_mce; /* POWER CPU machine checks */
 bool cpu_has_siar;
 bool cpu_has_heai;
+bool cpu_has_radix;
 bool cpu_has_prefix;
 bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
 bool cpu_has_pause_short;
@@ -124,6 +125,7 @@ static void cpu_init_params(void)
 		cpu_has_sc_lev = true;
 		cpu_has_pause_short = true;
 	case PVR_VER_POWER9:
+		cpu_has_radix = true;
 	case PVR_VER_POWER8E:
 	case PVR_VER_POWER8NVL:
 	case PVR_VER_POWER8:
@@ -198,6 +200,7 @@ void cpu_init(struct cpu *cpu, int cpu_id)
 	cpu->stack += SZ_64K - 64;
 	cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K);
 	cpu->exception_stack += SZ_64K - 64;
+	cpu->pgtable = NULL;
 }
 
 void setup(const void *fdt)
@@ -216,6 +219,7 @@ void setup(const void *fdt)
 	cpu->server_no = fdt_boot_cpuid_phys(fdt);
 	cpu->exception_stack = (unsigned long)boot_exception_stack;
 	cpu->exception_stack += SZ_64K - 64;
+	cpu->pgtable = NULL;
 
 	mtspr(SPR_SPRG0, (unsigned long)cpu);
 	__current_cpu = cpu;
diff --git a/lib/ppc64/asm/mmu.h b/lib/ppc64/asm/mmu.h
new file mode 100644
index 000000000..fadeee4bc
--- /dev/null
+++ b/lib/ppc64/asm/mmu.h
@@ -0,0 +1,11 @@
+#ifndef _ASMPOWERPC_MMU_H_
+#define _ASMPOWERPC_MMU_H_
+
+#include <asm/pgtable.h>
+
+bool vm_available(void);
+bool mmu_enabled(void);
+void mmu_enable(pgd_t *pgtable);
+void mmu_disable(void);
+
+#endif
diff --git a/lib/ppc64/asm/page.h b/lib/ppc64/asm/page.h
index 1a8b62711..95d5131cc 100644
--- a/lib/ppc64/asm/page.h
+++ b/lib/ppc64/asm/page.h
@@ -1 +1,66 @@
-#include <asm-generic/page.h>
+#ifndef _ASMPPC64_PAGE_H_
+#define _ASMPPC64_PAGE_H_
+/*
+ * Adapted from
+ *   lib/arm64/asm/page.h and Linux kernel defines.
+ *
+ * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include <config.h>
+#include <linux/const.h>
+#include <libcflat.h>
+
+#define VA_BITS			52
+
+#define PAGE_SIZE		CONFIG_PAGE_SIZE
+#if PAGE_SIZE == SZ_64K
+#define PAGE_SHIFT		16
+#elif PAGE_SIZE == SZ_4K
+#define PAGE_SHIFT		12
+#else
+#error Unsupported PAGE_SIZE
+#endif
+#define PAGE_MASK		(~(PAGE_SIZE-1))
+
+#ifndef __ASSEMBLY__
+
+#define PAGE_ALIGN(addr)	ALIGN(addr, PAGE_SIZE)
+
+typedef u64 pteval_t;
+typedef u64 pmdval_t;
+typedef u64 pudval_t;
+typedef u64 pgdval_t;
+typedef struct { pteval_t pte; } pte_t;
+typedef struct { pmdval_t pmd; } pmd_t;
+typedef struct { pudval_t pud; } pud_t;
+typedef struct { pgdval_t pgd; } pgd_t;
+typedef struct { pteval_t pgprot; } pgprot_t;
+
+#define pte_val(x)		((x).pte)
+#define pmd_val(x)		((x).pmd)
+#define pud_val(x)		((x).pud)
+#define pgd_val(x)		((x).pgd)
+#define pgprot_val(x)		((x).pgprot)
+
+#define __pte(x)		((pte_t) { (x) } )
+#define __pmd(x)		((pmd_t) { (x) } )
+#define __pud(x)		((pud_t) { (x) } )
+#define __pgd(x)		((pgd_t) { (x) } )
+#define __pgprot(x)		((pgprot_t) { (x) } )
+
+#define __va(x)			((void *)__phys_to_virt((phys_addr_t)(x)))
+#define __pa(x)			__virt_to_phys((unsigned long)(x))
+
+#define virt_to_pfn(kaddr)	(__pa(kaddr) >> PAGE_SHIFT)
+#define pfn_to_virt(pfn)	__va((pfn) << PAGE_SHIFT)
+
+extern phys_addr_t __virt_to_phys(unsigned long addr);
+extern unsigned long __phys_to_virt(phys_addr_t addr);
+
+extern void *__ioremap(phys_addr_t phys_addr, size_t size);
+
+#endif /* !__ASSEMBLY__ */
+#endif /* _ASMPPC64_PAGE_H_ */
diff --git a/lib/ppc64/asm/pgtable-hwdef.h b/lib/ppc64/asm/pgtable-hwdef.h
new file mode 100644
index 000000000..6b20eaf09
--- /dev/null
+++ b/lib/ppc64/asm/pgtable-hwdef.h
@@ -0,0 +1,67 @@
+#ifndef _ASMPPC64_PGTABLE_HWDEF_H_
+#define _ASMPPC64_PGTABLE_HWDEF_H_
+/*
+ * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com>
+ *
+ * Derived from Linux kernel MMU code.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+
+#include <asm/page.h>
+
+#define UL(x) _AC(x, UL)
+
+/*
+ * Book3S-64 Radix page table
+ */
+#define PGDIR_SHIFT		39
+#define PUD_SHIFT		30
+#define PMD_SHIFT		21
+
+#define PTRS_PER_PGD		(SZ_64K / 8)
+#define PTRS_PER_PUD		(SZ_4K / 8)
+#define PTRS_PER_PMD		(SZ_4K / 8)
+#if PAGE_SIZE == SZ_4K
+#define PTRS_PER_PTE		(SZ_4K / 8)
+#else /* 64K */
+#define PTRS_PER_PTE		(256 / 8)
+#endif
+
+#define PGDIR_SIZE		(UL(1) << PGDIR_SHIFT)
+#define PGDIR_MASK		(~(PGDIR_SIZE-1))
+
+#define PUD_SIZE		(UL(1) << PUD_SHIFT)
+#define PUD_MASK		(~(PUD_SIZE-1))
+
+#define PMD_SIZE		(UL(1) << PMD_SHIFT)
+#define PMD_MASK		(~(PMD_SIZE-1))
+
+#define _PAGE_VALID		0x8000000000000000UL
+#define _PAGE_PTE		0x4000000000000000UL
+
+#define _PAGE_EXEC              0x00001 /* execute permission */
+#define _PAGE_WRITE             0x00002 /* write access allowed */
+#define _PAGE_READ              0x00004 /* read access allowed */
+#define _PAGE_PRIVILEGED        0x00008 /* kernel access only */
+#define _PAGE_SAO               0x00010 /* Strong access order */
+#define _PAGE_NON_IDEMPOTENT    0x00020 /* non idempotent memory */
+#define _PAGE_TOLERANT          0x00030 /* tolerant memory, cache inhibited */
+#define _PAGE_DIRTY             0x00080 /* C: page changed */
+#define _PAGE_ACCESSED          0x00100 /* R: page referenced */
+
+/*
+ * Software bits
+ */
+#define _PAGE_SW0		0x2000000000000000UL
+#define _PAGE_SW1		0x00800UL
+#define _PAGE_SW2		0x00400UL
+#define _PAGE_SW3		0x00200UL
+
+/*
+ * Highest possible physical address.
+ */
+#define PHYS_MASK_SHIFT		(48)
+#define PHYS_MASK		((UL(1) << PHYS_MASK_SHIFT) - 1)
+
+#endif /* _ASMPPC64_PGTABLE_HWDEF_H_ */
diff --git a/lib/ppc64/asm/pgtable.h b/lib/ppc64/asm/pgtable.h
new file mode 100644
index 000000000..c8a081543
--- /dev/null
+++ b/lib/ppc64/asm/pgtable.h
@@ -0,0 +1,126 @@
+#ifndef _ASMARM64_PGTABLE_H_
+#define _ASMARM64_PGTABLE_H_
+/*
+ * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com>
+ *
+ * Derived from Linux kernel MMU code.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+#include <alloc.h>
+#include <alloc_page.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <asm/page.h>
+#include <asm/pgtable-hwdef.h>
+
+#include <linux/compiler.h>
+
+/*
+ * We can convert va <=> pa page table addresses with simple casts
+ * because we always allocate their pages with alloc_page(), and
+ * alloc_page() always returns identity mapped pages.
+ */
+#define pgtable_va(x)		((void *)(unsigned long)(x))
+#define pgtable_pa(x)		((unsigned long)(x))
+
+#define pgd_none(pgd)		(!pgd_val(pgd))
+#define pud_none(pud)		(!pud_val(pud))
+#define pmd_none(pmd)		(!pmd_val(pmd))
+#define pte_none(pte)		(!pte_val(pte))
+
+#define pgd_valid(pgd)		(pgd_val(pgd) & cpu_to_be64(_PAGE_VALID))
+#define pud_valid(pud)		(pud_val(pud) & cpu_to_be64(_PAGE_VALID))
+#define pmd_valid(pmd)		(pmd_val(pmd) & cpu_to_be64(_PAGE_VALID))
+#define pte_valid(pte)		(pte_val(pte) & cpu_to_be64(_PAGE_VALID))
+
+#define pmd_huge(pmd)		false
+
+static inline pud_t *pgd_page_vaddr(pgd_t pgd)
+{
+	return pgtable_va(be64_to_cpu(pgd_val(pgd)) & PHYS_MASK & ~0xfffULL);
+}
+
+static inline pmd_t *pud_page_vaddr(pud_t pud)
+{
+	return pgtable_va(be64_to_cpu(pud_val(pud)) & PHYS_MASK & ~0xfffULL);
+}
+
+static inline pte_t *pmd_page_vaddr(pmd_t pmd)
+{
+	return pgtable_va(be64_to_cpu(pmd_val(pmd)) & PHYS_MASK & ~0xfffULL);
+}
+
+#define pgd_index(addr)		(((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
+#define pgd_offset(pt, addr)	((pt) + pgd_index(addr))
+#define pud_index(addr)		(((addr) >> PUD_SHIFT) & (PTRS_PER_PUD - 1))
+#define pud_offset(pgd, addr)	(pgd_page_vaddr(*(pgd)) + pud_index(addr))
+#define pmd_index(addr)		(((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
+#define pmd_offset(pud, addr)	(pud_page_vaddr(*(pud)) + pmd_index(addr))
+#define pte_index(addr)		(((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+#define pte_offset(pmd, addr)	(pmd_page_vaddr(*(pmd)) + pte_index(addr))
+
+#define pgd_free(pgd) free(pgd)
+static inline pgd_t *pgd_alloc_one(void)
+{
+	size_t sz = PTRS_PER_PGD * sizeof(pgd_t);
+	pgd_t *pgd = memalign_pages(sz, sz);
+	memset(pgd, 0, sz);
+	return pgd;
+}
+
+#define pud_free(pud) free(pud)
+static inline pud_t *pud_alloc_one(void)
+{
+	size_t sz = PTRS_PER_PGD * sizeof(pud_t);
+	pud_t *pud = memalign_pages(sz, sz);
+	memset(pud, 0, sz);
+	return pud;
+}
+static inline pud_t *pud_alloc(pgd_t *pgd, unsigned long addr)
+{
+	if (pgd_none(*pgd)) {
+		pgd_t entry;
+		pgd_val(entry) = cpu_to_be64(pgtable_pa(pud_alloc_one()) | _PAGE_VALID | (12 - 3) /* 4k pud page */);
+		WRITE_ONCE(*pgd, entry);
+	}
+	return pud_offset(pgd, addr);
+}
+
+#define pmd_free(pmd) free(pmd)
+static inline pmd_t *pmd_alloc_one(void)
+{
+	size_t sz = PTRS_PER_PMD * sizeof(pmd_t);
+	pmd_t *pmd = memalign_pages(sz, sz);
+	memset(pmd, 0, sz);
+	return pmd;
+}
+static inline pmd_t *pmd_alloc(pud_t *pud, unsigned long addr)
+{
+	if (pud_none(*pud)) {
+		pud_t entry;
+		pud_val(entry) = cpu_to_be64(pgtable_pa(pmd_alloc_one()) | _PAGE_VALID | (12 - 3) /* 4k pmd page */);
+		WRITE_ONCE(*pud, entry);
+	}
+	return pmd_offset(pud, addr);
+}
+
+#define pte_free(pte) free(pte)
+static inline pte_t *pte_alloc_one(void)
+{
+	size_t sz = PTRS_PER_PTE * sizeof(pte_t);
+	pte_t *pte = memalign_pages(sz, sz);
+	memset(pte, 0, sz);
+	return pte;
+}
+static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
+{
+	if (pmd_none(*pmd)) {
+		pmd_t entry;
+		pmd_val(entry) = cpu_to_be64(pgtable_pa(pte_alloc_one()) | _PAGE_VALID | (21 - PAGE_SHIFT) /* 4k/256B pte page */);
+		WRITE_ONCE(*pmd, entry);
+	}
+	return pte_offset(pmd, addr);
+}
+
+#endif /* _ASMPPC64_PGTABLE_H_ */
diff --git a/lib/ppc64/mmu.c b/lib/ppc64/mmu.c
new file mode 100644
index 000000000..6adf94151
--- /dev/null
+++ b/lib/ppc64/mmu.c
@@ -0,0 +1,273 @@
+/*
+ * Radix MMU support
+ *
+ * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com>
+ *
+ * Derived from Linux kernel MMU code.
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2.
+ */
+#include <asm/mmu.h>
+#include <asm/setup.h>
+#include <asm/smp.h>
+#include <asm/page.h>
+#include <asm/io.h>
+#include <asm/processor.h>
+#include <asm/hcall.h>
+
+#include "alloc_page.h"
+#include "vmalloc.h"
+#include <asm/pgtable-hwdef.h>
+#include <asm/pgtable.h>
+
+#include <linux/compiler.h>
+
+static pgd_t *identity_pgd;
+
+bool vm_available(void)
+{
+	return cpu_has_radix;
+}
+
+bool mmu_enabled(void)
+{
+	return current_cpu()->pgtable != NULL;
+}
+
+void mmu_enable(pgd_t *pgtable)
+{
+	struct cpu *cpu = current_cpu();
+
+	if (!pgtable)
+		pgtable = identity_pgd;
+
+	cpu->pgtable = pgtable;
+
+	mtmsr(mfmsr() | (MSR_IR|MSR_DR));
+}
+
+void mmu_disable(void)
+{
+	struct cpu *cpu = current_cpu();
+
+	cpu->pgtable = NULL;
+
+	mtmsr(mfmsr() & ~(MSR_IR|MSR_DR));
+}
+
+static inline void tlbie(unsigned long rb, unsigned long rs, int ric, int prs, int r)
+{
+	asm volatile(".machine push ; .machine power9; ptesync ; tlbie %0,%1,%2,%3,%4 ; eieio ; tlbsync ; ptesync ; .machine pop" :: "r"(rb), "r"(rs), "i"(ric), "i"(prs), "i"(r) : "memory");
+}
+
+static void flush_tlb_page(uintptr_t vaddr)
+{
+	unsigned long rb;
+	unsigned long ap;
+
+	/* AP should come from dt (for pseries, at least) */
+	if (PAGE_SIZE == SZ_4K)
+		ap = 0;
+	else if (PAGE_SIZE == SZ_64K)
+		ap = 5;
+	else if (PAGE_SIZE == SZ_2M)
+		ap = 1;
+	else if (PAGE_SIZE == SZ_1G)
+		ap = 2;
+	else
+		assert(0);
+
+	rb = vaddr & ~((1UL << 12) - 1);
+	rb |= ap << 5;
+
+	tlbie(rb, 0, 0, 1, 1);
+}
+
+static pteval_t *get_pte(pgd_t *pgtable, uintptr_t vaddr)
+{
+	pgd_t *pgd = pgd_offset(pgtable, vaddr);
+	pud_t *pud = pud_alloc(pgd, vaddr);
+	pmd_t *pmd = pmd_alloc(pud, vaddr);
+	pte_t *pte = pte_alloc(pmd, vaddr);
+
+	return &pte_val(*pte);
+}
+
+static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)
+{
+	pteval_t *p_pte = get_pte(pgtable, vaddr);
+
+	if (READ_ONCE(*p_pte) & cpu_to_be64(_PAGE_VALID)) {
+		WRITE_ONCE(*p_pte, 0);
+		flush_tlb_page(vaddr);
+	}
+
+	WRITE_ONCE(*p_pte, cpu_to_be64(pte));
+
+	return p_pte;
+}
+
+static pteval_t *install_page_prot(pgd_t *pgtable, phys_addr_t phys,
+				   uintptr_t vaddr, pgprot_t prot)
+{
+	pteval_t pte = phys;
+	pte |= _PAGE_VALID | _PAGE_PTE;
+	pte |= pgprot_val(prot);
+	return install_pte(pgtable, vaddr, pte);
+}
+
+pteval_t *install_page(pgd_t *pgtable, phys_addr_t phys, void *virt)
+{
+	return install_page_prot(pgtable, phys, (uintptr_t)virt,
+				 __pgprot(_PAGE_VALID | _PAGE_PTE |
+					  _PAGE_READ | _PAGE_WRITE |
+					  _PAGE_EXEC | _PAGE_ACCESSED |
+					  _PAGE_DIRTY));
+}
+
+static pteval_t *follow_pte(pgd_t *pgtable, uintptr_t vaddr)
+{
+	pgd_t *pgd;
+	pud_t *pud;
+	pmd_t *pmd;
+	pte_t *pte;
+
+	pgd = pgd_offset(pgtable, vaddr);
+	if (!pgd_valid(*pgd))
+		return NULL;
+
+	pud = pud_offset(pgd, vaddr);
+	if (!pud_valid(*pud))
+		return NULL;
+
+	pmd = pmd_offset(pud, vaddr);
+	if (!pmd_valid(*pmd))
+		return NULL;
+	if (pmd_huge(*pmd))
+		return &pmd_val(*pmd);
+
+	pte = pte_offset(pmd, vaddr);
+	if (!pte_valid(*pte))
+		return NULL;
+
+        return &pte_val(*pte);
+}
+
+phys_addr_t virt_to_pte_phys(pgd_t *pgtable, void *virt)
+{
+	phys_addr_t mask;
+	pteval_t *pteval;
+
+	pteval = follow_pte(pgtable, (uintptr_t)virt);
+	if (!pteval) {
+		install_page(pgtable, (phys_addr_t)(unsigned long)virt, virt);
+		return (phys_addr_t)(unsigned long)virt;
+	}
+
+	if (pmd_huge(__pmd(*pteval)))
+		mask = PMD_MASK;
+	else
+		mask = PAGE_MASK;
+
+	return (be64_to_cpu(*pteval) & PHYS_MASK & mask) |
+		((phys_addr_t)(unsigned long)virt & ~mask);
+}
+
+struct partition_table_entry {
+	uint64_t dw0;
+	uint64_t dw1;
+};
+
+static struct partition_table_entry *partition_table;
+
+struct process_table_entry {
+	uint64_t dw0;
+	uint64_t dw1;
+};
+
+static struct process_table_entry *process_table;
+
+void *setup_mmu(phys_addr_t phys_end, void *unused)
+{
+	phys_addr_t addr;
+	uint64_t dw0, dw1;
+
+	if (identity_pgd)
+		goto enable;
+
+	assert_msg(cpu_has_radix, "MMU support requires radix MMU.");
+
+	/* 32G address is reserved for vmalloc, cap phys_end at 31G */
+	if (phys_end > (31ul << 30)) {
+		/* print warning */
+		phys_end = 31ul << 30;
+	}
+
+	init_alloc_vpage((void *)(32ul << 30));
+
+	process_table = memalign_pages(SZ_4K, SZ_4K);
+	memset(process_table, 0, SZ_4K);
+
+	identity_pgd = pgd_alloc_one();
+
+	dw0 = (unsigned long)identity_pgd;
+	dw0 |= 16UL - 3; /* 64K pgd size */
+	dw0 |= (0x2UL << 61) | (0x5UL << 5); /* 52-bit virt */
+	process_table[1].dw0 = cpu_to_be64(dw0);
+
+	if (machine_is_pseries()) {
+		int ret;
+
+		ret = hcall(H_REGISTER_PROCESS_TABLE, PTBL_NEW | PTBL_RADIX | PTBL_GTSE, process_table, 0, 0 /* 4K size */);
+		assert_msg(!ret, "H_REGISTER_PROCESS_TABLE failed! err=%d\n", ret);
+	} else if (machine_is_powernv()) {
+		partition_table = memalign_pages(SZ_4K, SZ_4K);
+		memset(partition_table, 0, SZ_4K);
+
+		mtspr(SPR_PTCR, (unsigned long)partition_table); /* 4KB size */
+
+		/* Reuse dw0 for partition table */
+		dw0 |= 1ULL << 63; /* Host radix */
+		dw1 = (unsigned long)process_table; /* 4K size */
+		partition_table[0].dw0 = cpu_to_be64(dw0);
+		partition_table[0].dw1 = cpu_to_be64(dw1);
+
+		mtspr(SPR_LPIDR, 0);
+		/* Set LPCR[UPRT] and LPCR[HR] for radix */
+		mtspr(SPR_LPCR, mfspr(SPR_LPCR) | (1ULL << 22) | (1ULL << 20));
+	} else {
+		/* Only pseries and powernv support radix so far */
+		assert(0);
+	}
+
+	for (addr = 0; addr < phys_end; addr += PAGE_SIZE)
+		install_page(identity_pgd, addr, __va(addr));
+
+enable:
+	/* PID=1 is used because PID=0 is also mapped in quadrant 3 */
+	mtspr(SPR_PIDR, 1);
+
+	mmu_enable(identity_pgd);
+
+	return identity_pgd;
+}
+
+phys_addr_t __virt_to_phys(unsigned long addr)
+{
+	if (mmu_enabled()) {
+		pgd_t *pgtable = current_cpu()->pgtable;
+		return virt_to_pte_phys(pgtable, (void *)addr);
+	}
+	return addr;
+}
+
+unsigned long __phys_to_virt(phys_addr_t addr)
+{
+	/*
+	 * We don't guarantee that phys_to_virt(virt_to_phys(vaddr)) == vaddr, but
+	 * the default page tables do identity map all physical addresses, which
+	 * means phys_to_virt(virt_to_phys((void *)paddr)) == paddr.
+	 */
+	assert(!mmu_enabled() || __virt_to_phys(addr) == addr);
+	return addr;
+}
diff --git a/lib/ppc64/opal-calls.S b/lib/ppc64/opal-calls.S
index 8cb4c3e91..bc9c51f84 100644
--- a/lib/ppc64/opal-calls.S
+++ b/lib/ppc64/opal-calls.S
@@ -25,8 +25,8 @@ opal_call:
 	mfmsr	r12
 	std	r12,-16(r1) /* use redzone */
 
-	/* switch to BE when we enter OPAL */
-	li	r11,(1 << MSR_LE_BIT)
+	/* switch to BE and real-mode when we enter OPAL */
+	li	r11,(1 << MSR_LE_BIT) | MSR_IR | MSR_DR
 	ori	r11,r11,(1 << MSR_EE_BIT)
 	andc	r12,r12,r11
 	mtspr	SPR_HSRR1,r12
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 1348f658b..5871da47a 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -41,6 +41,8 @@ cflatobjs += lib/util.o
 cflatobjs += lib/getchar.o
 cflatobjs += lib/alloc_phys.o
 cflatobjs += lib/alloc.o
+cflatobjs += lib/alloc_page.o
+cflatobjs += lib/vmalloc.o
 cflatobjs += lib/devicetree.o
 cflatobjs += lib/migrate.o
 cflatobjs += lib/powerpc/io.o
diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
index a18a9628f..2466471f9 100644
--- a/powerpc/Makefile.ppc64
+++ b/powerpc/Makefile.ppc64
@@ -18,6 +18,7 @@ reloc.o  = $(TEST_DIR)/reloc64.o
 
 OBJDIRS += lib/ppc64
 cflatobjs += lib/ppc64/stack.o
+cflatobjs += lib/ppc64/mmu.o
 cflatobjs += lib/ppc64/opal.o
 cflatobjs += lib/ppc64/opal-calls.o
 
diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
index 442f8c569..35a47581c 100644
--- a/powerpc/interrupts.c
+++ b/powerpc/interrupts.c
@@ -15,6 +15,9 @@
 #include <asm/processor.h>
 #include <asm/time.h>
 #include <asm/barrier.h>
+#include <asm/mmu.h>
+#include "alloc_phys.h"
+#include "vmalloc.h"
 
 static volatile bool got_interrupt;
 static volatile struct pt_regs recorded_regs;
@@ -45,6 +48,7 @@ static void test_mce(void)
 	unsigned long addr = -4ULL;
 	uint8_t tmp;
 	bool is_fetch;
+	bool mmu = mmu_enabled();
 
 	report_prefix_push("mce");
 
@@ -54,6 +58,9 @@ static void test_mce(void)
 	handle_exception(0x400, fault_handler, NULL);
 	handle_exception(0x480, fault_handler, NULL);
 
+	if (mmu)
+		mmu_disable();
+
 	if (machine_is_powernv()) {
 		enable_mcheck();
 	} else {
@@ -72,7 +79,6 @@ static void test_mce(void)
 
 	is_fetch = false;
 	asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
-
 	report(got_interrupt, "MCE on access to invalid real address");
 	if (got_interrupt) {
 		report(mfspr(SPR_DAR) == addr, "MCE sets DAR correctly");
@@ -91,6 +97,9 @@ static void test_mce(void)
 		got_interrupt = false;
 	}
 
+	if (mmu)
+		mmu_enable(NULL);
+
 	handle_exception(0x200, NULL, NULL);
 	handle_exception(0x300, NULL, NULL);
 	handle_exception(0x380, NULL, NULL);
@@ -100,29 +109,36 @@ static void test_mce(void)
 	report_prefix_pop();
 }
 
-static void dseg_handler(struct pt_regs *regs, void *data)
+static void dside_handler(struct pt_regs *regs, void *data)
 {
 	got_interrupt = true;
 	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
 	regs_advance_insn(regs);
-	regs->msr &= ~MSR_DR;
 }
 
-static void test_dseg(void)
+static void iside_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	regs->nip = regs->link;
+}
+
+static void test_dseg_nommu(void)
 {
 	uint64_t msr, tmp;
 
-	report_prefix_push("data segment");
+	report_prefix_push("dseg");
 
 	/* Some HV start in radix mode and need 0x300 */
-	handle_exception(0x300, &dseg_handler, NULL);
-	handle_exception(0x380, &dseg_handler, NULL);
+	handle_exception(0x300, &dside_handler, NULL);
+	handle_exception(0x380, &dside_handler, NULL);
 
 	asm volatile(
 "		mfmsr	%0		\n \
-		ori	%0,%0,%2	\n \
-		mtmsrd	%0		\n \
-		lbz	%1,0(0)		"
+		ori	%1,%0,%2	\n \
+		mtmsrd	%1		\n \
+		lbz	%1,0(0)		\n \
+		mtmsrd	%0		"
 		: "=r"(msr), "=r"(tmp) : "i"(MSR_DR): "memory");
 
 	report(got_interrupt, "interrupt on NULL dereference");
@@ -134,6 +150,61 @@ static void test_dseg(void)
 	report_prefix_pop();
 }
 
+static void test_mmu(void)
+{
+	uint64_t tmp, addr;
+	phys_addr_t base, top;
+
+	if (!mmu_enabled()) {
+		test_dseg_nommu();
+		return;
+	}
+
+	phys_alloc_get_unused(&base, &top);
+
+	report_prefix_push("dsi");
+	addr = top + PAGE_SIZE;
+	handle_exception(0x300, &dside_handler, NULL);
+	asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
+	report(got_interrupt, "dsi on out of range dereference");
+	report(mfspr(SPR_DAR) == addr, "DAR set correctly");
+	report(mfspr(SPR_DSISR) & (1ULL << 30), "DSISR set correctly");
+	got_interrupt = false;
+	handle_exception(0x300, NULL, NULL);
+	report_prefix_pop();
+
+	report_prefix_push("dseg");
+	addr = -4ULL;
+	handle_exception(0x380, &dside_handler, NULL);
+	asm volatile("lbz %0,0(%1)" : "=r"(tmp) : "r"(addr));
+	report(got_interrupt, "dseg on out of range dereference");
+	report(mfspr(SPR_DAR) == addr, "DAR set correctly");
+	got_interrupt = false;
+	handle_exception(0x380, NULL, NULL);
+	report_prefix_pop();
+
+	report_prefix_push("isi");
+	addr = top + PAGE_SIZE;
+	handle_exception(0x400, &iside_handler, NULL);
+	asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
+	report(got_interrupt, "isi on out of range fetch");
+	report(recorded_regs.nip == addr, "SRR0 set correctly");
+	report(recorded_regs.msr & (1ULL << 30), "SRR1 set correctly");
+	got_interrupt = false;
+	handle_exception(0x400, NULL, NULL);
+	report_prefix_pop();
+
+	report_prefix_push("iseg");
+	addr = -4ULL;
+	handle_exception(0x480, &iside_handler, NULL);
+	asm volatile("mtctr %0 ; bctrl" :: "r"(addr) : "ctr", "lr");
+	report(got_interrupt, "isi on out of range fetch");
+	report(recorded_regs.nip == addr, "SRR0 set correctly");
+	got_interrupt = false;
+	handle_exception(0x480, NULL, NULL);
+	report_prefix_pop();
+}
+
 static void dec_handler(struct pt_regs *regs, void *data)
 {
 	got_interrupt = true;
@@ -401,9 +472,12 @@ int main(int argc, char **argv)
 {
 	report_prefix_push("interrupts");
 
+	if (vm_available())
+		setup_vm();
+
 	if (cpu_has_power_mce)
 		test_mce();
-	test_dseg();
+	test_mmu();
 	test_illegal();
 	test_dec();
 	test_sc();
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (22 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:03   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU Nicholas Piggin
                   ` (7 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, linux-s390, Nico Böhr, Janosch Frank, kvm,
	David Hildenbrand, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, Claudio Imbrenda, linuxppc-dev

There is no good reason to put setup_vm in libcflat.h when it's
defined in vmalloc.h.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <thuth@redhat.com>
Cc: Andrew Jones <andrew.jones@linux.dev>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: Nico Böhr <nrb@linux.ibm.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: kvm@vger.kernel.org
Cc: linux-s390@vger.kernel.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 common/sieve.c         | 1 +
 lib/libcflat.h         | 2 --
 lib/s390x/io.c         | 1 +
 lib/s390x/uv.h         | 1 +
 lib/x86/vm.h           | 1 +
 s390x/mvpg.c           | 1 +
 s390x/selftest.c       | 1 +
 x86/pmu.c              | 1 +
 x86/pmu_lbr.c          | 1 +
 x86/vmexit.c           | 1 +
 x86/vmware_backdoors.c | 1 +
 11 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/common/sieve.c b/common/sieve.c
index 8150f2d98..8fe05ef13 100644
--- a/common/sieve.c
+++ b/common/sieve.c
@@ -1,5 +1,6 @@
 #include "alloc.h"
 #include "libcflat.h"
+#include "vmalloc.h"
 
 static int sieve(char* data, int size)
 {
diff --git a/lib/libcflat.h b/lib/libcflat.h
index 700f43527..8c8dd0286 100644
--- a/lib/libcflat.h
+++ b/lib/libcflat.h
@@ -152,8 +152,6 @@ do {									\
 void binstr(unsigned long x, char out[BINSTR_SZ]);
 void print_binstr(unsigned long x);
 
-extern void setup_vm(void);
-
 #endif /* !__ASSEMBLY__ */
 
 #define SZ_256			(1 << 8)
diff --git a/lib/s390x/io.c b/lib/s390x/io.c
index fb7b7ddaa..2b28ccaa0 100644
--- a/lib/s390x/io.c
+++ b/lib/s390x/io.c
@@ -10,6 +10,7 @@
  */
 #include <libcflat.h>
 #include <argv.h>
+#include <vmalloc.h>
 #include <asm/spinlock.h>
 #include <asm/facility.h>
 #include <asm/sigp.h>
diff --git a/lib/s390x/uv.h b/lib/s390x/uv.h
index 286933caa..00a370410 100644
--- a/lib/s390x/uv.h
+++ b/lib/s390x/uv.h
@@ -4,6 +4,7 @@
 
 #include <sie.h>
 #include <asm/pgtable.h>
+#include <vmalloc.h>
 
 bool uv_os_is_guest(void);
 bool uv_os_is_host(void);
diff --git a/lib/x86/vm.h b/lib/x86/vm.h
index 4b714bad7..cf39787aa 100644
--- a/lib/x86/vm.h
+++ b/lib/x86/vm.h
@@ -2,6 +2,7 @@
 #define _X86_VM_H_
 
 #include "processor.h"
+#include "vmalloc.h"
 #include "asm/page.h"
 #include "asm/io.h"
 #include "asm/bitops.h"
diff --git a/s390x/mvpg.c b/s390x/mvpg.c
index 296338d4f..a0cfc575a 100644
--- a/s390x/mvpg.c
+++ b/s390x/mvpg.c
@@ -15,6 +15,7 @@
 #include <asm/page.h>
 #include <asm/facility.h>
 #include <asm/mem.h>
+#include <vmalloc.h>
 #include <alloc_page.h>
 #include <bitops.h>
 #include <hardware.h>
diff --git a/s390x/selftest.c b/s390x/selftest.c
index 92ed4e5d3..3eaae9b06 100644
--- a/s390x/selftest.c
+++ b/s390x/selftest.c
@@ -9,6 +9,7 @@
 #include <libcflat.h>
 #include <util.h>
 #include <alloc.h>
+#include <vmalloc.h>
 #include <asm/interrupt.h>
 #include <asm/barrier.h>
 #include <asm/pgtable.h>
diff --git a/x86/pmu.c b/x86/pmu.c
index 47a1a602a..7062c1ad9 100644
--- a/x86/pmu.c
+++ b/x86/pmu.c
@@ -6,6 +6,7 @@
 #include "x86/apic.h"
 #include "x86/desc.h"
 #include "x86/isr.h"
+#include "vmalloc.h"
 #include "alloc.h"
 
 #include "libcflat.h"
diff --git a/x86/pmu_lbr.c b/x86/pmu_lbr.c
index 40b63fa3d..c6f010847 100644
--- a/x86/pmu_lbr.c
+++ b/x86/pmu_lbr.c
@@ -2,6 +2,7 @@
 #include "x86/processor.h"
 #include "x86/pmu.h"
 #include "x86/desc.h"
+#include "vmalloc.h"
 
 #define N 1000000
 
diff --git a/x86/vmexit.c b/x86/vmexit.c
index eb5d3023a..48a38f60f 100644
--- a/x86/vmexit.c
+++ b/x86/vmexit.c
@@ -1,6 +1,7 @@
 #include "libcflat.h"
 #include "acpi.h"
 #include "smp.h"
+#include "vmalloc.h"
 #include "pci.h"
 #include "x86/vm.h"
 #include "x86/desc.h"
diff --git a/x86/vmware_backdoors.c b/x86/vmware_backdoors.c
index bc1002056..f8cf7ecb1 100644
--- a/x86/vmware_backdoors.c
+++ b/x86/vmware_backdoors.c
@@ -6,6 +6,7 @@
 #include "x86/desc.h"
 #include "x86/isr.h"
 #include "alloc.h"
+#include "vmalloc.h"
 #include "setjmp.h"
 #include "usermode.h"
 #include "fault_test.h"
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (23 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:04   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 26/32] powerpc: Add sieve.c common test Nicholas Piggin
                   ` (6 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Not all powerpc CPUs provide MMU support. Define vm_available() that is
true by default but archs can override it. Use this to run VM tests.

Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <thuth@redhat.com>
Cc: Andrew Jones <andrew.jones@linux.dev>
Cc: kvm@vger.kernel.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 common/sieve.c      | 14 ++++++++------
 lib/ppc64/asm/mmu.h |  1 -
 lib/ppc64/mmu.c     |  2 +-
 lib/vmalloc.c       |  7 +++++++
 lib/vmalloc.h       |  2 ++
 5 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/common/sieve.c b/common/sieve.c
index 8fe05ef13..db084691a 100644
--- a/common/sieve.c
+++ b/common/sieve.c
@@ -40,12 +40,14 @@ int main(void)
 
     printf("starting sieve\n");
     test_sieve("static", static_data, STATIC_SIZE);
-    setup_vm();
-    test_sieve("mapped", static_data, STATIC_SIZE);
-    for (i = 0; i < 3; ++i) {
-	v = malloc(VSIZE);
-	test_sieve("virtual", v, VSIZE);
-	free(v);
+    if (vm_available()) {
+	    setup_vm();
+	    test_sieve("mapped", static_data, STATIC_SIZE);
+	    for (i = 0; i < 3; ++i) {
+		v = malloc(VSIZE);
+		test_sieve("virtual", v, VSIZE);
+		free(v);
+	    }
     }
 
     return 0;
diff --git a/lib/ppc64/asm/mmu.h b/lib/ppc64/asm/mmu.h
index fadeee4bc..eaff0f1f7 100644
--- a/lib/ppc64/asm/mmu.h
+++ b/lib/ppc64/asm/mmu.h
@@ -3,7 +3,6 @@
 
 #include <asm/pgtable.h>
 
-bool vm_available(void);
 bool mmu_enabled(void);
 void mmu_enable(pgd_t *pgtable);
 void mmu_disable(void);
diff --git a/lib/ppc64/mmu.c b/lib/ppc64/mmu.c
index 6adf94151..f1219033a 100644
--- a/lib/ppc64/mmu.c
+++ b/lib/ppc64/mmu.c
@@ -24,7 +24,7 @@
 
 static pgd_t *identity_pgd;
 
-bool vm_available(void)
+bool vm_available(void) /* weak override */
 {
 	return cpu_has_radix;
 }
diff --git a/lib/vmalloc.c b/lib/vmalloc.c
index 572682576..cf2ef7a70 100644
--- a/lib/vmalloc.c
+++ b/lib/vmalloc.c
@@ -206,10 +206,17 @@ void init_alloc_vpage(void *top)
 	spin_unlock(&lock);
 }
 
+bool __attribute__((__weak__)) vm_available(void)
+{
+	return true;
+}
+
 void __setup_vm(void *opaque)
 {
 	phys_addr_t base, top;
 
+	assert_msg(vm_available(), "Virtual memory not available. Must check vm_available() before calling setup_vm()");
+
 	if (alloc_ops == &vmalloc_ops)
 		return;
 
diff --git a/lib/vmalloc.h b/lib/vmalloc.h
index 0269fdde9..e81be39f4 100644
--- a/lib/vmalloc.h
+++ b/lib/vmalloc.h
@@ -17,6 +17,8 @@ extern void setup_vm(void);
 /* As above, plus passes an opaque value to setup_mmu(). */
 extern void __setup_vm(void *opaque);
 
+/* common/ tests must check availability before calling setup_vm() */
+extern bool vm_available(void);
 /* Set up paging */
 extern void *setup_mmu(phys_addr_t top, void *opaque);
 /* Walk the page table and resolve the virtual address to a physical address */
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 26/32] powerpc: Add sieve.c common test
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (24 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 27/32] powerpc: add usermode support Nicholas Piggin
                   ` (5 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Now that sieve copes with lack of MMU support, it can be run by
powerpc.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 powerpc/Makefile.common | 1 +
 powerpc/sieve.c         | 1 +
 powerpc/unittests.cfg   | 3 +++
 3 files changed, 5 insertions(+)
 create mode 120000 powerpc/sieve.c

diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 5871da47a..410a675d9 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -8,6 +8,7 @@ tests-common = \
 	$(TEST_DIR)/selftest.elf \
 	$(TEST_DIR)/selftest-migration.elf \
 	$(TEST_DIR)/memory-verify.elf \
+	$(TEST_DIR)/sieve.elf \
 	$(TEST_DIR)/spapr_hcall.elf \
 	$(TEST_DIR)/rtas.elf \
 	$(TEST_DIR)/emulator.elf \
diff --git a/powerpc/sieve.c b/powerpc/sieve.c
new file mode 120000
index 000000000..fe299f309
--- /dev/null
+++ b/powerpc/sieve.c
@@ -0,0 +1 @@
+../common/sieve.c
\ No newline at end of file
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 3ebdf9dd3..008559b43 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -136,3 +136,6 @@ file = sprs.elf
 machine = pseries
 extra_params = -append '-w'
 groups = migration
+
+[sieve]
+file = sieve.elf
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 27/32] powerpc: add usermode support
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (25 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 26/32] powerpc: Add sieve.c common test Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 28/32] powerpc: add pmu tests Nicholas Piggin
                   ` (4 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

The biggest difficulty for user mode is MMU support. Otherwise it is
a simple matter of setting and clearing MSR[PR] with rfid and sc
respectively.

Some common harness operations will fail in usermode, so some workarounds
are reqiured (e.g., puts() can't be used directly).

A usermode privileged instruction interrupt test is added.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |  9 +++++++++
 lib/powerpc/asm/reg.h       |  1 +
 lib/powerpc/asm/smp.h       |  1 +
 lib/powerpc/io.c            |  7 +++++++
 lib/powerpc/processor.c     | 38 +++++++++++++++++++++++++++++++++++++
 lib/powerpc/rtas.c          |  3 +++
 lib/powerpc/setup.c         |  8 ++++++--
 lib/powerpc/spinlock.c      |  4 ++++
 lib/ppc64/mmu.c             |  2 ++
 powerpc/interrupts.c        | 28 +++++++++++++++++++++++++++
 10 files changed, 99 insertions(+), 2 deletions(-)

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index d348239c5..749155696 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -19,6 +19,8 @@ extern bool cpu_has_prefix;
 extern bool cpu_has_sc_lev;
 extern bool cpu_has_pause_short;
 
+bool in_usermode(void);
+
 static inline uint64_t mfspr(int nr)
 {
 	uint64_t ret;
@@ -51,6 +53,8 @@ static inline void local_irq_enable(void)
 {
 	unsigned long msr;
 
+	assert(!in_usermode());
+
 	asm volatile(
 "		mfmsr	%0		\n \
 		ori	%0,%0,%1	\n \
@@ -62,6 +66,8 @@ static inline void local_irq_disable(void)
 {
 	unsigned long msr;
 
+	assert(!in_usermode());
+
 	asm volatile(
 "		mfmsr	%0		\n \
 		andc	%0,%0,%1	\n \
@@ -90,4 +96,7 @@ static inline bool machine_is_pseries(void)
 void enable_mcheck(void);
 void disable_mcheck(void);
 
+void enter_usermode(void);
+void exit_usermode(void);
+
 #endif /* _ASMPOWERPC_PROCESSOR_H_ */
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index b2fab4313..69ef21adb 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -58,5 +58,6 @@
 #define MSR_SE		UL(0x0400)		/* Single Step Enable */
 #define MSR_EE		UL(0x8000)
 #define MSR_ME		UL(0x1000)
+#define MSR_PR		UL(0x4000)
 
 #endif
diff --git a/lib/powerpc/asm/smp.h b/lib/powerpc/asm/smp.h
index 820c05e9e..b96a55903 100644
--- a/lib/powerpc/asm/smp.h
+++ b/lib/powerpc/asm/smp.h
@@ -11,6 +11,7 @@ struct cpu {
 	unsigned long server_no;
 	unsigned long stack;
 	unsigned long exception_stack;
+	bool in_user;
 	secondary_entry_fn entry;
 	pgd_t *pgtable;
 } __attribute__((packed)); /* used by asm */
diff --git a/lib/powerpc/io.c b/lib/powerpc/io.c
index cb7f2f050..5c2810884 100644
--- a/lib/powerpc/io.c
+++ b/lib/powerpc/io.c
@@ -11,6 +11,7 @@
 #include <asm/setup.h>
 #include <asm/processor.h>
 #include <asm/atomic.h>
+#include <asm/smp.h>
 #include "io.h"
 
 static struct spinlock print_lock;
@@ -41,10 +42,16 @@ void io_init(void)
 
 void puts(const char *s)
 {
+	bool user = in_usermode();
+
+	if (user)
+		exit_usermode();
 	spin_lock(&print_lock);
 	while (*s)
 		putchar(*s++);
 	spin_unlock(&print_lock);
+	if (user)
+		enter_usermode();
 }
 
 /*
diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
index 09f6bb9d8..6c3000d5c 100644
--- a/lib/powerpc/processor.c
+++ b/lib/powerpc/processor.c
@@ -47,6 +47,8 @@ void do_handle_exception(struct pt_regs *regs)
 	unsigned char v;
 
 	__current_cpu = (struct cpu *)mfspr(SPR_SPRG0);
+	if (in_usermode())
+		current_cpu()->in_user = false;
 
 	/*
 	 * We run with AIL=0, so interrupts taken with MMU disabled.
@@ -60,6 +62,8 @@ void do_handle_exception(struct pt_regs *regs)
 
 	if (v < 128 && handlers[v].func) {
 		handlers[v].func(regs, handlers[v].data);
+		if (regs->msr & MSR_PR)
+			current_cpu()->in_user = true;
 		return;
 	}
 
@@ -169,3 +173,37 @@ void disable_mcheck(void)
 {
 	rfid_msr(mfmsr() & ~MSR_ME);
 }
+
+bool in_usermode(void)
+{
+	return current_cpu()->in_user;
+}
+
+static void usermode_sc_handler(struct pt_regs *regs, void *data)
+{
+	regs->msr &= ~(MSR_PR|MSR_EE);
+	/* Interrupt return handler will keep in_user clear */
+}
+
+void enter_usermode(void)
+{
+	assert_msg(!in_usermode(), "enter_usermode called with in_usermode");
+	/* mfmsr would fault in usermode anyway */
+	assert_msg(!(mfmsr() & MSR_PR), "enter_usermode called from user mode");
+	assert_msg(!(mfmsr() & MSR_EE), "enter_usermode called with interrupts enabled");
+	assert_msg((mfmsr() & (MSR_IR|MSR_DR)) == (MSR_IR|MSR_DR),
+		"enter_usermode called with virtual memory disabled");
+
+	handle_exception(0xc00, usermode_sc_handler, NULL);
+	rfid_msr(mfmsr() | (MSR_PR|MSR_IR|MSR_DR|MSR_EE));
+	current_cpu()->in_user = true;
+}
+
+void exit_usermode(void)
+{
+	assert_msg(in_usermode(), "enter_usermode called with !in_usermode");
+	asm volatile("sc 0" ::: "memory");
+	handle_exception(0xc00, NULL, NULL);
+	assert(!in_usermode());
+	assert(!(mfmsr() & MSR_PR));
+}
diff --git a/lib/powerpc/rtas.c b/lib/powerpc/rtas.c
index b477a38e0..9c1e0affc 100644
--- a/lib/powerpc/rtas.c
+++ b/lib/powerpc/rtas.c
@@ -9,6 +9,7 @@
 #include <libfdt/libfdt.h>
 #include <devicetree.h>
 #include <asm/spinlock.h>
+#include <asm/smp.h>
 #include <asm/hcall.h>
 #include <asm/io.h>
 #include <asm/rtas.h>
@@ -137,6 +138,8 @@ int rtas_call(int token, int nargs, int nret, int *outputs, ...)
 	va_list list;
 	int ret;
 
+	assert_msg(!in_usermode(), "May not make RTAS call from user mode\n");
+
 	spin_lock(&rtas_lock);
 
 	va_start(list, outputs);
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index ba659cc2b..30b988a5c 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -201,8 +201,11 @@ void cpu_init(struct cpu *cpu, int cpu_id)
 	cpu->exception_stack = (unsigned long)memalign(SZ_4K, SZ_64K);
 	cpu->exception_stack += SZ_64K - 64;
 	cpu->pgtable = NULL;
+	cpu->in_user = false;
 }
 
+bool is_hvmode;
+
 void setup(const void *fdt)
 {
 	void *freemem = &stacktop;
@@ -211,8 +214,6 @@ void setup(const void *fdt)
 	u32 fdt_size;
 	int ret;
 
-	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
-
 	memset(cpus, 0xff, sizeof(cpus));
 
 	cpu = &cpus[0];
@@ -220,10 +221,13 @@ void setup(const void *fdt)
 	cpu->exception_stack = (unsigned long)boot_exception_stack;
 	cpu->exception_stack += SZ_64K - 64;
 	cpu->pgtable = NULL;
+	cpu->in_user = false;
 
 	mtspr(SPR_SPRG0, (unsigned long)cpu);
 	__current_cpu = cpu;
 
+	cpu_has_hv = !!(mfmsr() & (1ULL << MSR_HV_BIT));
+
 	enable_mcheck();
 
 	/*
diff --git a/lib/powerpc/spinlock.c b/lib/powerpc/spinlock.c
index 238549f12..59dc4fbb5 100644
--- a/lib/powerpc/spinlock.c
+++ b/lib/powerpc/spinlock.c
@@ -8,6 +8,8 @@
  */
 void spin_lock(struct spinlock *lock)
 {
+	assert(!in_usermode());
+
 	if (!multithreaded) {
 		assert(lock->v == 0);
 		lock->v = 1;
@@ -19,7 +21,9 @@ void spin_lock(struct spinlock *lock)
 
 void spin_unlock(struct spinlock *lock)
 {
+	assert(!in_usermode());
 	assert(lock->v == 1);
+
 	if (!multithreaded) {
 		lock->v = 0;
 	} else {
diff --git a/lib/ppc64/mmu.c b/lib/ppc64/mmu.c
index f1219033a..313da6e78 100644
--- a/lib/ppc64/mmu.c
+++ b/lib/ppc64/mmu.c
@@ -43,6 +43,7 @@ void mmu_enable(pgd_t *pgtable)
 
 	cpu->pgtable = pgtable;
 
+	assert(!in_usermode());
 	mtmsr(mfmsr() | (MSR_IR|MSR_DR));
 }
 
@@ -52,6 +53,7 @@ void mmu_disable(void)
 
 	cpu->pgtable = NULL;
 
+	assert(!in_usermode());
 	mtmsr(mfmsr() & ~(MSR_IR|MSR_DR));
 }
 
diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
index 35a47581c..1f5f7cfcd 100644
--- a/powerpc/interrupts.c
+++ b/powerpc/interrupts.c
@@ -327,6 +327,33 @@ static void test_illegal(void)
 	report_prefix_pop();
 }
 
+static void dec_ignore_handler(struct pt_regs *regs, void *data)
+{
+	mtspr(SPR_DEC, 0x7fffffff);
+}
+
+static void test_privileged(void)
+{
+	unsigned long msr;
+
+	if (!mmu_enabled())
+		return;
+
+	report_prefix_push("privileged instruction");
+
+	handle_exception(0x700, &program_handler, NULL);
+	handle_exception(0x900, &dec_ignore_handler, NULL);
+	enter_usermode();
+	asm volatile("mfmsr %0" : "=r"(msr) :: "memory");
+	exit_usermode();
+	report(got_interrupt, "interrupt on privileged instruction");
+	got_interrupt = false;
+	handle_exception(0x900, NULL, NULL);
+	handle_exception(0x700, NULL, NULL);
+
+	report_prefix_pop();
+}
+
 static void sc_handler(struct pt_regs *regs, void *data)
 {
 	got_interrupt = true;
@@ -479,6 +506,7 @@ int main(int argc, char **argv)
 		test_mce();
 	test_mmu();
 	test_illegal();
+	test_privileged();
 	test_dec();
 	test_sc();
 	test_trace();
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 28/32] powerpc: add pmu tests
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (26 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 27/32] powerpc: add usermode support Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch Nicholas Piggin
                   ` (3 subsequent siblings)
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

Add some initial PMU testing.

- PMC5/6 tests
- PMAE / PMI test
- BHRB basic tests

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 lib/powerpc/asm/processor.h |   2 +
 lib/powerpc/asm/reg.h       |   9 +
 lib/powerpc/asm/setup.h     |   1 +
 lib/powerpc/setup.c         |  23 +++
 powerpc/Makefile.common     |   3 +-
 powerpc/pmu.c               | 337 ++++++++++++++++++++++++++++++++++++
 powerpc/unittests.cfg       |   3 +
 7 files changed, 377 insertions(+), 1 deletion(-)
 create mode 100644 powerpc/pmu.c

diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
index 749155696..28239c610 100644
--- a/lib/powerpc/asm/processor.h
+++ b/lib/powerpc/asm/processor.h
@@ -14,6 +14,8 @@ extern bool cpu_has_hv;
 extern bool cpu_has_power_mce;
 extern bool cpu_has_siar;
 extern bool cpu_has_heai;
+extern bool cpu_has_bhrb;
+extern bool cpu_has_p10_bhrb;
 extern bool cpu_has_radix;
 extern bool cpu_has_prefix;
 extern bool cpu_has_sc_lev;
diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
index 69ef21adb..602fba1b6 100644
--- a/lib/powerpc/asm/reg.h
+++ b/lib/powerpc/asm/reg.h
@@ -40,10 +40,19 @@
 #define SPR_LPIDR	0x13f
 #define SPR_HEIR	0x153
 #define SPR_PTCR	0x1d0
+#define SPR_MMCRA	0x312
+#define   MMCRA_BHRBRD		UL(0x0000002000000000)
+#define   MMCRA_IFM_MASK	UL(0x00000000c0000000)
+#define SPR_PMC5	0x317
+#define SPR_PMC6	0x318
 #define SPR_MMCR0	0x31b
 #define   MMCR0_FC		UL(0x80000000)
+#define   MMCR0_FCP		UL(0x20000000)
 #define   MMCR0_PMAE		UL(0x04000000)
+#define   MMCR0_BHRBA		UL(0x00200000)
+#define   MMCR0_FCPC		UL(0x00001000)
 #define   MMCR0_PMAO		UL(0x00000080)
+#define   MMCR0_FC56		UL(0x00000010)
 #define SPR_SIAR	0x31c
 
 /* Machine State Register definitions: */
diff --git a/lib/powerpc/asm/setup.h b/lib/powerpc/asm/setup.h
index 9ca318ce6..8f0b58ed0 100644
--- a/lib/powerpc/asm/setup.h
+++ b/lib/powerpc/asm/setup.h
@@ -10,6 +10,7 @@
 #define NR_CPUS			8	/* arbitrarily set for now */
 
 extern uint64_t tb_hz;
+extern uint64_t cpu_hz;
 
 #define NR_MEM_REGIONS		8
 #define MR_F_PRIMARY		(1U << 0)
diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
index 30b988a5c..42ba06ad1 100644
--- a/lib/powerpc/setup.c
+++ b/lib/powerpc/setup.c
@@ -32,6 +32,7 @@ u32 initrd_size;
 u32 cpu_to_hwid[NR_CPUS] = { [0 ... NR_CPUS-1] = (~0U) };
 int nr_cpus_present;
 uint64_t tb_hz;
+uint64_t cpu_hz;
 
 struct mem_region mem_regions[NR_MEM_REGIONS];
 phys_addr_t __physical_start, __physical_end;
@@ -41,6 +42,7 @@ struct cpu_set_params {
 	unsigned icache_bytes;
 	unsigned dcache_bytes;
 	uint64_t tb_hz;
+	uint64_t cpu_hz;
 };
 
 static void cpu_set(int fdtnode, u64 regval, void *info)
@@ -94,6 +96,22 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
 		data = (u32 *)prop->data;
 		params->tb_hz = fdt32_to_cpu(*data);
 
+		prop = fdt_get_property(dt_fdt(), fdtnode,
+					"ibm,extended-clock-frequency", NULL);
+		if (prop) {
+			data = (u32 *)prop->data;
+			params->cpu_hz = fdt32_to_cpu(*data);
+			params->cpu_hz <<= 32;
+			data = (u32 *)prop->data + 1;
+			params->cpu_hz |= fdt32_to_cpu(*data);
+		} else {
+			prop = fdt_get_property(dt_fdt(), fdtnode,
+						"clock-frequency", NULL);
+			assert(prop != NULL);
+			data = (u32 *)prop->data;
+			params->cpu_hz = fdt32_to_cpu(*data);
+		}
+
 		read_common_info = true;
 	}
 }
@@ -102,6 +120,8 @@ bool cpu_has_hv;
 bool cpu_has_power_mce; /* POWER CPU machine checks */
 bool cpu_has_siar;
 bool cpu_has_heai;
+bool cpu_has_bhrb;
+bool cpu_has_p10_bhrb;
 bool cpu_has_radix;
 bool cpu_has_prefix;
 bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
@@ -118,12 +138,14 @@ static void cpu_init_params(void)
 	__icache_bytes = params.icache_bytes;
 	__dcache_bytes = params.dcache_bytes;
 	tb_hz = params.tb_hz;
+	cpu_hz = params.cpu_hz;
 
 	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
 	case PVR_VER_POWER10:
 		cpu_has_prefix = true;
 		cpu_has_sc_lev = true;
 		cpu_has_pause_short = true;
+		cpu_has_p10_bhrb = true;
 	case PVR_VER_POWER9:
 		cpu_has_radix = true;
 	case PVR_VER_POWER8E:
@@ -132,6 +154,7 @@ static void cpu_init_params(void)
 		cpu_has_power_mce = true;
 		cpu_has_heai = true;
 		cpu_has_siar = true;
+		cpu_has_bhrb = true;
 		break;
 	default:
 		break;
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
index 410a675d9..64a3d93e4 100644
--- a/powerpc/Makefile.common
+++ b/powerpc/Makefile.common
@@ -17,7 +17,8 @@ tests-common = \
 	$(TEST_DIR)/smp.elf \
 	$(TEST_DIR)/sprs.elf \
 	$(TEST_DIR)/timebase.elf \
-	$(TEST_DIR)/interrupts.elf
+	$(TEST_DIR)/interrupts.elf \
+	$(TEST_DIR)/pmu.elf
 
 tests-all = $(tests-common) $(tests)
 all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
diff --git a/powerpc/pmu.c b/powerpc/pmu.c
new file mode 100644
index 000000000..df0604261
--- /dev/null
+++ b/powerpc/pmu.c
@@ -0,0 +1,337 @@
+/*
+ * Test PMU
+ *
+ * Copyright 2024 Nicholas Piggin, IBM Corp.
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include <libcflat.h>
+#include <util.h>
+#include <migrate.h>
+#include <alloc.h>
+#include <asm/setup.h>
+#include <asm/handlers.h>
+#include <asm/hcall.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/barrier.h>
+#include <asm/mmu.h>
+#include "alloc_phys.h"
+#include "vmalloc.h"
+
+static volatile bool got_interrupt;
+static volatile struct pt_regs recorded_regs;
+static volatile unsigned long recorded_mmcr0;
+
+static void reset_mmcr0(void)
+{
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56));
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_PMAE | MMCR0_PMAO));
+}
+
+static __attribute__((__noinline__)) unsigned long pmc5_count_nr_insns(unsigned long nr)
+{
+	reset_mmcr0();
+	mtspr(SPR_PMC5, 0);
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FC56));
+	asm volatile("mtctr %0 ; 1: bdnz 1b" :: "r"(nr) : "ctr");
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56));
+
+	return mfspr(SPR_PMC5);
+}
+
+static void test_pmc56(void)
+{
+	unsigned long tmp;
+
+	report_prefix_push("pmc56");
+
+	reset_mmcr0();
+	mtspr(SPR_PMC5, 0);
+	mtspr(SPR_PMC6, 0);
+	report(mfspr(SPR_PMC5) == 0, "PMC5 zeroed");
+	report(mfspr(SPR_PMC6) == 0, "PMC6 zeroed");
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_FC);
+	msleep(100);
+	report(mfspr(SPR_PMC5) == 0, "PMC5 frozen");
+	report(mfspr(SPR_PMC6) == 0, "PMC6 frozen");
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_FC56);
+	mdelay(100);
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | (MMCR0_FC | MMCR0_FC56));
+	report(mfspr(SPR_PMC5) != 0, "PMC5 counting");
+	report(mfspr(SPR_PMC6) != 0, "PMC6 counting");
+
+	/* Dynamic frequency scaling could cause to be out, so don't fail. */
+	tmp = mfspr(SPR_PMC6);
+	report(true, "PMC6 ratio to reported clock frequency is %ld%%", tmp * 1000 / cpu_hz);
+
+	tmp = pmc5_count_nr_insns(100);
+	tmp = pmc5_count_nr_insns(1000) - tmp;
+	report(tmp == 900, "PMC5 counts instructions precisely");
+
+	report_prefix_pop();
+}
+
+static void dec_ignore_handler(struct pt_regs *regs, void *data)
+{
+	mtspr(SPR_DEC, 0x7fffffff);
+}
+
+static void pmi_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	memcpy((void *)&recorded_regs, regs, sizeof(struct pt_regs));
+	recorded_mmcr0 = mfspr(SPR_MMCR0);
+	if (mfspr(SPR_MMCR0) & MMCR0_PMAO) {
+		/* This may cause infinite interrupts, so clear it. */
+		mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAO);
+	}
+}
+
+static void test_pmi(void)
+{
+	report_prefix_push("pmi");
+	handle_exception(0x900, &dec_ignore_handler, NULL);
+	handle_exception(0xf00, &pmi_handler, NULL);
+	reset_mmcr0();
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAO);
+	mtmsr(mfmsr() | MSR_EE);
+	mtmsr(mfmsr() & ~MSR_EE);
+	report(got_interrupt, "PMAO caused interrupt");
+	handle_exception(0xf00, NULL, NULL);
+	handle_exception(0x900, NULL, NULL);
+	report_prefix_pop();
+}
+
+static void clrbhrb(void)
+{
+	asm volatile("clrbhrb" ::: "memory");
+}
+
+static inline unsigned long mfbhrbe(int nr)
+{
+	unsigned long e;
+
+	asm volatile("mfbhrbe %0,%1" : "=r"(e) : "i"(nr) : "memory");
+
+	return e;
+}
+
+extern unsigned char dummy_branch_1[];
+extern unsigned char dummy_branch_2[];
+
+static __attribute__((__noinline__)) void bhrb_dummy(int i)
+{
+	asm volatile(
+	"	cmpdi %0,1	\n\t"
+	"	beq 1f		\n\t"
+	".global dummy_branch_1	\n\t"
+	"dummy_branch_1:	\n\t"
+	"	b 2f		\n\t"
+	"1:	trap		\n\t"
+	".global dummy_branch_2	\n\t"
+	"dummy_branch_2:	\n\t"
+	"2:	bne 3f		\n\t"
+	"	trap		\n\t"
+	"3:	nop		\n\t"
+	: : "r"(i));
+}
+
+#define NR_BHRBE 16
+static unsigned long bhrbe[NR_BHRBE];
+static int nr_bhrbe;
+
+static void run_and_load_bhrb(void)
+{
+	int i;
+
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE);
+	clrbhrb();
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_BHRBA);
+	mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~(MMCR0_FC | MMCR0_FCP | MMCR0_FCPC));
+	mtspr(SPR_MMCRA, mfspr(SPR_MMCRA) & ~(MMCRA_BHRBRD | MMCRA_IFM_MASK));
+
+	if (cpu_has_p10_bhrb) {
+		mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAE);
+		asm volatile("isync" ::: "memory");
+		enter_usermode();
+		bhrb_dummy(0);
+		exit_usermode();
+		mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE);
+		asm volatile("isync" ::: "memory");
+	} else {
+		mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) | MMCR0_PMAE);
+		asm volatile("isync" ::: "memory");
+		mtmsr(mfmsr());
+		asm volatile(".rept 100 ; nop ; .endr");
+		bhrb_dummy(0);
+		mtspr(SPR_MMCR0, mfspr(SPR_MMCR0) & ~MMCR0_PMAE);
+		asm volatile("isync" ::: "memory");
+	}
+
+	bhrbe[0] = mfbhrbe(0);
+	bhrbe[1] = mfbhrbe(1);
+	bhrbe[2] = mfbhrbe(2);
+	bhrbe[3] = mfbhrbe(3);
+	bhrbe[4] = mfbhrbe(4);
+	bhrbe[5] = mfbhrbe(5);
+	bhrbe[6] = mfbhrbe(6);
+	bhrbe[7] = mfbhrbe(7);
+	bhrbe[8] = mfbhrbe(8);
+	bhrbe[9] = mfbhrbe(9);
+	bhrbe[10] = mfbhrbe(10);
+	bhrbe[11] = mfbhrbe(11);
+	bhrbe[12] = mfbhrbe(12);
+	bhrbe[13] = mfbhrbe(13);
+	bhrbe[14] = mfbhrbe(14);
+	bhrbe[15] = mfbhrbe(15);
+
+	for (i = 0; i < NR_BHRBE; i++) {
+		bhrbe[i] &= ~0x1UL; /* remove prediction bit */
+		if (!bhrbe[i])
+			break;
+	}
+	nr_bhrbe = i;
+}
+
+static void illegal_handler(struct pt_regs *regs, void *data)
+{
+	got_interrupt = true;
+	regs_advance_insn(regs);
+}
+
+static void test_bhrb(void)
+{
+	int i;
+
+	if (cpu_has_p10_bhrb && !vm_available())
+		return;
+
+	report_prefix_push("bhrb");
+
+	/* TCG doesn't impelment BHRB yet */
+	handle_exception(0x700, &illegal_handler, NULL);
+	handle_exception(0xe40, &illegal_handler, NULL);
+	clrbhrb();
+	handle_exception(0x700, NULL, NULL);
+	handle_exception(0xe40, NULL, NULL);
+	if (got_interrupt) {
+		got_interrupt = false;
+		report_skip("BHRB support missing");
+		report_prefix_pop();
+		return;
+	}
+
+	handle_exception(0x900, &illegal_handler, NULL);
+
+	if (vm_available()) {
+		handle_exception(0x900, &dec_ignore_handler, NULL);
+		setup_vm();
+	}
+	reset_mmcr0();
+	clrbhrb();
+	if (cpu_has_p10_bhrb) {
+		enter_usermode();
+		bhrb_dummy(0);
+		exit_usermode();
+	} else {
+		bhrb_dummy(0);
+	}
+	report(mfbhrbe(0) == 0, "BHRB is frozen");
+
+	/*
+	 * BHRB may be cleared at any time (e.g., by OS or hypervisor)
+	 * so this test could be occasionally incorrect. Try several
+	 * times before giving up...
+	 */
+
+	if (cpu_has_p10_bhrb) {
+		/*
+		 * BHRB should have 8 entries:
+		 * 1. enter_usermode blr
+		 * 2. enter_usermode blr target
+		 * 3. bl dummy
+		 * 4. dummy unconditional
+		 * 5. dummy conditional
+		 * 6. dummy blr
+		 * 7. dummy blr target
+		 * 8. exit_usermode bl
+		 *
+		 * POWER10 often gives 4 entries, if other threads are
+		 * running on the core, it seems to struggle.
+		 */
+		for (i = 0; i < 200; i++) {
+			run_and_load_bhrb();
+			if (nr_bhrbe == 8)
+				break;
+			if (i > 100 && nr_bhrbe == 4)
+				break;
+		}
+		if (nr_bhrbe != 8)
+			printf("nr_bhrbe=%d\n", nr_bhrbe);
+		report(nr_bhrbe, "BHRB has been written");
+		if (nr_bhrbe == 8) {
+			report(nr_bhrbe == 8, "BHRB has written 8 entries");
+			report(bhrbe[4] == (unsigned long)dummy_branch_1,
+					"correct unconditional branch address");
+			report(bhrbe[3] == (unsigned long)dummy_branch_2,
+					"correct conditional branch address");
+		} else if (nr_bhrbe == 4) {
+			/* POWER10 workaround */
+			report(nr_bhrbe == 4, "BHRB has written 4 entries");
+			report(bhrbe[3] == (unsigned long)dummy_branch_2,
+					"correct conditional branch address");
+		}
+	} else {
+		/*
+		 * BHRB should have 6 entries:
+		 * 1. bl dummy
+		 * 2. dummy unconditional
+		 * 3. dummy conditional
+		 * 4. dummy blr
+		 * 5. dummy blr target
+		 * 6. Final b loop before disabled.
+		 *
+		 * POWER9 often gives 4 entries, if other threads are
+		 * running on the core, it seems to struggle.
+		 */
+		for (i = 0; i < 200; i++) {
+			run_and_load_bhrb();
+			if (nr_bhrbe == 6)
+				break;
+			if (i > 100 && nr_bhrbe == 4)
+				break;
+		}
+		report(nr_bhrbe, "BHRB has been written");
+		report(nr_bhrbe == 6, "BHRB has written 6 entries");
+		if (nr_bhrbe == 6) {
+			report(bhrbe[4] == (unsigned long)dummy_branch_1,
+					"correct unconditional branch address");
+			report(bhrbe[3] == (unsigned long)dummy_branch_2,
+					"correct conditional branch address");
+		} else if (nr_bhrbe == 4) {
+			/* POWER9 workaround */
+			report(nr_bhrbe == 4, "BHRB has written 4 entries");
+			report(bhrbe[3] == (unsigned long)dummy_branch_2,
+					"correct conditional branch address");
+		}
+	}
+
+	handle_exception(0x900, NULL, NULL);
+
+	report_prefix_pop();
+}
+
+int main(int argc, char **argv)
+{
+	report_prefix_push("pmu");
+
+	test_pmc56();
+	test_pmi();
+	if (cpu_has_bhrb)
+		test_bhrb();
+
+	report_prefix_pop();
+
+	return report_summary();
+}
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index 008559b43..e275f389b 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -89,6 +89,9 @@ file = emulator.elf
 [interrupts]
 file = interrupts.elf
 
+[pmu]
+file = pmu.elf
+
 [smp]
 file = smp.elf
 smp = 2
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (27 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 28/32] powerpc: add pmu tests Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:08   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity Nicholas Piggin
                   ` (2 subsequent siblings)
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, linux-s390, Nico Böhr, Janosch Frank, kvm,
	David Hildenbrand, Alexandru Elisei, Nicholas Piggin,
	Andrew Jones, Eric Auger, Joel Stanley, kvm-riscv, kvmarm,
	Paolo Bonzini, Claudio Imbrenda, linuxppc-dev

configure will accept an unknown arch, and if it is the name of a
directory in the source tree the command will silently succeed. Make
it only accept supported arch names.

Also print the full path of a missing test directory to disambiguate
the error in out of tree builds.

Cc: Alexandru Elisei <alexandru.elisei@arm.com>
Cc: Andrew Jones <andrew.jones@linux.dev>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Eric Auger <eric.auger@redhat.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Laurent Vivier <lvivier@redhat.com>
Cc: Nico Böhr <nrb@linux.ibm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <thuth@redhat.com>
Cc: kvm@vger.kernel.org
Cc: linux-s390@vger.kernel.org
Cc: kvmarm@lists.linux.dev
Cc: kvm-riscv@lists.infradead.org
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 configure | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/configure b/configure
index 6907ccbbb..ae522c556 100755
--- a/configure
+++ b/configure
@@ -45,7 +45,8 @@ usage() {
 	Usage: $0 [options]
 
 	Options include:
-	    --arch=ARCH            architecture to compile for ($arch)
+	    --arch=ARCH            architecture to compile for ($arch). ARCH can be one of:
+	                           arm, arm64, i386, ppc64, riscv32, riscv64, s390x, x86_64
 	    --processor=PROCESSOR  processor to compile for ($arch)
 	    --target=TARGET        target platform that the tests will be running on (qemu or
 	                           kvmtool, default is qemu) (arm/arm64 only)
@@ -321,11 +322,15 @@ elif [ "$arch" = "ppc64" ]; then
 elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then
     testdir=riscv
     arch_libdir=riscv
+elif [ "$arch" = "s390x" ]; then
+    testdir=s390x
 else
-    testdir=$arch
+    echo "arch $arch is not supported!"
+    arch=
+    usage
 fi
 if [ ! -d "$srcdir/$testdir" ]; then
-    echo "$testdir does not exist!"
+    echo "$srcdir/$testdir does not exist!"
     exit 1
 fi
 
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (28 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:13   ` Andrew Jones
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 31/32] powerpc: Remove remnants of ppc64 directory and build structure Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update Nicholas Piggin
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, linux-s390, Nico Böhr, Janosch Frank, kvm,
	David Hildenbrand, Alexandru Elisei, Nicholas Piggin,
	Andrew Jones, Eric Auger, Joel Stanley, kvm-riscv, kvmarm,
	Paolo Bonzini, Claudio Imbrenda, linuxppc-dev

arch_libdir was brought in to improve the heuristic determination of
the lib/ directory based on arch and testdir names, but it did not
entirely clean that mess up.

Remove the arch_libdir->arch->testdir heuristic and just require
everybody sets arch_libdir correctly. Fail if the lib/arch or
lib/arch/asm directories can not be found.

Cc: Alexandru Elisei <alexandru.elisei@arm.com>
Cc: Andrew Jones <andrew.jones@linux.dev>
Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
Cc: David Hildenbrand <david@redhat.com>
Cc: Eric Auger <eric.auger@redhat.com>
Cc: Janosch Frank <frankja@linux.ibm.com>
Cc: Laurent Vivier <lvivier@redhat.com>
Cc: Nico Böhr <nrb@linux.ibm.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Thomas Huth <thuth@redhat.com>
Cc: kvm@vger.kernel.org
Cc: linux-s390@vger.kernel.org
Cc: kvmarm@lists.linux.dev
Cc: kvm-riscv@lists.infradead.org
Cc: linuxppc-dev@lists.ozlabs.org
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 Makefile  |  2 +-
 configure | 18 +++++++++++++-----
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile
index 4f35fffc6..4e0f54543 100644
--- a/Makefile
+++ b/Makefile
@@ -10,7 +10,7 @@ include config.mak
 VPATH = $(SRCDIR)
 
 libdirs-get = $(shell [ -d "lib/$(1)" ] && echo "lib/$(1) lib/$(1)/asm")
-ARCH_LIBDIRS := $(call libdirs-get,$(ARCH_LIBDIR)) $(call libdirs-get,$(TEST_DIR))
+ARCH_LIBDIRS := $(call libdirs-get,$(ARCH_LIBDIR))
 OBJDIRS := $(ARCH_LIBDIRS)
 
 DESTDIR := $(PREFIX)/share/kvm-unit-tests/
diff --git a/configure b/configure
index ae522c556..8c0e3506f 100755
--- a/configure
+++ b/configure
@@ -199,7 +199,6 @@ fi
 arch_name=$arch
 [ "$arch" = "aarch64" ] && arch="arm64"
 [ "$arch_name" = "arm64" ] && arch_name="aarch64"
-arch_libdir=$arch
 
 if [ "$arch" = "riscv" ]; then
     echo "riscv32 or riscv64 must be specified"
@@ -264,8 +263,10 @@ fi
 
 if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then
     testdir=x86
+    arch_libdir=x86
 elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
     testdir=arm
+    arch_libdir=$arch
     if [ "$target" = "qemu" ]; then
         arm_uart_early_addr=0x09000000
     elif [ "$target" = "kvmtool" ]; then
@@ -314,6 +315,7 @@ elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
     fi
 elif [ "$arch" = "ppc64" ]; then
     testdir=powerpc
+    arch_libdir=ppc64
     firmware="$testdir/boot_rom.bin"
     if [ "$endian" != "little" ] && [ "$endian" != "big" ]; then
         echo "You must provide endianness (big or little)!"
@@ -324,6 +326,7 @@ elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then
     arch_libdir=riscv
 elif [ "$arch" = "s390x" ]; then
     testdir=s390x
+    arch_libdir=s390x
 else
     echo "arch $arch is not supported!"
     arch=
@@ -333,6 +336,10 @@ if [ ! -d "$srcdir/$testdir" ]; then
     echo "$srcdir/$testdir does not exist!"
     exit 1
 fi
+if [ ! -d "$srcdir/lib/$arch_libdir" ]; then
+    echo "$srcdir/lib/$arch_libdir does not exist!"
+    exit 1
+fi
 
 if [ "$efi" = "y" ] && [ -f "$srcdir/$testdir/efi/run" ]; then
     ln -fs "$srcdir/$testdir/efi/run" $testdir-run
@@ -395,10 +402,11 @@ fi
 # link lib/asm for the architecture
 rm -f lib/asm
 asm="asm-generic"
-if [ -d "$srcdir/lib/$arch/asm" ]; then
-	asm="$srcdir/lib/$arch/asm"
-elif [ -d "$srcdir/lib/$testdir/asm" ]; then
-	asm="$srcdir/lib/$testdir/asm"
+if [ -d "$srcdir/lib/$arch_libdir/asm" ]; then
+    asm="$srcdir/lib/$arch_libdir/asm"
+else
+    echo "$srcdir/lib/$arch_libdir/asm does not exist"
+    exit 1
 fi
 mkdir -p lib
 ln -sf "$asm" lib/asm
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 31/32] powerpc: Remove remnants of ppc64 directory and build structure
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (29 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update Nicholas Piggin
  31 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This moves merges ppc64 directories and files into powerpc, and
merges the 3 makefiles into one.

The configure --arch=powerpc option is aliased to ppc64 for
good measure.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 MAINTAINERS                                |   1 -
 configure                                  |   3 +-
 lib/{ppc64 => powerpc}/asm-offsets.c       |   0
 lib/{ppc64 => powerpc}/asm/asm-offsets.h   |   0
 lib/{ppc64 => powerpc}/asm/atomic.h        |   0
 lib/{ppc64 => powerpc}/asm/barrier.h       |   4 +-
 lib/{ppc64 => powerpc}/asm/bitops.h        |   4 +-
 lib/{ppc64 => powerpc}/asm/io.h            |   4 +-
 lib/{ppc64 => powerpc}/asm/mmu.h           |   0
 lib/{ppc64 => powerpc}/asm/opal.h          |   4 +-
 lib/{ppc64 => powerpc}/asm/page.h          |   6 +-
 lib/{ppc64 => powerpc}/asm/pgtable-hwdef.h |   6 +-
 lib/{ppc64 => powerpc}/asm/pgtable.h       |   2 +-
 lib/{ppc64 => powerpc}/asm/ptrace.h        |   6 +-
 lib/{ppc64 => powerpc}/asm/spinlock.h      |   6 +-
 lib/powerpc/asm/stack.h                    |   3 +
 lib/{ppc64 => powerpc}/asm/vpa.h           |   0
 lib/{ppc64 => powerpc}/mmu.c               |   0
 lib/{ppc64 => powerpc}/opal-calls.S        |   0
 lib/{ppc64 => powerpc}/opal.c              |   0
 lib/{ppc64 => powerpc}/stack.c             |   0
 lib/ppc64/.gitignore                       |   1 -
 lib/ppc64/asm/handlers.h                   |   1 -
 lib/ppc64/asm/hcall.h                      |   1 -
 lib/ppc64/asm/memory_areas.h               |   6 --
 lib/ppc64/asm/ppc_asm.h                    |   1 -
 lib/ppc64/asm/processor.h                  |   1 -
 lib/ppc64/asm/reg.h                        |   1 -
 lib/ppc64/asm/rtas.h                       |   1 -
 lib/ppc64/asm/setup.h                      |   1 -
 lib/ppc64/asm/smp.h                        |   1 -
 lib/ppc64/asm/stack.h                      |  11 --
 powerpc/Makefile                           | 111 ++++++++++++++++++++-
 powerpc/Makefile.common                    |  95 ------------------
 powerpc/Makefile.ppc64                     |  31 ------
 35 files changed, 136 insertions(+), 176 deletions(-)
 rename lib/{ppc64 => powerpc}/asm-offsets.c (100%)
 rename lib/{ppc64 => powerpc}/asm/asm-offsets.h (100%)
 rename lib/{ppc64 => powerpc}/asm/atomic.h (100%)
 rename lib/{ppc64 => powerpc}/asm/barrier.h (83%)
 rename lib/{ppc64 => powerpc}/asm/bitops.h (69%)
 rename lib/{ppc64 => powerpc}/asm/io.h (50%)
 rename lib/{ppc64 => powerpc}/asm/mmu.h (100%)
 rename lib/{ppc64 => powerpc}/asm/opal.h (90%)
 rename lib/{ppc64 => powerpc}/asm/page.h (94%)
 rename lib/{ppc64 => powerpc}/asm/pgtable-hwdef.h (93%)
 rename lib/{ppc64 => powerpc}/asm/pgtable.h (99%)
 rename lib/{ppc64 => powerpc}/asm/ptrace.h (89%)
 rename lib/{ppc64 => powerpc}/asm/spinlock.h (54%)
 rename lib/{ppc64 => powerpc}/asm/vpa.h (100%)
 rename lib/{ppc64 => powerpc}/mmu.c (100%)
 rename lib/{ppc64 => powerpc}/opal-calls.S (100%)
 rename lib/{ppc64 => powerpc}/opal.c (100%)
 rename lib/{ppc64 => powerpc}/stack.c (100%)
 delete mode 100644 lib/ppc64/.gitignore
 delete mode 100644 lib/ppc64/asm/handlers.h
 delete mode 100644 lib/ppc64/asm/hcall.h
 delete mode 100644 lib/ppc64/asm/memory_areas.h
 delete mode 100644 lib/ppc64/asm/ppc_asm.h
 delete mode 100644 lib/ppc64/asm/processor.h
 delete mode 100644 lib/ppc64/asm/reg.h
 delete mode 100644 lib/ppc64/asm/rtas.h
 delete mode 100644 lib/ppc64/asm/setup.h
 delete mode 100644 lib/ppc64/asm/smp.h
 delete mode 100644 lib/ppc64/asm/stack.h
 delete mode 100644 powerpc/Makefile.common
 delete mode 100644 powerpc/Makefile.ppc64

diff --git a/MAINTAINERS b/MAINTAINERS
index a2fa437da..1309863f2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -92,7 +92,6 @@ S: Maintained
 L: linuxppc-dev@lists.ozlabs.org
 F: powerpc/
 F: lib/powerpc/
-F: lib/ppc64/
 
 RISCV
 M: Andrew Jones <andrew.jones@linux.dev>
diff --git a/configure b/configure
index 8c0e3506f..2dbc101cb 100755
--- a/configure
+++ b/configure
@@ -198,6 +198,7 @@ fi
 
 arch_name=$arch
 [ "$arch" = "aarch64" ] && arch="arm64"
+[ "$arch" = "powerpc" ] && arch="ppc64"
 [ "$arch_name" = "arm64" ] && arch_name="aarch64"
 
 if [ "$arch" = "riscv" ]; then
@@ -315,7 +316,7 @@ elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
     fi
 elif [ "$arch" = "ppc64" ]; then
     testdir=powerpc
-    arch_libdir=ppc64
+    arch_libdir=powerpc
     firmware="$testdir/boot_rom.bin"
     if [ "$endian" != "little" ] && [ "$endian" != "big" ]; then
         echo "You must provide endianness (big or little)!"
diff --git a/lib/ppc64/asm-offsets.c b/lib/powerpc/asm-offsets.c
similarity index 100%
rename from lib/ppc64/asm-offsets.c
rename to lib/powerpc/asm-offsets.c
diff --git a/lib/ppc64/asm/asm-offsets.h b/lib/powerpc/asm/asm-offsets.h
similarity index 100%
rename from lib/ppc64/asm/asm-offsets.h
rename to lib/powerpc/asm/asm-offsets.h
diff --git a/lib/ppc64/asm/atomic.h b/lib/powerpc/asm/atomic.h
similarity index 100%
rename from lib/ppc64/asm/atomic.h
rename to lib/powerpc/asm/atomic.h
diff --git a/lib/ppc64/asm/barrier.h b/lib/powerpc/asm/barrier.h
similarity index 83%
rename from lib/ppc64/asm/barrier.h
rename to lib/powerpc/asm/barrier.h
index 475434b6a..22349d691 100644
--- a/lib/ppc64/asm/barrier.h
+++ b/lib/powerpc/asm/barrier.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_BARRIER_H_
-#define _ASMPPC64_BARRIER_H_
+#ifndef _ASMPOWERPC_BARRIER_H_
+#define _ASMPOWERPC_BARRIER_H_
 
 #define cpu_relax() asm volatile("or 1,1,1 ; or 2,2,2" ::: "memory")
 #define pause_short() asm volatile(".long 0x7c40003c" ::: "memory")
diff --git a/lib/ppc64/asm/bitops.h b/lib/powerpc/asm/bitops.h
similarity index 69%
rename from lib/ppc64/asm/bitops.h
rename to lib/powerpc/asm/bitops.h
index c93d64bb9..dc1b8cd3f 100644
--- a/lib/ppc64/asm/bitops.h
+++ b/lib/powerpc/asm/bitops.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_BITOPS_H_
-#define _ASMPPC64_BITOPS_H_
+#ifndef _ASMPOWERPC_BITOPS_H_
+#define _ASMPOWERPC_BITOPS_H_
 
 #ifndef _BITOPS_H_
 #error only <bitops.h> can be included directly
diff --git a/lib/ppc64/asm/io.h b/lib/powerpc/asm/io.h
similarity index 50%
rename from lib/ppc64/asm/io.h
rename to lib/powerpc/asm/io.h
index 08d7297c3..cfe099f01 100644
--- a/lib/ppc64/asm/io.h
+++ b/lib/powerpc/asm/io.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_IO_H_
-#define _ASMPPC64_IO_H_
+#ifndef _ASMPOWERPC_IO_H_
+#define _ASMPOWERPC_IO_H_
 
 #define __iomem
 
diff --git a/lib/ppc64/asm/mmu.h b/lib/powerpc/asm/mmu.h
similarity index 100%
rename from lib/ppc64/asm/mmu.h
rename to lib/powerpc/asm/mmu.h
diff --git a/lib/ppc64/asm/opal.h b/lib/powerpc/asm/opal.h
similarity index 90%
rename from lib/ppc64/asm/opal.h
rename to lib/powerpc/asm/opal.h
index 6c3e9ffe2..44e62d80d 100644
--- a/lib/ppc64/asm/opal.h
+++ b/lib/powerpc/asm/opal.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-or-later */
-#ifndef _ASMPPC64_OPAL_H_
-#define _ASMPPC64_OPAL_H_
+#ifndef _ASMPOWERPC_OPAL_H_
+#define _ASMPOWERPC_OPAL_H_
 
 #include <stdint.h>
 
diff --git a/lib/ppc64/asm/page.h b/lib/powerpc/asm/page.h
similarity index 94%
rename from lib/ppc64/asm/page.h
rename to lib/powerpc/asm/page.h
index 95d5131cc..85292f738 100644
--- a/lib/ppc64/asm/page.h
+++ b/lib/powerpc/asm/page.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_PAGE_H_
-#define _ASMPPC64_PAGE_H_
+#ifndef _ASMPOWERPC_PAGE_H_
+#define _ASMPOWERPC_PAGE_H_
 /*
  * Adapted from
  *   lib/arm64/asm/page.h and Linux kernel defines.
@@ -63,4 +63,4 @@ extern unsigned long __phys_to_virt(phys_addr_t addr);
 extern void *__ioremap(phys_addr_t phys_addr, size_t size);
 
 #endif /* !__ASSEMBLY__ */
-#endif /* _ASMPPC64_PAGE_H_ */
+#endif /* _ASMPOWERPC_PAGE_H_ */
diff --git a/lib/ppc64/asm/pgtable-hwdef.h b/lib/powerpc/asm/pgtable-hwdef.h
similarity index 93%
rename from lib/ppc64/asm/pgtable-hwdef.h
rename to lib/powerpc/asm/pgtable-hwdef.h
index 6b20eaf09..fce62b89a 100644
--- a/lib/ppc64/asm/pgtable-hwdef.h
+++ b/lib/powerpc/asm/pgtable-hwdef.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_PGTABLE_HWDEF_H_
-#define _ASMPPC64_PGTABLE_HWDEF_H_
+#ifndef _ASMPOWERPC_PGTABLE_HWDEF_H_
+#define _ASMPOWERPC_PGTABLE_HWDEF_H_
 /*
  * Copyright (C) 2024, IBM Inc, Nicholas Piggin <npiggin@gmail.com>
  *
@@ -64,4 +64,4 @@
 #define PHYS_MASK_SHIFT		(48)
 #define PHYS_MASK		((UL(1) << PHYS_MASK_SHIFT) - 1)
 
-#endif /* _ASMPPC64_PGTABLE_HWDEF_H_ */
+#endif /* _ASMPOWERPC_PGTABLE_HWDEF_H_ */
diff --git a/lib/ppc64/asm/pgtable.h b/lib/powerpc/asm/pgtable.h
similarity index 99%
rename from lib/ppc64/asm/pgtable.h
rename to lib/powerpc/asm/pgtable.h
index c8a081543..7e49de59d 100644
--- a/lib/ppc64/asm/pgtable.h
+++ b/lib/powerpc/asm/pgtable.h
@@ -123,4 +123,4 @@ static inline pte_t *pte_alloc(pmd_t *pmd, unsigned long addr)
 	return pte_offset(pmd, addr);
 }
 
-#endif /* _ASMPPC64_PGTABLE_H_ */
+#endif /* _ASMPOWERPC_PGTABLE_H_ */
diff --git a/lib/ppc64/asm/ptrace.h b/lib/powerpc/asm/ptrace.h
similarity index 89%
rename from lib/ppc64/asm/ptrace.h
rename to lib/powerpc/asm/ptrace.h
index db263a59e..39ea950e7 100644
--- a/lib/ppc64/asm/ptrace.h
+++ b/lib/powerpc/asm/ptrace.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_PTRACE_H_
-#define _ASMPPC64_PTRACE_H_
+#ifndef _ASMPOWERPC_PTRACE_H_
+#define _ASMPOWERPC_PTRACE_H_
 
 #define KERNEL_REDZONE_SIZE	288
 #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
@@ -38,4 +38,4 @@ static inline void regs_advance_insn(struct pt_regs *regs)
 
 #endif /* __ASSEMBLY__ */
 
-#endif /* _ASMPPC64_PTRACE_H_ */
+#endif /* _ASMPOWERPC_PTRACE_H_ */
diff --git a/lib/ppc64/asm/spinlock.h b/lib/powerpc/asm/spinlock.h
similarity index 54%
rename from lib/ppc64/asm/spinlock.h
rename to lib/powerpc/asm/spinlock.h
index b952386da..9dbe716fd 100644
--- a/lib/ppc64/asm/spinlock.h
+++ b/lib/powerpc/asm/spinlock.h
@@ -1,5 +1,5 @@
-#ifndef _ASMPPC64_SPINLOCK_H_
-#define _ASMPPC64_SPINLOCK_H_
+#ifndef _ASMPOWERPC_SPINLOCK_H_
+#define _ASMPOWERPC_SPINLOCK_H_
 
 struct spinlock {
 	unsigned int v;
@@ -8,4 +8,4 @@ struct spinlock {
 void spin_lock(struct spinlock *lock);
 void spin_unlock(struct spinlock *lock);
 
-#endif /* _ASMPPC64_SPINLOCK_H_ */
+#endif /* _ASMPOWERPC_SPINLOCK_H_ */
diff --git a/lib/powerpc/asm/stack.h b/lib/powerpc/asm/stack.h
index e1c46ee09..eea139a45 100644
--- a/lib/powerpc/asm/stack.h
+++ b/lib/powerpc/asm/stack.h
@@ -5,4 +5,7 @@
 #error Do not directly include <asm/stack.h>. Just use <stack.h>.
 #endif
 
+#define HAVE_ARCH_BACKTRACE
+#define HAVE_ARCH_BACKTRACE_FRAME
+
 #endif
diff --git a/lib/ppc64/asm/vpa.h b/lib/powerpc/asm/vpa.h
similarity index 100%
rename from lib/ppc64/asm/vpa.h
rename to lib/powerpc/asm/vpa.h
diff --git a/lib/ppc64/mmu.c b/lib/powerpc/mmu.c
similarity index 100%
rename from lib/ppc64/mmu.c
rename to lib/powerpc/mmu.c
diff --git a/lib/ppc64/opal-calls.S b/lib/powerpc/opal-calls.S
similarity index 100%
rename from lib/ppc64/opal-calls.S
rename to lib/powerpc/opal-calls.S
diff --git a/lib/ppc64/opal.c b/lib/powerpc/opal.c
similarity index 100%
rename from lib/ppc64/opal.c
rename to lib/powerpc/opal.c
diff --git a/lib/ppc64/stack.c b/lib/powerpc/stack.c
similarity index 100%
rename from lib/ppc64/stack.c
rename to lib/powerpc/stack.c
diff --git a/lib/ppc64/.gitignore b/lib/ppc64/.gitignore
deleted file mode 100644
index 84872bf19..000000000
--- a/lib/ppc64/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-asm-offsets.[hs]
diff --git a/lib/ppc64/asm/handlers.h b/lib/ppc64/asm/handlers.h
deleted file mode 100644
index 92e6fb247..000000000
--- a/lib/ppc64/asm/handlers.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/handlers.h"
diff --git a/lib/ppc64/asm/hcall.h b/lib/ppc64/asm/hcall.h
deleted file mode 100644
index daabaca51..000000000
--- a/lib/ppc64/asm/hcall.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/hcall.h"
diff --git a/lib/ppc64/asm/memory_areas.h b/lib/ppc64/asm/memory_areas.h
deleted file mode 100644
index b9fd46b9e..000000000
--- a/lib/ppc64/asm/memory_areas.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASMPPC64_MEMORY_AREAS_H_
-#define _ASMPPC64_MEMORY_AREAS_H_
-
-#include <asm-generic/memory_areas.h>
-
-#endif
diff --git a/lib/ppc64/asm/ppc_asm.h b/lib/ppc64/asm/ppc_asm.h
deleted file mode 100644
index e3929eeee..000000000
--- a/lib/ppc64/asm/ppc_asm.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/ppc_asm.h"
diff --git a/lib/ppc64/asm/processor.h b/lib/ppc64/asm/processor.h
deleted file mode 100644
index 066a51a00..000000000
--- a/lib/ppc64/asm/processor.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/processor.h"
diff --git a/lib/ppc64/asm/reg.h b/lib/ppc64/asm/reg.h
deleted file mode 100644
index bc407b555..000000000
--- a/lib/ppc64/asm/reg.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/reg.h"
diff --git a/lib/ppc64/asm/rtas.h b/lib/ppc64/asm/rtas.h
deleted file mode 100644
index fe77f635c..000000000
--- a/lib/ppc64/asm/rtas.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/rtas.h"
diff --git a/lib/ppc64/asm/setup.h b/lib/ppc64/asm/setup.h
deleted file mode 100644
index 201929859..000000000
--- a/lib/ppc64/asm/setup.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/setup.h"
diff --git a/lib/ppc64/asm/smp.h b/lib/ppc64/asm/smp.h
deleted file mode 100644
index 67ced7567..000000000
--- a/lib/ppc64/asm/smp.h
+++ /dev/null
@@ -1 +0,0 @@
-#include "../../powerpc/asm/smp.h"
diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
deleted file mode 100644
index 94fd1021c..000000000
--- a/lib/ppc64/asm/stack.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef _ASMPPC64_STACK_H_
-#define _ASMPPC64_STACK_H_
-
-#ifndef _STACK_H_
-#error Do not directly include <asm/stack.h>. Just use <stack.h>.
-#endif
-
-#define HAVE_ARCH_BACKTRACE
-#define HAVE_ARCH_BACKTRACE_FRAME
-
-#endif
diff --git a/powerpc/Makefile b/powerpc/Makefile
index 8a007ab54..9f13d436d 100644
--- a/powerpc/Makefile
+++ b/powerpc/Makefile
@@ -1 +1,110 @@
-include $(SRCDIR)/$(TEST_DIR)/Makefile.$(ARCH)
+#
+# powerpc makefile
+#
+# Authors: Andrew Jones <drjones@redhat.com>
+#
+tests = \
+	$(TEST_DIR)/selftest.elf \
+	$(TEST_DIR)/selftest-migration.elf \
+	$(TEST_DIR)/memory-verify.elf \
+	$(TEST_DIR)/sieve.elf \
+	$(TEST_DIR)/spapr_vpa.elf \
+	$(TEST_DIR)/spapr_hcall.elf \
+	$(TEST_DIR)/rtas.elf \
+	$(TEST_DIR)/emulator.elf \
+	$(TEST_DIR)/atomics.elf \
+	$(TEST_DIR)/tm.elf \
+	$(TEST_DIR)/smp.elf \
+	$(TEST_DIR)/sprs.elf \
+	$(TEST_DIR)/timebase.elf \
+	$(TEST_DIR)/interrupts.elf \
+	$(TEST_DIR)/pmu.elf
+
+all: directories $(TEST_DIR)/boot_rom.bin $(tests)
+
+cstart.o = $(TEST_DIR)/cstart64.o
+reloc.o  = $(TEST_DIR)/reloc64.o
+
+OBJDIRS += lib/powerpc
+cflatobjs += lib/powerpc/stack.o
+cflatobjs += lib/powerpc/mmu.o
+cflatobjs += lib/powerpc/opal.o
+cflatobjs += lib/powerpc/opal-calls.o
+cflatobjs += lib/util.o
+cflatobjs += lib/getchar.o
+cflatobjs += lib/alloc_phys.o
+cflatobjs += lib/alloc.o
+cflatobjs += lib/alloc_page.o
+cflatobjs += lib/vmalloc.o
+cflatobjs += lib/devicetree.o
+cflatobjs += lib/migrate.o
+cflatobjs += lib/powerpc/io.o
+cflatobjs += lib/powerpc/hcall.o
+cflatobjs += lib/powerpc/setup.o
+cflatobjs += lib/powerpc/rtas.o
+cflatobjs += lib/powerpc/processor.o
+cflatobjs += lib/powerpc/handlers.o
+cflatobjs += lib/powerpc/smp.o
+cflatobjs += lib/powerpc/spinlock.o
+
+##################################################################
+
+bits = 64
+
+ifeq ($(ENDIAN),little)
+    arch_CFLAGS = -mlittle-endian
+    arch_LDFLAGS = -EL
+else
+    arch_CFLAGS = -mbig-endian
+    arch_LDFLAGS = -EB
+endif
+
+mabi_no_altivec := $(call cc-option,-mabi=no-altivec,"")
+
+CFLAGS += -std=gnu99
+CFLAGS += -ffreestanding
+CFLAGS += -O2 -msoft-float -mno-altivec $(mabi_no_altivec)
+CFLAGS += -I $(SRCDIR)/lib -I $(SRCDIR)/lib/libfdt -I lib
+CFLAGS += -Wa,-mregnames
+
+# We want to keep intermediate files
+.PRECIOUS: %.o
+
+asm-offsets = lib/powerpc/asm-offsets.h
+include $(SRCDIR)/scripts/asm-offsets.mak
+
+%.aux.o: $(SRCDIR)/lib/auxinfo.c
+	$(CC) $(CFLAGS) -c -o $@ $< -DPROGNAME=\"$(@:.aux.o=.elf)\"
+
+FLATLIBS = $(libcflat) $(LIBFDT_archive)
+%.elf: CFLAGS += $(arch_CFLAGS)
+%.elf: LDFLAGS += $(arch_LDFLAGS) -pie -n
+%.elf: %.o $(FLATLIBS) $(SRCDIR)/powerpc/flat.lds $(cstart.o) $(reloc.o) %.aux.o
+	$(LD) $(LDFLAGS) -o $@ \
+		-T $(SRCDIR)/powerpc/flat.lds --build-id=none \
+		$(filter %.o, $^) $(FLATLIBS)
+	@chmod a-x $@
+	@echo -n Checking $@ for unsupported reloc types...
+	@if $(OBJDUMP) -R $@ | grep R_ | grep -v R_PPC64_RELATIVE; then	\
+		false;							\
+	else								\
+		echo " looks good.";					\
+	fi
+
+$(TEST_DIR)/boot_rom.bin: $(TEST_DIR)/boot_rom.elf
+	dd if=/dev/zero of=$@ bs=256 count=1
+	$(OBJCOPY) -O binary $^ $@.tmp
+	cat $@.tmp >> $@
+	$(RM) $@.tmp
+
+$(TEST_DIR)/boot_rom.elf: CFLAGS = -mbig-endian
+$(TEST_DIR)/boot_rom.elf: $(TEST_DIR)/boot_rom.o
+	$(LD) -EB -nostdlib -Ttext=0x100 --entry=start --build-id=none -o $@ $<
+	@chmod a-x $@
+
+arch_clean: asm_offsets_clean
+	$(RM) $(TEST_DIR)/*.{o,elf} $(TEST_DIR)/boot_rom.bin \
+	      $(TEST_DIR)/.*.d lib/powerpc/.*.d
+
+generated-files = $(asm-offsets)
+$(tests:.elf=.o) $(cstart.o) $(cflatobjs): $(generated-files)
diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
deleted file mode 100644
index 64a3d93e4..000000000
--- a/powerpc/Makefile.common
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# powerpc common makefile
-#
-# Authors: Andrew Jones <drjones@redhat.com>
-#
-
-tests-common = \
-	$(TEST_DIR)/selftest.elf \
-	$(TEST_DIR)/selftest-migration.elf \
-	$(TEST_DIR)/memory-verify.elf \
-	$(TEST_DIR)/sieve.elf \
-	$(TEST_DIR)/spapr_hcall.elf \
-	$(TEST_DIR)/rtas.elf \
-	$(TEST_DIR)/emulator.elf \
-	$(TEST_DIR)/atomics.elf \
-	$(TEST_DIR)/tm.elf \
-	$(TEST_DIR)/smp.elf \
-	$(TEST_DIR)/sprs.elf \
-	$(TEST_DIR)/timebase.elf \
-	$(TEST_DIR)/interrupts.elf \
-	$(TEST_DIR)/pmu.elf
-
-tests-all = $(tests-common) $(tests)
-all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
-
-##################################################################
-
-mabi_no_altivec := $(call cc-option,-mabi=no-altivec,"")
-
-CFLAGS += -std=gnu99
-CFLAGS += -ffreestanding
-CFLAGS += -O2 -msoft-float -mno-altivec $(mabi_no_altivec)
-CFLAGS += -I $(SRCDIR)/lib -I $(SRCDIR)/lib/libfdt -I lib
-CFLAGS += -Wa,-mregnames
-
-# We want to keep intermediate files
-.PRECIOUS: %.o
-
-asm-offsets = lib/$(ARCH)/asm-offsets.h
-include $(SRCDIR)/scripts/asm-offsets.mak
-
-cflatobjs += lib/util.o
-cflatobjs += lib/getchar.o
-cflatobjs += lib/alloc_phys.o
-cflatobjs += lib/alloc.o
-cflatobjs += lib/alloc_page.o
-cflatobjs += lib/vmalloc.o
-cflatobjs += lib/devicetree.o
-cflatobjs += lib/migrate.o
-cflatobjs += lib/powerpc/io.o
-cflatobjs += lib/powerpc/hcall.o
-cflatobjs += lib/powerpc/setup.o
-cflatobjs += lib/powerpc/rtas.o
-cflatobjs += lib/powerpc/processor.o
-cflatobjs += lib/powerpc/handlers.o
-cflatobjs += lib/powerpc/smp.o
-cflatobjs += lib/powerpc/spinlock.o
-
-OBJDIRS += lib/powerpc
-
-%.aux.o: $(SRCDIR)/lib/auxinfo.c
-	$(CC) $(CFLAGS) -c -o $@ $< -DPROGNAME=\"$(@:.aux.o=.elf)\"
-
-FLATLIBS = $(libcflat) $(LIBFDT_archive)
-%.elf: CFLAGS += $(arch_CFLAGS)
-%.elf: LDFLAGS += $(arch_LDFLAGS) -pie -n
-%.elf: %.o $(FLATLIBS) $(SRCDIR)/powerpc/flat.lds $(cstart.o) $(reloc.o) %.aux.o
-	$(LD) $(LDFLAGS) -o $@ \
-		-T $(SRCDIR)/powerpc/flat.lds --build-id=none \
-		$(filter %.o, $^) $(FLATLIBS)
-	@chmod a-x $@
-	@echo -n Checking $@ for unsupported reloc types...
-	@if $(OBJDUMP) -R $@ | grep R_ | grep -v R_PPC64_RELATIVE; then	\
-		false;							\
-	else								\
-		echo " looks good.";					\
-	fi
-
-$(TEST_DIR)/boot_rom.bin: $(TEST_DIR)/boot_rom.elf
-	dd if=/dev/zero of=$@ bs=256 count=1
-	$(OBJCOPY) -O binary $^ $@.tmp
-	cat $@.tmp >> $@
-	$(RM) $@.tmp
-
-$(TEST_DIR)/boot_rom.elf: CFLAGS = -mbig-endian
-$(TEST_DIR)/boot_rom.elf: $(TEST_DIR)/boot_rom.o
-	$(LD) -EB -nostdlib -Ttext=0x100 --entry=start --build-id=none -o $@ $<
-	@chmod a-x $@
-
-powerpc_clean: asm_offsets_clean
-	$(RM) $(TEST_DIR)/*.{o,elf} $(TEST_DIR)/boot_rom.bin \
-	      $(TEST_DIR)/.*.d lib/powerpc/.*.d
-
-generated-files = $(asm-offsets)
-$(tests-all:.elf=.o) $(cstart.o) $(cflatobjs): $(generated-files)
diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
deleted file mode 100644
index 2466471f9..000000000
--- a/powerpc/Makefile.ppc64
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# ppc64 makefile
-#
-# Authors: Andrew Jones <drjones@redhat.com>
-#
-bits = 64
-
-ifeq ($(ENDIAN),little)
-    arch_CFLAGS = -mlittle-endian
-    arch_LDFLAGS = -EL
-else
-    arch_CFLAGS = -mbig-endian
-    arch_LDFLAGS = -EB
-endif
-
-cstart.o = $(TEST_DIR)/cstart64.o
-reloc.o  = $(TEST_DIR)/reloc64.o
-
-OBJDIRS += lib/ppc64
-cflatobjs += lib/ppc64/stack.o
-cflatobjs += lib/ppc64/mmu.o
-cflatobjs += lib/ppc64/opal.o
-cflatobjs += lib/ppc64/opal-calls.o
-
-# ppc64 specific tests
-tests = $(TEST_DIR)/spapr_vpa.elf
-
-include $(SRCDIR)/$(TEST_DIR)/Makefile.common
-
-arch_clean: powerpc_clean
-	$(RM) lib/ppc64/.*.d
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update
  2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
                   ` (30 preceding siblings ...)
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 31/32] powerpc: Remove remnants of ppc64 directory and build structure Nicholas Piggin
@ 2024-02-26 10:12 ` Nicholas Piggin
  2024-02-28 12:16   ` Andrew Jones
  31 siblings, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-26 10:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

This adds testing for the powernv machine, and adds a gitlab-ci test
group instead of specifying all tests in .gitlab-ci.yml.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 .gitlab-ci.yml        | 16 ++++++----------
 powerpc/unittests.cfg | 15 ++++++++-------
 2 files changed, 14 insertions(+), 17 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 61f196d5d..51a593021 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -69,11 +69,9 @@ build-ppc64be:
  - cd build
  - ../configure --arch=ppc64 --endian=big --cross-prefix=powerpc64-linux-gnu-
  - make -j2
- - ACCEL=tcg ./run_tests.sh
-     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
-     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
-     emulator
-     | tee results.txt
+ - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
+ - if grep -q FAIL results.txt ; then exit 1 ; fi
+ - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
  - if grep -q FAIL results.txt ; then exit 1 ; fi
 
 build-ppc64le:
@@ -82,11 +80,9 @@ build-ppc64le:
  - dnf install -y qemu-system-ppc gcc-powerpc64-linux-gnu nmap-ncat
  - ./configure --arch=ppc64 --endian=little --cross-prefix=powerpc64-linux-gnu-
  - make -j2
- - ACCEL=tcg ./run_tests.sh
-     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
-     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
-     emulator
-     | tee results.txt
+ - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
+ - if grep -q FAIL results.txt ; then exit 1 ; fi
+ - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
  - if grep -q FAIL results.txt ; then exit 1 ; fi
 
 # build-riscv32:
diff --git a/powerpc/unittests.cfg b/powerpc/unittests.cfg
index e275f389b..21071a1a1 100644
--- a/powerpc/unittests.cfg
+++ b/powerpc/unittests.cfg
@@ -34,17 +34,17 @@
 file = selftest.elf
 smp = 2
 extra_params = -m 1g -append 'setup smp=2 mem=1024'
-groups = selftest
+groups = selftest gitlab-ci
 
 [selftest-migration]
 file = selftest-migration.elf
 machine = pseries
-groups = selftest migration
+groups = selftest migration gitlab-ci
 
 [selftest-migration-skip]
 file = selftest-migration.elf
 machine = pseries
-groups = selftest migration
+groups = selftest migration gitlab-ci
 extra_params = -append "skip"
 
 # This fails due to a QEMU TCG bug so KVM-only until QEMU is fixed upstream
@@ -56,7 +56,7 @@ groups = migration
 
 [spapr_hcall]
 file = spapr_hcall.elf
-machine = pseries
+machine = pseries gitlab-ci
 
 [spapr_vpa]
 file = spapr_vpa.elf
@@ -67,24 +67,25 @@ file = rtas.elf
 machine = pseries
 timeout = 5
 extra_params = -append "get-time-of-day date=$(date +%s)"
-groups = rtas
+groups = rtas gitlab-ci
 
 [rtas-get-time-of-day-base]
 file = rtas.elf
 machine = pseries
 timeout = 5
 extra_params = -rtc base="2006-06-17" -append "get-time-of-day date=$(date --date="2006-06-17 UTC" +%s)"
-groups = rtas
+groups = rtas gitlab-ci
 
 [rtas-set-time-of-day]
 file = rtas.elf
 machine = pseries
 extra_params = -append "set-time-of-day"
 timeout = 5
-groups = rtas
+groups = rtas gitlab-ci
 
 [emulator]
 file = emulator.elf
+groups = gitlab-ci
 
 [interrupts]
 file = interrupts.elf
-- 
2.42.0


^ permalink raw reply related	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts Nicholas Piggin
@ 2024-02-26 11:33   ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-02-26 11:33 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> KVM does not like to run on POWER9 hosts without cap-ccf-assist=off.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   powerpc/run | 2 ++
>   1 file changed, 2 insertions(+)
> 
> diff --git a/powerpc/run b/powerpc/run
> index e469f1eb3..5cdb94194 100755
> --- a/powerpc/run
> +++ b/powerpc/run
> @@ -24,6 +24,8 @@ M+=",accel=$ACCEL$ACCEL_PROPS"
>   
>   if [[ "$ACCEL" == "tcg" ]] ; then
>   	M+=",cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken,cap-ccf-assist=off"
> +elif [[ "$ACCEL" == "kvm" ]] ; then
> +	M+=",cap-ccf-assist=off"
>   fi

Since it is needed in both cases, you could also move it out of the 
if-statement and remove it from the tcg part.

Anyway,
Reviewed-by: Thomas Huth <thuth@redhat.com>


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value Nicholas Piggin
@ 2024-02-26 11:35   ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-02-26 11:35 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> getchar() didn't get the shift value correct and never returned the
> first character. This never really mattered since it was only ever
> used for press-a-key-to-continue prompts. but it tripped me up when
> debugging a QEMU console output problem.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/hcall.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/lib/powerpc/hcall.c b/lib/powerpc/hcall.c
> index 711cb1b0f..b4d39ac65 100644
> --- a/lib/powerpc/hcall.c
> +++ b/lib/powerpc/hcall.c
> @@ -43,5 +43,5 @@ int __getchar(void)
>   	asm volatile (" sc 1 "  : "+r"(r3), "+r"(r4), "=r"(r5)
>   				: "r"(r3),  "r"(r4));
>   
> -	return r3 == H_SUCCESS && r4 > 0 ? r5 >> 48 : -1;
> +	return r3 == H_SUCCESS && r4 > 0 ? r5 >> 56 : -1;
>   }

Reviewed-by: Thomas Huth <thuth@redhat.com>


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination Nicholas Piggin
@ 2024-02-27  8:50   ` Thomas Huth
  2024-03-01  9:45     ` Thomas Huth
  2024-03-05  6:29     ` Nicholas Piggin
  0 siblings, 2 replies; 71+ messages in thread
From: Thomas Huth @ 2024-02-27  8:50 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> The backtrace handler terminates when it sees a NULL caller address,
> but the powerpc stack setup does not keep such a NULL caller frame
> at the start of the stack.
> 
> This happens to work on pseries because the memory at 0 is mapped and
> it contains 0 at the location of the return address pointer if it
> were a stack frame. But this is fragile, and does not work with powernv
> where address 0 contains firmware instructions.
> 
> Use the existing dummy frame on stack as the NULL caller, and create a
> new frame on stack for the entry code.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   powerpc/cstart64.S | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)

Thanks for tackling this! ... however, not doing powerpc work since years 
anymore, I have some ignorant questions below...

> diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> index e18ae9a22..14ab0c6c8 100644
> --- a/powerpc/cstart64.S
> +++ b/powerpc/cstart64.S
> @@ -46,8 +46,16 @@ start:
>   	add	r1, r1, r31
>   	add	r2, r2, r31
>   
> +	/* Zero backpointers in initial stack frame so backtrace() stops */
> +	li	r0,0
> +	std	r0,0(r1)

0(r1) is the back chain pointer ...

> +	std	r0,16(r1)

... but what is 16(r1) ? I suppose that should be the "LR save word" ? But 
isn't that at 8(r1) instead?? (not sure whether I'm looking at the right ELF 
abi spec right now...)

Anyway, a comment in the source would be helpful here.

> +
> +	/* Create entry frame */
> +	stdu	r1,-INT_FRAME_SIZE(r1)

Since we already create an initial frame via stackptr from powerpc/flat.lds, 
do we really need to create this additional one here? Or does the one from 
flat.lds have to be completely empty, i.e. also no DTB pointer in it?

>   	/* save DTB pointer */
> -	std	r3, 56(r1)
> +	SAVE_GPR(3,r1)

Isn't SAVE_GPR rather meant for the interrupt frame, not for the normal C 
calling convention frames?

Sorry for asking dumb questions ... I still have a hard time understanding 
the changes here... :-/

>   	/*
>   	 * Call relocate. relocate is C code, but careful to not use
> @@ -101,7 +109,7 @@ start:
>   	stw	r4, 0(r3)
>   
>   	/* complete setup */
> -1:	ld	r3, 56(r1)
> +1:	REST_GPR(3, r1)
>   	bl	setup
>   
>   	/* run the test */

  Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing Nicholas Piggin
@ 2024-02-28 11:46   ` Andrew Jones
  2024-02-29  3:50     ` Nicholas Piggin
  2024-03-01  9:53   ` Thomas Huth
  1 sibling, 1 reply; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 11:46 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:11:50PM +1000, Nicholas Piggin wrote:
> Add support for backtracing across interrupt stacks, and
> add interrupt frame backtrace for unhandled interrupts.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  lib/powerpc/processor.c |  4 ++-
>  lib/ppc64/asm/stack.h   |  3 +++
>  lib/ppc64/stack.c       | 55 +++++++++++++++++++++++++++++++++++++++++
>  powerpc/Makefile.ppc64  |  1 +
>  powerpc/cstart64.S      |  7 ++++--
>  5 files changed, 67 insertions(+), 3 deletions(-)
>  create mode 100644 lib/ppc64/stack.c
> 
> diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
> index ad0d95666..114584024 100644
> --- a/lib/powerpc/processor.c
> +++ b/lib/powerpc/processor.c
> @@ -51,7 +51,9 @@ void do_handle_exception(struct pt_regs *regs)
>  		return;
>  	}
>  
> -	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
> +	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
> +			regs->trap, regs->nip, regs->msr);
> +	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
>  	abort();
>  }
>  
> diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
> index 9734bbb8f..94fd1021c 100644
> --- a/lib/ppc64/asm/stack.h
> +++ b/lib/ppc64/asm/stack.h
> @@ -5,4 +5,7 @@
>  #error Do not directly include <asm/stack.h>. Just use <stack.h>.
>  #endif
>  
> +#define HAVE_ARCH_BACKTRACE
> +#define HAVE_ARCH_BACKTRACE_FRAME
> +
>  #endif
> diff --git a/lib/ppc64/stack.c b/lib/ppc64/stack.c
> new file mode 100644
> index 000000000..fcb7fa860
> --- /dev/null
> +++ b/lib/ppc64/stack.c
> @@ -0,0 +1,55 @@
> +#include <libcflat.h>
> +#include <asm/ptrace.h>
> +#include <stack.h>
> +
> +extern char exception_stack_marker[];
> +
> +int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
> +{
> +	static int walking;
> +	int depth = 0;
> +	const unsigned long *bp = (unsigned long *)frame;
> +	void *return_addr;
> +
> +	asm volatile("" ::: "lr"); /* Force it to save LR */
> +
> +	if (walking) {
> +		printf("RECURSIVE STACK WALK!!!\n");
> +		return 0;
> +	}
> +	walking = 1;
> +
> +	bp = (unsigned long *)bp[0];
> +	return_addr = (void *)bp[2];
> +
> +	for (depth = 0; bp && depth < max_depth; depth++) {
> +		return_addrs[depth] = return_addr;
> +		if (return_addrs[depth] == 0)
> +			break;
> +		if (return_addrs[depth] == exception_stack_marker) {
> +			struct pt_regs *regs;
> +
> +			regs = (void *)bp + STACK_FRAME_OVERHEAD;
> +			bp = (unsigned long *)bp[0];
> +			/* Represent interrupt frame with vector number */
> +			return_addr = (void *)regs->trap;
> +			if (depth + 1 < max_depth) {
> +				depth++;
> +				return_addrs[depth] = return_addr;
> +				return_addr = (void *)regs->nip;
> +			}
> +		} else {
> +			bp = (unsigned long *)bp[0];
> +			return_addr = (void *)bp[2];
> +		}
> +	}
> +
> +	walking = 0;
> +	return depth;
> +}
> +
> +int backtrace(const void **return_addrs, int max_depth)
> +{
> +	return backtrace_frame(__builtin_frame_address(0), return_addrs,
> +			       max_depth);
> +}

I'm about to post a series which has a couple treewide tracing changes
in them. Depending on which series goes first the other will need to
accommodate.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg Nicholas Piggin
@ 2024-02-28 11:47   ` Andrew Jones
  2024-02-29  3:52     ` Nicholas Piggin
  0 siblings, 1 reply; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 11:47 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:11:55PM +1000, Nicholas Piggin wrote:
> This allows different machines with different requirements to be
> supported by run_tests.sh, similarly to how different accelerators
> are handled.
> 
> Acked-by: Thomas Huth <thuth@redhat.com>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  scripts/common.bash  |  8 ++++++--
>  scripts/runtime.bash | 16 ++++++++++++----
>  2 files changed, 18 insertions(+), 6 deletions(-)

Please also update the unittests.cfg documentation. Currently that
documentation lives in the header of each unittests.cfg file, but
we could maybe change each file to have a single line which points
at a single document.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages Nicholas Piggin
@ 2024-02-28 11:51   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 11:51 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:03PM +1000, Nicholas Piggin wrote:
> In SMP tests, it's possible for multiple CPUs to print an exit
> message if they abort concurrently, confusing the harness:
> 
>   EXIT: STATUS=127
> 
>   EXIT: STATUS=127
>   scripts/arch-run.bash: line 85: [: too many arguments
>   scripts/arch-run.bash: line 93: return: too many arguments
> 
> lib/arch code should probably serialise this to prevent it, but
> at the moment not all do. So make the parser handle this by
> just looking at the first EXIT.
> 
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: Andrew Jones <andrew.jones@linux.dev>
> Cc: kvm@vger.kernel.org
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  scripts/arch-run.bash | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/scripts/arch-run.bash b/scripts/arch-run.bash
> index 5c7e72036..4af670f1c 100644
> --- a/scripts/arch-run.bash
> +++ b/scripts/arch-run.bash
> @@ -79,7 +79,7 @@ run_qemu_status ()
>  	exec {stdout}>&-
>  
>  	if [ $ret -eq 1 ]; then
> -		testret=$(grep '^EXIT: ' <<<"$lines" | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/')
> +		testret=$(grep '^EXIT: ' <<<"$lines" | head -n1 | sed 's/.*STATUS=\([0-9][0-9]*\).*/\1/')
>  		if [ "$testret" ]; then
>  			if [ $testret -eq 1 ]; then
>  				ret=0
> -- 
> 2.42.0
>

Acked-by: Andrew Jones <andrew.jones@linux.dev>

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences Nicholas Piggin
@ 2024-02-28 11:52   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 11:52 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:11:56PM +1000, Nicholas Piggin wrote:
> The QEMU powerpc powernv machine has minor differences that must be
> accommodated for in output parsing:
> 
> - Summary parsing must search more lines of output for the summary
>   line, to accommodate OPAL message on shutdown.
> - Premature failure testing must tolerate case differences in kernel
>   load error message.
> 
> Acked-by: Thomas Huth <thuth@redhat.com>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  scripts/runtime.bash | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/scripts/runtime.bash b/scripts/runtime.bash
> index 8f9672d0d..bb32c0d10 100644
> --- a/scripts/runtime.bash
> +++ b/scripts/runtime.bash
> @@ -9,7 +9,7 @@ FAIL() { echo -ne "\e[31mFAIL\e[0m"; }
>  extract_summary()
>  {
>      local cr=$'\r'
> -    tail -3 | grep '^SUMMARY: ' | sed 's/^SUMMARY: /(/;s/'"$cr"'\{0,1\}$/)/'
> +    tail -5 | grep '^SUMMARY: ' | sed 's/^SUMMARY: /(/;s/'"$cr"'\{0,1\}$/)/'
>  }
>  
>  # We assume that QEMU is going to work if it tried to load the kernel
> @@ -18,7 +18,7 @@ premature_failure()
>      local log="$(eval "$(get_cmdline _NO_FILE_4Uhere_)" 2>&1)"
>  
>      echo "$log" | grep "_NO_FILE_4Uhere_" |
> -        grep -q -e "could not \(load\|open\) kernel" -e "error loading" &&
> +        grep -q -e "[Cc]ould not \(load\|open\) kernel" -e "error loading" &&
>          return 1
>  
>      RUNTIME_log_stderr <<< "$log"
> -- 
> 2.42.0
>

Acked-by: Andrew Jones <andrew.jones@linux.dev>

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support Nicholas Piggin
@ 2024-02-28 12:01   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:01 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:09PM +1000, Nicholas Piggin wrote:
> Add support for radix MMU, 4kB and 64kB pages.
> 
> This also adds MMU interrupt test cases, and runs the interrupts
> test entirely with MMU enabled if it is available (aside from
> machine check tests).
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  configure                     |  39 +++--
>  lib/powerpc/asm/hcall.h       |   6 +
>  lib/powerpc/asm/processor.h   |   1 +
>  lib/powerpc/asm/reg.h         |   3 +
>  lib/powerpc/asm/smp.h         |   2 +
>  lib/powerpc/processor.c       |   9 ++
>  lib/powerpc/setup.c           |   4 +
>  lib/ppc64/asm/mmu.h           |  11 ++
>  lib/ppc64/asm/page.h          |  67 ++++++++-
>  lib/ppc64/asm/pgtable-hwdef.h |  67 +++++++++
>  lib/ppc64/asm/pgtable.h       | 126 ++++++++++++++++
>  lib/ppc64/mmu.c               | 273 ++++++++++++++++++++++++++++++++++
>  lib/ppc64/opal-calls.S        |   4 +-
>  powerpc/Makefile.common       |   2 +
>  powerpc/Makefile.ppc64        |   1 +
>  powerpc/interrupts.c          |  96 ++++++++++--
>  16 files changed, 684 insertions(+), 27 deletions(-)
>  create mode 100644 lib/ppc64/asm/mmu.h
>  create mode 100644 lib/ppc64/asm/pgtable-hwdef.h
>  create mode 100644 lib/ppc64/asm/pgtable.h
>  create mode 100644 lib/ppc64/mmu.c
> 
> diff --git a/configure b/configure
> index 05e6702ea..6907ccbbb 100755
> --- a/configure
> +++ b/configure
> @@ -222,29 +222,35 @@ fi
>  if [ -z "$page_size" ]; then
>      if [ "$efi" = 'y' ] && [ "$arch" = "arm64" ]; then
>          page_size="4096"
> -    elif [ "$arch" = "arm64" ]; then
> +    elif [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then
>          page_size="65536"
>      elif [ "$arch" = "arm" ]; then
>          page_size="4096"
>      fi
>  else
> -    if [ "$arch" != "arm64" ]; then
> -        echo "--page-size is not supported for $arch"
> -        usage
> -    fi
> -
>      if [ "${page_size: -1}" = "K" ] || [ "${page_size: -1}" = "k" ]; then
>          page_size=$(( ${page_size%?} * 1024 ))
>      fi
> -    if [ "$page_size" != "4096" ] && [ "$page_size" != "16384" ] &&
> -           [ "$page_size" != "65536" ]; then
> -        echo "arm64 doesn't support page size of $page_size"
> +
> +    if [ "$arch" = "arm64" ]; then
> +        if [ "$page_size" != "4096" ] && [ "$page_size" != "16384" ] &&
> +               [ "$page_size" != "65536" ]; then
> +            echo "arm64 doesn't support page size of $page_size"
> +            usage
> +        fi
> +        if [ "$efi" = 'y' ] && [ "$page_size" != "4096" ]; then
> +            echo "efi must use 4K pages"
> +            exit 1
> +        fi
> +    elif [ "$arch" = "ppc64" ]; then
> +        if [ "$page_size" != "4096" ] && [ "$page_size" != "65536" ]; then
> +            echo "ppc64 doesn't support page size of $page_size"
> +            usage
> +        fi
> +    else
> +        echo "--page-size is not supported for $arch"
>          usage
>      fi
> -    if [ "$efi" = 'y' ] && [ "$page_size" != "4096" ]; then
> -        echo "efi must use 4K pages"
> -        exit 1
> -    fi
>  fi
>  
>  [ -z "$processor" ] && processor="$arch"
> @@ -444,6 +450,13 @@ cat <<EOF >> lib/config.h
>  
>  #define CONFIG_UART_EARLY_BASE ${arm_uart_early_addr}
>  #define CONFIG_ERRATA_FORCE ${errata_force}
> +
> +EOF
> +fi
> +
> +if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then
> +cat <<EOF >> lib/config.h
> +
>  #define CONFIG_PAGE_SIZE _AC(${page_size}, UL)
>  
>  EOF

Ack for the configure changes.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition Nicholas Piggin
@ 2024-02-28 12:03   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:03 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, linux-s390, Thomas Huth, Nico Böhr,
	Janosch Frank, kvm, David Hildenbrand, Joel Stanley,
	Paolo Bonzini, Claudio Imbrenda, linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:10PM +1000, Nicholas Piggin wrote:
> There is no good reason to put setup_vm in libcflat.h when it's
> defined in vmalloc.h.
> 
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: Andrew Jones <andrew.jones@linux.dev>
> Cc: Janosch Frank <frankja@linux.ibm.com>
> Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
> Cc: Nico Böhr <nrb@linux.ibm.com>
> Cc: David Hildenbrand <david@redhat.com>
> Cc: kvm@vger.kernel.org
> Cc: linux-s390@vger.kernel.org
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  common/sieve.c         | 1 +
>  lib/libcflat.h         | 2 --
>  lib/s390x/io.c         | 1 +
>  lib/s390x/uv.h         | 1 +
>  lib/x86/vm.h           | 1 +
>  s390x/mvpg.c           | 1 +
>  s390x/selftest.c       | 1 +
>  x86/pmu.c              | 1 +
>  x86/pmu_lbr.c          | 1 +
>  x86/vmexit.c           | 1 +
>  x86/vmware_backdoors.c | 1 +
>  11 files changed, 10 insertions(+), 2 deletions(-)
>

Acked-by: Andrew Jones <andrew.jones@linux.dev>

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU Nicholas Piggin
@ 2024-02-28 12:04   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:04 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:11PM +1000, Nicholas Piggin wrote:
> Not all powerpc CPUs provide MMU support. Define vm_available() that is
> true by default but archs can override it. Use this to run VM tests.
> 
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: Andrew Jones <andrew.jones@linux.dev>
> Cc: kvm@vger.kernel.org
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  common/sieve.c      | 14 ++++++++------
>  lib/ppc64/asm/mmu.h |  1 -
>  lib/ppc64/mmu.c     |  2 +-
>  lib/vmalloc.c       |  7 +++++++
>  lib/vmalloc.h       |  2 ++
>  5 files changed, 18 insertions(+), 8 deletions(-)
>

Reviewed-by: Andrew Jones <andrew.jones@linux.dev>

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch Nicholas Piggin
@ 2024-02-28 12:08   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:08 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, linux-s390, Thomas Huth, Nico Böhr,
	Janosch Frank, kvm, David Hildenbrand, Alexandru Elisei,
	Eric Auger, Joel Stanley, kvm-riscv, kvmarm, Paolo Bonzini,
	Claudio Imbrenda, linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:15PM +1000, Nicholas Piggin wrote:
> configure will accept an unknown arch, and if it is the name of a
> directory in the source tree the command will silently succeed. Make
> it only accept supported arch names.
> 
> Also print the full path of a missing test directory to disambiguate
> the error in out of tree builds.
> 
> Cc: Alexandru Elisei <alexandru.elisei@arm.com>
> Cc: Andrew Jones <andrew.jones@linux.dev>
> Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
> Cc: David Hildenbrand <david@redhat.com>
> Cc: Eric Auger <eric.auger@redhat.com>
> Cc: Janosch Frank <frankja@linux.ibm.com>
> Cc: Laurent Vivier <lvivier@redhat.com>
> Cc: Nico Böhr <nrb@linux.ibm.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: kvm@vger.kernel.org
> Cc: linux-s390@vger.kernel.org
> Cc: kvmarm@lists.linux.dev
> Cc: kvm-riscv@lists.infradead.org
> Cc: linuxppc-dev@lists.ozlabs.org
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  configure | 11 ++++++++---
>  1 file changed, 8 insertions(+), 3 deletions(-)
> 
> diff --git a/configure b/configure
> index 6907ccbbb..ae522c556 100755
> --- a/configure
> +++ b/configure
> @@ -45,7 +45,8 @@ usage() {
>  	Usage: $0 [options]
>  
>  	Options include:
> -	    --arch=ARCH            architecture to compile for ($arch)
> +	    --arch=ARCH            architecture to compile for ($arch). ARCH can be one of:
> +	                           arm, arm64, i386, ppc64, riscv32, riscv64, s390x, x86_64
>  	    --processor=PROCESSOR  processor to compile for ($arch)
>  	    --target=TARGET        target platform that the tests will be running on (qemu or
>  	                           kvmtool, default is qemu) (arm/arm64 only)
> @@ -321,11 +322,15 @@ elif [ "$arch" = "ppc64" ]; then
>  elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then
>      testdir=riscv
>      arch_libdir=riscv
> +elif [ "$arch" = "s390x" ]; then
> +    testdir=s390x
>  else
> -    testdir=$arch
> +    echo "arch $arch is not supported!"
> +    arch=
> +    usage
>  fi
>  if [ ! -d "$srcdir/$testdir" ]; then
> -    echo "$testdir does not exist!"
> +    echo "$srcdir/$testdir does not exist!"
>      exit 1
>  fi
>  
> -- 
> 2.42.0
>

Reviewed-by: Andrew Jones <andrew.jones@linux.dev>

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity Nicholas Piggin
@ 2024-02-28 12:13   ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:13 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, linux-s390, Thomas Huth, Nico Böhr,
	Janosch Frank, kvm, David Hildenbrand, Alexandru Elisei,
	Eric Auger, Joel Stanley, kvm-riscv, kvmarm, Paolo Bonzini,
	Claudio Imbrenda, linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:16PM +1000, Nicholas Piggin wrote:
> arch_libdir was brought in to improve the heuristic determination of
> the lib/ directory based on arch and testdir names, but it did not
> entirely clean that mess up.
> 
> Remove the arch_libdir->arch->testdir heuristic and just require
> everybody sets arch_libdir correctly. Fail if the lib/arch or
> lib/arch/asm directories can not be found.
> 
> Cc: Alexandru Elisei <alexandru.elisei@arm.com>
> Cc: Andrew Jones <andrew.jones@linux.dev>
> Cc: Claudio Imbrenda <imbrenda@linux.ibm.com>
> Cc: David Hildenbrand <david@redhat.com>
> Cc: Eric Auger <eric.auger@redhat.com>
> Cc: Janosch Frank <frankja@linux.ibm.com>
> Cc: Laurent Vivier <lvivier@redhat.com>
> Cc: Nico Böhr <nrb@linux.ibm.com>
> Cc: Paolo Bonzini <pbonzini@redhat.com>
> Cc: Thomas Huth <thuth@redhat.com>
> Cc: kvm@vger.kernel.org
> Cc: linux-s390@vger.kernel.org
> Cc: kvmarm@lists.linux.dev
> Cc: kvm-riscv@lists.infradead.org
> Cc: linuxppc-dev@lists.ozlabs.org
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  Makefile  |  2 +-
>  configure | 18 +++++++++++++-----
>  2 files changed, 14 insertions(+), 6 deletions(-)
> 
> diff --git a/Makefile b/Makefile
> index 4f35fffc6..4e0f54543 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -10,7 +10,7 @@ include config.mak
>  VPATH = $(SRCDIR)
>  
>  libdirs-get = $(shell [ -d "lib/$(1)" ] && echo "lib/$(1) lib/$(1)/asm")
> -ARCH_LIBDIRS := $(call libdirs-get,$(ARCH_LIBDIR)) $(call libdirs-get,$(TEST_DIR))
> +ARCH_LIBDIRS := $(call libdirs-get,$(ARCH_LIBDIR))
>  OBJDIRS := $(ARCH_LIBDIRS)
>  
>  DESTDIR := $(PREFIX)/share/kvm-unit-tests/
> diff --git a/configure b/configure
> index ae522c556..8c0e3506f 100755
> --- a/configure
> +++ b/configure
> @@ -199,7 +199,6 @@ fi
>  arch_name=$arch
>  [ "$arch" = "aarch64" ] && arch="arm64"
>  [ "$arch_name" = "arm64" ] && arch_name="aarch64"
> -arch_libdir=$arch
>  
>  if [ "$arch" = "riscv" ]; then
>      echo "riscv32 or riscv64 must be specified"
> @@ -264,8 +263,10 @@ fi
>  
>  if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then
>      testdir=x86
> +    arch_libdir=x86
>  elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
>      testdir=arm
> +    arch_libdir=$arch
>      if [ "$target" = "qemu" ]; then
>          arm_uart_early_addr=0x09000000
>      elif [ "$target" = "kvmtool" ]; then
> @@ -314,6 +315,7 @@ elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then
>      fi
>  elif [ "$arch" = "ppc64" ]; then
>      testdir=powerpc
> +    arch_libdir=ppc64
>      firmware="$testdir/boot_rom.bin"
>      if [ "$endian" != "little" ] && [ "$endian" != "big" ]; then
>          echo "You must provide endianness (big or little)!"
> @@ -324,6 +326,7 @@ elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then
>      arch_libdir=riscv
>  elif [ "$arch" = "s390x" ]; then
>      testdir=s390x
> +    arch_libdir=s390x

Probably could have left the arch_libdir=$arch above and only added the
ppc64 line, but either way.

>  else
>      echo "arch $arch is not supported!"
>      arch=
> @@ -333,6 +336,10 @@ if [ ! -d "$srcdir/$testdir" ]; then
>      echo "$srcdir/$testdir does not exist!"
>      exit 1
>  fi
> +if [ ! -d "$srcdir/lib/$arch_libdir" ]; then
> +    echo "$srcdir/lib/$arch_libdir does not exist!"
> +    exit 1
> +fi
>  
>  if [ "$efi" = "y" ] && [ -f "$srcdir/$testdir/efi/run" ]; then
>      ln -fs "$srcdir/$testdir/efi/run" $testdir-run
> @@ -395,10 +402,11 @@ fi
>  # link lib/asm for the architecture
>  rm -f lib/asm
>  asm="asm-generic"
> -if [ -d "$srcdir/lib/$arch/asm" ]; then
> -	asm="$srcdir/lib/$arch/asm"
> -elif [ -d "$srcdir/lib/$testdir/asm" ]; then
> -	asm="$srcdir/lib/$testdir/asm"
> +if [ -d "$srcdir/lib/$arch_libdir/asm" ]; then
> +    asm="$srcdir/lib/$arch_libdir/asm"
> +else
> +    echo "$srcdir/lib/$arch_libdir/asm does not exist"
> +    exit 1
>  fi
>  mkdir -p lib
>  ln -sf "$asm" lib/asm
> -- 
> 2.42.0
> 
>

Reviewed-by: Andrew Jones <andrew.jones@linux.dev>

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update Nicholas Piggin
@ 2024-02-28 12:16   ` Andrew Jones
  2024-02-29  3:55     ` Nicholas Piggin
  0 siblings, 1 reply; 71+ messages in thread
From: Andrew Jones @ 2024-02-28 12:16 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Mon, Feb 26, 2024 at 08:12:18PM +1000, Nicholas Piggin wrote:
> This adds testing for the powernv machine, and adds a gitlab-ci test
> group instead of specifying all tests in .gitlab-ci.yml.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>  .gitlab-ci.yml        | 16 ++++++----------
>  powerpc/unittests.cfg | 15 ++++++++-------
>  2 files changed, 14 insertions(+), 17 deletions(-)
> 
> diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
> index 61f196d5d..51a593021 100644
> --- a/.gitlab-ci.yml
> +++ b/.gitlab-ci.yml
> @@ -69,11 +69,9 @@ build-ppc64be:
>   - cd build
>   - ../configure --arch=ppc64 --endian=big --cross-prefix=powerpc64-linux-gnu-
>   - make -j2
> - - ACCEL=tcg ./run_tests.sh
> -     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
> -     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
> -     emulator
> -     | tee results.txt
> + - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
> + - if grep -q FAIL results.txt ; then exit 1 ; fi
> + - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
>   - if grep -q FAIL results.txt ; then exit 1 ; fi
>  
>  build-ppc64le:
> @@ -82,11 +80,9 @@ build-ppc64le:
>   - dnf install -y qemu-system-ppc gcc-powerpc64-linux-gnu nmap-ncat
>   - ./configure --arch=ppc64 --endian=little --cross-prefix=powerpc64-linux-gnu-
>   - make -j2
> - - ACCEL=tcg ./run_tests.sh
> -     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
> -     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
> -     emulator
> -     | tee results.txt
> + - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
> + - if grep -q FAIL results.txt ; then exit 1 ; fi
> + - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
>   - if grep -q FAIL results.txt ; then exit 1 ; fi
>  

We're slowly migrating all tests like these to

 grep -q PASS results.txt && ! grep -q FAIL results.txt

Here's a good opportunity to change ppc's.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing
  2024-02-28 11:46   ` Andrew Jones
@ 2024-02-29  3:50     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-29  3:50 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Wed Feb 28, 2024 at 9:46 PM AEST, Andrew Jones wrote:
> On Mon, Feb 26, 2024 at 08:11:50PM +1000, Nicholas Piggin wrote:
> > Add support for backtracing across interrupt stacks, and
> > add interrupt frame backtrace for unhandled interrupts.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >  lib/powerpc/processor.c |  4 ++-
> >  lib/ppc64/asm/stack.h   |  3 +++
> >  lib/ppc64/stack.c       | 55 +++++++++++++++++++++++++++++++++++++++++
> >  powerpc/Makefile.ppc64  |  1 +
> >  powerpc/cstart64.S      |  7 ++++--
> >  5 files changed, 67 insertions(+), 3 deletions(-)
> >  create mode 100644 lib/ppc64/stack.c
> > 
> > diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
> > index ad0d95666..114584024 100644
> > --- a/lib/powerpc/processor.c
> > +++ b/lib/powerpc/processor.c
> > @@ -51,7 +51,9 @@ void do_handle_exception(struct pt_regs *regs)
> >  		return;
> >  	}
> >  
> > -	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
> > +	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
> > +			regs->trap, regs->nip, regs->msr);
> > +	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
> >  	abort();
> >  }
> >  
> > diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
> > index 9734bbb8f..94fd1021c 100644
> > --- a/lib/ppc64/asm/stack.h
> > +++ b/lib/ppc64/asm/stack.h
> > @@ -5,4 +5,7 @@
> >  #error Do not directly include <asm/stack.h>. Just use <stack.h>.
> >  #endif
> >  
> > +#define HAVE_ARCH_BACKTRACE
> > +#define HAVE_ARCH_BACKTRACE_FRAME
> > +
> >  #endif
> > diff --git a/lib/ppc64/stack.c b/lib/ppc64/stack.c
> > new file mode 100644
> > index 000000000..fcb7fa860
> > --- /dev/null
> > +++ b/lib/ppc64/stack.c
> > @@ -0,0 +1,55 @@
> > +#include <libcflat.h>
> > +#include <asm/ptrace.h>
> > +#include <stack.h>
> > +
> > +extern char exception_stack_marker[];
> > +
> > +int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
> > +{
> > +	static int walking;
> > +	int depth = 0;
> > +	const unsigned long *bp = (unsigned long *)frame;
> > +	void *return_addr;
> > +
> > +	asm volatile("" ::: "lr"); /* Force it to save LR */
> > +
> > +	if (walking) {
> > +		printf("RECURSIVE STACK WALK!!!\n");
> > +		return 0;
> > +	}
> > +	walking = 1;
> > +
> > +	bp = (unsigned long *)bp[0];
> > +	return_addr = (void *)bp[2];
> > +
> > +	for (depth = 0; bp && depth < max_depth; depth++) {
> > +		return_addrs[depth] = return_addr;
> > +		if (return_addrs[depth] == 0)
> > +			break;
> > +		if (return_addrs[depth] == exception_stack_marker) {
> > +			struct pt_regs *regs;
> > +
> > +			regs = (void *)bp + STACK_FRAME_OVERHEAD;
> > +			bp = (unsigned long *)bp[0];
> > +			/* Represent interrupt frame with vector number */
> > +			return_addr = (void *)regs->trap;
> > +			if (depth + 1 < max_depth) {
> > +				depth++;
> > +				return_addrs[depth] = return_addr;
> > +				return_addr = (void *)regs->nip;
> > +			}
> > +		} else {
> > +			bp = (unsigned long *)bp[0];
> > +			return_addr = (void *)bp[2];
> > +		}
> > +	}
> > +
> > +	walking = 0;
> > +	return depth;
> > +}
> > +
> > +int backtrace(const void **return_addrs, int max_depth)
> > +{
> > +	return backtrace_frame(__builtin_frame_address(0), return_addrs,
> > +			       max_depth);
> > +}
>
> I'm about to post a series which has a couple treewide tracing changes
> in them. Depending on which series goes first the other will need to
> accommodate.

Yeah that's fine.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg
  2024-02-28 11:47   ` Andrew Jones
@ 2024-02-29  3:52     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-29  3:52 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Wed Feb 28, 2024 at 9:47 PM AEST, Andrew Jones wrote:
> On Mon, Feb 26, 2024 at 08:11:55PM +1000, Nicholas Piggin wrote:
> > This allows different machines with different requirements to be
> > supported by run_tests.sh, similarly to how different accelerators
> > are handled.
> > 
> > Acked-by: Thomas Huth <thuth@redhat.com>
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >  scripts/common.bash  |  8 ++++++--
> >  scripts/runtime.bash | 16 ++++++++++++----
> >  2 files changed, 18 insertions(+), 6 deletions(-)
>
> Please also update the unittests.cfg documentation.

Yeah good catch, I will do.

> Currently that
> documentation lives in the header of each unittests.cfg file, but
> we could maybe change each file to have a single line which points
> at a single document.

I'll take a look and do something if it's simple enough,
otherwise I'll just update the unittests.cfg.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update
  2024-02-28 12:16   ` Andrew Jones
@ 2024-02-29  3:55     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-02-29  3:55 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Laurent Vivier, Thomas Huth, kvm, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Wed Feb 28, 2024 at 10:16 PM AEST, Andrew Jones wrote:
> On Mon, Feb 26, 2024 at 08:12:18PM +1000, Nicholas Piggin wrote:
> > This adds testing for the powernv machine, and adds a gitlab-ci test
> > group instead of specifying all tests in .gitlab-ci.yml.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >  .gitlab-ci.yml        | 16 ++++++----------
> >  powerpc/unittests.cfg | 15 ++++++++-------
> >  2 files changed, 14 insertions(+), 17 deletions(-)
> > 
> > diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
> > index 61f196d5d..51a593021 100644
> > --- a/.gitlab-ci.yml
> > +++ b/.gitlab-ci.yml
> > @@ -69,11 +69,9 @@ build-ppc64be:
> >   - cd build
> >   - ../configure --arch=ppc64 --endian=big --cross-prefix=powerpc64-linux-gnu-
> >   - make -j2
> > - - ACCEL=tcg ./run_tests.sh
> > -     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
> > -     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
> > -     emulator
> > -     | tee results.txt
> > + - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
> > + - if grep -q FAIL results.txt ; then exit 1 ; fi
> > + - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
> >   - if grep -q FAIL results.txt ; then exit 1 ; fi
> >  
> >  build-ppc64le:
> > @@ -82,11 +80,9 @@ build-ppc64le:
> >   - dnf install -y qemu-system-ppc gcc-powerpc64-linux-gnu nmap-ncat
> >   - ./configure --arch=ppc64 --endian=little --cross-prefix=powerpc64-linux-gnu-
> >   - make -j2
> > - - ACCEL=tcg ./run_tests.sh
> > -     selftest-setup selftest-migration selftest-migration-skip spapr_hcall
> > -     rtas-get-time-of-day rtas-get-time-of-day-base rtas-set-time-of-day
> > -     emulator
> > -     | tee results.txt
> > + - ACCEL=tcg MAX_SMP=8 ./run_tests.sh -g gitlab-ci | tee results.txt
> > + - if grep -q FAIL results.txt ; then exit 1 ; fi
> > + - ACCEL=tcg MAX_SMP=8 MACHINE=powernv ./run_tests.sh -g gitlab-ci | tee results.txt
> >   - if grep -q FAIL results.txt ; then exit 1 ; fi
> >  
>
> We're slowly migrating all tests like these to
>
>  grep -q PASS results.txt && ! grep -q FAIL results.txt
>
> Here's a good opportunity to change ppc's.

Sure, I'll do that.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-02-27  8:50   ` Thomas Huth
@ 2024-03-01  9:45     ` Thomas Huth
  2024-03-05  2:08       ` Nicholas Piggin
  2024-03-05  6:29     ` Nicholas Piggin
  1 sibling, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-01  9:45 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 27/02/2024 09.50, Thomas Huth wrote:
> On 26/02/2024 11.11, Nicholas Piggin wrote:
>> The backtrace handler terminates when it sees a NULL caller address,
>> but the powerpc stack setup does not keep such a NULL caller frame
>> at the start of the stack.
>>
>> This happens to work on pseries because the memory at 0 is mapped and
>> it contains 0 at the location of the return address pointer if it
>> were a stack frame. But this is fragile, and does not work with powernv
>> where address 0 contains firmware instructions.
>>
>> Use the existing dummy frame on stack as the NULL caller, and create a
>> new frame on stack for the entry code.
>>
>> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
>> ---
>>   powerpc/cstart64.S | 12 ++++++++++--
>>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> Thanks for tackling this! ... however, not doing powerpc work since years 
> anymore, I have some ignorant questions below...
> 
>> diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
>> index e18ae9a22..14ab0c6c8 100644
>> --- a/powerpc/cstart64.S
>> +++ b/powerpc/cstart64.S
>> @@ -46,8 +46,16 @@ start:
>>       add    r1, r1, r31
>>       add    r2, r2, r31
>> +    /* Zero backpointers in initial stack frame so backtrace() stops */
>> +    li    r0,0
>> +    std    r0,0(r1)
> 
> 0(r1) is the back chain pointer ...
> 
>> +    std    r0,16(r1)
> 
> ... but what is 16(r1) ? I suppose that should be the "LR save word" ? But 
> isn't that at 8(r1) instead?? (not sure whether I'm looking at the right ELF 
> abi spec right now...)

Ok, I was looking at the wrong ELF spec, indeed (it was an ancient 32-bit 
spec, not the 64-bit ABI). Sorry for the confusion. Having a proper #define 
or a comment for the 16 here would still be helpful, though.

  Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing Nicholas Piggin
  2024-02-28 11:46   ` Andrew Jones
@ 2024-03-01  9:53   ` Thomas Huth
  2024-03-05  2:12     ` Nicholas Piggin
  1 sibling, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-01  9:53 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> Add support for backtracing across interrupt stacks, and
> add interrupt frame backtrace for unhandled interrupts.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/processor.c |  4 ++-
>   lib/ppc64/asm/stack.h   |  3 +++
>   lib/ppc64/stack.c       | 55 +++++++++++++++++++++++++++++++++++++++++
>   powerpc/Makefile.ppc64  |  1 +
>   powerpc/cstart64.S      |  7 ++++--
>   5 files changed, 67 insertions(+), 3 deletions(-)
>   create mode 100644 lib/ppc64/stack.c
> 
> diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
> index ad0d95666..114584024 100644
> --- a/lib/powerpc/processor.c
> +++ b/lib/powerpc/processor.c
> @@ -51,7 +51,9 @@ void do_handle_exception(struct pt_regs *regs)
>   		return;
>   	}
>   
> -	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
> +	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
> +			regs->trap, regs->nip, regs->msr);
> +	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
>   	abort();
>   }
>   
> diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
> index 9734bbb8f..94fd1021c 100644
> --- a/lib/ppc64/asm/stack.h
> +++ b/lib/ppc64/asm/stack.h
> @@ -5,4 +5,7 @@
>   #error Do not directly include <asm/stack.h>. Just use <stack.h>.
>   #endif
>   
> +#define HAVE_ARCH_BACKTRACE
> +#define HAVE_ARCH_BACKTRACE_FRAME
> +
>   #endif
> diff --git a/lib/ppc64/stack.c b/lib/ppc64/stack.c
> new file mode 100644
> index 000000000..fcb7fa860
> --- /dev/null
> +++ b/lib/ppc64/stack.c
> @@ -0,0 +1,55 @@
> +#include <libcflat.h>
> +#include <asm/ptrace.h>
> +#include <stack.h>
> +
> +extern char exception_stack_marker[];
> +
> +int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
> +{
> +	static int walking;
> +	int depth = 0;
> +	const unsigned long *bp = (unsigned long *)frame;
> +	void *return_addr;
> +
> +	asm volatile("" ::: "lr"); /* Force it to save LR */
> +
> +	if (walking) {
> +		printf("RECURSIVE STACK WALK!!!\n");
> +		return 0;
> +	}
> +	walking = 1;
> +
> +	bp = (unsigned long *)bp[0];
> +	return_addr = (void *)bp[2];
> +
> +	for (depth = 0; bp && depth < max_depth; depth++) {
> +		return_addrs[depth] = return_addr;
> +		if (return_addrs[depth] == 0)
> +			break;
> +		if (return_addrs[depth] == exception_stack_marker) {
> +			struct pt_regs *regs;
> +
> +			regs = (void *)bp + STACK_FRAME_OVERHEAD;
> +			bp = (unsigned long *)bp[0];
> +			/* Represent interrupt frame with vector number */
> +			return_addr = (void *)regs->trap;
> +			if (depth + 1 < max_depth) {
> +				depth++;
> +				return_addrs[depth] = return_addr;
> +				return_addr = (void *)regs->nip;
> +			}
> +		} else {
> +			bp = (unsigned long *)bp[0];
> +			return_addr = (void *)bp[2];
> +		}
> +	}
> +
> +	walking = 0;
> +	return depth;
> +}
> +
> +int backtrace(const void **return_addrs, int max_depth)
> +{
> +	return backtrace_frame(__builtin_frame_address(0), return_addrs,
> +			       max_depth);
> +}
> diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
> index b0ed2b104..eb682c226 100644
> --- a/powerpc/Makefile.ppc64
> +++ b/powerpc/Makefile.ppc64
> @@ -17,6 +17,7 @@ cstart.o = $(TEST_DIR)/cstart64.o
>   reloc.o  = $(TEST_DIR)/reloc64.o
>   
>   OBJDIRS += lib/ppc64
> +cflatobjs += lib/ppc64/stack.o
>   
>   # ppc64 specific tests
>   tests = $(TEST_DIR)/spapr_vpa.elf
> diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> index 14ab0c6c8..278af84a6 100644
> --- a/powerpc/cstart64.S
> +++ b/powerpc/cstart64.S
> @@ -188,6 +188,7 @@ call_handler:
>   	.endr
>   	mfsprg1	r0
>   	std	r0,GPR1(r1)
> +	std	r0,0(r1)
>   
>   	/* lr, xer, ccr */
>   
> @@ -206,12 +207,12 @@ call_handler:
>   	subi	r31, r31, 0b - start_text
>   	ld	r2, (p_toc_text - start_text)(r31)
>   
> -	/* FIXME: build stack frame */
> -
>   	/* call generic handler */
>   
>   	addi	r3,r1,STACK_FRAME_OVERHEAD
>   	bl	do_handle_exception
> +	.global exception_stack_marker
> +exception_stack_marker:
>   
>   	/* restore context */
>   
> @@ -321,6 +322,7 @@ handler_trampoline:
>   	/* nip and msr */
>   	mfsrr0	r0
>   	std	r0, _NIP(r1)
> +	std	r0, INT_FRAME_SIZE+16(r1)

So if I got that right, INT_FRAME_SIZE+16 points to the stack frame of the 
function that was running before the interrupt handler? Is it such a good 
idea to change that here? Please add a comment here explaining this line. 
Thanks!


>   	mfsrr1	r0
>   	std	r0, _MSR(r1)
> @@ -337,6 +339,7 @@ handler_htrampoline:
>   	/* nip and msr */
>   	mfspr	r0, SPR_HSRR0
>   	std	r0, _NIP(r1)
> +	std	r0, INT_FRAME_SIZE+16(r1)

dito.

>   	mfspr	r0, SPR_HSRR1
>   	std	r0, _MSR(r1)

  Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions Nicholas Piggin
@ 2024-03-01 11:11   ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 11:11 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> Move SPR and MSR defines out of ppc_asm.h and processor.h and into a
> new include, asm/reg.h.
> 
> Add a define for the PVR SPR and various processor versions, and replace
> the open coded numbers in the sprs.c test case.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/asm/ppc_asm.h   |  8 +-------
>   lib/powerpc/asm/processor.h |  7 +------
>   lib/powerpc/asm/reg.h       | 30 ++++++++++++++++++++++++++++++
>   lib/powerpc/asm/time.h      |  1 +
>   lib/ppc64/asm/reg.h         |  1 +
>   powerpc/sprs.c              | 21 ++++++++++-----------
>   6 files changed, 44 insertions(+), 24 deletions(-)
>   create mode 100644 lib/powerpc/asm/reg.h
>   create mode 100644 lib/ppc64/asm/reg.h

Reviewed-by: Thomas Huth <thuth@redhat.com>


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness Nicholas Piggin
@ 2024-03-01 11:15   ` Thomas Huth
  2024-03-05  2:14     ` Nicholas Piggin
  0 siblings, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 11:15 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> SPRs annotated with SPR_HARNESS can change between consecutive reads
> because the test harness code has changed them. Avoid failing the
> test in this case.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   powerpc/sprs.c | 2 +-
>   1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/powerpc/sprs.c b/powerpc/sprs.c
> index 8253ea971..44edd0d7b 100644
> --- a/powerpc/sprs.c
> +++ b/powerpc/sprs.c
> @@ -563,7 +563,7 @@ int main(int argc, char **argv)
>   			if (before[i] >> 32)
>   				pass = false;
>   		}
> -		if (!(sprs[i].type & SPR_ASYNC) && (before[i] != after[i]))
> +		if (!(sprs[i].type & (SPR_HARNESS|SPR_ASYNC)) && (before[i] != after[i]))
>   			pass = false;
>   
>   		if (sprs[i].width == 32 && !(before[i] >> 32) && !(after[i] >> 32))

I guess you could also squash this into the previous patch (to avoid 
problems with bisecting later?) ...

Anyway:
Reviewed-by: Thomas Huth <thuth@redhat.com>


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing Nicholas Piggin
@ 2024-03-01 11:25   ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 11:25 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> Storing certain values in MMCR0 can cause PMU interrupts when msleep
> enables MSR[EE], and this crashes the test. Freeze the PMU counters
> and clear any PMU exception before calling msleep.
> 
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/asm/reg.h |  4 ++++
>   powerpc/sprs.c        | 17 +++++++++++------
>   2 files changed, 15 insertions(+), 6 deletions(-)

Reviewed-by: Thomas Huth <thuth@redhat.com>


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv
  2024-02-26 10:11 ` [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv Nicholas Piggin
@ 2024-03-01 11:50   ` Thomas Huth
  2024-03-05  2:14     ` Nicholas Piggin
  0 siblings, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 11:50 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.11, Nicholas Piggin wrote:
> Illegal instructions cause 0xe40 (HEAI) interrupts rather
> than program interrupts.
> 
> Acked-by: Thomas Huth <thuth@redhat.com>
> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/asm/processor.h |  1 +
>   lib/powerpc/setup.c         | 13 +++++++++++++
>   powerpc/emulator.c          | 21 ++++++++++++++++++++-
>   3 files changed, 34 insertions(+), 1 deletion(-)
> 
> diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> index 9d8061962..cf1b9d8ff 100644
> --- a/lib/powerpc/asm/processor.h
> +++ b/lib/powerpc/asm/processor.h
> @@ -11,6 +11,7 @@ void do_handle_exception(struct pt_regs *regs);
>   #endif /* __ASSEMBLY__ */
>   
>   extern bool cpu_has_hv;
> +extern bool cpu_has_heai;
>   
>   static inline uint64_t mfspr(int nr)
>   {
> diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> index 89e5157f2..3c81aee9e 100644
> --- a/lib/powerpc/setup.c
> +++ b/lib/powerpc/setup.c
> @@ -87,6 +87,7 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
>   }
>   
>   bool cpu_has_hv;
> +bool cpu_has_heai;
>   
>   static void cpu_init(void)
>   {
> @@ -108,6 +109,18 @@ static void cpu_init(void)
>   		hcall(H_SET_MODE, 0, 4, 0, 0);
>   #endif
>   	}
> +
> +	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
> +	case PVR_VER_POWER10:
> +	case PVR_VER_POWER9:
> +	case PVR_VER_POWER8E:
> +	case PVR_VER_POWER8NVL:
> +	case PVR_VER_POWER8:
> +		cpu_has_heai = true;
> +		break;
> +	default:
> +		break;
> +	}
>   }
>   
>   static void mem_init(phys_addr_t freemem_start)
> diff --git a/powerpc/emulator.c b/powerpc/emulator.c
> index 39dd59645..c9b17f742 100644
> --- a/powerpc/emulator.c
> +++ b/powerpc/emulator.c
> @@ -31,6 +31,20 @@ static void program_check_handler(struct pt_regs *regs, void *opaque)
>   	regs->nip += 4;
>   }
>   
> +static void heai_handler(struct pt_regs *regs, void *opaque)
> +{
> +	int *data = opaque;
> +
> +	if (verbose) {
> +		printf("Detected invalid instruction %#018lx: %08x\n",
> +		       regs->nip, *(uint32_t*)regs->nip);
> +	}
> +
> +	*data = 8; /* Illegal instruction */
> +
> +	regs->nip += 4;
> +}
> +
>   static void alignment_handler(struct pt_regs *regs, void *opaque)
>   {
>   	int *data = opaque;
> @@ -362,7 +376,12 @@ int main(int argc, char **argv)
>   {
>   	int i;
>   
> -	handle_exception(0x700, program_check_handler, (void *)&is_invalid);
> +	if (cpu_has_heai) {
> +		handle_exception(0xe40, heai_handler, (void *)&is_invalid);
> +		handle_exception(0x700, program_check_handler, (void *)&is_invalid);
> +	} else {
> +		handle_exception(0x700, program_check_handler, (void *)&is_invalid);

The 0x700 line looks identical to the other part of the if-statement ... I'd 
suggest to leave it outside of the if-statement, drop the else-part and just 
set 0xe40 if cpu_has_heai.

  Thomas

> +	}
>   	handle_exception(0x600, alignment_handler, (void *)&alignment);
>   
>   	for (i = 1; i < argc; i++) {


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-02-26 10:12 ` [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests Nicholas Piggin
@ 2024-03-01 12:41   ` Thomas Huth
  2024-03-01 13:45     ` Andrew Jones
  2024-03-05  2:19     ` Nicholas Piggin
  0 siblings, 2 replies; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 12:41 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 26/02/2024 11.12, Nicholas Piggin wrote:
> Add basic testing of various kinds of interrupts, machine check,
> page fault, illegal, decrementer, trace, syscall, etc.
> 
> This has a known failure on QEMU TCG pseries machines where MSR[ME]
> can be incorrectly set to 0.

Two questions out of curiosity:

Any chance that this could be fixed easily in QEMU?

Or is there a way to detect TCG from within the test? (for example, we have 
a host_is_tcg() function for s390x so we can e.g. use report_xfail() for 
tests that are known to fail on TCG there)

> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> ---
>   lib/powerpc/asm/processor.h |   4 +
>   lib/powerpc/asm/reg.h       |  17 ++
>   lib/powerpc/setup.c         |  11 +
>   lib/ppc64/asm/ptrace.h      |  16 ++
>   powerpc/Makefile.common     |   3 +-
>   powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
>   powerpc/unittests.cfg       |   3 +
>   7 files changed, 468 insertions(+), 1 deletion(-)
>   create mode 100644 powerpc/interrupts.c
> 
> diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> index cf1b9d8ff..eed37d1f4 100644
> --- a/lib/powerpc/asm/processor.h
> +++ b/lib/powerpc/asm/processor.h
> @@ -11,7 +11,11 @@ void do_handle_exception(struct pt_regs *regs);
>   #endif /* __ASSEMBLY__ */
>   
>   extern bool cpu_has_hv;
> +extern bool cpu_has_power_mce;
> +extern bool cpu_has_siar;
>   extern bool cpu_has_heai;
> +extern bool cpu_has_prefix;
> +extern bool cpu_has_sc_lev;
>   
>   static inline uint64_t mfspr(int nr)
>   {
> diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
> index 782e75527..d6097f48f 100644
> --- a/lib/powerpc/asm/reg.h
> +++ b/lib/powerpc/asm/reg.h
> @@ -5,8 +5,15 @@
>   
>   #define UL(x) _AC(x, UL)
>   
> +#define SPR_DSISR	0x012
> +#define SPR_DAR		0x013
> +#define SPR_DEC		0x016
>   #define SPR_SRR0	0x01a
>   #define SPR_SRR1	0x01b
> +#define   SRR1_PREFIX		UL(0x20000000)
> +#define SPR_FSCR	0x099
> +#define   FSCR_PREFIX		UL(0x2000)
> +#define SPR_HFSCR	0x0be
>   #define SPR_TB		0x10c
>   #define SPR_SPRG0	0x110
>   #define SPR_SPRG1	0x111
> @@ -22,12 +29,17 @@
>   #define   PVR_VER_POWER8	UL(0x004d0000)
>   #define   PVR_VER_POWER9	UL(0x004e0000)
>   #define   PVR_VER_POWER10	UL(0x00800000)
> +#define SPR_HDEC	0x136
>   #define SPR_HSRR0	0x13a
>   #define SPR_HSRR1	0x13b
> +#define SPR_LPCR	0x13e
> +#define   LPCR_HDICE		UL(0x1)
> +#define SPR_HEIR	0x153
>   #define SPR_MMCR0	0x31b
>   #define   MMCR0_FC		UL(0x80000000)
>   #define   MMCR0_PMAE		UL(0x04000000)
>   #define   MMCR0_PMAO		UL(0x00000080)
> +#define SPR_SIAR	0x31c
>   
>   /* Machine State Register definitions: */
>   #define MSR_LE_BIT	0
> @@ -35,6 +47,11 @@
>   #define MSR_HV_BIT	60			/* Hypervisor mode */
>   #define MSR_SF_BIT	63			/* 64-bit mode */
>   
> +#define MSR_DR		UL(0x0010)
> +#define MSR_IR		UL(0x0020)
> +#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
> +#define MSR_SE		UL(0x0400)		/* Single Step Enable */
> +#define MSR_EE		UL(0x8000)
>   #define MSR_ME		UL(0x1000)
>   
>   #endif
> diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> index 3c81aee9e..9b665f59c 100644
> --- a/lib/powerpc/setup.c
> +++ b/lib/powerpc/setup.c
> @@ -87,7 +87,11 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
>   }
>   
>   bool cpu_has_hv;
> +bool cpu_has_power_mce; /* POWER CPU machine checks */
> +bool cpu_has_siar;
>   bool cpu_has_heai;
> +bool cpu_has_prefix;
> +bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
>   
>   static void cpu_init(void)
>   {
> @@ -112,15 +116,22 @@ static void cpu_init(void)
>   
>   	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
>   	case PVR_VER_POWER10:
> +		cpu_has_prefix = true;
> +		cpu_has_sc_lev = true;
>   	case PVR_VER_POWER9:
>   	case PVR_VER_POWER8E:
>   	case PVR_VER_POWER8NVL:
>   	case PVR_VER_POWER8:
> +		cpu_has_power_mce = true;
>   		cpu_has_heai = true;
> +		cpu_has_siar = true;
>   		break;
>   	default:
>   		break;
>   	}
> +
> +	if (!cpu_has_hv) /* HEIR is HV register */
> +		cpu_has_heai = false;
>   }
>   
>   static void mem_init(phys_addr_t freemem_start)
> diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
> index 12de7499b..db263a59e 100644
> --- a/lib/ppc64/asm/ptrace.h
> +++ b/lib/ppc64/asm/ptrace.h
> @@ -5,6 +5,9 @@
>   #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
>   
>   #ifndef __ASSEMBLY__
> +
> +#include <asm/reg.h>
> +
>   struct pt_regs {
>   	unsigned long gpr[32];
>   	unsigned long nip;
> @@ -17,6 +20,19 @@ struct pt_regs {
>   	unsigned long _pad; /* stack must be 16-byte aligned */
>   };
>   
> +static inline bool regs_is_prefix(volatile struct pt_regs *regs)
> +{
> +	return regs->msr & SRR1_PREFIX;
> +}
> +
> +static inline void regs_advance_insn(struct pt_regs *regs)
> +{
> +	if (regs_is_prefix(regs))
> +		regs->nip += 8;
> +	else
> +		regs->nip += 4;
> +}
> +
>   #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
>   				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
>   
> diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
> index 1e181da69..68165fc25 100644
> --- a/powerpc/Makefile.common
> +++ b/powerpc/Makefile.common
> @@ -12,7 +12,8 @@ tests-common = \
>   	$(TEST_DIR)/rtas.elf \
>   	$(TEST_DIR)/emulator.elf \
>   	$(TEST_DIR)/tm.elf \
> -	$(TEST_DIR)/sprs.elf
> +	$(TEST_DIR)/sprs.elf \
> +	$(TEST_DIR)/interrupts.elf
>   
>   tests-all = $(tests-common) $(tests)
>   all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
> diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
> new file mode 100644
> index 000000000..442f8c569
> --- /dev/null
> +++ b/powerpc/interrupts.c
> @@ -0,0 +1,415 @@
> +/*
> + * Test interrupts
> + *
> + * Copyright 2024 Nicholas Piggin, IBM Corp.
> + *
> + * This work is licensed under the terms of the GNU LGPL, version 2.

I know, we're using this line in a lot of source files ... but maybe we 
should do better for new files at least: "LGPL, version 2" is a little bit 
ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL 
version 2.1"? Maybe you could clarify by additionally providing a SPDX 
identifier here, or by explicitly writing 2.0 or 2.1.

  Thanks,
   Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 12:41   ` Thomas Huth
@ 2024-03-01 13:45     ` Andrew Jones
  2024-03-01 13:57       ` Thomas Huth
  2024-03-05  2:30       ` Nicholas Piggin
  2024-03-05  2:19     ` Nicholas Piggin
  1 sibling, 2 replies; 71+ messages in thread
From: Andrew Jones @ 2024-03-01 13:45 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> On 26/02/2024 11.12, Nicholas Piggin wrote:
> > Add basic testing of various kinds of interrupts, machine check,
> > page fault, illegal, decrementer, trace, syscall, etc.
> > 
> > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > can be incorrectly set to 0.
> 
> Two questions out of curiosity:
> 
> Any chance that this could be fixed easily in QEMU?
> 
> Or is there a way to detect TCG from within the test? (for example, we have
> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> tests that are known to fail on TCG there)

If there's nothing better, then it should be possible to check the
QEMU_ACCEL environment variable which will be there with the default
environ.

> 
> > @@ -0,0 +1,415 @@
> > +/*
> > + * Test interrupts
> > + *
> > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
> 
> I know, we're using this line in a lot of source files ... but maybe we
> should do better for new files at least: "LGPL, version 2" is a little bit
> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
> version 2.1"? Maybe you could clarify by additionally providing a SPDX
> identifier here, or by explicitly writing 2.0 or 2.1.

Let's only add SPDX identifiers to new files.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 13:45     ` Andrew Jones
@ 2024-03-01 13:57       ` Thomas Huth
  2024-03-01 14:14         ` Andrew Jones
  2024-03-05  2:30       ` Nicholas Piggin
  1 sibling, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-01 13:57 UTC (permalink / raw)
  To: Andrew Jones
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

On 01/03/2024 14.45, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>> Add basic testing of various kinds of interrupts, machine check,
>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>
>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>> can be incorrectly set to 0.
>>
>> Two questions out of curiosity:
>>
>> Any chance that this could be fixed easily in QEMU?
>>
>> Or is there a way to detect TCG from within the test? (for example, we have
>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>> tests that are known to fail on TCG there)
> 
> If there's nothing better, then it should be possible to check the
> QEMU_ACCEL environment variable which will be there with the default
> environ.

Well, but that's only available from the host side, not within the test 
(i.e. the guest). So that does not help much with report_xfail...
I was rather thinking of something like checking the device tree, e.g. for 
the compatible property in /hypervisor to see whether it's KVM or TCG...?

  Thomas



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 13:57       ` Thomas Huth
@ 2024-03-01 14:14         ` Andrew Jones
  2024-03-05  2:35           ` Nicholas Piggin
  0 siblings, 1 reply; 71+ messages in thread
From: Andrew Jones @ 2024-03-01 14:14 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

On Fri, Mar 01, 2024 at 02:57:04PM +0100, Thomas Huth wrote:
> On 01/03/2024 14.45, Andrew Jones wrote:
> > On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > Add basic testing of various kinds of interrupts, machine check,
> > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > 
> > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > can be incorrectly set to 0.
> > > 
> > > Two questions out of curiosity:
> > > 
> > > Any chance that this could be fixed easily in QEMU?
> > > 
> > > Or is there a way to detect TCG from within the test? (for example, we have
> > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > tests that are known to fail on TCG there)
> > 
> > If there's nothing better, then it should be possible to check the
> > QEMU_ACCEL environment variable which will be there with the default
> > environ.
> 
> Well, but that's only available from the host side, not within the test
> (i.e. the guest). So that does not help much with report_xfail...

powerpc has had environment variables in guests since commit f266c3e8ef15
("powerpc: enable environ"). QEMU_ACCEL is one of the environment
variables given to unit tests by default when ENVIRON_DEFAULT is 'yes', as
is the default set in configure. But...

> I was rather thinking of something like checking the device tree, e.g. for
> the compatible property in /hypervisor to see whether it's KVM or TCG...?

...while QEMU_ACCEL will work when the environ is present, DT will always
be present, so checking the hypervisor node sounds better to me.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-03-01  9:45     ` Thomas Huth
@ 2024-03-05  2:08       ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:08 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 7:45 PM AEST, Thomas Huth wrote:
> On 27/02/2024 09.50, Thomas Huth wrote:
> > On 26/02/2024 11.11, Nicholas Piggin wrote:
> >> The backtrace handler terminates when it sees a NULL caller address,
> >> but the powerpc stack setup does not keep such a NULL caller frame
> >> at the start of the stack.
> >>
> >> This happens to work on pseries because the memory at 0 is mapped and
> >> it contains 0 at the location of the return address pointer if it
> >> were a stack frame. But this is fragile, and does not work with powernv
> >> where address 0 contains firmware instructions.
> >>
> >> Use the existing dummy frame on stack as the NULL caller, and create a
> >> new frame on stack for the entry code.
> >>
> >> Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> >> ---
> >>   powerpc/cstart64.S | 12 ++++++++++--
> >>   1 file changed, 10 insertions(+), 2 deletions(-)
> > 
> > Thanks for tackling this! ... however, not doing powerpc work since years 
> > anymore, I have some ignorant questions below...
> > 
> >> diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> >> index e18ae9a22..14ab0c6c8 100644
> >> --- a/powerpc/cstart64.S
> >> +++ b/powerpc/cstart64.S
> >> @@ -46,8 +46,16 @@ start:
> >>       add    r1, r1, r31
> >>       add    r2, r2, r31
> >> +    /* Zero backpointers in initial stack frame so backtrace() stops */
> >> +    li    r0,0
> >> +    std    r0,0(r1)
> > 
> > 0(r1) is the back chain pointer ...
> > 
> >> +    std    r0,16(r1)
> > 
> > ... but what is 16(r1) ? I suppose that should be the "LR save word" ? But 
> > isn't that at 8(r1) instead?? (not sure whether I'm looking at the right ELF 
> > abi spec right now...)
>
> Ok, I was looking at the wrong ELF spec, indeed (it was an ancient 32-bit 
> spec, not the 64-bit ABI). Sorry for the confusion. Having a proper #define 
> or a comment for the 16 here would still be helpful, though.

Thanks for the deailed reviews as always. I've been a little busy with
QEMU so may not get another series out for a bit. I'll probably wait for
Andrew's stack backtrace changes to land too before resend.

But, yes a comment makes sense. I'll add.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing
  2024-03-01  9:53   ` Thomas Huth
@ 2024-03-05  2:12     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 7:53 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.11, Nicholas Piggin wrote:
> > Add support for backtracing across interrupt stacks, and
> > add interrupt frame backtrace for unhandled interrupts.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   lib/powerpc/processor.c |  4 ++-
> >   lib/ppc64/asm/stack.h   |  3 +++
> >   lib/ppc64/stack.c       | 55 +++++++++++++++++++++++++++++++++++++++++
> >   powerpc/Makefile.ppc64  |  1 +
> >   powerpc/cstart64.S      |  7 ++++--
> >   5 files changed, 67 insertions(+), 3 deletions(-)
> >   create mode 100644 lib/ppc64/stack.c
> > 
> > diff --git a/lib/powerpc/processor.c b/lib/powerpc/processor.c
> > index ad0d95666..114584024 100644
> > --- a/lib/powerpc/processor.c
> > +++ b/lib/powerpc/processor.c
> > @@ -51,7 +51,9 @@ void do_handle_exception(struct pt_regs *regs)
> >   		return;
> >   	}
> >   
> > -	printf("unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n", regs->trap, regs->nip, regs->msr);
> > +	printf("Unhandled cpu exception %#lx at NIA:0x%016lx MSR:0x%016lx\n",
> > +			regs->trap, regs->nip, regs->msr);
> > +	dump_frame_stack((void *)regs->nip, (void *)regs->gpr[1]);
> >   	abort();
> >   }
> >   
> > diff --git a/lib/ppc64/asm/stack.h b/lib/ppc64/asm/stack.h
> > index 9734bbb8f..94fd1021c 100644
> > --- a/lib/ppc64/asm/stack.h
> > +++ b/lib/ppc64/asm/stack.h
> > @@ -5,4 +5,7 @@
> >   #error Do not directly include <asm/stack.h>. Just use <stack.h>.
> >   #endif
> >   
> > +#define HAVE_ARCH_BACKTRACE
> > +#define HAVE_ARCH_BACKTRACE_FRAME
> > +
> >   #endif
> > diff --git a/lib/ppc64/stack.c b/lib/ppc64/stack.c
> > new file mode 100644
> > index 000000000..fcb7fa860
> > --- /dev/null
> > +++ b/lib/ppc64/stack.c
> > @@ -0,0 +1,55 @@
> > +#include <libcflat.h>
> > +#include <asm/ptrace.h>
> > +#include <stack.h>
> > +
> > +extern char exception_stack_marker[];
> > +
> > +int backtrace_frame(const void *frame, const void **return_addrs, int max_depth)
> > +{
> > +	static int walking;
> > +	int depth = 0;
> > +	const unsigned long *bp = (unsigned long *)frame;
> > +	void *return_addr;
> > +
> > +	asm volatile("" ::: "lr"); /* Force it to save LR */
> > +
> > +	if (walking) {
> > +		printf("RECURSIVE STACK WALK!!!\n");
> > +		return 0;
> > +	}
> > +	walking = 1;
> > +
> > +	bp = (unsigned long *)bp[0];
> > +	return_addr = (void *)bp[2];
> > +
> > +	for (depth = 0; bp && depth < max_depth; depth++) {
> > +		return_addrs[depth] = return_addr;
> > +		if (return_addrs[depth] == 0)
> > +			break;
> > +		if (return_addrs[depth] == exception_stack_marker) {
> > +			struct pt_regs *regs;
> > +
> > +			regs = (void *)bp + STACK_FRAME_OVERHEAD;
> > +			bp = (unsigned long *)bp[0];
> > +			/* Represent interrupt frame with vector number */
> > +			return_addr = (void *)regs->trap;
> > +			if (depth + 1 < max_depth) {
> > +				depth++;
> > +				return_addrs[depth] = return_addr;
> > +				return_addr = (void *)regs->nip;
> > +			}
> > +		} else {
> > +			bp = (unsigned long *)bp[0];
> > +			return_addr = (void *)bp[2];
> > +		}
> > +	}
> > +
> > +	walking = 0;
> > +	return depth;
> > +}
> > +
> > +int backtrace(const void **return_addrs, int max_depth)
> > +{
> > +	return backtrace_frame(__builtin_frame_address(0), return_addrs,
> > +			       max_depth);
> > +}
> > diff --git a/powerpc/Makefile.ppc64 b/powerpc/Makefile.ppc64
> > index b0ed2b104..eb682c226 100644
> > --- a/powerpc/Makefile.ppc64
> > +++ b/powerpc/Makefile.ppc64
> > @@ -17,6 +17,7 @@ cstart.o = $(TEST_DIR)/cstart64.o
> >   reloc.o  = $(TEST_DIR)/reloc64.o
> >   
> >   OBJDIRS += lib/ppc64
> > +cflatobjs += lib/ppc64/stack.o
> >   
> >   # ppc64 specific tests
> >   tests = $(TEST_DIR)/spapr_vpa.elf
> > diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> > index 14ab0c6c8..278af84a6 100644
> > --- a/powerpc/cstart64.S
> > +++ b/powerpc/cstart64.S
> > @@ -188,6 +188,7 @@ call_handler:
> >   	.endr
> >   	mfsprg1	r0
> >   	std	r0,GPR1(r1)
> > +	std	r0,0(r1)
> >   
> >   	/* lr, xer, ccr */
> >   
> > @@ -206,12 +207,12 @@ call_handler:
> >   	subi	r31, r31, 0b - start_text
> >   	ld	r2, (p_toc_text - start_text)(r31)
> >   
> > -	/* FIXME: build stack frame */
> > -
> >   	/* call generic handler */
> >   
> >   	addi	r3,r1,STACK_FRAME_OVERHEAD
> >   	bl	do_handle_exception
> > +	.global exception_stack_marker
> > +exception_stack_marker:
> >   
> >   	/* restore context */
> >   
> > @@ -321,6 +322,7 @@ handler_trampoline:
> >   	/* nip and msr */
> >   	mfsrr0	r0
> >   	std	r0, _NIP(r1)
> > +	std	r0, INT_FRAME_SIZE+16(r1)
>
> So if I got that right, INT_FRAME_SIZE+16 points to the stack frame of the 
> function that was running before the interrupt handler? Is it such a good 
> idea to change that here?

No, we switch to exception stack and don't support recursing interrupts
on stack at the moment, so this goes into the initial frame.

> Please add a comment here explaining this line. 
> Thanks!

Yes, good idea.

Thanks,
Nick

>
>
> >   	mfsrr1	r0
> >   	std	r0, _MSR(r1)
> > @@ -337,6 +339,7 @@ handler_htrampoline:
> >   	/* nip and msr */
> >   	mfspr	r0, SPR_HSRR0
> >   	std	r0, _NIP(r1)
> > +	std	r0, INT_FRAME_SIZE+16(r1)
>
> dito.
>
> >   	mfspr	r0, SPR_HSRR1
> >   	std	r0, _MSR(r1)
>
>   Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness
  2024-03-01 11:15   ` Thomas Huth
@ 2024-03-05  2:14     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:14 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 9:15 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.11, Nicholas Piggin wrote:
> > SPRs annotated with SPR_HARNESS can change between consecutive reads
> > because the test harness code has changed them. Avoid failing the
> > test in this case.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   powerpc/sprs.c | 2 +-
> >   1 file changed, 1 insertion(+), 1 deletion(-)
> > 
> > diff --git a/powerpc/sprs.c b/powerpc/sprs.c
> > index 8253ea971..44edd0d7b 100644
> > --- a/powerpc/sprs.c
> > +++ b/powerpc/sprs.c
> > @@ -563,7 +563,7 @@ int main(int argc, char **argv)
> >   			if (before[i] >> 32)
> >   				pass = false;
> >   		}
> > -		if (!(sprs[i].type & SPR_ASYNC) && (before[i] != after[i]))
> > +		if (!(sprs[i].type & (SPR_HARNESS|SPR_ASYNC)) && (before[i] != after[i]))
> >   			pass = false;
> >   
> >   		if (sprs[i].width == 32 && !(before[i] >> 32) && !(after[i] >> 32))
>
> I guess you could also squash this into the previous patch (to avoid 
> problems with bisecting later?) ...

Yeah, I guess it doesn't make much sense to split out since lots of
other stuff changes in the previous patch too.

>
> Anyway:
> Reviewed-by: Thomas Huth <thuth@redhat.com>

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv
  2024-03-01 11:50   ` Thomas Huth
@ 2024-03-05  2:14     ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:14 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 9:50 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.11, Nicholas Piggin wrote:
> > Illegal instructions cause 0xe40 (HEAI) interrupts rather
> > than program interrupts.
> > 
> > Acked-by: Thomas Huth <thuth@redhat.com>
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   lib/powerpc/asm/processor.h |  1 +
> >   lib/powerpc/setup.c         | 13 +++++++++++++
> >   powerpc/emulator.c          | 21 ++++++++++++++++++++-
> >   3 files changed, 34 insertions(+), 1 deletion(-)
> > 
> > diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> > index 9d8061962..cf1b9d8ff 100644
> > --- a/lib/powerpc/asm/processor.h
> > +++ b/lib/powerpc/asm/processor.h
> > @@ -11,6 +11,7 @@ void do_handle_exception(struct pt_regs *regs);
> >   #endif /* __ASSEMBLY__ */
> >   
> >   extern bool cpu_has_hv;
> > +extern bool cpu_has_heai;
> >   
> >   static inline uint64_t mfspr(int nr)
> >   {
> > diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> > index 89e5157f2..3c81aee9e 100644
> > --- a/lib/powerpc/setup.c
> > +++ b/lib/powerpc/setup.c
> > @@ -87,6 +87,7 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
> >   }
> >   
> >   bool cpu_has_hv;
> > +bool cpu_has_heai;
> >   
> >   static void cpu_init(void)
> >   {
> > @@ -108,6 +109,18 @@ static void cpu_init(void)
> >   		hcall(H_SET_MODE, 0, 4, 0, 0);
> >   #endif
> >   	}
> > +
> > +	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
> > +	case PVR_VER_POWER10:
> > +	case PVR_VER_POWER9:
> > +	case PVR_VER_POWER8E:
> > +	case PVR_VER_POWER8NVL:
> > +	case PVR_VER_POWER8:
> > +		cpu_has_heai = true;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> >   }
> >   
> >   static void mem_init(phys_addr_t freemem_start)
> > diff --git a/powerpc/emulator.c b/powerpc/emulator.c
> > index 39dd59645..c9b17f742 100644
> > --- a/powerpc/emulator.c
> > +++ b/powerpc/emulator.c
> > @@ -31,6 +31,20 @@ static void program_check_handler(struct pt_regs *regs, void *opaque)
> >   	regs->nip += 4;
> >   }
> >   
> > +static void heai_handler(struct pt_regs *regs, void *opaque)
> > +{
> > +	int *data = opaque;
> > +
> > +	if (verbose) {
> > +		printf("Detected invalid instruction %#018lx: %08x\n",
> > +		       regs->nip, *(uint32_t*)regs->nip);
> > +	}
> > +
> > +	*data = 8; /* Illegal instruction */
> > +
> > +	regs->nip += 4;
> > +}
> > +
> >   static void alignment_handler(struct pt_regs *regs, void *opaque)
> >   {
> >   	int *data = opaque;
> > @@ -362,7 +376,12 @@ int main(int argc, char **argv)
> >   {
> >   	int i;
> >   
> > -	handle_exception(0x700, program_check_handler, (void *)&is_invalid);
> > +	if (cpu_has_heai) {
> > +		handle_exception(0xe40, heai_handler, (void *)&is_invalid);
> > +		handle_exception(0x700, program_check_handler, (void *)&is_invalid);
> > +	} else {
> > +		handle_exception(0x700, program_check_handler, (void *)&is_invalid);
>
> The 0x700 line looks identical to the other part of the if-statement ... I'd 
> suggest to leave it outside of the if-statement, drop the else-part and just 
> set 0xe40 if cpu_has_heai.

Can do.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 12:41   ` Thomas Huth
  2024-03-01 13:45     ` Andrew Jones
@ 2024-03-05  2:19     ` Nicholas Piggin
  2024-03-05  6:26       ` Thomas Huth
  1 sibling, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:19 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.12, Nicholas Piggin wrote:
> > Add basic testing of various kinds of interrupts, machine check,
> > page fault, illegal, decrementer, trace, syscall, etc.
> > 
> > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > can be incorrectly set to 0.
>
> Two questions out of curiosity:
>
> Any chance that this could be fixed easily in QEMU?

Yes I have a fix on the mailing list. It should get into 9.0 and
probably stable.

> Or is there a way to detect TCG from within the test? (for example, we have 
> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for 
> tests that are known to fail on TCG there)

I do have a half-done patch which adds exactly this.

One (minor) annoyance is that it doesn't seem possible to detect QEMU
version to add workarounds. E.g., we would like to test the fixed
functionality, but older qemu should not. Maybe that's going too much
into a rabbit hole. We *could* put a QEMU version into device tree
to deal with this though...

Thanks,
Nick
>
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   lib/powerpc/asm/processor.h |   4 +
> >   lib/powerpc/asm/reg.h       |  17 ++
> >   lib/powerpc/setup.c         |  11 +
> >   lib/ppc64/asm/ptrace.h      |  16 ++
> >   powerpc/Makefile.common     |   3 +-
> >   powerpc/interrupts.c        | 415 ++++++++++++++++++++++++++++++++++++
> >   powerpc/unittests.cfg       |   3 +
> >   7 files changed, 468 insertions(+), 1 deletion(-)
> >   create mode 100644 powerpc/interrupts.c
> > 
> > diff --git a/lib/powerpc/asm/processor.h b/lib/powerpc/asm/processor.h
> > index cf1b9d8ff..eed37d1f4 100644
> > --- a/lib/powerpc/asm/processor.h
> > +++ b/lib/powerpc/asm/processor.h
> > @@ -11,7 +11,11 @@ void do_handle_exception(struct pt_regs *regs);
> >   #endif /* __ASSEMBLY__ */
> >   
> >   extern bool cpu_has_hv;
> > +extern bool cpu_has_power_mce;
> > +extern bool cpu_has_siar;
> >   extern bool cpu_has_heai;
> > +extern bool cpu_has_prefix;
> > +extern bool cpu_has_sc_lev;
> >   
> >   static inline uint64_t mfspr(int nr)
> >   {
> > diff --git a/lib/powerpc/asm/reg.h b/lib/powerpc/asm/reg.h
> > index 782e75527..d6097f48f 100644
> > --- a/lib/powerpc/asm/reg.h
> > +++ b/lib/powerpc/asm/reg.h
> > @@ -5,8 +5,15 @@
> >   
> >   #define UL(x) _AC(x, UL)
> >   
> > +#define SPR_DSISR	0x012
> > +#define SPR_DAR		0x013
> > +#define SPR_DEC		0x016
> >   #define SPR_SRR0	0x01a
> >   #define SPR_SRR1	0x01b
> > +#define   SRR1_PREFIX		UL(0x20000000)
> > +#define SPR_FSCR	0x099
> > +#define   FSCR_PREFIX		UL(0x2000)
> > +#define SPR_HFSCR	0x0be
> >   #define SPR_TB		0x10c
> >   #define SPR_SPRG0	0x110
> >   #define SPR_SPRG1	0x111
> > @@ -22,12 +29,17 @@
> >   #define   PVR_VER_POWER8	UL(0x004d0000)
> >   #define   PVR_VER_POWER9	UL(0x004e0000)
> >   #define   PVR_VER_POWER10	UL(0x00800000)
> > +#define SPR_HDEC	0x136
> >   #define SPR_HSRR0	0x13a
> >   #define SPR_HSRR1	0x13b
> > +#define SPR_LPCR	0x13e
> > +#define   LPCR_HDICE		UL(0x1)
> > +#define SPR_HEIR	0x153
> >   #define SPR_MMCR0	0x31b
> >   #define   MMCR0_FC		UL(0x80000000)
> >   #define   MMCR0_PMAE		UL(0x04000000)
> >   #define   MMCR0_PMAO		UL(0x00000080)
> > +#define SPR_SIAR	0x31c
> >   
> >   /* Machine State Register definitions: */
> >   #define MSR_LE_BIT	0
> > @@ -35,6 +47,11 @@
> >   #define MSR_HV_BIT	60			/* Hypervisor mode */
> >   #define MSR_SF_BIT	63			/* 64-bit mode */
> >   
> > +#define MSR_DR		UL(0x0010)
> > +#define MSR_IR		UL(0x0020)
> > +#define MSR_BE		UL(0x0200)		/* Branch Trace Enable */
> > +#define MSR_SE		UL(0x0400)		/* Single Step Enable */
> > +#define MSR_EE		UL(0x8000)
> >   #define MSR_ME		UL(0x1000)
> >   
> >   #endif
> > diff --git a/lib/powerpc/setup.c b/lib/powerpc/setup.c
> > index 3c81aee9e..9b665f59c 100644
> > --- a/lib/powerpc/setup.c
> > +++ b/lib/powerpc/setup.c
> > @@ -87,7 +87,11 @@ static void cpu_set(int fdtnode, u64 regval, void *info)
> >   }
> >   
> >   bool cpu_has_hv;
> > +bool cpu_has_power_mce; /* POWER CPU machine checks */
> > +bool cpu_has_siar;
> >   bool cpu_has_heai;
> > +bool cpu_has_prefix;
> > +bool cpu_has_sc_lev; /* sc interrupt has LEV field in SRR1 */
> >   
> >   static void cpu_init(void)
> >   {
> > @@ -112,15 +116,22 @@ static void cpu_init(void)
> >   
> >   	switch (mfspr(SPR_PVR) & PVR_VERSION_MASK) {
> >   	case PVR_VER_POWER10:
> > +		cpu_has_prefix = true;
> > +		cpu_has_sc_lev = true;
> >   	case PVR_VER_POWER9:
> >   	case PVR_VER_POWER8E:
> >   	case PVR_VER_POWER8NVL:
> >   	case PVR_VER_POWER8:
> > +		cpu_has_power_mce = true;
> >   		cpu_has_heai = true;
> > +		cpu_has_siar = true;
> >   		break;
> >   	default:
> >   		break;
> >   	}
> > +
> > +	if (!cpu_has_hv) /* HEIR is HV register */
> > +		cpu_has_heai = false;
> >   }
> >   
> >   static void mem_init(phys_addr_t freemem_start)
> > diff --git a/lib/ppc64/asm/ptrace.h b/lib/ppc64/asm/ptrace.h
> > index 12de7499b..db263a59e 100644
> > --- a/lib/ppc64/asm/ptrace.h
> > +++ b/lib/ppc64/asm/ptrace.h
> > @@ -5,6 +5,9 @@
> >   #define STACK_FRAME_OVERHEAD    112     /* size of minimum stack frame */
> >   
> >   #ifndef __ASSEMBLY__
> > +
> > +#include <asm/reg.h>
> > +
> >   struct pt_regs {
> >   	unsigned long gpr[32];
> >   	unsigned long nip;
> > @@ -17,6 +20,19 @@ struct pt_regs {
> >   	unsigned long _pad; /* stack must be 16-byte aligned */
> >   };
> >   
> > +static inline bool regs_is_prefix(volatile struct pt_regs *regs)
> > +{
> > +	return regs->msr & SRR1_PREFIX;
> > +}
> > +
> > +static inline void regs_advance_insn(struct pt_regs *regs)
> > +{
> > +	if (regs_is_prefix(regs))
> > +		regs->nip += 8;
> > +	else
> > +		regs->nip += 4;
> > +}
> > +
> >   #define STACK_INT_FRAME_SIZE    (sizeof(struct pt_regs) + \
> >   				 STACK_FRAME_OVERHEAD + KERNEL_REDZONE_SIZE)
> >   
> > diff --git a/powerpc/Makefile.common b/powerpc/Makefile.common
> > index 1e181da69..68165fc25 100644
> > --- a/powerpc/Makefile.common
> > +++ b/powerpc/Makefile.common
> > @@ -12,7 +12,8 @@ tests-common = \
> >   	$(TEST_DIR)/rtas.elf \
> >   	$(TEST_DIR)/emulator.elf \
> >   	$(TEST_DIR)/tm.elf \
> > -	$(TEST_DIR)/sprs.elf
> > +	$(TEST_DIR)/sprs.elf \
> > +	$(TEST_DIR)/interrupts.elf
> >   
> >   tests-all = $(tests-common) $(tests)
> >   all: directories $(TEST_DIR)/boot_rom.bin $(tests-all)
> > diff --git a/powerpc/interrupts.c b/powerpc/interrupts.c
> > new file mode 100644
> > index 000000000..442f8c569
> > --- /dev/null
> > +++ b/powerpc/interrupts.c
> > @@ -0,0 +1,415 @@
> > +/*
> > + * Test interrupts
> > + *
> > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > + *
> > + * This work is licensed under the terms of the GNU LGPL, version 2.
>
> I know, we're using this line in a lot of source files ... but maybe we 
> should do better for new files at least: "LGPL, version 2" is a little bit 
> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL 
> version 2.1"? Maybe you could clarify by additionally providing a SPDX 
> identifier here, or by explicitly writing 2.0 or 2.1.

Sure I'll try to improve this for all the new files.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 13:45     ` Andrew Jones
  2024-03-01 13:57       ` Thomas Huth
@ 2024-03-05  2:30       ` Nicholas Piggin
  2024-03-05  6:18         ` Thomas Huth
  1 sibling, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:30 UTC (permalink / raw)
  To: Andrew Jones, Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Fri Mar 1, 2024 at 11:45 PM AEST, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > Add basic testing of various kinds of interrupts, machine check,
> > > page fault, illegal, decrementer, trace, syscall, etc.
> > > 
> > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > can be incorrectly set to 0.
> > 
> > Two questions out of curiosity:
> > 
> > Any chance that this could be fixed easily in QEMU?
> > 
> > Or is there a way to detect TCG from within the test? (for example, we have
> > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > tests that are known to fail on TCG there)
>
> If there's nothing better, then it should be possible to check the
> QEMU_ACCEL environment variable which will be there with the default
> environ.
>
> > 
> > > @@ -0,0 +1,415 @@
> > > +/*
> > > + * Test interrupts
> > > + *
> > > + * Copyright 2024 Nicholas Piggin, IBM Corp.
> > > + *
> > > + * This work is licensed under the terms of the GNU LGPL, version 2.
> > 
> > I know, we're using this line in a lot of source files ... but maybe we
> > should do better for new files at least: "LGPL, version 2" is a little bit
> > ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
> > version 2.1"? Maybe you could clarify by additionally providing a SPDX
> > identifier here, or by explicitly writing 2.0 or 2.1.
>
> Let's only add SPDX identifiers to new files.

+1

Speaking of which, a bunch of these just got inherited from the file
that was copied to begin with (I tried not to remove copyright
notices unless there was really nothing of the original remaining).
So for new code/files, is there any particular preference for the
license to use? I don't much mind between the *GPL*. Looks like almost
all the SPDX code use GPL 2.0 only, but that could be just from
coming from Linux. I might just go with that.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-01 14:14         ` Andrew Jones
@ 2024-03-05  2:35           ` Nicholas Piggin
  0 siblings, 0 replies; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  2:35 UTC (permalink / raw)
  To: Andrew Jones, Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Sat Mar 2, 2024 at 12:14 AM AEST, Andrew Jones wrote:
> On Fri, Mar 01, 2024 at 02:57:04PM +0100, Thomas Huth wrote:
> > On 01/03/2024 14.45, Andrew Jones wrote:
> > > On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
> > > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > > Add basic testing of various kinds of interrupts, machine check,
> > > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > > 
> > > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > > can be incorrectly set to 0.
> > > > 
> > > > Two questions out of curiosity:
> > > > 
> > > > Any chance that this could be fixed easily in QEMU?
> > > > 
> > > > Or is there a way to detect TCG from within the test? (for example, we have
> > > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > > tests that are known to fail on TCG there)
> > > 
> > > If there's nothing better, then it should be possible to check the
> > > QEMU_ACCEL environment variable which will be there with the default
> > > environ.
> > 
> > Well, but that's only available from the host side, not within the test
> > (i.e. the guest). So that does not help much with report_xfail...
>
> powerpc has had environment variables in guests since commit f266c3e8ef15
> ("powerpc: enable environ"). QEMU_ACCEL is one of the environment
> variables given to unit tests by default when ENVIRON_DEFAULT is 'yes', as
> is the default set in configure. But...
>
> > I was rather thinking of something like checking the device tree, e.g. for
> > the compatible property in /hypervisor to see whether it's KVM or TCG...?
>
> ...while QEMU_ACCEL will work when the environ is present, DT will always
> be present, so checking the hypervisor node sounds better to me.

Yeah I got that.

One issue with xfail I noted when looking at this earlier, is that it
*always* expects a fail, and fails if it succeeds. So if you fix a QEMU
bug then you introduce a fail to kvm-unit-tests. So we really want a
report_known_bug(cond, "SPRs look sane", "QEMU TCG before v9.0 and
POWER9 DD2.0 is known to fail..."); and that could report a maybe-fail
that doesn't make the test group fail.

Thanks,
Nick

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-05  2:30       ` Nicholas Piggin
@ 2024-03-05  6:18         ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-03-05  6:18 UTC (permalink / raw)
  To: Nicholas Piggin, Andrew Jones
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 05/03/2024 03.30, Nicholas Piggin wrote:
> On Fri Mar 1, 2024 at 11:45 PM AEST, Andrew Jones wrote:
>> On Fri, Mar 01, 2024 at 01:41:22PM +0100, Thomas Huth wrote:
>>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>>> Add basic testing of various kinds of interrupts, machine check,
>>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>>
>>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>>> can be incorrectly set to 0.
>>>
>>> Two questions out of curiosity:
>>>
>>> Any chance that this could be fixed easily in QEMU?
>>>
>>> Or is there a way to detect TCG from within the test? (for example, we have
>>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>>> tests that are known to fail on TCG there)
>>
>> If there's nothing better, then it should be possible to check the
>> QEMU_ACCEL environment variable which will be there with the default
>> environ.
>>
>>>
>>>> @@ -0,0 +1,415 @@
>>>> +/*
>>>> + * Test interrupts
>>>> + *
>>>> + * Copyright 2024 Nicholas Piggin, IBM Corp.
>>>> + *
>>>> + * This work is licensed under the terms of the GNU LGPL, version 2.
>>>
>>> I know, we're using this line in a lot of source files ... but maybe we
>>> should do better for new files at least: "LGPL, version 2" is a little bit
>>> ambiguous: Does it mean the "Library GPL version 2.0" or the "Lesser GPL
>>> version 2.1"? Maybe you could clarify by additionally providing a SPDX
>>> identifier here, or by explicitly writing 2.0 or 2.1.
>>
>> Let's only add SPDX identifiers to new files.
> 
> +1
> 
> Speaking of which, a bunch of these just got inherited from the file
> that was copied to begin with (I tried not to remove copyright
> notices unless there was really nothing of the original remaining).
> So for new code/files, is there any particular preference for the
> license to use? I don't much mind between the *GPL*. Looks like almost
> all the SPDX code use GPL 2.0 only, but that could be just from
> coming from Linux. I might just go with that.

k-u-t were originally licensed under the LGPL, but in the course of time, 
code has been copied from the Linux kernel here and there, so we updated the 
license to GPL 2.
So yes, for code that might have been copied from the kernel, we have to use 
GPL 2. For other code, it's up to the author to chose. (IIRC we once 
discussed that LGPL would be nice for the files in the lib/ directory, while 
GPL is fine for the other folders, but my memory might fail here and it was 
never written in stone anyway)

  Thomas



^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-05  2:19     ` Nicholas Piggin
@ 2024-03-05  6:26       ` Thomas Huth
  2024-03-05 12:12         ` Andrew Jones
  0 siblings, 1 reply; 71+ messages in thread
From: Thomas Huth @ 2024-03-05  6:26 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 05/03/2024 03.19, Nicholas Piggin wrote:
> On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
>> On 26/02/2024 11.12, Nicholas Piggin wrote:
>>> Add basic testing of various kinds of interrupts, machine check,
>>> page fault, illegal, decrementer, trace, syscall, etc.
>>>
>>> This has a known failure on QEMU TCG pseries machines where MSR[ME]
>>> can be incorrectly set to 0.
>>
>> Two questions out of curiosity:
>>
>> Any chance that this could be fixed easily in QEMU?
> 
> Yes I have a fix on the mailing list. It should get into 9.0 and
> probably stable.

Ok, then it's IMHO not worth the effort to make the k-u-t work around this 
bug in older QEMU versions.

>> Or is there a way to detect TCG from within the test? (for example, we have
>> a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
>> tests that are known to fail on TCG there)
> 
> I do have a half-done patch which adds exactly this.
> 
> One (minor) annoyance is that it doesn't seem possible to detect QEMU
> version to add workarounds. E.g., we would like to test the fixed
> functionality, but older qemu should not. Maybe that's going too much
> into a rabbit hole. We *could* put a QEMU version into device tree
> to deal with this though...

No, let's better not do this - hardwired version checks are often a bad 
idea, e.g. when a bug is in QEMU 8.0.0 and 8.1.0, it could be fixed in 8.0.1 
and then it could get really messy with the version checks...

  Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-02-27  8:50   ` Thomas Huth
  2024-03-01  9:45     ` Thomas Huth
@ 2024-03-05  6:29     ` Nicholas Piggin
  2024-03-05  6:59       ` Thomas Huth
  1 sibling, 1 reply; 71+ messages in thread
From: Nicholas Piggin @ 2024-03-05  6:29 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On Tue Feb 27, 2024 at 6:50 PM AEST, Thomas Huth wrote:
> On 26/02/2024 11.11, Nicholas Piggin wrote:
> > The backtrace handler terminates when it sees a NULL caller address,
> > but the powerpc stack setup does not keep such a NULL caller frame
> > at the start of the stack.
> > 
> > This happens to work on pseries because the memory at 0 is mapped and
> > it contains 0 at the location of the return address pointer if it
> > were a stack frame. But this is fragile, and does not work with powernv
> > where address 0 contains firmware instructions.
> > 
> > Use the existing dummy frame on stack as the NULL caller, and create a
> > new frame on stack for the entry code.
> > 
> > Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
> > ---
> >   powerpc/cstart64.S | 12 ++++++++++--
> >   1 file changed, 10 insertions(+), 2 deletions(-)
>
> Thanks for tackling this! ... however, not doing powerpc work since years 
> anymore, I have some ignorant questions below...
>
> > diff --git a/powerpc/cstart64.S b/powerpc/cstart64.S
> > index e18ae9a22..14ab0c6c8 100644
> > --- a/powerpc/cstart64.S
> > +++ b/powerpc/cstart64.S
> > @@ -46,8 +46,16 @@ start:
> >   	add	r1, r1, r31
> >   	add	r2, r2, r31
> >   
> > +	/* Zero backpointers in initial stack frame so backtrace() stops */
> > +	li	r0,0
> > +	std	r0,0(r1)
>
> 0(r1) is the back chain pointer ...
>
> > +	std	r0,16(r1)
>
> ... but what is 16(r1) ? I suppose that should be the "LR save word" ? But 
> isn't that at 8(r1) instead?? (not sure whether I'm looking at the right ELF 
> abi spec right now...)
>
> Anyway, a comment in the source would be helpful here.
>
> > +
> > +	/* Create entry frame */
> > +	stdu	r1,-INT_FRAME_SIZE(r1)
>
> Since we already create an initial frame via stackptr from powerpc/flat.lds, 
> do we really need to create this additional one here? Or does the one from 
> flat.lds have to be completely empty, i.e. also no DTB pointer in it?

Oh you already figured the above questions. For this, we do have
one frame allocated already statically yes. But if we don't create
another one here then our callee will store LR into it, but the
unwinder only exits when it sees a NULL return address so it would
keep trying to walk.

We could make it terminate on NULL back chain pointer, but that's
a bit more change that also touches non-powerpc code in the generic
unwinder, and still needs some changes here. Maybe we should do
that after this series though. I'll include a comment to look at
redoing it later.

>
> >   	/* save DTB pointer */
> > -	std	r3, 56(r1)
> > +	SAVE_GPR(3,r1)
>
> Isn't SAVE_GPR rather meant for the interrupt frame, not for the normal C 
> calling convention frames?
>
> Sorry for asking dumb questions ... I still have a hard time understanding 
> the changes here... :-/

Ah, that was me being lazy and using an interrupt frame for the new
frame.

Thanks,
Nick

>
> >   	/*
> >   	 * Call relocate. relocate is C code, but careful to not use
> > @@ -101,7 +109,7 @@ start:
> >   	stw	r4, 0(r3)
> >   
> >   	/* complete setup */
> > -1:	ld	r3, 56(r1)
> > +1:	REST_GPR(3, r1)
> >   	bl	setup
> >   
> >   	/* run the test */
>
>   Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination
  2024-03-05  6:29     ` Nicholas Piggin
@ 2024-03-05  6:59       ` Thomas Huth
  0 siblings, 0 replies; 71+ messages in thread
From: Thomas Huth @ 2024-03-05  6:59 UTC (permalink / raw)
  To: Nicholas Piggin
  Cc: Laurent Vivier, kvm, Andrew Jones, Joel Stanley, Paolo Bonzini,
	linuxppc-dev

On 05/03/2024 07.29, Nicholas Piggin wrote:
> On Tue Feb 27, 2024 at 6:50 PM AEST, Thomas Huth wrote:
>> On 26/02/2024 11.11, Nicholas Piggin wrote:
...
>>>    	/* save DTB pointer */
>>> -	std	r3, 56(r1)
>>> +	SAVE_GPR(3,r1)
>>
>> Isn't SAVE_GPR rather meant for the interrupt frame, not for the normal C
>> calling convention frames?
>>
>> Sorry for asking dumb questions ... I still have a hard time understanding
>> the changes here... :-/
> 
> Ah, that was me being lazy and using an interrupt frame for the new
> frame.

Ah, ok. It's super-confusing (at least for me) to see an interrupt frame 
here out of no reason... could you please either add proper comments here 
explaining this, or even better switch to a normal stack frame, please?

  Thanks,
   Thomas


^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests
  2024-03-05  6:26       ` Thomas Huth
@ 2024-03-05 12:12         ` Andrew Jones
  0 siblings, 0 replies; 71+ messages in thread
From: Andrew Jones @ 2024-03-05 12:12 UTC (permalink / raw)
  To: Thomas Huth
  Cc: Laurent Vivier, kvm, Nicholas Piggin, Andrew Jones, Joel Stanley,
	Paolo Bonzini, linuxppc-dev

On Tue, Mar 05, 2024 at 07:26:18AM +0100, Thomas Huth wrote:
> On 05/03/2024 03.19, Nicholas Piggin wrote:
> > On Fri Mar 1, 2024 at 10:41 PM AEST, Thomas Huth wrote:
> > > On 26/02/2024 11.12, Nicholas Piggin wrote:
> > > > Add basic testing of various kinds of interrupts, machine check,
> > > > page fault, illegal, decrementer, trace, syscall, etc.
> > > > 
> > > > This has a known failure on QEMU TCG pseries machines where MSR[ME]
> > > > can be incorrectly set to 0.
> > > 
> > > Two questions out of curiosity:
> > > 
> > > Any chance that this could be fixed easily in QEMU?
> > 
> > Yes I have a fix on the mailing list. It should get into 9.0 and
> > probably stable.
> 
> Ok, then it's IMHO not worth the effort to make the k-u-t work around this
> bug in older QEMU versions.
> 
> > > Or is there a way to detect TCG from within the test? (for example, we have
> > > a host_is_tcg() function for s390x so we can e.g. use report_xfail() for
> > > tests that are known to fail on TCG there)
> > 
> > I do have a half-done patch which adds exactly this.
> > 
> > One (minor) annoyance is that it doesn't seem possible to detect QEMU
> > version to add workarounds. E.g., we would like to test the fixed
> > functionality, but older qemu should not. Maybe that's going too much
> > into a rabbit hole. We *could* put a QEMU version into device tree
> > to deal with this though...
> 
> No, let's better not do this - hardwired version checks are often a bad
> idea, e.g. when a bug is in QEMU 8.0.0 and 8.1.0, it could be fixed in 8.0.1
> and then it could get really messy with the version checks...
>

We've tried to address this type of issue (but for KVM, so kernel versions
instead of QEMU versions) in the past by inventing the errata framework,
which is based on environment variables. Instead of checking for versions,
we check for a hash (which is just the commit hash of the fix). While we
do guess that the fix is present by version number, it can always be
manually set as present as well. In any case, the test is simply skipped
when the errata environment variable isn't present, so in the worst case
we lose some coverage we could have had, but the rest of the tests still
complete and we don't get the same failures over and over. An example of
its use is in arm/psci.c. Look for the ERRATA() calls.

We could extend the errata framework for QEMU/TCG. We just need to add
another bit of data to the errata.txt file for it to know it should
check QEMU versions instead of kernel versions for those errata. We can
also ignore the errata framework and just create the errata environment
variable which would by 'n' by default now and later, after distros have
fixes, it could be changed to 'y'.

Thanks,
drew

^ permalink raw reply	[flat|nested] 71+ messages in thread

end of thread, other threads:[~2024-03-05 12:13 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-02-26 10:11 [kvm-unit-tests PATCH 00/32] powerpc improvements Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 01/32] powerpc: Fix KVM caps on POWER9 hosts Nicholas Piggin
2024-02-26 11:33   ` Thomas Huth
2024-02-26 10:11 ` [kvm-unit-tests PATCH 02/32] powerpc: Fix pseries getchar return value Nicholas Piggin
2024-02-26 11:35   ` Thomas Huth
2024-02-26 10:11 ` [kvm-unit-tests PATCH 03/32] powerpc: Fix stack backtrace termination Nicholas Piggin
2024-02-27  8:50   ` Thomas Huth
2024-03-01  9:45     ` Thomas Huth
2024-03-05  2:08       ` Nicholas Piggin
2024-03-05  6:29     ` Nicholas Piggin
2024-03-05  6:59       ` Thomas Huth
2024-02-26 10:11 ` [kvm-unit-tests PATCH 04/32] powerpc: interrupt stack backtracing Nicholas Piggin
2024-02-28 11:46   ` Andrew Jones
2024-02-29  3:50     ` Nicholas Piggin
2024-03-01  9:53   ` Thomas Huth
2024-03-05  2:12     ` Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 05/32] powerpc: Cleanup SPR and MSR definitions Nicholas Piggin
2024-03-01 11:11   ` Thomas Huth
2024-02-26 10:11 ` [kvm-unit-tests PATCH 06/32] powerpc/sprs: Specify SPRs with data rather than code Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 07/32] powerpc/sprs: Don't fail changed SPRs that are used by the test harness Nicholas Piggin
2024-03-01 11:15   ` Thomas Huth
2024-03-05  2:14     ` Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 08/32] powerpc/sprs: Avoid taking PMU interrupts caused by register fuzzing Nicholas Piggin
2024-03-01 11:25   ` Thomas Huth
2024-02-26 10:11 ` [kvm-unit-tests PATCH 09/32] scripts: allow machine option to be specified in unittests.cfg Nicholas Piggin
2024-02-28 11:47   ` Andrew Jones
2024-02-29  3:52     ` Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 10/32] scripts: Accommodate powerpc powernv machine differences Nicholas Piggin
2024-02-28 11:52   ` Andrew Jones
2024-02-26 10:11 ` [kvm-unit-tests PATCH 11/32] powerpc: Support powernv machine with QEMU TCG Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 12/32] powerpc: Fix emulator illegal instruction test for powernv Nicholas Piggin
2024-03-01 11:50   ` Thomas Huth
2024-03-05  2:14     ` Nicholas Piggin
2024-02-26 10:11 ` [kvm-unit-tests PATCH 13/32] powerpc/sprs: Test hypervisor registers on powernv machine Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 14/32] powerpc: general interrupt tests Nicholas Piggin
2024-03-01 12:41   ` Thomas Huth
2024-03-01 13:45     ` Andrew Jones
2024-03-01 13:57       ` Thomas Huth
2024-03-01 14:14         ` Andrew Jones
2024-03-05  2:35           ` Nicholas Piggin
2024-03-05  2:30       ` Nicholas Piggin
2024-03-05  6:18         ` Thomas Huth
2024-03-05  2:19     ` Nicholas Piggin
2024-03-05  6:26       ` Thomas Huth
2024-03-05 12:12         ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 15/32] powerpc: Add rtas stop-self support Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 16/32] powerpc: Remove broken SMP exception stack setup Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 17/32] arch-run: Fix handling multiple exit status messages Nicholas Piggin
2024-02-28 11:51   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 18/32] powerpc: add SMP and IPI support Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 19/32] powerpc: Permit ACCEL=tcg,thread=single Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 20/32] powerpc: Avoid using larx/stcx. in spinlocks when only one CPU is running Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 21/32] powerpc: Add atomics tests Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 22/32] powerpc: Add timebase tests Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 23/32] powerpc: Add MMU support Nicholas Piggin
2024-02-28 12:01   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 24/32] common/sieve: Use vmalloc.h for setup_mmu definition Nicholas Piggin
2024-02-28 12:03   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 25/32] common/sieve: Support machines without MMU Nicholas Piggin
2024-02-28 12:04   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 26/32] powerpc: Add sieve.c common test Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 27/32] powerpc: add usermode support Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 28/32] powerpc: add pmu tests Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 29/32] configure: Fail on unknown arch Nicholas Piggin
2024-02-28 12:08   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 30/32] configure: Make arch_libdir a first-class entity Nicholas Piggin
2024-02-28 12:13   ` Andrew Jones
2024-02-26 10:12 ` [kvm-unit-tests PATCH 31/32] powerpc: Remove remnants of ppc64 directory and build structure Nicholas Piggin
2024-02-26 10:12 ` [kvm-unit-tests PATCH 32/32] powerpc: gitlab CI update Nicholas Piggin
2024-02-28 12:16   ` Andrew Jones
2024-02-29  3:55     ` Nicholas Piggin

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).