* [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress
@ 2026-09-01 17:06 Mark Brown
2026-09-01 17:06 ` [PATCH 01/11] kselftest/arm64: Factor Linux syscalls out of asm-utils.S Mark Brown
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
Presently we don't have any automatic stress testing for the handling of
floating point save and restore with KVM, nor it's interaction with the
host's management of the floating point registers. The majority of the
focused stress testing has been done by running the existing fp-stress
selftest on the host, starting a guest and then running fp-stress in
there too but this has been a manual process rather than something done
as a matter of routine by CI.
Plug this gap by reworking the test loads so that they can be run as KVM
guests, using a simple VMM that provides the interface that fp-stress
expects from it's test loads. We run the FPSIMD and SVE tests in KVM
guests for half the CPUs, if EL2 is available then half the KVM tests
will use it. The tests run in a minimal environment with the VMM and
test binaries tightly coupled to each other.
There's more coverage could be added, but this is an improvement over
the current situation and can be built on in future. Off the top of my
head:
- We only test nested guests running at EL2 with E2H set.
- We only run the guests at the highest VL they support.
- Enabling the MMU would avoid surprises due to the performance
differences.
- We do not run any protected guests (this currently taints the kernel
so should wait until the pKVM support is less experimental).
- It might be useful to have the VMM signal handler save and restore
the guest register state to exercise that path.
- Possibly it would be useful to disable irrelevant features so less
time is spent context switching non-FP state?
Signed-off-by: Mark Brown <broonie@kernel.org>
---
Mark Brown (11):
kselftest/arm64: Factor Linux syscalls out of asm-utils.S
kselftest/arm64: Factor shared signal handlers out of fp-stress loads
kselftest/arm64: Move exit calls out of fp-stress loads
kselftest/arm64: Use exit_error() rather than SIGABRT in fp-stress
kselftest/arm64: Exit with an error code on data mismatches in fp-stress
kselftest/arm64: Factor startup code out of fp-stress load programs
kselftest/arm64: Remove the sched_yield()s from the fp-stress loads
kselftest/arm64: Add a very simple VMM for use in fp-stress
kselftest/arm64: Build KVM guest versions of the fp-stress loads
kselftest/arm64: Use execv() to start fp-stress test loads
kselftest/arm64: Run KVM guests from fp-stress
tools/testing/selftests/arm64/fp/.gitignore | 4 +
tools/testing/selftests/arm64/fp/Makefile | 64 ++-
tools/testing/selftests/arm64/fp/asm-utils-kvm.S | 86 +++
tools/testing/selftests/arm64/fp/asm-utils-linux.S | 181 +++++++
tools/testing/selftests/arm64/fp/asm-utils.S | 36 --
tools/testing/selftests/arm64/fp/fp-pidbench.S | 6 +-
.../testing/selftests/arm64/fp/fp-stress-kvm.lds.S | 58 ++
tools/testing/selftests/arm64/fp/fp-stress-vmm.c | 596 +++++++++++++++++++++
tools/testing/selftests/arm64/fp/fp-stress-vmm.h | 17 +
tools/testing/selftests/arm64/fp/fp-stress.c | 230 +++++++-
tools/testing/selftests/arm64/fp/fpsimd-test.S | 121 +----
tools/testing/selftests/arm64/fp/sve-test.S | 139 +----
tools/testing/selftests/arm64/fp/za-test.S | 138 +----
tools/testing/selftests/arm64/fp/zt-test.S | 131 +----
14 files changed, 1244 insertions(+), 563 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20250401-arm64-fp-stress-kvm-4720ca307da4
Best regards,
--
Mark Brown <broonie@kernel.org>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/11] kselftest/arm64: Factor Linux syscalls out of asm-utils.S
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 02/11] kselftest/arm64: Factor shared signal handlers out of fp-stress loads Mark Brown
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
In preparation for building versions of the load programs for fp-stress
that run as KVM guests move the functions that perform syscalls out of
the shared code assembly code in asm-utils.S into a new Linux specific
file asm-utils-linux.S.
No functional change.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/Makefile | 17 +++++----
tools/testing/selftests/arm64/fp/asm-utils-linux.S | 43 ++++++++++++++++++++++
tools/testing/selftests/arm64/fp/asm-utils.S | 36 ------------------
3 files changed, 53 insertions(+), 43 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile
index d171021e4cdd..183e9d319b65 100644
--- a/tools/testing/selftests/arm64/fp/Makefile
+++ b/tools/testing/selftests/arm64/fp/Makefile
@@ -22,21 +22,24 @@ TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
vlset
TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
-EXTRA_CLEAN += $(OUTPUT)/asm-utils.o $(OUTPUT)/rdvl.o $(OUTPUT)/za-fork-asm.o
+EXTRA_CLEAN += $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o \
+ $(OUTPUT)/rdvl.o $(OUTPUT)/za-fork-asm.o
+
+ASM_UTILS := $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o
# Build with nolibc to avoid effects due to libc's clone() support
-$(OUTPUT)/fp-pidbench: fp-pidbench.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/fp-pidbench: fp-pidbench.S $(ASM_UTILS)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/fp-ptrace: fp-ptrace.c fp-ptrace-asm.S
-$(OUTPUT)/fpsimd-test: fpsimd-test.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/fpsimd-test: fpsimd-test.S $(ASM_UTILS)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/rdvl-sve: rdvl-sve.c $(OUTPUT)/rdvl.o
$(OUTPUT)/rdvl-sme: rdvl-sme.c $(OUTPUT)/rdvl.o
$(OUTPUT)/sve-ptrace: sve-ptrace.c
$(OUTPUT)/sve-probe-vls: sve-probe-vls.c $(OUTPUT)/rdvl.o
-$(OUTPUT)/sve-test: sve-test.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/sve-test: sve-test.S $(ASM_UTILS)
$(CC) -nostdlib $^ -o $@
-$(OUTPUT)/ssve-test: sve-test.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/ssve-test: sve-test.S $(ASM_UTILS)
$(CC) -DSSVE -nostdlib $^ -o $@
$(OUTPUT)/vec-syscfg: vec-syscfg.c $(OUTPUT)/rdvl.o
$(OUTPUT)/vlset: vlset.c
@@ -45,10 +48,10 @@ $(OUTPUT)/za-fork: za-fork.c $(OUTPUT)/za-fork-asm.o
-include ../../../../include/nolibc/nolibc.h -I../..\
-static -ffreestanding -Wall $^ -o $@
$(OUTPUT)/za-ptrace: za-ptrace.c
-$(OUTPUT)/za-test: za-test.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/za-test: za-test.S $(ASM_UTILS)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/zt-ptrace: zt-ptrace.c
-$(OUTPUT)/zt-test: zt-test.S $(OUTPUT)/asm-utils.o
+$(OUTPUT)/zt-test: zt-test.S $(ASM_UTILS)
$(CC) -nostdlib $^ -o $@
include ../../lib.mk
diff --git a/tools/testing/selftests/arm64/fp/asm-utils-linux.S b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
new file mode 100644
index 000000000000..e6c22b41dc5e
--- /dev/null
+++ b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (C) 2015-2021 ARM Limited.
+// Original author: Dave Martin <Dave.Martin@arm.com>
+//
+// Utility functions for assembly code which use Linux syscalls.
+
+#include <asm/unistd.h>
+#include "assembler.h"
+
+// Print a single character x0 to stdout
+// Clobbers x0-x2,x8
+function putc
+ str x0, [sp, #-16]!
+
+ mov x0, #1 // STDOUT_FILENO
+ mov x1, sp
+ mov x2, #1
+ mov x8, #__NR_write
+ svc #0
+
+ add sp, sp, #16
+ ret
+endfunction
+.globl putc
+
+// Print a NUL-terminated string starting at address x0 to stdout
+// Clobbers x0-x3,x8
+function puts
+ mov x1, x0
+
+ mov x2, #0
+0: ldrb w3, [x0], #1
+ cbz w3, 1f
+ add x2, x2, #1
+ b 0b
+
+1: mov w0, #1 // STDOUT_FILENO
+ mov x8, #__NR_write
+ svc #0
+
+ ret
+endfunction
+.globl puts
diff --git a/tools/testing/selftests/arm64/fp/asm-utils.S b/tools/testing/selftests/arm64/fp/asm-utils.S
index 4b9728efc18d..6d43eb10e01f 100644
--- a/tools/testing/selftests/arm64/fp/asm-utils.S
+++ b/tools/testing/selftests/arm64/fp/asm-utils.S
@@ -4,44 +4,8 @@
//
// Utility functions for assembly code.
-#include <asm/unistd.h>
#include "assembler.h"
-// Print a single character x0 to stdout
-// Clobbers x0-x2,x8
-function putc
- str x0, [sp, #-16]!
-
- mov x0, #1 // STDOUT_FILENO
- mov x1, sp
- mov x2, #1
- mov x8, #__NR_write
- svc #0
-
- add sp, sp, #16
- ret
-endfunction
-.globl putc
-
-// Print a NUL-terminated string starting at address x0 to stdout
-// Clobbers x0-x3,x8
-function puts
- mov x1, x0
-
- mov x2, #0
-0: ldrb w3, [x0], #1
- cbz w3, 1f
- add x2, x2, #1
- b 0b
-
-1: mov w0, #1 // STDOUT_FILENO
- mov x8, #__NR_write
- svc #0
-
- ret
-endfunction
-.globl puts
-
// Print an unsigned decimal number x0 to stdout
// Clobbers x0-x4,x8
function putdec
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 02/11] kselftest/arm64: Factor shared signal handlers out of fp-stress loads
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
2026-09-01 17:06 ` [PATCH 01/11] kselftest/arm64: Factor Linux syscalls out of asm-utils.S Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 03/11] kselftest/arm64: Move exit calls " Mark Brown
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
The assembly programs run by fp-stress all have identical signal
handlers for cleanly exiting the program and simply countil signal
deliveries, consolidate them into a single copies in asm-utils-linux.S.
No functional change.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/Makefile | 14 +++----
tools/testing/selftests/arm64/fp/asm-utils-linux.S | 43 ++++++++++++++++++++++
tools/testing/selftests/arm64/fp/fpsimd-test.S | 28 --------------
tools/testing/selftests/arm64/fp/sve-test.S | 28 --------------
tools/testing/selftests/arm64/fp/za-test.S | 28 --------------
tools/testing/selftests/arm64/fp/zt-test.S | 28 --------------
6 files changed, 50 insertions(+), 119 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile
index 183e9d319b65..d92a755837ff 100644
--- a/tools/testing/selftests/arm64/fp/Makefile
+++ b/tools/testing/selftests/arm64/fp/Makefile
@@ -25,21 +25,21 @@ TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
EXTRA_CLEAN += $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o \
$(OUTPUT)/rdvl.o $(OUTPUT)/za-fork-asm.o
-ASM_UTILS := $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o
+ASM_UTILS_LINUX := $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o
# Build with nolibc to avoid effects due to libc's clone() support
-$(OUTPUT)/fp-pidbench: fp-pidbench.S $(ASM_UTILS)
+$(OUTPUT)/fp-pidbench: fp-pidbench.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/fp-ptrace: fp-ptrace.c fp-ptrace-asm.S
-$(OUTPUT)/fpsimd-test: fpsimd-test.S $(ASM_UTILS)
+$(OUTPUT)/fpsimd-test: fpsimd-test.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/rdvl-sve: rdvl-sve.c $(OUTPUT)/rdvl.o
$(OUTPUT)/rdvl-sme: rdvl-sme.c $(OUTPUT)/rdvl.o
$(OUTPUT)/sve-ptrace: sve-ptrace.c
$(OUTPUT)/sve-probe-vls: sve-probe-vls.c $(OUTPUT)/rdvl.o
-$(OUTPUT)/sve-test: sve-test.S $(ASM_UTILS)
+$(OUTPUT)/sve-test: sve-test.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
-$(OUTPUT)/ssve-test: sve-test.S $(ASM_UTILS)
+$(OUTPUT)/ssve-test: sve-test.S $(ASM_UTILS_LINUX)
$(CC) -DSSVE -nostdlib $^ -o $@
$(OUTPUT)/vec-syscfg: vec-syscfg.c $(OUTPUT)/rdvl.o
$(OUTPUT)/vlset: vlset.c
@@ -48,10 +48,10 @@ $(OUTPUT)/za-fork: za-fork.c $(OUTPUT)/za-fork-asm.o
-include ../../../../include/nolibc/nolibc.h -I../..\
-static -ffreestanding -Wall $^ -o $@
$(OUTPUT)/za-ptrace: za-ptrace.c
-$(OUTPUT)/za-test: za-test.S $(ASM_UTILS)
+$(OUTPUT)/za-test: za-test.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
$(OUTPUT)/zt-ptrace: zt-ptrace.c
-$(OUTPUT)/zt-test: zt-test.S $(ASM_UTILS)
+$(OUTPUT)/zt-test: zt-test.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
include ../../lib.mk
diff --git a/tools/testing/selftests/arm64/fp/asm-utils-linux.S b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
index e6c22b41dc5e..3effeb1fbf50 100644
--- a/tools/testing/selftests/arm64/fp/asm-utils-linux.S
+++ b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
@@ -6,6 +6,7 @@
#include <asm/unistd.h>
#include "assembler.h"
+#include "asm-offsets.h"
// Print a single character x0 to stdout
// Clobbers x0-x2,x8
@@ -41,3 +42,45 @@ function puts
ret
endfunction
.globl puts
+
+// Signal handler which reports a clean exit and terminates the program.
+// The tests keep their iteration count in x22 and their signal count in
+// x23, report those from the interrupted context.
+//
+// w0: signal number
+// x2: ucontext
+function terminate_handler
+ mov w21, w0
+ mov x20, x2
+
+ puts "Terminated by signal "
+ mov w0, w21
+ bl putdec
+ puts ", no error, iterations="
+ ldr x0, [x20, #ucontext_regs + 8 * 22]
+ bl putdec
+ puts ", signals="
+ ldr x0, [x20, #ucontext_regs + 8 * 23]
+ bl putdecn
+
+ mov x0, #0
+ mov x8, #__NR_exit
+ svc #0
+endfunction
+.globl terminate_handler
+
+// Signal handler which only records that the program was interrupted.
+// The tests keep their signal count in x23, update that in the
+// interrupted context.
+//
+// w0: signal number
+// x2: ucontext
+function tickle_handler
+ // Increment the signal count (x23):
+ ldr x0, [x2, #ucontext_regs + 8 * 23]
+ add x0, x0, #1
+ str x0, [x2, #ucontext_regs + 8 * 23]
+
+ ret
+endfunction
+.globl tickle_handler
diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S
index f89d67894c2e..9db7b5419397 100644
--- a/tools/testing/selftests/arm64/fp/fpsimd-test.S
+++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S
@@ -149,34 +149,6 @@ function irritator_handler
ret
endfunction
-function tickle_handler
- // Increment the signal count (x23):
- ldr x0, [x2, #ucontext_regs + 8 * 23]
- add x0, x0, #1
- str x0, [x2, #ucontext_regs + 8 * 23]
-
- ret
-endfunction
-
-function terminate_handler
- mov w21, w0
- mov x20, x2
-
- puts "Terminated by signal "
- mov w0, w21
- bl putdec
- puts ", no error, iterations="
- ldr x0, [x20, #ucontext_regs + 8 * 22]
- bl putdec
- puts ", signals="
- ldr x0, [x20, #ucontext_regs + 8 * 23]
- bl putdecn
-
- mov x0, #0
- mov x8, #__NR_exit
- svc #0
-endfunction
-
// w0: signal number
// x1: sa_action
// w2: sa_flags
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 7ef7835389e7..2aadb5c3c0f3 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -317,34 +317,6 @@ function irritator_handler
ret
endfunction
-function tickle_handler
- // Increment the signal count (x23):
- ldr x0, [x2, #ucontext_regs + 8 * 23]
- add x0, x0, #1
- str x0, [x2, #ucontext_regs + 8 * 23]
-
- ret
-endfunction
-
-function terminate_handler
- mov w21, w0
- mov x20, x2
-
- puts "Terminated by signal "
- mov w0, w21
- bl putdec
- puts ", no error, iterations="
- ldr x0, [x20, #ucontext_regs + 8 * 22]
- bl putdec
- puts ", signals="
- ldr x0, [x20, #ucontext_regs + 8 * 23]
- bl putdecn
-
- mov x0, #0
- mov x8, #__NR_exit
- svc #0
-endfunction
-
// w0: signal number
// x1: sa_action
// w2: sa_flags
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index 9c33e13e9dc4..84e63e510f23 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -162,34 +162,6 @@ function irritator_handler
ret
endfunction
-function tickle_handler
- // Increment the signal count (x23):
- ldr x0, [x2, #ucontext_regs + 8 * 23]
- add x0, x0, #1
- str x0, [x2, #ucontext_regs + 8 * 23]
-
- ret
-endfunction
-
-function terminate_handler
- mov w21, w0
- mov x20, x2
-
- puts "Terminated by signal "
- mov w0, w21
- bl putdec
- puts ", no error, iterations="
- ldr x0, [x20, #ucontext_regs + 8 * 22]
- bl putdec
- puts ", signals="
- ldr x0, [x20, #ucontext_regs + 8 * 23]
- bl putdecn
-
- mov x0, #0
- mov x8, #__NR_exit
- svc #0
-endfunction
-
// w0: signal number
// x1: sa_action
// w2: sa_flags
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index a8df05771670..afcadb5aff23 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -131,34 +131,6 @@ function irritator_handler
ret
endfunction
-function tickle_handler
- // Increment the signal count (x23):
- ldr x0, [x2, #ucontext_regs + 8 * 23]
- add x0, x0, #1
- str x0, [x2, #ucontext_regs + 8 * 23]
-
- ret
-endfunction
-
-function terminate_handler
- mov w21, w0
- mov x20, x2
-
- puts "Terminated by signal "
- mov w0, w21
- bl putdec
- puts ", no error, iterations="
- ldr x0, [x20, #ucontext_regs + 8 * 22]
- bl putdec
- puts ", signals="
- ldr x0, [x20, #ucontext_regs + 8 * 23]
- bl putdecn
-
- mov x0, #0
- mov x8, #__NR_exit
- svc #0
-endfunction
-
// w0: signal number
// x1: sa_action
// w2: sa_flags
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 03/11] kselftest/arm64: Move exit calls out of fp-stress loads
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
2026-09-01 17:06 ` [PATCH 01/11] kselftest/arm64: Factor Linux syscalls out of asm-utils.S Mark Brown
2026-09-01 17:06 ` [PATCH 02/11] kselftest/arm64: Factor shared signal handlers out of fp-stress loads Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 04/11] kselftest/arm64: Use exit_error() rather than SIGABRT in fp-stress Mark Brown
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
When they detect corruption the fp-stress test loads print load specific
diagnostics and exits with a failure status. In preparation for
building versions intended to run as KVM guests factor the exits into
asm-utils-linux.S.
No functional change.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/asm-utils-linux.S | 8 ++++++++
tools/testing/selftests/arm64/fp/fpsimd-test.S | 4 +---
tools/testing/selftests/arm64/fp/sve-test.S | 8 ++------
tools/testing/selftests/arm64/fp/za-test.S | 8 ++------
tools/testing/selftests/arm64/fp/zt-test.S | 4 +---
5 files changed, 14 insertions(+), 18 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/asm-utils-linux.S b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
index 3effeb1fbf50..9cd31dc47709 100644
--- a/tools/testing/selftests/arm64/fp/asm-utils-linux.S
+++ b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
@@ -84,3 +84,11 @@ function tickle_handler
ret
endfunction
.globl tickle_handler
+
+// Terminate the program, reporting failure. Does not return.
+function exit_error
+ mov x0, #1
+ mov x8, #__NR_exit
+ svc #0
+endfunction
+.globl exit_error
diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S
index 9db7b5419397..5ee8ef51ea4e 100644
--- a/tools/testing/selftests/arm64/fp/fpsimd-test.S
+++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S
@@ -298,7 +298,5 @@ function barf
bl dumphex
puts "]\n"
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 2aadb5c3c0f3..3447fe0e5554 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -543,9 +543,7 @@ function vl_barf
mov x0, x10
bl putdecn
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
function svcr_barf
@@ -555,7 +553,5 @@ function svcr_barf
mov x0, x10
bl putdecn
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index 84e63e510f23..33dc4ce8bfa1 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -354,9 +354,7 @@ function vl_barf
mov x0, x10
bl putdecn
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
function svcr_barf
@@ -366,7 +364,5 @@ function svcr_barf
mov x0, x10
bl putdecn
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index afcadb5aff23..c67242c2d5ab 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -284,7 +284,5 @@ function svcr_barf
mov x0, x10
bl putdecn
- mov x8, #__NR_exit
- mov x1, #1
- svc #0
+ b exit_error
endfunction
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 04/11] kselftest/arm64: Use exit_error() rather than SIGABRT in fp-stress
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (2 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 03/11] kselftest/arm64: Move exit calls " Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 05/11] kselftest/arm64: Exit with an error code on data mismatches " Mark Brown
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
The fp-stress test loads all deliver a SIGABRT to the process group to
report some errors they detect. There is no particular reason to use
this mechanism over exiting with an error code, the fp-stress harness does
not care. In order to facilitate running as KVM guests replace this with
use of the exit_error() function we added.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/fpsimd-test.S | 10 ++--------
tools/testing/selftests/arm64/fp/sve-test.S | 10 ++--------
tools/testing/selftests/arm64/fp/za-test.S | 10 ++--------
tools/testing/selftests/arm64/fp/zt-test.S | 8 +-------
4 files changed, 7 insertions(+), 31 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S
index 5ee8ef51ea4e..fcbb3a872490 100644
--- a/tools/testing/selftests/arm64/fp/fpsimd-test.S
+++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S
@@ -176,7 +176,7 @@ function setsignal
cbz w0, 1f
puts "sigaction failure\n"
- b .Labort
+ b exit_error
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
ret
@@ -224,7 +224,7 @@ function _start
1: puts "Bad vector length: "
mov x0, x19
bl putdecn
- b .Labort
+ b exit_error
2: puts "Vector length:\t"
mov x0, x19
@@ -266,12 +266,6 @@ function _start
add x22, x22, #1
b .Ltest_loop
-
-.Labort:
- mov x0, #0
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
endfunction
function barf
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 3447fe0e5554..26efdbdd88f9 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -344,7 +344,7 @@ function setsignal
cbz w0, 1f
puts "sigaction failure\n"
- b .Labort
+ b exit_error
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
ret
@@ -397,7 +397,7 @@ function _start
1: puts "Bad vector length: "
mov x0, x19
bl putdecn
- b .Labort
+ b exit_error
2: puts "Vector length:\t"
mov x0, x19
@@ -474,12 +474,6 @@ function _start
add x22, x22, #1
b .Ltest_loop
-
-.Labort:
- mov x0, #0
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
endfunction
function barf
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index 33dc4ce8bfa1..7208bb2db9f1 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -189,7 +189,7 @@ function setsignal
cbz w0, 1f
puts "sigaction failure\n"
- b .Labort
+ b exit_error
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
ret
@@ -240,7 +240,7 @@ function _start
1: puts "bad vector length: "
mov x0, x19
bl putdecn
- b .Labort
+ b exit_error
2: puts "vector length:\t"
mov x0, x19
@@ -288,12 +288,6 @@ function _start
add x22, x22, #1 // Everything still working
b .Ltest_loop
-
-.Labort:
- mov x0, #0
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
endfunction
function barf
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index c67242c2d5ab..c6cfbdeb2f0c 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -158,7 +158,7 @@ function setsignal
cbz w0, 1f
puts "sigaction failure\n"
- b .Labort
+ b exit_error
1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
ret
@@ -222,12 +222,6 @@ function _start
add x22, x22, #1 // Everything still working
b .Ltest_loop
-
-.Labort:
- mov x0, #0
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
endfunction
function barf
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 05/11] kselftest/arm64: Exit with an error code on data mismatches in fp-stress
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (3 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 04/11] kselftest/arm64: Use exit_error() rather than SIGABRT in fp-stress Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 06/11] kselftest/arm64: Factor startup code out of fp-stress load programs Mark Brown
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
Several of the fp-stress test loads have blocks at the end of their barf
functions with a range of commented out options for how to exit the program
after logging an error. Comments indicate that these were intended to
interact with some long gone debugging code in the kernel. At the minute
the option selected by all the programs is to delivera SIGABRT to
themselves but there is no need to do this over a normal exit with a non
zero status. In order to facilitate running as a KVM guest replace these
repeated blocks with calls to exit_error(), this will integrate just as
well with the fp-stress harness.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/sve-test.S | 14 +-------------
tools/testing/selftests/arm64/fp/za-test.S | 14 +-------------
tools/testing/selftests/arm64/fp/zt-test.S | 14 +-------------
3 files changed, 3 insertions(+), 39 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 26efdbdd88f9..88c3220caf7c 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -515,19 +515,7 @@ function barf
bl putdecn
#endif
- mov x8, #__NR_getpid
- svc #0
-// fpsimd.c acitivty log dump hack
-// ldr w0, =0xdeadc0de
-// mov w8, #__NR_exit
-// svc #0
-// ^ end of hack
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
-// mov x8, #__NR_exit
-// mov x1, #1
-// svc #0
+ b exit_error
endfunction
function vl_barf
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index 7208bb2db9f1..4a5b4b37d475 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -326,19 +326,7 @@ function barf
mov x0, x13
bl putdecn
- mov x8, #__NR_getpid
- svc #0
-// fpsimd.c acitivty log dump hack
-// ldr w0, =0xdeadc0de
-// mov w8, #__NR_exit
-// svc #0
-// ^ end of hack
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
-// mov x8, #__NR_exit
-// mov x1, #1
-// svc #0
+ b exit_error
endfunction
function vl_barf
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index c6cfbdeb2f0c..82a76bb2307a 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -256,19 +256,7 @@ function barf
mov x0, x13
bl putdecn
- mov x8, #__NR_getpid
- svc #0
-// fpsimd.c acitivty log dump hack
-// ldr w0, =0xdeadc0de
-// mov w8, #__NR_exit
-// svc #0
-// ^ end of hack
- mov x1, #SIGABRT
- mov x8, #__NR_kill
- svc #0
-// mov x8, #__NR_exit
-// mov x1, #1
-// svc #0
+ b exit_error
endfunction
function svcr_barf
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 06/11] kselftest/arm64: Factor startup code out of fp-stress load programs
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (4 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 05/11] kselftest/arm64: Exit with an error code on data mismatches " Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 07/11] kselftest/arm64: Remove the sched_yield()s from the fp-stress loads Mark Brown
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
The fp-stress load programs all start up with some startup code which
installs signal handlers and fetches and reports the current PID into
x20. In order to facilitate running as KVM guests and reduce duplication
factor this code out into a asm-utils-linux.S, making the per-test entry
point main() similarly to more normal environments. As the
irritator_handler varies per test they are left in place.
Since the PID is now reported before main() runs it is printed before
rather than after the vector length for those programs that report one,
fp-stress does not parse the output so this has no impact.
Since asm-utils is also used by fp-pidbench that also gets converted to
the new _start implementation, this will install some signal handlers
that were not previously there and report a PID which was not previously
reported but should otherwise have no impact. Since it does not need an
irritator none is provided and a weak definition left in
asm-utils-linux.S, this will end up as 0 which happens to be SIG_DFL
which is what we would have anyway.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/asm-utils-linux.S | 87 ++++++++++++++++++++++
tools/testing/selftests/arm64/fp/fp-pidbench.S | 6 +-
tools/testing/selftests/arm64/fp/fpsimd-test.S | 76 +------------------
tools/testing/selftests/arm64/fp/sve-test.S | 76 +------------------
tools/testing/selftests/arm64/fp/za-test.S | 75 +------------------
tools/testing/selftests/arm64/fp/zt-test.S | 75 +------------------
6 files changed, 106 insertions(+), 289 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/asm-utils-linux.S b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
index 9cd31dc47709..9519be4d8f1e 100644
--- a/tools/testing/selftests/arm64/fp/asm-utils-linux.S
+++ b/tools/testing/selftests/arm64/fp/asm-utils-linux.S
@@ -92,3 +92,90 @@ function exit_error
svc #0
endfunction
.globl exit_error
+
+// Install a signal handler
+//
+// w0: signal number
+// x1: sa_action
+// w2: sa_flags
+// Clobbers x0-x6,x8
+function setsignal
+ str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
+
+ mov w4, w0
+ mov x5, x1
+ mov w6, w2
+
+ add x0, sp, #16
+ mov x1, #sa_sz
+ bl memclr
+
+ mov w0, w4
+ add x1, sp, #16
+ str w6, [x1, #sa_flags]
+ str x5, [x1, #sa_handler]
+ mov x2, #0
+ mov x3, #sa_mask_sz
+ mov x8, #__NR_rt_sigaction
+ svc #0
+
+ cbz w0, 1f
+
+ puts "sigaction failure\n"
+ b exit_error
+
+1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
+ ret
+endfunction
+.globl setsignal
+
+// Programs which want to be interrupted by SIGUSR1 provide an
+// irritator_handler(), for those that don't the signal is left alone.
+.weak irritator_handler
+
+// Main program entry point. Install the signal handlers shared by the
+// programs, zeroing the signal count in x23 that the handlers maintain,
+// read and report the PID the programs use for uniqueness in x20, then
+// run the program specific main().
+.globl _start
+function _start
+ enable_gcs
+
+ mov x23, #0 // signal count
+
+ mov w0, #SIGINT
+ adr x1, terminate_handler
+ mov w2, #SA_SIGINFO
+ bl setsignal
+
+ mov w0, #SIGTERM
+ adr x1, terminate_handler
+ mov w2, #SA_SIGINFO
+ bl setsignal
+
+ // Load as a literal, adr can't reference the symbol when the
+ // program provides no handler and it resolves to 0
+ ldr x1, =irritator_handler
+ cbz x1, 1f
+ mov w0, #SIGUSR1
+ mov w2, #SA_SIGINFO
+ orr w2, w2, #SA_NODEFER
+ bl setsignal
+
+1: mov w0, #SIGUSR2
+ adr x1, tickle_handler
+ mov w2, #SA_SIGINFO
+ orr w2, w2, #SA_NODEFER
+ bl setsignal
+
+ // Obtain our PID, to ensure test pattern uniqueness between processes
+ mov x8, #__NR_getpid
+ svc #0
+ mov x20, x0
+
+ puts "PID:\t"
+ mov x0, x20
+ bl putdecn
+
+ b main
+endfunction
diff --git a/tools/testing/selftests/arm64/fp/fp-pidbench.S b/tools/testing/selftests/arm64/fp/fp-pidbench.S
index 881dfa3b342e..80f45560290d 100644
--- a/tools/testing/selftests/arm64/fp/fp-pidbench.S
+++ b/tools/testing/selftests/arm64/fp/fp-pidbench.S
@@ -28,9 +28,9 @@
puts "\n"
.endm
-// Main program entry point
-.globl _start
-function _start
+// Program specific main function
+.globl main
+function main
puts "Iterations per test: "
mov x20, #10000
lsl x20, x20, #12
diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S
index fcbb3a872490..df040283d076 100644
--- a/tools/testing/selftests/arm64/fp/fpsimd-test.S
+++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S
@@ -148,69 +148,11 @@ function irritator_handler
ret
endfunction
+.globl irritator_handler
-// w0: signal number
-// x1: sa_action
-// w2: sa_flags
-// Clobbers x0-x6,x8
-function setsignal
- str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
-
- mov w4, w0
- mov x5, x1
- mov w6, w2
-
- add x0, sp, #16
- mov x1, #sa_sz
- bl memclr
-
- mov w0, w4
- add x1, sp, #16
- str w6, [x1, #sa_flags]
- str x5, [x1, #sa_handler]
- mov x2, #0
- mov x3, #sa_mask_sz
- mov x8, #__NR_rt_sigaction
- svc #0
-
- cbz w0, 1f
-
- puts "sigaction failure\n"
- b exit_error
-
-1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
- ret
-endfunction
-
-// Main program entry point
-.globl _start
-function _start
- enable_gcs
-
- mov x23, #0 // signal count
-
- mov w0, #SIGINT
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGTERM
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGUSR1
- adr x1, irritator_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
- mov w0, #SIGUSR2
- adr x1, tickle_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
+// Program specific main function
+.globl main
+function main
// Sanity-check and report the vector length
mov x19, #128
@@ -231,16 +173,6 @@ function _start
bl putdec
puts " bits\n"
- // Obtain our PID, to ensure test pattern uniqueness between processes
-
- mov x8, #__NR_getpid
- svc #0
- mov x20, x0
-
- puts "PID:\t"
- mov x0, x20
- bl putdecn
-
mov x22, #0 // generation number, increments per iteration
.Ltest_loop:
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index 88c3220caf7c..c7d9514f964e 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -316,69 +316,11 @@ function irritator_handler
ret
endfunction
+.globl irritator_handler
-// w0: signal number
-// x1: sa_action
-// w2: sa_flags
-// Clobbers x0-x6,x8
-function setsignal
- str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
-
- mov w4, w0
- mov x5, x1
- mov w6, w2
-
- add x0, sp, #16
- mov x1, #sa_sz
- bl memclr
-
- mov w0, w4
- add x1, sp, #16
- str w6, [x1, #sa_flags]
- str x5, [x1, #sa_handler]
- mov x2, #0
- mov x3, #sa_mask_sz
- mov x8, #__NR_rt_sigaction
- svc #0
-
- cbz w0, 1f
-
- puts "sigaction failure\n"
- b exit_error
-
-1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
- ret
-endfunction
-
-// Main program entry point
-.globl _start
-function _start
- enable_gcs
-
- mov x23, #0 // Irritation signal count
-
- mov w0, #SIGINT
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGTERM
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGUSR1
- adr x1, irritator_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
- mov w0, #SIGUSR2
- adr x1, tickle_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
+// Program specific main function
+.globl main
+function main
#ifdef SSVE
puts "Streaming mode "
smstart_sm
@@ -404,16 +346,6 @@ function _start
bl putdec
puts " bits\n"
- // Obtain our PID, to ensure test pattern uniqueness between processes
-
- mov x8, #__NR_getpid
- svc #0
- mov x20, x0
-
- puts "PID:\t"
- mov x0, x20
- bl putdecn
-
#ifdef SSVE
smstart_sm // syscalls will have exited streaming mode
#endif
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index 4a5b4b37d475..a0165a81a937 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -161,69 +161,11 @@ function irritator_handler
ret
endfunction
+.globl irritator_handler
-// w0: signal number
-// x1: sa_action
-// w2: sa_flags
-// Clobbers x0-x6,x8
-function setsignal
- str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
-
- mov w4, w0
- mov x5, x1
- mov w6, w2
-
- add x0, sp, #16
- mov x1, #sa_sz
- bl memclr
-
- mov w0, w4
- add x1, sp, #16
- str w6, [x1, #sa_flags]
- str x5, [x1, #sa_handler]
- mov x2, #0
- mov x3, #sa_mask_sz
- mov x8, #__NR_rt_sigaction
- svc #0
-
- cbz w0, 1f
-
- puts "sigaction failure\n"
- b exit_error
-
-1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
- ret
-endfunction
-
-// Main program entry point
-.globl _start
-function _start
- enable_gcs
-
- mov x23, #0 // signal count
-
- mov w0, #SIGINT
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGTERM
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGUSR1
- adr x1, irritator_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
- mov w0, #SIGUSR2
- adr x1, tickle_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
+// Program specific main function
+.globl main
+function main
puts "Streaming mode "
smstart_za
@@ -247,15 +189,6 @@ function _start
bl putdec
puts " bits\n"
- // Obtain our PID, to ensure test pattern uniqueness between processes
- mov x8, #__NR_getpid
- svc #0
- mov x20, x0
-
- puts "PID:\t"
- mov x0, x20
- bl putdecn
-
mov x22, #0 // generation number, increments per iteration
.Ltest_loop:
rdsvl 0, 8
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index 82a76bb2307a..1c7db1298fb1 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -130,80 +130,13 @@ function irritator_handler
ret
endfunction
+.globl irritator_handler
-// w0: signal number
-// x1: sa_action
-// w2: sa_flags
-// Clobbers x0-x6,x8
-function setsignal
- str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]!
-
- mov w4, w0
- mov x5, x1
- mov w6, w2
-
- add x0, sp, #16
- mov x1, #sa_sz
- bl memclr
-
- mov w0, w4
- add x1, sp, #16
- str w6, [x1, #sa_flags]
- str x5, [x1, #sa_handler]
- mov x2, #0
- mov x3, #sa_mask_sz
- mov x8, #__NR_rt_sigaction
- svc #0
-
- cbz w0, 1f
-
- puts "sigaction failure\n"
- b exit_error
-
-1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
- ret
-endfunction
-
-// Main program entry point
-.globl _start
-function _start
- enable_gcs
-
- mov x23, #0 // signal count
-
- mov w0, #SIGINT
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGTERM
- adr x1, terminate_handler
- mov w2, #SA_SIGINFO
- bl setsignal
-
- mov w0, #SIGUSR1
- adr x1, irritator_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
- mov w0, #SIGUSR2
- adr x1, tickle_handler
- mov w2, #SA_SIGINFO
- orr w2, w2, #SA_NODEFER
- bl setsignal
-
+// Program specific main function
+.globl main
+function main
smstart_za
- // Obtain our PID, to ensure test pattern uniqueness between processes
- mov x8, #__NR_getpid
- svc #0
- mov x20, x0
-
- puts "PID:\t"
- mov x0, x20
- bl putdecn
-
mov x22, #0 // generation number, increments per iteration
.Ltest_loop:
mov x0, x20
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 07/11] kselftest/arm64: Remove the sched_yield()s from the fp-stress loads
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (5 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 06/11] kselftest/arm64: Factor startup code out of fp-stress load programs Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 08/11] kselftest/arm64: Add a very simple VMM for use in fp-stress Mark Brown
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
The fp-stress load programs call sched_yield() on each iteration of
their test loops in order to encourage preemption, except for sve-test
where the call is commented out since the SVE state is destroyed by
system calls. This is most likely counterproductive since we are more
likely to notice corruption if preemption happens while setting or
reading the register state, and since fp-stress starts multiple test
programs per CPU we should have no trouble observing preemption anyway.
Remove these calls, including the commented out one in sve-test, both
due to this and in order to prepare for running as KVM guests.
We also remove the inclusions of unistd.h since we no longer need it in
any of the programs. za-test had an unreferenced local label in the
middle of the sched_yield(), remove that too.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/fpsimd-test.S | 5 -----
tools/testing/selftests/arm64/fp/sve-test.S | 5 -----
tools/testing/selftests/arm64/fp/za-test.S | 5 -----
tools/testing/selftests/arm64/fp/zt-test.S | 4 ----
4 files changed, 19 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/fpsimd-test.S b/tools/testing/selftests/arm64/fp/fpsimd-test.S
index df040283d076..5e19799f1b2e 100644
--- a/tools/testing/selftests/arm64/fp/fpsimd-test.S
+++ b/tools/testing/selftests/arm64/fp/fpsimd-test.S
@@ -10,7 +10,6 @@
// (leave it running for as long as you want...)
// kill $pids
-#include <asm/unistd.h>
#include "assembler.h"
#include "asm-offsets.h"
@@ -185,10 +184,6 @@ function main
cmp x21, #NVR
b.lo 0b
-// Can't do this when SVE state is volatile across SVC:
- mov x8, #__NR_sched_yield // Encourage preemption
- svc #0
-
mov x21, #0
0: mov x0, x21
bl check_vreg
diff --git a/tools/testing/selftests/arm64/fp/sve-test.S b/tools/testing/selftests/arm64/fp/sve-test.S
index c7d9514f964e..fdb295bde45c 100644
--- a/tools/testing/selftests/arm64/fp/sve-test.S
+++ b/tools/testing/selftests/arm64/fp/sve-test.S
@@ -10,7 +10,6 @@
// (leave it running for as long as you want...)
// kill $pids
-#include <asm/unistd.h>
#include "assembler.h"
#include "asm-offsets.h"
#include "sme-inst.h"
@@ -378,10 +377,6 @@ function main
cmp x21, #NZR + NPR
b.lo 0b
-// Can't do this when SVE state is volatile across SVC:
-// mov x8, #__NR_sched_yield // Encourage preemption
-// svc #0
-
#ifdef SSVE
mrs x0, S3_3_C4_C2_2 // SVCR should have ZA=0,SM=1
and x1, x0, #3
diff --git a/tools/testing/selftests/arm64/fp/za-test.S b/tools/testing/selftests/arm64/fp/za-test.S
index a0165a81a937..8d449e114301 100644
--- a/tools/testing/selftests/arm64/fp/za-test.S
+++ b/tools/testing/selftests/arm64/fp/za-test.S
@@ -10,7 +10,6 @@
// (leave it running for as long as you want...)
// kill $pids
-#include <asm/unistd.h>
#include "assembler.h"
#include "asm-offsets.h"
#include "sme-inst.h"
@@ -203,10 +202,6 @@ function main
subs x21, x21, #1
b.ne 0b
- mov x8, #__NR_sched_yield // encourage preemption
-1:
- svc #0
-
mrs x0, S3_3_C4_C2_2 // SVCR should have ZA=1,SM=0
and x1, x0, #3
cmp x1, #2
diff --git a/tools/testing/selftests/arm64/fp/zt-test.S b/tools/testing/selftests/arm64/fp/zt-test.S
index 1c7db1298fb1..2333e72760a5 100644
--- a/tools/testing/selftests/arm64/fp/zt-test.S
+++ b/tools/testing/selftests/arm64/fp/zt-test.S
@@ -6,7 +6,6 @@
// Repeatedly writes unique test patterns into ZT0
// and reads them back to verify integrity.
-#include <asm/unistd.h>
#include "assembler.h"
#include "asm-offsets.h"
#include "sme-inst.h"
@@ -143,9 +142,6 @@ function main
mov x1, x22
bl setup_zt
- mov x8, #__NR_sched_yield // Encourage preemption
- svc #0
-
mrs x0, S3_3_C4_C2_2 // SVCR should have ZA=1,SM=0
and x1, x0, #3
cmp x1, #2
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 08/11] kselftest/arm64: Add a very simple VMM for use in fp-stress
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (6 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 07/11] kselftest/arm64: Remove the sched_yield()s from the fp-stress loads Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 09/11] kselftest/arm64: Build KVM guest versions of the fp-stress loads Mark Brown
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
In order to have fp-stress cover the interaction of KVM guests with the
rest of the floating point state management we need to provide some KVM
guest versions of the test loads that fp-stress runs. fp-stress itself
expects to start test loads which continually load, save and verify the
floating point state, shutting down when requested via signals or if
they detect an error. The host versions of these are written in
assembler and all follow a very similar template.
Provide a simple VMM for running under fp-stress which loads a flat
binary at a fixed address and runs it. The guest can output characters
by writing to a MMIO address and can report errors by using PSCI to
request a shutdown. If fp-stress sends a signal requesting a shutdown
the program exits. We allocate some extra space after the test binary
for the program to use for it's stack, and provide a handler for guest
exceptions which logs the exception state based on the guest executing
BRK in all exception vectors.
fp-stress uses SIGUSR1 signals to peturb the scheduling and exercise the
signal handling paths, we add a handler for this which simply counts the
number of signals delivered (as we do in the host versions). Exiting
the guest to userspace to handle the signals should still provide some
disruption, it would be better to do something more extensive like
saving and restoring at least the floating point register state in the
handler but we don't do that here. We also include SIGUSR2 in the
count, this is also supported by the host versions as it can be useful
in manual testing.
Command line options are provided for enabling pKVM, NV and SVE. With
NV we start the guest at EL2 in VHE mode so the guest doesn't need to do
anything EL specific. For SVE we configure the VL at the VMM level and
set all bits in ZCR_ELx.LEN so KVM is context switching the VL that is
configured. We configure FPEN and ZEN in CAPCR_ELx for the guest
because it's a little simpler than doing anything feature conditional in
guest code. We don't do any validation of the combination of options
that is provided, we rely on the caller for that.
On the host the test programs differentiate data between instances by
using the PID, they store this in x20 so we prefill this for them with
our own PID. The programs also log the number of iterations they have
run on exit, storing the number of iterations in x22. When the host
requests an exit we log this value as part of a similar format clean
exit message to that generated by the host programs.
We don't use the generated sysreg-defs.h but instead replicate a few
defines from there, this is because an empty stub stub introduced as
part of 8bc7c5e52558 (selftests: vDSO: don't include generated headers
for chacha test) breaks that. This will be looked at separately, we
should fix it independently of this work.
This is all very much specialised for fp-stress - the VMM hard codes
specifics of register allocation in the guest code it, implements the
signal and exit code interface that fp-stress expects and we're not
making much effort to handle anything surprising the guest might to.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/.gitignore | 1 +
tools/testing/selftests/arm64/fp/Makefile | 9 +-
tools/testing/selftests/arm64/fp/fp-stress-vmm.c | 596 +++++++++++++++++++++++
tools/testing/selftests/arm64/fp/fp-stress-vmm.h | 17 +
4 files changed, 622 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/arm64/fp/.gitignore b/tools/testing/selftests/arm64/fp/.gitignore
index 8362e7ec35ad..229a5c65b65f 100644
--- a/tools/testing/selftests/arm64/fp/.gitignore
+++ b/tools/testing/selftests/arm64/fp/.gitignore
@@ -1,6 +1,7 @@
fp-pidbench
fp-ptrace
fp-stress
+fp-stress-vmm
fpsimd-test
kernel-test
rdvl-sme
diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile
index d92a755837ff..3c4e17a34776 100644
--- a/tools/testing/selftests/arm64/fp/Makefile
+++ b/tools/testing/selftests/arm64/fp/Makefile
@@ -11,7 +11,7 @@ TEST_GEN_PROGS := \
sve-ptrace sve-probe-vls \
vec-syscfg \
za-fork za-ptrace
-TEST_GEN_PROGS_EXTENDED := fp-pidbench fpsimd-test \
+TEST_GEN_PROGS_EXTENDED := fp-pidbench fp-stress-vmm fpsimd-test \
kernel-test \
rdvl-sme rdvl-sve \
sve-test \
@@ -27,6 +27,13 @@ EXTRA_CLEAN += $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o \
ASM_UTILS_LINUX := $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o
+# For sysreg definitons
+ARCH_INCLUDES := $(KHDR_INCLUDES) -I$(top_srcdir)/tools/arch/arm64/include \
+ -I$(top_srcdir)/tools/include \
+ -I$(top_srcdir)/tools/include/generated
+
+$(OUTPUT)/fp-stress-vmm: CFLAGS += $(ARCH_INCLUDES)
+
# Build with nolibc to avoid effects due to libc's clone() support
$(OUTPUT)/fp-pidbench: fp-pidbench.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
diff --git a/tools/testing/selftests/arm64/fp/fp-stress-vmm.c b/tools/testing/selftests/arm64/fp/fp-stress-vmm.c
new file mode 100644
index 000000000000..4c5cfa43a170
--- /dev/null
+++ b/tools/testing/selftests/arm64/fp/fp-stress-vmm.c
@@ -0,0 +1,596 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Minimal arm64 KVM VMM used to run fp-stress test loads in VMs.
+ *
+ * Runs a single vCPU guest loaded from a flat binary read from a file, with
+ * PSCI 0.2 enabled so the guest can shut itself down via SYSTEM_OFF and a
+ * simple MMIO console for guest output.
+ *
+ * Unhandled guest exceptions are reported via the guest's vector
+ * table: each entry is a BRK instruction and we use software * breakpoint
+ * debugging to us with KVM_EXIT_DEBUG.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <linux/kvm.h>
+
+#include <asm/sigcontext.h>
+
+#include <asm/brk-imm.h>
+#include <asm/esr.h>
+#include <asm/sysreg.h>
+
+#include "fp-stress-vmm.h"
+
+#define ARM64_CORE_REG(name) \
+ (KVM_REG_ARM64 | KVM_REG_SIZE_U64 | KVM_REG_ARM_CORE | \
+ KVM_REG_ARM_CORE_REG(name))
+
+#define CPACR_EL1 ARM64_SYS_REG(3, 0, 1, 0, 2)
+#define CPTR_EL2 ARM64_SYS_REG(3, 4, 1, 1, 2)
+#define ELR_EL2 ARM64_SYS_REG(3, 4, 4, 0, 1)
+#define ESR_EL1 ARM64_SYS_REG(3, 0, 5, 2, 0)
+#define ESR_EL2 ARM64_SYS_REG(3, 4, 5, 2, 0)
+#define FAR_EL1 ARM64_SYS_REG(3, 0, 6, 0, 0)
+#define FAR_EL2 ARM64_SYS_REG(3, 4, 6, 0, 0)
+#define HCR_EL2 ARM64_SYS_REG(3, 4, 1, 1, 0)
+#define ZCR_EL1 ARM64_SYS_REG(3, 0, 1, 2, 0)
+#define ZCR_EL2 ARM64_SYS_REG(3, 4, 1, 2, 0)
+
+/* Both halves of the field, as the guest's own no-trapping value */
+#define CPACR_EL1_FPEN (CPACR_EL1_FPEN_EL1EN | CPACR_EL1_FPEN_EL0EN)
+#define CPACR_EL1_ZEN (CPACR_EL1_ZEN_EL1EN | CPACR_EL1_ZEN_EL0EN)
+
+/* We should fix the build system to let us get these from sysreg-defs.h */
+#define HCR_EL2_E2H BIT(34)
+#define ZCR_ELx_LEN 0xf
+
+/* VGIC MMIO ranges, below the console and guest RAM */
+#define VGIC_DIST_BASE 0x08000000
+#define VGIC_REDIST_BASE 0x08010000
+
+/* The vector length set is a bitmap of vector quadwords, index into it */
+#define vq_word(vq) (((vq) - KVM_ARM64_SVE_VQ_MIN) / 64)
+#define vq_mask(vq) (1ULL << (((vq) - KVM_ARM64_SVE_VQ_MIN) % 64))
+#define vq_present(vqs, vq) (!!((vqs)[vq_word(vq)] & vq_mask(vq)))
+
+/* The guest's vector entries BRK with their entry number as immediate */
+#define NR_VECTORS 16
+
+/* The vCPU our signal handler reports the state of, -1 until we have one */
+static int vcpu_fd = -1;
+
+static bool guest_pkvm;
+static bool guest_el2;
+
+/* SVE vector length in bits for the guest, 0 for no SVE, from --sve */
+static int guest_sve_vl;
+
+static const char *const vector_origins[] = {
+ "current EL, SP_EL0",
+ "current EL, SP_ELx",
+ "lower EL, AArch64",
+ "lower EL, AArch32",
+};
+
+static const char *const vector_kinds[] = {
+ "synchronous", "IRQ", "FIQ", "SError",
+};
+
+static void __attribute__((noreturn, format(printf, 1, 2)))
+die(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ putchar('\n');
+
+ exit(EXIT_FAILURE);
+}
+
+static void __attribute__((noreturn)) die_perror(const char *what)
+{
+ die("%s: %d (%s)", what, errno, strerror(errno));
+}
+
+static void *load_guest(const char *path, size_t *ram_size)
+{
+ struct stat st;
+ long page_size;
+ ssize_t ret;
+ size_t off;
+ void *ram;
+ int fd;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ die("%s: %s", path, strerror(errno));
+
+ if (fstat(fd, &st))
+ die_perror("fstat");
+
+ if (!st.st_size)
+ die("%s: image is empty", path);
+
+ page_size = sysconf(_SC_PAGESIZE);
+ if (page_size < 0)
+ die_perror("sysconf(_SC_PAGESIZE)");
+
+ /* Guest memory is allocated in whole pages */
+ *ram_size = ((size_t)st.st_size + GUEST_EXTRA_RAM + page_size - 1) &
+ ~((size_t)page_size - 1);
+
+ ram = mmap(NULL, *ram_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (ram == MAP_FAILED)
+ die_perror("mmap(guest RAM)");
+
+ for (off = 0; off < (size_t)st.st_size; off += ret) {
+ ret = read(fd, (char *)ram + off, st.st_size - off);
+ if (ret < 0)
+ die_perror("read");
+ if (!ret)
+ die("%s: short read", path);
+ }
+
+ close(fd);
+
+ return ram;
+}
+
+static uint64_t get_reg(int vcpu_fd, uint64_t id)
+{
+ uint64_t val;
+ struct kvm_one_reg reg = {
+ .id = id,
+ .addr = (uint64_t)&val,
+ };
+
+ if (ioctl(vcpu_fd, KVM_GET_ONE_REG, ®))
+ die_perror("KVM_GET_ONE_REG");
+
+ return val;
+}
+
+/*
+ * The guest shouldn't be doing anything that results in us seeing an
+ * exception, report anything we do see as an error.
+ */
+static void __attribute__((noreturn))
+report_guest_exception(int vcpu_fd, unsigned int vector)
+{
+ uint64_t esr, elr, far, ec;
+ int el;
+
+ if (guest_el2) {
+ esr = get_reg(vcpu_fd, ESR_EL2);
+ elr = get_reg(vcpu_fd, ELR_EL2);
+ far = get_reg(vcpu_fd, FAR_EL2);
+ el = 2;
+ } else {
+ esr = get_reg(vcpu_fd, ESR_EL1);
+ elr = get_reg(vcpu_fd, ARM64_CORE_REG(elr_el1));
+ far = get_reg(vcpu_fd, FAR_EL1);
+ el = 1;
+ }
+
+ ec = ESR_ELx_EC(esr);
+
+ printf("Unhandled guest exception: %s, %s\n",
+ vector_kinds[vector % 4], vector_origins[vector / 4]);
+ printf(" ESR_EL%d 0x%016lx (EC 0x%02lx)\n", el, esr, ec);
+ printf(" ELR_EL%d 0x%016lx FAR_EL%d 0x%016lx\n", el, elr, el, far);
+
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * The natively built loads are poked with SIGUSR1 and SIGUSR2 while they
+ * run and count what they were sent in x23. Those signals come to us
+ * rather than the guest so keep the count out here instead, the guest
+ * leaves its own x23 at zero.
+ */
+static volatile sig_atomic_t signal_count;
+
+static void handle_count_signal(int sig)
+{
+ signal_count++;
+}
+
+/*
+ * We were asked to stop, report where the guest got to and exit
+ * successfully in the same form as the natively built loads do from
+ * their own terminate handler. They keep their iteration count in x22.
+ */
+static void handle_exit_signal(int sig)
+{
+ uint64_t iterations = 0;
+ char buf[128];
+ int len;
+
+ /* We can be signalled before there is a vCPU */
+ if (vcpu_fd >= 0)
+ iterations = get_reg(vcpu_fd, ARM64_CORE_REG(regs.regs[22]));
+
+ len = snprintf(buf, sizeof(buf),
+ "Terminated by signal %d, iterations=%llu, signals=%llu\n",
+ sig, (unsigned long long)iterations,
+ (unsigned long long)signal_count);
+ write(STDOUT_FILENO, buf, len);
+
+ _exit(EXIT_SUCCESS);
+}
+
+/*
+ * Arrange for SIGTERM and SIGINT to stop us, reporting where the guest
+ * got to rather than dying on the spot, and for SIGUSR1 and SIGUSR2 to
+ * be counted. Done before we have a VM so that we do not die by default
+ * action if we are signalled while setting one up.
+ */
+static void setup_signals(void)
+{
+ struct sigaction stop = { .sa_handler = handle_exit_signal };
+ struct sigaction count = { .sa_handler = handle_count_signal };
+
+ sigemptyset(&stop.sa_mask);
+ if (sigaction(SIGTERM, &stop, NULL) || sigaction(SIGINT, &stop, NULL))
+ die_perror("sigaction");
+
+ sigemptyset(&count.sa_mask);
+ sigaddset(&count.sa_mask, SIGUSR1);
+ sigaddset(&count.sa_mask, SIGUSR2);
+ if (sigaction(SIGUSR1, &count, NULL) || sigaction(SIGUSR2, &count, NULL))
+ die_perror("sigaction");
+}
+
+static void set_reg(int vcpu_fd, uint64_t id, uint64_t val)
+{
+ struct kvm_one_reg reg = {
+ .id = id,
+ .addr = (uint64_t)&val,
+ };
+
+ if (ioctl(vcpu_fd, KVM_SET_ONE_REG, ®))
+ die_perror("KVM_SET_ONE_REG");
+}
+
+static void set_device_attr(int fd, uint32_t group, uint64_t attr,
+ void *addr)
+{
+ struct kvm_device_attr device_attr = {
+ .group = group,
+ .attr = attr,
+ .addr = (uint64_t)addr,
+ };
+
+ if (ioctl(fd, KVM_SET_DEVICE_ATTR, &device_attr))
+ die_perror("KVM_SET_DEVICE_ATTR");
+}
+
+/*
+ * For NV we need a vGIC since KVM uses it to provide the virtual
+ * maintenance interrupt,
+ */
+static void setup_vgic(int vm_fd)
+{
+ struct kvm_create_device device = {
+ .type = KVM_DEV_TYPE_ARM_VGIC_V3,
+ };
+ uint64_t addr;
+
+ if (ioctl(vm_fd, KVM_CREATE_DEVICE, &device))
+ die_perror("KVM_CREATE_DEVICE(VGICv3)");
+
+ addr = VGIC_DIST_BASE;
+ set_device_attr(device.fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_DIST, &addr);
+
+ addr = VGIC_REDIST_BASE;
+ set_device_attr(device.fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr);
+
+ set_device_attr(device.fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
+ close(device.fd);
+}
+
+/*
+ * Tell KVM which vector lengths the guest may use. It can only limit
+ * the maximum rather than hide individual VLs, the set we ask for has to
+ * match the ones the host has exactly up to that maximum, so start from
+ * what KVM is offering us and mask off everything above what we want.
+ */
+static void setup_sve(int vl)
+{
+ __u64 vqs[KVM_ARM64_SVE_VLS_WORDS];
+ struct kvm_one_reg reg = {
+ .id = KVM_REG_ARM64_SVE_VLS,
+ .addr = (uint64_t)vqs,
+ };
+ /* Our VLs are in bits, the vector quadword helpers work in bytes */
+ unsigned int max_vq = sve_vq_from_vl(vl / 8);
+ int feature = KVM_ARM_VCPU_SVE;
+ unsigned int vq;
+
+ if (ioctl(vcpu_fd, KVM_GET_ONE_REG, ®))
+ die_perror("KVM_GET_ONE_REG(SVE_VLS)");
+
+ if (!vq_present(vqs, max_vq))
+ die("KVM has no SVE VL %d", vl);
+
+ for (vq = max_vq + 1; vq <= KVM_ARM64_SVE_VQ_MAX; vq++)
+ vqs[vq_word(vq)] &= ~vq_mask(vq);
+
+ if (ioctl(vcpu_fd, KVM_SET_ONE_REG, ®))
+ die_perror("KVM_SET_ONE_REG(SVE_VLS)");
+
+ if (ioctl(vcpu_fd, KVM_ARM_VCPU_FINALIZE, &feature))
+ die_perror("KVM_ARM_VCPU_FINALIZE(SVE)");
+
+ /* Rely on the VM level configuration to constrain the VL */
+ if (guest_el2)
+ set_reg(vcpu_fd, ZCR_EL2, ZCR_ELx_LEN);
+ set_reg(vcpu_fd, ZCR_EL1, ZCR_ELx_LEN);
+}
+
+static void setup_vm(const char *path, struct kvm_run **run)
+{
+ struct kvm_userspace_memory_region mem;
+ struct kvm_guest_debug dbg;
+ struct kvm_vcpu_init init;
+ uint64_t cpacr, pstate;
+ unsigned long vm_type;
+ int kvm_fd, vm_fd;
+ size_t ram_size;
+ long mmap_size;
+ void *ram;
+
+ kvm_fd = open("/dev/kvm", O_RDWR | O_CLOEXEC);
+ if (kvm_fd < 0)
+ die_perror("/dev/kvm");
+
+ if (ioctl(kvm_fd, KVM_GET_API_VERSION, 0) != KVM_API_VERSION)
+ die("KVM API version mismatch");
+
+ if (guest_pkvm)
+ vm_type = KVM_VM_TYPE_ARM_PROTECTED;
+ else
+ vm_type = 0;
+ vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, vm_type);
+ if (vm_fd < 0)
+ die_perror("KVM_CREATE_VM");
+
+ ram = load_guest(path, &ram_size);
+
+ mem = (struct kvm_userspace_memory_region) {
+ .slot = 0,
+ .guest_phys_addr = GUEST_RAM_BASE,
+ .memory_size = ram_size,
+ .userspace_addr = (uint64_t)ram,
+ };
+ if (ioctl(vm_fd, KVM_SET_USER_MEMORY_REGION, &mem))
+ die_perror("KVM_SET_USER_MEMORY_REGION");
+
+ vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0);
+ if (vcpu_fd < 0)
+ die_perror("KVM_CREATE_VCPU");
+
+ if (ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &init))
+ die_perror("KVM_ARM_PREFERRED_TARGET");
+
+ if (!ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_PSCI_0_2))
+ die("KVM_CAP_ARM_PSCI_0_2 not supported");
+ init.features[0] |= 1UL << KVM_ARM_VCPU_PSCI_0_2;
+
+ /*
+ * Running the guest at EL2 gets us coverage of the nested paths
+ * through the floating point context switching.
+ */
+ if (guest_el2) {
+ if (!ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_EL2))
+ die("KVM_CAP_ARM_EL2 not supported");
+ init.features[0] |= 1UL << KVM_ARM_VCPU_HAS_EL2;
+ }
+
+ if (guest_sve_vl) {
+ if (!ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_SVE))
+ die("KVM_CAP_ARM_SVE not supported");
+ init.features[0] |= 1UL << KVM_ARM_VCPU_SVE;
+ }
+
+ if (ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &init))
+ die_perror("KVM_ARM_VCPU_INIT");
+
+ /*
+ * A vGIC is only needed for EL2 guests so skip otherwise,
+ * avoids issues with configuring on GICv2 systems.
+ */
+ if (guest_el2)
+ setup_vgic(vm_fd);
+
+ if (guest_sve_vl)
+ setup_sve(guest_sve_vl);
+
+ set_reg(vcpu_fd, ARM64_CORE_REG(regs.pc), GUEST_RAM_BASE);
+
+ /*
+ * Disable FP traps for the guest, we set things up here since it's
+ * easier than looking at ID registers in the guest.
+ */
+ if (guest_el2) {
+ cpacr = get_reg(vcpu_fd, CPTR_EL2) | CPACR_EL1_FPEN;
+ if (guest_sve_vl)
+ cpacr |= CPACR_EL1_ZEN;
+ set_reg(vcpu_fd, CPTR_EL2, cpacr);
+
+ }
+ cpacr = get_reg(vcpu_fd, CPACR_EL1) | CPACR_EL1_FPEN;
+ if (guest_sve_vl)
+ cpacr |= CPACR_EL1_ZEN;
+ set_reg(vcpu_fd, CPACR_EL1, cpacr);
+
+ /*
+ * An EL2 guest needs to be in VHE mode, everything it sets up
+ * uses the EL1 registers and those only reach the EL2 ones it is
+ * really running on while E2H is set. KVM only sets E2H for us
+ * at reset on hardware without FEAT_E2H0.
+ */
+ if (guest_el2)
+ set_reg(vcpu_fd, HCR_EL2,
+ get_reg(vcpu_fd, HCR_EL2) | HCR_EL2_E2H);
+
+ /*
+ * Guests can't use getpid() to disambiguate their data, let's call
+ * it for them.
+ */
+ set_reg(vcpu_fd, ARM64_CORE_REG(regs.regs[20]), getpid());
+
+ /*
+ * Trap guest BRKs to us so the guest's vector stubs can report
+ * unhandled exceptions.
+ */
+ if (!ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_SET_GUEST_DEBUG))
+ die("KVM_CAP_SET_GUEST_DEBUG not supported");
+
+ dbg = (struct kvm_guest_debug) {
+ .control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP,
+ };
+ if (ioctl(vcpu_fd, KVM_SET_GUEST_DEBUG, &dbg))
+ die_perror("KVM_SET_GUEST_DEBUG");
+
+ mmap_size = ioctl(kvm_fd, KVM_GET_VCPU_MMAP_SIZE, 0);
+ if (mmap_size < 0)
+ die_perror("KVM_GET_VCPU_MMAP_SIZE");
+
+ *run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ vcpu_fd, 0);
+ if (*run == MAP_FAILED)
+ die_perror("mmap(kvm_run)");
+
+ /* Mask everything by default, and start in the expected mode */
+ pstate = PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT;
+ if (guest_el2)
+ pstate |= PSR_MODE_EL2h;
+ else
+ pstate |= PSR_MODE_EL1h;
+ set_reg(vcpu_fd, ARM64_CORE_REG(regs.pstate), pstate);
+}
+
+static const struct option options[] = {
+ { "el1", no_argument, NULL, 'e' },
+ { "el2", no_argument, NULL, 'E' },
+ { "protected", no_argument, NULL, 'p' },
+ { "sve", required_argument, NULL, 's' },
+ { }
+};
+
+int main(int argc, char *argv[])
+{
+ struct kvm_run *run;
+ int c;
+
+ while ((c = getopt_long(argc, argv, "es:", options, NULL)) != -1) {
+ switch (c) {
+ case 'e':
+ break;
+ case 'E':
+ guest_el2 = true;
+ break;
+ case 'p':
+ guest_pkvm = true;
+ break;
+ case 's':
+ if (sscanf(optarg, "%d", &guest_sve_vl) != 1)
+ die("Failed to parse SVE VL %s", optarg);
+ if (guest_sve_vl < 128 || guest_sve_vl % 128 ||
+ guest_sve_vl / 8 > __SVE_VL_MAX)
+ die("Invalid SVE VL %d", guest_sve_vl);
+ break;
+ default:
+ die("Usage: %s [--el2] [--sve VL] <guest flat binary>",
+ argv[0]);
+ }
+ }
+
+ if (optind != argc - 1)
+ die("Usage: %s [--el2] [--protected] [--sve VL] <guest image>",
+ argv[0]);
+
+ /* Ensure output is unbuffered to get it to the monitor promptly */
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+ setup_signals();
+
+ setup_vm(argv[optind], &run);
+
+ for (;;) {
+ if (ioctl(vcpu_fd, KVM_RUN, 0)) {
+ /*
+ * The signals we handle do not come back here,
+ * anything else that interrupts us can just go
+ * round again.
+ */
+ if (errno == EINTR)
+ continue;
+
+ die_perror("KVM_RUN");
+ }
+
+ switch (run->exit_reason) {
+ case KVM_EXIT_MMIO:
+ if (!run->mmio.is_write ||
+ run->mmio.phys_addr != CONSOLE_BASE ||
+ run->mmio.len != 1)
+ die("Unhandled MMIO %s at 0x%llx len %u",
+ run->mmio.is_write ? "write" : "read",
+ run->mmio.phys_addr, run->mmio.len);
+
+ putchar(run->mmio.data[0]);
+ break;
+ case KVM_EXIT_DEBUG: {
+ uint32_t esr = run->debug.arch.hsr;
+
+ if (ESR_ELx_EC(esr) != ESR_ELx_EC_BRK64 ||
+ esr_brk_comment(esr) >= NR_VECTORS)
+ die("Unexpected debug exit, ESR 0x%x", esr);
+
+ report_guest_exception(vcpu_fd, esr_brk_comment(esr));
+ }
+ case KVM_EXIT_SYSTEM_EVENT:
+ switch (run->system_event.type) {
+ case KVM_SYSTEM_EVENT_SHUTDOWN:
+ /*
+ * The guest should report errors by logging
+ * and shutting down, add our own log in case
+ * we get here without a log.
+ */
+ die("Guest requested shutdown");
+ default:
+ die("Unexpected system event %u",
+ run->system_event.type);
+ }
+ break;
+ default:
+ die("Unexpected exit reason %u", run->exit_reason);
+ }
+ }
+
+ /* We should never get here */
+ return EXIT_FAILURE;
+}
diff --git a/tools/testing/selftests/arm64/fp/fp-stress-vmm.h b/tools/testing/selftests/arm64/fp/fp-stress-vmm.h
new file mode 100644
index 000000000000..cdf96b617db5
--- /dev/null
+++ b/tools/testing/selftests/arm64/fp/fp-stress-vmm.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#ifndef FP_STRESS_H
+#define FP_STRESS_H
+
+#define GUEST_RAM_BASE 0x80000000
+
+/* For stack */
+#define GUEST_EXTRA_RAM (1024 * 1024)
+
+/*
+ * Console: byte writes to CONSOLE_BASE are emitted on stdout. Not
+ * backed by a memslot, so guest accesses trap with KVM_EXIT_MMIO.
+ */
+#define CONSOLE_BASE 0x09000000
+
+#endif
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 09/11] kselftest/arm64: Build KVM guest versions of the fp-stress loads
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (7 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 08/11] kselftest/arm64: Add a very simple VMM for use in fp-stress Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 10/11] kselftest/arm64: Use execv() to start fp-stress test loads Mark Brown
2026-09-01 17:06 ` [PATCH 11/11] kselftest/arm64: Run KVM guests from fp-stress Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
Now that all the Linux specifics have been factored out of the load
programs that fp-stress uses and we have a simple VMM we can build
versions for running with that VMM. Provide an asm-utils-kvm.S with
startup code that's easier in the guest and implementations of the
functions that the programs call, writing output to the fixed address
expected by the VMM and using PSCI to shut down on error.
The programs are built into flat binaries to simplify the VMM, this is a
bit more Makefile effort but it's all fairly standard for bare metal
programming.
We install single instruction BRK handlers for all exceptions with the
expectation that the VMM will trap BRKs and log exception state, we do
not expect to trigger any exceptions in normal operation.
We log the current EL to help with diagnostics.
Since we're constantly reading and writing the contents of the floating
point registers to and from memory there is fairly heavy memory traffic.
We do not currently turn the MMU on so on hardware the number of
iterations a test does will be very much lower that that seen on the
host, this can be addressed in future.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/.gitignore | 3 +
tools/testing/selftests/arm64/fp/Makefile | 40 +++++++++-
tools/testing/selftests/arm64/fp/asm-utils-kvm.S | 86 ++++++++++++++++++++++
.../testing/selftests/arm64/fp/fp-stress-kvm.lds.S | 58 +++++++++++++++
4 files changed, 186 insertions(+), 1 deletion(-)
diff --git a/tools/testing/selftests/arm64/fp/.gitignore b/tools/testing/selftests/arm64/fp/.gitignore
index 229a5c65b65f..7a788b23ae76 100644
--- a/tools/testing/selftests/arm64/fp/.gitignore
+++ b/tools/testing/selftests/arm64/fp/.gitignore
@@ -1,6 +1,7 @@
fp-pidbench
fp-ptrace
fp-stress
+fp-stress-kvm.lds
fp-stress-vmm
fpsimd-test
kernel-test
@@ -17,3 +18,5 @@ za-ptrace
za-test
zt-ptrace
zt-test
+*-kvm.bin
+*-kvm.elf
diff --git a/tools/testing/selftests/arm64/fp/Makefile b/tools/testing/selftests/arm64/fp/Makefile
index 3c4e17a34776..5322cb66f0a9 100644
--- a/tools/testing/selftests/arm64/fp/Makefile
+++ b/tools/testing/selftests/arm64/fp/Makefile
@@ -21,17 +21,41 @@ TEST_GEN_PROGS_EXTENDED := fp-pidbench fp-stress-vmm fpsimd-test \
zt-test \
vlset
TEST_PROGS_EXTENDED := fpsimd-stress sve-stress ssve-stress za-stress
+TEST_GEN_FILES := \
+ fpsimd-test-kvm.bin \
+ sve-test-kvm.bin \
+ ssve-test-kvm.bin \
+ za-test-kvm.bin \
+ zt-test-kvm.bin
EXTRA_CLEAN += $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o \
- $(OUTPUT)/rdvl.o $(OUTPUT)/za-fork-asm.o
+ $(OUTPUT)/rdvl.o $(OUTPUT)/za-fork-asm.o \
+ $(patsubst %.bin,%.elf,$(TEST_GEN_FILES)) \
+ $(KVM_LDS)
ASM_UTILS_LINUX := $(OUTPUT)/asm-utils.o $(OUTPUT)/asm-utils-linux.o
+ASM_UTILS_KVM := asm-utils.S asm-utils-kvm.S
+
+OBJCOPY ?= $(CROSS_COMPILE)objcopy
# For sysreg definitons
ARCH_INCLUDES := $(KHDR_INCLUDES) -I$(top_srcdir)/tools/arch/arm64/include \
-I$(top_srcdir)/tools/include \
-I$(top_srcdir)/tools/include/generated
+# The guests run with no OS under them at all, link them as flat binaries
+# with the link script placing them where fp-stress-vmm puts their RAM.
+KVM_LDS := $(OUTPUT)/fp-stress-kvm.lds
+KVM_LDFLAGS := -nostdlib -static $(ARCH_INCLUDES) -D__ASSEMBLY__ \
+ -Wl,-T,$(KVM_LDS)
+
+# Supported/required from GNU ld 2.39, not confirmed LLD but after 22.1.2
+COMMA=,
+KVM_LDFLAGS += $(call try-run,$(LD) --warn-rwx-segments -v,-Wl$(COMMA)--warn-rwx-segments)
+
+$(KVM_LDS): fp-stress-kvm.lds.S fp-stress-vmm.h
+ $(CC) -E -P -x c $< -o $@
+
$(OUTPUT)/fp-stress-vmm: CFLAGS += $(ARCH_INCLUDES)
# Build with nolibc to avoid effects due to libc's clone() support
@@ -61,4 +85,18 @@ $(OUTPUT)/zt-ptrace: zt-ptrace.c
$(OUTPUT)/zt-test: zt-test.S $(ASM_UTILS_LINUX)
$(CC) -nostdlib $^ -o $@
+$(OUTPUT)/fpsimd-test-kvm.elf: fpsimd-test.S $(ASM_UTILS_KVM) $(KVM_LDS)
+ $(CC) $(KVM_LDFLAGS) $(filter %.S,$^) -o $@
+$(OUTPUT)/sve-test-kvm.elf: sve-test.S $(ASM_UTILS_KVM) $(KVM_LDS)
+ $(CC) $(KVM_LDFLAGS) $(filter %.S,$^) -o $@
+$(OUTPUT)/ssve-test-kvm.elf: sve-test.S $(ASM_UTILS_KVM) $(KVM_LDS)
+ $(CC) -DSSVE $(KVM_LDFLAGS) $(filter %.S,$^) -o $@
+$(OUTPUT)/za-test-kvm.elf: za-test.S $(ASM_UTILS_KVM) $(KVM_LDS)
+ $(CC) $(KVM_LDFLAGS) $(filter %.S,$^) -o $@
+$(OUTPUT)/zt-test-kvm.elf: zt-test.S $(ASM_UTILS_KVM) $(KVM_LDS)
+ $(CC) $(KVM_LDFLAGS) $(filter %.S,$^) -o $@
+
+$(OUTPUT)/%-kvm.bin: $(OUTPUT)/%-kvm.elf
+ $(OBJCOPY) -O binary $< $@
+
include ../../lib.mk
diff --git a/tools/testing/selftests/arm64/fp/asm-utils-kvm.S b/tools/testing/selftests/arm64/fp/asm-utils-kvm.S
new file mode 100644
index 000000000000..422d6425ab56
--- /dev/null
+++ b/tools/testing/selftests/arm64/fp/asm-utils-kvm.S
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Utility functions for assembly code programs run under fp-stress-vmm.
+//
+// Copyright 2026, Arm Ltd
+
+#include <asm/sysreg.h>
+
+#include "assembler.h"
+#include "asm-offsets.h"
+#include "fp-stress-vmm.h"
+
+#define PSCI_0_2_FN_SYSTEM_OFF 0x84000008
+
+// Print a single character x0 to the console
+// Clobbers x1
+function putc
+ movz x1, #(CONSOLE_BASE >> 16), lsl #16
+ strb w0, [x1]
+ ret
+endfunction
+.globl putc
+
+// Print a \0 terminated string starting at address x0 to the console.
+// Doesn't use putc as a slight optimisation.
+// Clobbers x0-x3
+function puts
+ mov x2, x0
+ movz x3, #(CONSOLE_BASE >> 16), lsl #16
+
+0: ldrb w0, [x2], #1
+ cbz w0, 1f
+ strb w0, [x3]
+ b 0b
+
+1: ret
+endfunction
+.globl puts
+
+// Report errors to the host by shutting down, there is no other reason one
+// of these test programs should exit.
+function exit_error
+ movz x0, #(PSCI_0_2_FN_SYSTEM_OFF >> 16), lsl #16
+ movk x0, #(PSCI_0_2_FN_SYSTEM_OFF & 0xffff)
+ hvc #0
+
+0: b 0b // PSCI should not have returned
+endfunction
+.globl exit_error
+
+// Exception vectors. We have no handlers for anything so each entry
+// just BRKs with its own number.
+ .balign 2048
+vectors:
+ .irp vector, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
+ .balign 0x80
+ brk #\vector
+ .endr
+
+// Main program entry point.
+//
+// The VMM starts us at the very start of the image so the link script
+// places .text.boot first.
+ .section .text.boot, "ax"
+.globl _start
+function _start
+ // Vectors that report any exception we take to the VMM.
+ adr x0, vectors
+ msr VBAR_EL1, x0
+
+ // The VMM sizes our RAM as the image plus GUEST_EXTRA_RAM, put the
+ // stack at the top of that so it grows down away from the image.
+ ldr x0, =_end
+ ldr x1, =GUEST_EXTRA_RAM
+ add x0, x0, x1
+ and x0, x0, #~15
+ mov sp, x0
+
+ // Log the current EL for diagnostic purposes.
+ puts "EL:\t\tEL"
+ mrs x0, CurrentEL
+ ubfx x0, x0, #2, #2
+ bl putdecn
+
+ b main
+endfunction
diff --git a/tools/testing/selftests/arm64/fp/fp-stress-kvm.lds.S b/tools/testing/selftests/arm64/fp/fp-stress-kvm.lds.S
new file mode 100644
index 000000000000..f1d44f0ae49c
--- /dev/null
+++ b/tools/testing/selftests/arm64/fp/fp-stress-kvm.lds.S
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Link script for the flat binary builds of the fp-stress loads which run
+ * as guests under fp-stress-vmm.
+ *
+ * The VMM loads the image at the start of the guest RAM and starts the
+ * guest there so the startup code has to be first in the image, it is in
+ * .text.boot for that.
+ */
+
+#include "fp-stress-vmm.h"
+
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+
+SECTIONS
+{
+ . = GUEST_RAM_BASE;
+
+ .text : {
+ *(.text.boot)
+ *(.text)
+ *(.text.*)
+ }
+
+ .rodata : {
+ *(.rodata)
+ *(.rodata.*)
+ }
+
+ .data : {
+ *(.data)
+ *(.data.*)
+ }
+
+ .bss : {
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ }
+
+ . = ALIGN(16);
+ _end = .;
+
+ /DISCARD/ : {
+ *(.note*)
+ *(.comment)
+ *(.eh_frame*)
+ }
+}
+
+/*
+ * The VMM sizes the guest RAM from the size of the image file and the
+ * guest puts its stack immediately above _end, anything allocated but not
+ * present in the file would leave the two disagreeing about where the RAM
+ * ends.
+ */
+ASSERT(SIZEOF(.bss) == 0, "fp-stress guests must not have a .bss")
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 10/11] kselftest/arm64: Use execv() to start fp-stress test loads
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (8 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 09/11] kselftest/arm64: Build KVM guest versions of the fp-stress loads Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
2026-09-01 17:06 ` [PATCH 11/11] kselftest/arm64: Run KVM guests from fp-stress Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
In preparation for adding KVM test loads which will take command line
arguments switch to using execv() instead of execl() to start the test
load programs.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/fp-stress.c | 26 ++++++++++++++++----------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/fp-stress.c b/tools/testing/selftests/arm64/fp/fp-stress.c
index 65e01aba96ff..e96fe1ac1ad1 100644
--- a/tools/testing/selftests/arm64/fp/fp-stress.c
+++ b/tools/testing/selftests/arm64/fp/fp-stress.c
@@ -60,7 +60,7 @@ static int num_processors(void)
return nproc;
}
-static void child_start(struct child_data *child, const char *program)
+static void child_start(struct child_data *child, char *const prog_args[])
{
int ret, pipefd[2], i;
struct epoll_event ev;
@@ -116,9 +116,9 @@ static void child_start(struct child_data *child, const char *program)
printf("%d bytes of data on startup pipe\n", ret);
close(3);
- ret = execl(program, program, NULL);
- printf("execl(%s) failed: %d (%s)\n",
- program, errno, strerror(errno));
+ ret = execv(prog_args[0], prog_args);
+ printf("execv(%s) failed: %d (%s)\n",
+ prog_args[0], errno, strerror(errno));
exit(EXIT_FAILURE);
} else {
@@ -310,32 +310,35 @@ static void handle_exit_signal(int sig, siginfo_t *info, void *context)
static void start_fpsimd(struct child_data *child, int cpu, int copy)
{
+ char *args[] = { "./fpsimd-test", NULL };
int ret;
ret = asprintf(&child->name, "FPSIMD-%d-%d", cpu, copy);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
- child_start(child, "./fpsimd-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
static void start_kernel(struct child_data *child, int cpu, int copy)
{
+ char *args[] = { "./kernel-test", NULL };
int ret;
ret = asprintf(&child->name, "KERNEL-%d-%d", cpu, copy);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
- child_start(child, "./kernel-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
static void start_sve(struct child_data *child, int vl, int cpu)
{
+ char *args[] = { "./sve-test", NULL };
int ret;
ret = prctl(PR_SVE_SET_VL, vl | PR_SVE_VL_INHERIT);
@@ -346,13 +349,14 @@ static void start_sve(struct child_data *child, int vl, int cpu)
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
- child_start(child, "./sve-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
static void start_ssve(struct child_data *child, int vl, int cpu)
{
+ char *args[] = { "./ssve-test", NULL };
int ret;
ret = asprintf(&child->name, "SSVE-VL-%d-%d", vl, cpu);
@@ -363,13 +367,14 @@ static void start_ssve(struct child_data *child, int vl, int cpu)
if (ret < 0)
ksft_exit_fail_msg("Failed to set SME VL %d\n", ret);
- child_start(child, "./ssve-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
static void start_za(struct child_data *child, int vl, int cpu)
{
+ char *args[] = { "./za-test", NULL };
int ret;
ret = prctl(PR_SME_SET_VL, vl | PR_SVE_VL_INHERIT);
@@ -380,20 +385,21 @@ static void start_za(struct child_data *child, int vl, int cpu)
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
- child_start(child, "./za-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
static void start_zt(struct child_data *child, int cpu)
{
+ char *args[] = { "./zt-test", NULL };
int ret;
ret = asprintf(&child->name, "ZT-%d", cpu);
if (ret == -1)
ksft_exit_fail_msg("asprintf() failed\n");
- child_start(child, "./zt-test");
+ child_start(child, args);
ksft_print_msg("Started %s\n", child->name);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 11/11] kselftest/arm64: Run KVM guests from fp-stress
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
` (9 preceding siblings ...)
2026-09-01 17:06 ` [PATCH 10/11] kselftest/arm64: Use execv() to start fp-stress test loads Mark Brown
@ 2026-09-01 17:06 ` Mark Brown
10 siblings, 0 replies; 12+ messages in thread
From: Mark Brown @ 2026-09-01 17:06 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, Shuah Khan
Cc: Marc Zyngier, Oliver Upton, Fuad Tabba, Mark Rutland,
linux-kernel, linux-arm-kernel, linux-kselftest, Mark Brown
Now that we have a VMM that integrates with fp-stress and the guest code to
run with it extend fp-stress to run KVM guests along with host test loads.
When we have access to KVM we start FPSIMD and SVE test loads in KVM on odd
numbered CPUs. If nested virtualisation is supported half of those will be
run in guests with EL2 support.
Although the VMM supports protected guests we do not currently start any
since doing so taints the kernel, having this happen as standard is
likely to lead to confusion if other tests fail on a system with pKVM
enabled.
If any non-virtualisable SVE VLs are detected we will skip SVE, currently no
such systems are known to exist. Should any be encountered the appropriate
enumeration code can be implemented.
Signed-off-by: Mark Brown <broonie@kernel.org>
---
tools/testing/selftests/arm64/fp/fp-stress.c | 204 ++++++++++++++++++++++++++-
1 file changed, 199 insertions(+), 5 deletions(-)
diff --git a/tools/testing/selftests/arm64/fp/fp-stress.c b/tools/testing/selftests/arm64/fp/fp-stress.c
index e96fe1ac1ad1..968983a5e39f 100644
--- a/tools/testing/selftests/arm64/fp/fp-stress.c
+++ b/tools/testing/selftests/arm64/fp/fp-stress.c
@@ -7,6 +7,7 @@
#define _POSIX_C_SOURCE 199309L
#include <errno.h>
+#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <signal.h>
@@ -18,11 +19,13 @@
#include <unistd.h>
#include <sys/auxv.h>
#include <sys/epoll.h>
+#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <asm/hwcap.h>
+#include <linux/kvm.h>
#include "kselftest.h"
@@ -46,6 +49,11 @@ static struct epoll_event *evs;
static int tests;
static int num_children;
static bool terminate;
+static int sve_vl_count, sme_vl_count;
+static int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
+static bool have_kvm;
+static bool have_kvm_el2;
+static bool have_kvm_sve;
static int startup_pipe[2];
@@ -60,6 +68,14 @@ static int num_processors(void)
return nproc;
}
+static char *kvm_el_arg(int cpu)
+{
+ if (have_kvm_el2 && ((cpu % 4) == 3))
+ return "--el2";
+ else
+ return "--el1";
+}
+
static void child_start(struct child_data *child, char *const prog_args[])
{
int ret, pipefd[2], i;
@@ -322,6 +338,21 @@ static void start_fpsimd(struct child_data *child, int cpu, int copy)
ksft_print_msg("Started %s\n", child->name);
}
+static void start_fpsimd_kvm(struct child_data *child, int cpu, int copy)
+{
+ char *args[] = { "./fp-stress-vmm", kvm_el_arg(cpu),
+ "./fpsimd-test-kvm.bin", NULL };
+ int ret;
+
+ ret = asprintf(&child->name, "FPSIMD-KVM-%d-%d", cpu, copy);
+ if (ret == -1)
+ ksft_exit_fail_msg("asprintf() failed\n");
+
+ child_start(child, args);
+
+ ksft_print_msg("Started %s\n", child->name);
+}
+
static void start_kernel(struct child_data *child, int cpu, int copy)
{
char *args[] = { "./kernel-test", NULL };
@@ -354,6 +385,26 @@ static void start_sve(struct child_data *child, int vl, int cpu)
ksft_print_msg("Started %s\n", child->name);
}
+static void start_sve_kvm(struct child_data *child, int vl, int cpu)
+{
+ char *args[] = { "./fp-stress-vmm", "--sve", NULL, kvm_el_arg(cpu),
+ "./sve-test-kvm.bin", NULL };
+ char vl_str[32];
+ int ret;
+
+ /* Our VLs are in bytes, fp-stress-vmm wants bits */
+ snprintf(vl_str, sizeof(vl_str), "%d", vl * 8);
+ args[2] = vl_str;
+
+ ret = asprintf(&child->name, "SVE-KVM-VL-%d-%d", vl, cpu);
+ if (ret == -1)
+ ksft_exit_fail_msg("asprintf() failed\n");
+
+ child_start(child, args);
+
+ ksft_print_msg("Started %s\n", child->name);
+}
+
static void start_ssve(struct child_data *child, int vl, int cpu)
{
char *args[] = { "./ssve-test", NULL };
@@ -404,6 +455,136 @@ static void start_zt(struct child_data *child, int cpu)
ksft_print_msg("Started %s\n", child->name);
}
+static void kvm_put_vcpu(int vm_fd, int vcpu_fd)
+{
+ if (vcpu_fd >= 0)
+ close(vcpu_fd);
+ if (vm_fd >= 0)
+ close(vm_fd);
+}
+
+static bool kvm_get_vcpu(int kvm_fd, int feature, int *vm_fd, int *vcpu_fd)
+{
+ struct kvm_vcpu_init init;
+ int ret;
+
+ *vcpu_fd = -1;
+
+ *vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0);
+ if (*vm_fd < 0) {
+ ksft_print_msg("KVM_CREATE_VM failed: %s (%d)\n",
+ strerror(errno), errno);
+ return false;
+ }
+
+ *vcpu_fd = ioctl(*vm_fd, KVM_CREATE_VCPU, 0);
+ if (*vcpu_fd < 0) {
+ ksft_print_msg("KVM_CREATE_VCPU failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto err;
+ }
+
+ ret = ioctl(*vm_fd, KVM_ARM_PREFERRED_TARGET, &init);
+ if (ret) {
+ ksft_print_msg("KVM_ARM_PREFERRED_TARGET failed: %s (%d)\n",
+ strerror(errno), errno);
+ goto err;
+ }
+
+ init.features[0] |= 1 << feature;
+ ret = ioctl(*vcpu_fd, KVM_ARM_VCPU_INIT, &init);
+ if (ret) {
+ ksft_print_msg("KVM_ARM_VCPU_INIT feature %d failed: %s (%d)\n",
+ feature, strerror(errno), errno);
+ goto err;
+ }
+
+ return true;
+
+err:
+ kvm_put_vcpu(*vm_fd, *vcpu_fd);
+ return false;
+}
+
+static void probe_kvm_sve(int kvm_fd)
+{
+ __u64 vqs[KVM_ARM64_SVE_VLS_WORDS];
+ struct kvm_one_reg reg = {
+ .id = KVM_REG_ARM64_SVE_VLS,
+ .addr = (__u64)vqs,
+ };
+ int vm_fd, vcpu_fd, i;
+ unsigned int vq;
+
+ if (!sve_vl_count)
+ return;
+
+ if (!ioctl(kvm_fd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_SVE)) {
+ ksft_print_msg("No KVM SVE support\n");
+ return;
+ }
+
+ if (!kvm_get_vcpu(kvm_fd, KVM_ARM_VCPU_SVE, &vm_fd, &vcpu_fd))
+ return;
+
+ /*
+ * The vector length set is readable without finalizing the vCPU,
+ * we only want to look at it rather than run anything.
+ */
+ if (ioctl(vcpu_fd, KVM_GET_ONE_REG, ®)) {
+ ksft_print_msg("Failed to read KVM SVE VLs: %s (%d)\n",
+ strerror(errno), errno);
+ goto out;
+ }
+
+ have_kvm_sve = true;
+ for (i = 0; i < sve_vl_count; i++) {
+ vq = sve_vq_from_vl(sve_vls[i]);
+
+ if (!(vqs[(vq - KVM_ARM64_SVE_VQ_MIN) / 64] &
+ (1ULL << ((vq - KVM_ARM64_SVE_VQ_MIN) % 64)))) {
+ ksft_print_msg("KVM has no SVE VL %d\n", sve_vls[i]);
+ have_kvm_sve = false;
+ }
+ }
+
+out:
+ kvm_put_vcpu(vm_fd, vcpu_fd);
+}
+
+/*
+ * We don't act as a VMM for guests directly, this just enumerates what
+ * can be run with KVM.
+ */
+static void probe_kvm(void)
+{
+ int fd, ret;
+
+ fd = open("/dev/kvm", O_RDWR | O_CLOEXEC);
+ if (fd < 0) {
+ ksft_print_msg("Not using KVM, /dev/kvm: %s (%d)\n",
+ strerror(errno), errno);
+ return;
+ }
+
+ ret = ioctl(fd, KVM_GET_API_VERSION, 0);
+ if (ret != KVM_API_VERSION) {
+ ksft_print_msg("Not using KVM, API version %d not %d\n",
+ ret, KVM_API_VERSION);
+ close(fd);
+ return;
+ }
+
+ have_kvm = true;
+
+ if (ioctl(fd, KVM_CHECK_EXTENSION, KVM_CAP_ARM_EL2))
+ have_kvm_el2 = true;
+
+ probe_kvm_sve(fd);
+
+ close(fd);
+}
+
static void probe_vls(int vls[], int *vl_count, int set_vl)
{
unsigned int vq;
@@ -460,10 +641,8 @@ int main(int argc, char **argv)
int timeout = 10 * (1000 / SIGNAL_INTERVAL_MS);
int poll_interval = 5000;
int cpus, i, j, c;
- int sve_vl_count, sme_vl_count;
bool all_children_started = false;
int seen_children;
- int sve_vls[MAX_VLS], sme_vls[MAX_VLS];
bool have_sme2;
struct sigaction sa;
@@ -509,9 +688,15 @@ int main(int argc, char **argv)
ksft_print_header();
ksft_set_plan(tests);
+ probe_kvm();
+
ksft_print_msg("%d CPUs, %d SVE VLs, %d SME VLs, SME2 %s\n",
cpus, sve_vl_count, sme_vl_count,
have_sme2 ? "present" : "absent");
+ ksft_print_msg("KVM: %s, %s EL2, %s SVE\n",
+ have_kvm ? "present" : "absent",
+ have_kvm_el2 ? "with" : "without",
+ have_kvm_sve ? "with" : "without");
if (timeout > 0)
ksft_print_msg("Will run for %d\n", timeout);
@@ -559,11 +744,20 @@ int main(int argc, char **argv)
tests);
for (i = 0; i < cpus; i++) {
- start_fpsimd(&children[num_children++], i, 0);
+ if (have_kvm && (i % 2))
+ start_fpsimd_kvm(&children[num_children++], i, 0);
+ else
+ start_fpsimd(&children[num_children++], i, 0);
start_kernel(&children[num_children++], i, 0);
- for (j = 0; j < sve_vl_count; j++)
- start_sve(&children[num_children++], sve_vls[j], i);
+ for (j = 0; j < sve_vl_count; j++) {
+ if (have_kvm_sve && (i % 2))
+ start_sve_kvm(&children[num_children++],
+ sve_vls[j], i);
+ else
+ start_sve(&children[num_children++],
+ sve_vls[j], i);
+ }
for (j = 0; j < sme_vl_count; j++) {
start_ssve(&children[num_children++], sme_vls[j], i);
--
2.47.3
^ permalink raw reply related [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-09-01 17:10 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-01 17:06 [PATCH 00/11] kselftest/arm64: Add coverage of interactions with KVM to fp-stress Mark Brown
2026-09-01 17:06 ` [PATCH 01/11] kselftest/arm64: Factor Linux syscalls out of asm-utils.S Mark Brown
2026-09-01 17:06 ` [PATCH 02/11] kselftest/arm64: Factor shared signal handlers out of fp-stress loads Mark Brown
2026-09-01 17:06 ` [PATCH 03/11] kselftest/arm64: Move exit calls " Mark Brown
2026-09-01 17:06 ` [PATCH 04/11] kselftest/arm64: Use exit_error() rather than SIGABRT in fp-stress Mark Brown
2026-09-01 17:06 ` [PATCH 05/11] kselftest/arm64: Exit with an error code on data mismatches " Mark Brown
2026-09-01 17:06 ` [PATCH 06/11] kselftest/arm64: Factor startup code out of fp-stress load programs Mark Brown
2026-09-01 17:06 ` [PATCH 07/11] kselftest/arm64: Remove the sched_yield()s from the fp-stress loads Mark Brown
2026-09-01 17:06 ` [PATCH 08/11] kselftest/arm64: Add a very simple VMM for use in fp-stress Mark Brown
2026-09-01 17:06 ` [PATCH 09/11] kselftest/arm64: Build KVM guest versions of the fp-stress loads Mark Brown
2026-09-01 17:06 ` [PATCH 10/11] kselftest/arm64: Use execv() to start fp-stress test loads Mark Brown
2026-09-01 17:06 ` [PATCH 11/11] kselftest/arm64: Run KVM guests from fp-stress Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox