Module Name:    src
Committed By:   riastradh
Date:           Thu Feb 27 00:55:32 UTC 2025

Modified Files:
        src/distrib/sets/lists/debug: mi
        src/distrib/sets/lists/tests: mi
        src/tests/kernel: Makefile
Added Files:
        src/tests/kernel: h_execregs_unimpl.c t_execregs.c
        src/tests/kernel/arch/aarch64: execregs.c execregs.h h_execregs.S
        src/tests/kernel/arch/hppa: execregs.c execregs.h h_execregs.S
        src/tests/kernel/arch/i386: execregs.c execregs.h h_execregs.S
        src/tests/kernel/arch/vax: execregs.c execregs.h h_execregs.S
        src/tests/kernel/arch/x86_64: execregs.c execregs.h h_execregs.S

Log Message:
Test whether exec/spawn will zero registers.

Currently implemented only for a handful of architectures; should
extend this to all the others, and extend as appropriate if we find
more register content is worth testing (like maybe vector registers,
but they are managed differently anyway and less likely to leak).

VAX test contributed (and tested) by Kalvis Duckmanton, with some
tweaks by me; the others written and tested by me.  IA64 skipped,
even though I suspect it _would_ leak if the kernel code ran as is,
because I have no way to test it.

PR kern/59084: exec/spawn leaks register content


To generate a diff of this commit:
cvs rdiff -u -r1.465 -r1.466 src/distrib/sets/lists/debug/mi
cvs rdiff -u -r1.1358 -r1.1359 src/distrib/sets/lists/tests/mi
cvs rdiff -u -r1.84 -r1.85 src/tests/kernel/Makefile
cvs rdiff -u -r0 -r1.1 src/tests/kernel/h_execregs_unimpl.c \
    src/tests/kernel/t_execregs.c
cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/aarch64/execregs.c \
    src/tests/kernel/arch/aarch64/execregs.h \
    src/tests/kernel/arch/aarch64/h_execregs.S
cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/hppa/execregs.c \
    src/tests/kernel/arch/hppa/execregs.h \
    src/tests/kernel/arch/hppa/h_execregs.S
cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/i386/execregs.c \
    src/tests/kernel/arch/i386/execregs.h \
    src/tests/kernel/arch/i386/h_execregs.S
cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/vax/execregs.c \
    src/tests/kernel/arch/vax/execregs.h \
    src/tests/kernel/arch/vax/h_execregs.S
cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/x86_64/execregs.c \
    src/tests/kernel/arch/x86_64/execregs.h \
    src/tests/kernel/arch/x86_64/h_execregs.S

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/debug/mi
diff -u src/distrib/sets/lists/debug/mi:1.465 src/distrib/sets/lists/debug/mi:1.466
--- src/distrib/sets/lists/debug/mi:1.465	Mon Feb 24 18:02:02 2025
+++ src/distrib/sets/lists/debug/mi	Thu Feb 27 00:55:31 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.465 2025/02/24 18:02:02 martin Exp $
+# $NetBSD: mi,v 1.466 2025/02/27 00:55:31 riastradh Exp $
 #
 ./etc/mtree/set.debug                           comp-sys-root
 ./usr/lib					comp-sys-usr		compatdir
@@ -1771,6 +1771,7 @@
 ./usr/libdata/debug/usr/tests/kernel/arch/i386/t_ptrace_waitid.debug	tests-obsolete		obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/arch/i386/t_ptrace_waitpid.debug	tests-obsolete		obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/h_cloexec.debug			tests-kernel-tests	debug,atf,compattestfile
+./usr/libdata/debug/usr/tests/kernel/h_execregs.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/h_fexecve.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/h_fpufork.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/h_getprocpath.debug		tests-kernel-tests	debug,atf,compattestfile
@@ -1804,6 +1805,7 @@
 ./usr/libdata/debug/usr/tests/kernel/posix_spawn/t_spawnattr.debug	tests-obsolete	obsolete,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_cloexec.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_epoll.debug			tests-obsolete	obsolete,compattestfile
+./usr/libdata/debug/usr/tests/kernel/t_execregs.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_extattrctl.debug			tests-kernel-tests	debug,atf,rump
 ./usr/libdata/debug/usr/tests/kernel/t_extent.debug			tests-kernel-tests	debug,atf,compattestfile
 ./usr/libdata/debug/usr/tests/kernel/t_fcntl.debug			tests-kernel-tests	debug,atf

Index: src/distrib/sets/lists/tests/mi
diff -u src/distrib/sets/lists/tests/mi:1.1358 src/distrib/sets/lists/tests/mi:1.1359
--- src/distrib/sets/lists/tests/mi:1.1358	Thu Jan 23 12:36:15 2025
+++ src/distrib/sets/lists/tests/mi	Thu Feb 27 00:55:31 2025
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1358 2025/01/23 12:36:15 christos Exp $
+# $NetBSD: mi,v 1.1359 2025/02/27 00:55:31 riastradh Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2269,6 +2269,7 @@
 ./usr/tests/kernel/arch/i386/t_ptrace_waitpid		tests-obsolete		obsolete
 ./usr/tests/kernel/arch/x86				tests-obsolete		obsolete
 ./usr/tests/kernel/h_cloexec				tests-kernel-tests	compattestfile,atf
+./usr/tests/kernel/h_execregs				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/h_fexecve				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/h_fpufork				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/h_getprocpath			tests-kernel-tests	compattestfile,atf
@@ -2316,6 +2317,7 @@
 ./usr/tests/kernel/posix_spawn/t_spawnattr		tests-obsolete		obsolete
 ./usr/tests/kernel/t_cloexec				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_epoll				tests-obsolete		obsolete
+./usr/tests/kernel/t_execregs				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_extattrctl				tests-kernel-tests	atf,rump
 ./usr/tests/kernel/t_extent				tests-kernel-tests	compattestfile,atf
 ./usr/tests/kernel/t_fcntl				tests-kernel-tests	atf

Index: src/tests/kernel/Makefile
diff -u src/tests/kernel/Makefile:1.84 src/tests/kernel/Makefile:1.85
--- src/tests/kernel/Makefile:1.84	Sun Dec 22 23:25:15 2024
+++ src/tests/kernel/Makefile	Thu Feb 27 00:55:31 2025
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.84 2024/12/22 23:25:15 riastradh Exp $
+# $NetBSD: Makefile,v 1.85 2025/02/27 00:55:31 riastradh Exp $
 
 NOMAN=		# defined
 
@@ -9,6 +9,7 @@ TESTSDIR=	${TESTSBASE}/kernel
 TESTS_SUBDIRS+=	kqueue
 TESTS_C+=	t_cloexec
 #TESTS_C+=	t_epoll
+TESTS_C+=	t_execregs
 TESTS_C+=	t_fcntl
 .if ${MKRUMP} != "no"
 TESTS_C+=	t_fdrestart
@@ -51,6 +52,7 @@ TESTS_SH+=	t_umount
 
 BINDIR=		${TESTSDIR}
 PROGS+=		h_cloexec
+PROGS+=		h_execregs
 PROGS+=		h_fexecve
 PROGS+=		h_fpufork
 PROGS+=		h_getprocpath
@@ -132,4 +134,16 @@ CLEANFILES+=	t_subr_prf.c
 
 LDADD.h_segv+=	-lm
 
+.if exists(arch/${MACHINE_ARCH}/execregs.h)
+ARCHDIR:=		${.PARSEDIR}/arch/${MACHINE_ARCH}
+.PATH:			${ARCHDIR}
+CPPFLAGS.t_execregs.c+=	-I${ARCHDIR}
+CPPFLAGS.t_execregs.c+=	-DHAVE_EXECREGS_TEST
+SRCS.t_execregs+=	t_execregs.c
+SRCS.t_execregs+=	execregs.c
+LDFLAGS.h_execregs+=	-static -Wl,-e,execregs_start
+.else
+SRCS.h_execregs=	h_execregs_unimpl.c
+.endif
+
 .include <bsd.test.mk>

Added files:

Index: src/tests/kernel/h_execregs_unimpl.c
diff -u /dev/null src/tests/kernel/h_execregs_unimpl.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/h_execregs_unimpl.c	Thu Feb 27 00:55:31 2025
@@ -0,0 +1,37 @@
+/*	$NetBSD: h_execregs_unimpl.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: h_execregs_unimpl.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $");
+
+int
+main(void)
+{
+
+	return 127;
+}
Index: src/tests/kernel/t_execregs.c
diff -u /dev/null src/tests/kernel/t_execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/t_execregs.c	Thu Feb 27 00:55:31 2025
@@ -0,0 +1,185 @@
+/*	$NetBSD: t_execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $");
+
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <err.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_EXECREGS_TEST
+
+#include "execregs.h"
+#include "h_macros.h"
+
+static void
+readregs(int rfd, register_t regs[static NEXECREGS])
+{
+	uint8_t *p;
+	size_t n;
+	ssize_t nread;
+
+	p = (void *)regs;
+	n = NEXECREGS*sizeof(regs[0]);
+	while (n) {
+		RL(nread = read(rfd, p, n));
+		ATF_CHECK_MSG((size_t)nread <= n,
+		    "overlong read: %zu > %zu", (size_t)nread, n);
+		if (nread == 0)
+			break;
+		p += (size_t)nread;
+		n -= (size_t)nread;
+	}
+	ATF_REQUIRE_EQ_MSG(n, 0,
+	    "truncated read, missing %zu bytes", n);
+}
+
+static void
+checkregs(const register_t regs[static NEXECREGS])
+{
+	unsigned i;
+
+#if defined(__hppa__) || \
+    defined(__ia64__) || \
+    defined(__vax__) || \
+    defined(__x86_64__)
+	atf_tc_expect_fail("PR kern/59084: exec/spawn leaks register content");
+#endif
+
+	for (i = 0; i < NEXECREGS; i++) {
+		if (regs[i] != 0) {
+			for (i = 0; i < NEXECREGS; i++) {
+				fprintf(stderr, "[%s] %"PRIxREGISTER"\n",
+				    regname[i], regs[i]);
+			}
+			fprintf(stderr, "\n");
+			atf_tc_fail("registers not zeroed");
+		}
+	}
+}
+
+static void
+testregs(int child, const int pipefd[static 2],
+    register_t regs[static NEXECREGS])
+{
+	int status;
+
+	RL(close(pipefd[1]));
+
+	readregs(pipefd[0], regs);
+
+	RL(waitpid(child, &status, 0));
+	ATF_CHECK_EQ_MSG(status, 0, "status=0x%x", status);
+
+	checkregs(regs);
+}
+
+#endif
+
+ATF_TC(execregszero);
+ATF_TC_HEAD(execregszero, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Test execve(2) zeroes registers");
+}
+ATF_TC_BODY(execregszero, tc)
+{
+#ifdef HAVE_EXECREGS_TEST
+	char h_execregs[PATH_MAX];
+	int pipefd[2];
+	register_t regs[NEXECREGS];
+	pid_t child;
+
+	RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
+		atf_tc_get_config_var(tc, "srcdir")));
+
+	RL(pipe(pipefd));
+	memset(regs, 0x5a, sizeof(regs));
+
+	RL(child = fork());
+	if (child == 0) {
+		if (dup2(pipefd[1], STDOUT_FILENO) == -1)
+			err(1, "dup2");
+		if (closefrom(STDERR_FILENO + 1) == -1)
+			err(1, "closefrom");
+		if (execregschild(h_execregs) == -1)
+			err(1, "execve");
+		_exit(2);
+	}
+
+	testregs(child, pipefd, regs);
+#else
+	atf_tc_skip("missing test for PR kern/59084:"
+	    " exec/spawn leaks register content");
+#endif
+}
+
+ATF_TC(spawnregszero);
+ATF_TC_HEAD(spawnregszero, tc)
+{
+	atf_tc_set_md_var(tc, "descr",
+	    "Test posix_spawn(2) zeroes registers");
+}
+ATF_TC_BODY(spawnregszero, tc)
+{
+#ifdef HAVE_EXECREGS_TEST
+	char h_execregs[PATH_MAX];
+	int pipefd[2];
+	register_t regs[NEXECREGS];
+	pid_t child;
+
+	RL(snprintf(h_execregs, sizeof(h_execregs), "%s/h_execregs",
+		atf_tc_get_config_var(tc, "srcdir")));
+
+	RL(pipe(pipefd));
+	memset(regs, 0x5a, sizeof(regs));
+
+	RL(child = spawnregschild(h_execregs, pipefd[1]));
+
+	testregs(child, pipefd, regs);
+#else
+	atf_tc_skip("missing test for PR kern/59084:"
+	    " exec/spawn leaks register content");
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+	ATF_TP_ADD_TC(tp, execregszero);
+	ATF_TP_ADD_TC(tp, spawnregszero);
+
+	return atf_no_error();
+}

Index: src/tests/kernel/arch/aarch64/execregs.c
diff -u /dev/null src/tests/kernel/arch/aarch64/execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/aarch64/execregs.c	Thu Feb 27 00:55:31 2025
@@ -0,0 +1,226 @@
+/*	$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:31 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+	x |= x << 8;
+	x |= x << 16;
+	x |= x << 32;
+	return x;
+}
+
+int
+execregschild(char *path)
+{
+	/* x0: used to pass exec arg0, nonnull anyway (path) */
+	/* x1: used to pass exec arg1, nonnull anyway (argv) */
+	/* x2: used to pass exec arg2, nonnull anyway (environ) */
+	register long x3 __asm("x3") = nonnull(3);
+	register long x4 __asm("x4") = nonnull(4);
+	register long x5 __asm("x5") = nonnull(5);
+	register long x6 __asm("x6") = nonnull(6);
+	register long x7 __asm("x7") = nonnull(7);
+	register long x8 __asm("x8") = nonnull(8);
+	register long x9 __asm("x9") = nonnull(9);
+	register long x10 __asm("x10") = nonnull(10);
+	register long x11 __asm("x11") = nonnull(11);
+	register long x12 __asm("x12") = nonnull(12);
+	register long x13 __asm("x13") = nonnull(13);
+	register long x14 __asm("x14") = nonnull(14);
+	register long x15 __asm("x15") = nonnull(15);
+	register long x16 __asm("x16") = nonnull(16);
+	register long x17 __asm("x17") = nonnull(17);
+	register long x18 __asm("x18") = nonnull(18);
+	register long x19 __asm("x19") = nonnull(19);
+	register long x20 __asm("x20") = nonnull(20);
+	register long x21 __asm("x21") = nonnull(21);
+	register long x22 __asm("x22") = nonnull(22);
+	register long x23 __asm("x23") = nonnull(23);
+	register long x24 __asm("x24") = nonnull(24);
+	register long x25 __asm("x25") = nonnull(25);
+	register long x26 __asm("x26") = nonnull(26);
+	register long x27 __asm("x27") = nonnull(27);
+	register long x28 __asm("x28") = nonnull(28);
+	/* x29: frame pointer, nonnull anyway */
+	/* x30: link register, nonnull anyway */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(x3),
+	    "+r"(x4),
+	    "+r"(x5),
+	    "+r"(x6),
+	    "+r"(x7),
+	    "+r"(x8),
+	    "+r"(x9),
+	    "+r"(x10),
+	    "+r"(x11),
+	    "+r"(x12),
+	    "+r"(x13),
+	    "+r"(x14),
+	    "+r"(x15),
+	    "+r"(x16),
+	    "+r"(x17)
+	    :: "memory");
+	/* pacify gcc error: more than 30 operands in 'asm' */
+	__asm volatile("" :
+	    "+r"(x18),
+	    "+r"(x19),
+	    "+r"(x20),
+	    "+r"(x21),
+	    "+r"(x22),
+	    "+r"(x23),
+	    "+r"(x24),
+	    "+r"(x25),
+	    "+r"(x26),
+	    "+r"(x27),
+	    "+r"(x28)
+	    :: "memory");
+
+	return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+	/* x0: used to pass posix_spawn arg0, nonnull anyway (&pid) */
+	/* x1: used to pass posix_spawn arg1, nonnull anyway (path) */
+	/* x2: used to pass posix_spawn arg2, nonnull anyway (&fileacts) */
+	/* x3: used to pass posix_spawn arg3, nonnull anyway (&attr) */
+	/* x4: used to pass posix_spawn arg3, nonnull anyway (argv) */
+	/* x5: used to pass posix_spawn arg3, nonnull anyway (environ) */
+	register long x6 __asm("x6") = nonnull(6);
+	register long x7 __asm("x7") = nonnull(7);
+	register long x8 __asm("x8") = nonnull(8);
+	register long x9 __asm("x9") = nonnull(9);
+	register long x10 __asm("x10") = nonnull(10);
+	register long x11 __asm("x11") = nonnull(11);
+	register long x12 __asm("x12") = nonnull(12);
+	register long x13 __asm("x13") = nonnull(13);
+	register long x14 __asm("x14") = nonnull(14);
+	register long x15 __asm("x15") = nonnull(15);
+	register long x16 __asm("x16") = nonnull(16);
+	register long x17 __asm("x17") = nonnull(17);
+	register long x18 __asm("x18") = nonnull(18);
+	register long x19 __asm("x19") = nonnull(19);
+	register long x20 __asm("x20") = nonnull(20);
+	register long x21 __asm("x21") = nonnull(21);
+	register long x22 __asm("x22") = nonnull(22);
+	register long x23 __asm("x23") = nonnull(23);
+	register long x24 __asm("x24") = nonnull(24);
+	register long x25 __asm("x25") = nonnull(25);
+	register long x26 __asm("x26") = nonnull(26);
+	register long x27 __asm("x27") = nonnull(27);
+	register long x28 __asm("x28") = nonnull(28);
+	/* x29: frame pointer, nonnull anyway */
+	/* x30: link register, nonnull anyway */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+	posix_spawn_file_actions_t fileacts;
+	posix_spawnattr_t attr;
+	pid_t pid;
+	int error;
+
+	error = posix_spawn_file_actions_init(&fileacts);
+	if (error)
+		goto out;
+	error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+	if (error)
+		goto out;
+	error = posix_spawnattr_init(&attr);
+	if (error)
+		goto out;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(x6),
+	    "+r"(x7),
+	    "+r"(x8),
+	    "+r"(x9),
+	    "+r"(x10),
+	    "+r"(x11),
+	    "+r"(x12),
+	    "+r"(x13),
+	    "+r"(x14),
+	    "+r"(x15),
+	    "+r"(x16),
+	    "+r"(x17),
+	    "+r"(x18),
+	    "+r"(x19),
+	    "+r"(x20)
+	    :: "memory");
+	/* pacify gcc error: more than 30 operands in 'asm' */
+	__asm volatile("" :
+	    "+r"(x21),
+	    "+r"(x22),
+	    "+r"(x23),
+	    "+r"(x24),
+	    "+r"(x25),
+	    "+r"(x26),
+	    "+r"(x27),
+	    "+r"(x28)
+	    :: "memory");
+
+	error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+	if (error)
+		goto out;
+
+out:	posix_spawnattr_destroy(&attr);
+	posix_spawn_file_actions_destroy(&fileacts);
+	if (error) {
+		errno = error;
+		return -1;
+	}
+	return 0;
+}
Index: src/tests/kernel/arch/aarch64/execregs.h
diff -u /dev/null src/tests/kernel/arch/aarch64/execregs.h:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/aarch64/execregs.h	Thu Feb 27 00:55:31 2025
@@ -0,0 +1,86 @@
+/*	$NetBSD: execregs.h,v 1.1 2025/02/27 00:55:31 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H
+#define	TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define	NEXECREGS	31
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct reg in sys/arch/aarch64/include/reg.h for
+ * convenience of auditing.  Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+	"x0",
+	"x1",
+	/* x2: ps_strings */
+	"x3",
+	"x4",
+	"x5",
+	"x6",
+	"x7",
+	"x8",
+	"x9",
+	"x10",
+	"x11",
+	"x12",
+	"x13",
+	"x14",
+	"x15",
+	"x16",
+	"x17",
+	"x18",
+	"x19",
+	"x20",
+	"x21",
+	"x22",
+	"x23",
+	"x24",
+	"x25",
+	"x26",
+	"x27",
+	"x28",
+	"x29",
+	"x30",
+	"tpidr",
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int	execregschild(char *);
+pid_t	spawnregschild(char *, int);
+
+#endif	/* _LOCORE */
+
+#endif	/* TESTS_KERNEL_ARCH_AARCH64_EXECREGS_H */
Index: src/tests/kernel/arch/aarch64/h_execregs.S
diff -u /dev/null src/tests/kernel/arch/aarch64/h_execregs.S:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/aarch64/h_execregs.S	Thu Feb 27 00:55:31 2025
@@ -0,0 +1,84 @@
+/*	$NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:31 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define	_LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+	/* create a stack frame with NEXECREGS*8 bytes */
+	stp	fp, lr, [sp, #-(16 + NEXECREGS*8)]!
+
+	/* store registers to buffer on stack */
+	stp	x0, x1, [sp, #16]	/* order matches execregs.h */
+	/* x2: ps_strings */
+	stp	x3, x4, [sp, #(16 + 1*2*8)]
+	stp	x5, x6, [sp, #(16 + 2*2*8)]
+	stp	x7, x8, [sp, #(16 + 3*2*8)]
+	stp	x9, x10, [sp, #(16 + 4*2*8)]
+	stp	x11, x12, [sp, #(16 + 5*2*8)]
+	stp	x13, x14, [sp, #(16 + 6*2*8)]
+	stp	x15, x16, [sp, #(16 + 7*2*8)]
+	stp	x17, x18, [sp, #(16 + 8*2*8)]
+	stp	x19, x20, [sp, #(16 + 9*2*8)]
+	stp	x21, x22, [sp, #(16 + 10*2*8)]
+	stp	x23, x24, [sp, #(16 + 11*2*8)]
+	stp	x25, x26, [sp, #(16 + 12*2*8)]
+	stp	x27, x28, [sp, #(16 + 13*2*8)]
+	stp	x29, x30, [sp, #(16 + 14*2*8)]
+	mrs	x0, tpidr_el0
+	str	x0, [sp, #(16 + 15*2*8)]
+
+	/* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+	mov	x0, #1			/* arg0 := STDOUT_FILENO */
+	add	x1, sp, #16		/* arg1 := regs */
+	mov	x2, #(NEXECREGS*8)	/* arg2 := sizeof(regs) */
+	svc	#SYS_write
+
+	b.cs	2f			/* bail if write failed */
+	cmp	x0, #(NEXECREGS*8)	/* bail if wrote wrong # of bytes */
+	b.ne	2f
+
+	/* call exit(0) */
+	mov	x0, #0			/* arg0 := 0 */
+1:	svc	#SYS_exit
+	brk	#0xffff			/* paranoia */
+
+2:	/* call exit(127) */
+	mov	x0, #127		/* arg0 := 127 */
+	b	1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main)
+	brk	#0xffff
+END(main)

Index: src/tests/kernel/arch/hppa/execregs.c
diff -u /dev/null src/tests/kernel/arch/hppa/execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/hppa/execregs.c	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,281 @@
+/*	$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+	x |= x << 8;
+	x |= x << 16;
+	return x;
+}
+
+int
+execregschild(char *path)
+{
+	register long t1 __asm("r22") = nonnull(22);
+	register long t2 __asm("r21") = nonnull(21);
+	/* r30/sp: stack pointer */
+	register long t3 __asm("r20") = nonnull(20);
+	/* cr17/iisq_head: privileged */
+	/* cr17/iisq_tail: privileged */
+	/* cr18/iioq_head: privileged */
+	/* cr18/iioq_tail: privileged */
+	/* cr15/eiem: privileged */
+	/* cr22/ipsw: privileged */
+	/* sr3: privileged(?) */
+	/* cr8/pidr1: privileged */
+	/* cr20/isr: privileged */
+	/* cr21/ior: privileged */
+	/* cr19/iir: privileged */
+	/* flags: N/A(?) */
+	long sar = nonnull(0x8a);	/* cr11 */
+	/* r1: ADDIL (add immediate left) result, nonnull anyway */
+	/* r2/rp: return pointer, nonnull anyway */
+	/* r3: frame pointer, nonnull anyway */
+	register long r4 __asm("r4") = nonnull(4);
+	register long r5 __asm("r5") = nonnull(5);
+	register long r6 __asm("r6") = nonnull(6);
+	register long r7 __asm("r7") = nonnull(7);
+	register long r8 __asm("r8") = nonnull(8);
+	register long r9 __asm("r9") = nonnull(9);
+	register long r10 __asm("r10") = nonnull(10);
+	register long r11 __asm("r11") = nonnull(11);
+	register long r12 __asm("r12") = nonnull(12);
+	register long r13 __asm("r13") = nonnull(13);
+	register long r14 __asm("r14") = nonnull(14);
+	register long r15 __asm("r15") = nonnull(15);
+	register long r16 __asm("r16") = nonnull(16);
+	register long r17 __asm("r17") = nonnull(17);
+	register long r18 __asm("r18") = nonnull(18);
+	register long t4 __asm("r19") = nonnull(19);
+	register long arg3 __asm("r23") = nonnull(23);
+	/* r24/arg2: envp, nonnull anyway */
+	/* r25/arg1: argv, nonnull anyway */
+	/* r26/arg0: path, nonnull anyway */
+	/* r27/dp: data pointer, nonnull anyway */
+	register long ret0 __asm("r28") = nonnull(28);
+	register long ret1 __asm("r29") = nonnull(29);
+	register long r31 __asm("r31") = nonnull(31);
+	/* sr0-sr7: space registers initialized by kernel */
+	/* cr9/pidr2: privileged */
+	/* cr12/pidr3: privileged */
+	/* cr13/pidr4: privileged */
+	/* cr0/rctr: privileged */
+	/* cr10/ccr: privileged */
+	/* cr23/eirr: privileged */
+	/* cr24: privileged */
+	/* cr25/vtop: privileged */
+	/* cr27/tr3: _lwp_private, thread-local storage -- nonnull anyway */
+	/* cr28: privileged */
+	/* cr30/fpregs: privileged */
+	/* cr31: privileged */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("mtctl %[sar], %%sar"	/* cr11 */
+	    : /* outputs */
+	    : [sar] "r"(sar)
+	    : "memory");
+	__asm volatile("" :
+	    "+r"(t1),
+	    "+r"(t2),
+	    "+r"(t3),
+	    "+r"(r4),
+	    "+r"(r5),
+	    "+r"(r6),
+	    "+r"(r7),
+	    "+r"(r8),
+	    "+r"(r9),
+	    "+r"(r10),
+	    "+r"(r11),
+	    "+r"(r12)
+	    :: "memory");
+	/* pacify gcc error: more than 30 operands in 'asm' */
+	__asm volatile("" :
+	    "+r"(r13),
+	    "+r"(r14),
+	    "+r"(r15),
+	    "+r"(r16),
+	    "+r"(r17),
+	    "+r"(r18),
+	    "+r"(t4),
+	    "+r"(arg3),
+	    "+r"(ret0),
+	    "+r"(ret1),
+	    "+r"(r31)
+	    :: "memory");
+
+	return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+	register long t1 __asm("r22") = nonnull(22);
+	register long t2 __asm("r21") = nonnull(21);
+	/* r30/sp: stack pointer */
+	register long t3 __asm("r20") = nonnull(20);
+	/* cr17/iisq_head: privileged */
+	/* cr17/iisq_tail: privileged */
+	/* cr18/iioq_head: privileged */
+	/* cr18/iioq_tail: privileged */
+	/* cr15/eiem: privileged */
+	/* cr22/ipsw: privileged */
+	/* sr3: privileged(?) */
+	/* cr8/pidr1: privileged */
+	/* cr20/isr: privileged */
+	/* cr21/ior: privileged */
+	/* cr19/iir: privileged */
+	/* flags: N/A(?) */
+	long sar = nonnull(0x8a);	/* cr11 */
+	/* r1: ADDIL (add immediate left) result, nonnull anyway */
+	/* r2/rp: return pointer, nonnull anyway */
+	/* r3: frame pointer, nonnull anyway */
+	register long r4 __asm("r4") = nonnull(4);
+	register long r5 __asm("r5") = nonnull(5);
+	register long r6 __asm("r6") = nonnull(6);
+	register long r7 __asm("r7") = nonnull(7);
+	register long r8 __asm("r8") = nonnull(8);
+	register long r9 __asm("r9") = nonnull(9);
+	register long r10 __asm("r10") = nonnull(10);
+	register long r11 __asm("r11") = nonnull(11);
+	register long r12 __asm("r12") = nonnull(12);
+	register long r13 __asm("r13") = nonnull(13);
+	register long r14 __asm("r14") = nonnull(14);
+	register long r15 __asm("r15") = nonnull(15);
+	register long r16 __asm("r16") = nonnull(16);
+	register long r17 __asm("r17") = nonnull(17);
+	register long r18 __asm("r18") = nonnull(18);
+	register long t4 __asm("r19") = nonnull(19);
+	/* r23/arg3: attrp, nonnull anyway */
+	/* r24/arg2: fileactsp, nonnull anyway */
+	/* r25/arg1: path, nonnull anyway */
+	/* r26/arg0: pidp, nonnull anyway */
+	/* r27/dp: data pointer, nonnull anyway */
+	register long ret0 __asm("r28") = nonnull(28);
+	register long ret1 __asm("r29") = nonnull(29);
+	register long r31 __asm("r31") = nonnull(31);
+	/* sr0-sr7: space registers initialized by kernel */
+	/* cr9/pidr2: privileged */
+	/* cr12/pidr3: privileged */
+	/* cr13/pidr4: privileged */
+	/* cr0/rctr: privileged */
+	/* cr10/ccr: privileged */
+	/* cr23/eirr: privileged */
+	/* cr24: privileged */
+	/* cr25/vtop: privileged */
+	/* cr27/tr3: _lwp_private, thread-local storage -- nonnull anyway */
+	/* cr28: privileged */
+	/* cr30/fpregs: privileged */
+	/* cr31: privileged */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+	posix_spawn_file_actions_t fileacts;
+	posix_spawnattr_t attr;
+	pid_t pid;
+	int error;
+
+	error = posix_spawn_file_actions_init(&fileacts);
+	if (error)
+		goto out;
+	error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+	if (error)
+		goto out;
+	error = posix_spawnattr_init(&attr);
+	if (error)
+		goto out;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("mtctl %[sar], %%sar"	/* cr11 */
+	    : /* outputs */
+	    : [sar] "r"(sar)
+	    : "memory");
+	__asm volatile("" :
+	    "+r"(t1),
+	    "+r"(t2),
+	    "+r"(t3),
+	    "+r"(r4),
+	    "+r"(r5),
+	    "+r"(r6),
+	    "+r"(r7),
+	    "+r"(r8),
+	    "+r"(r9),
+	    "+r"(r10),
+	    "+r"(r11),
+	    "+r"(r12)
+	    :: "memory");
+	/* pacify gcc error: more than 30 operands in 'asm' */
+	__asm volatile("" :
+	    "+r"(r13),
+	    "+r"(r14),
+	    "+r"(r15),
+	    "+r"(r16),
+	    "+r"(r17),
+	    "+r"(r18),
+	    "+r"(t4),
+	    "+r"(ret0),
+	    "+r"(ret1),
+	    "+r"(r31)
+	    :: "memory");
+
+	error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+	if (error)
+		goto out;
+
+out:	posix_spawnattr_destroy(&attr);
+	posix_spawn_file_actions_destroy(&fileacts);
+	if (error) {
+		errno = error;
+		return -1;
+	}
+	return 0;
+}
Index: src/tests/kernel/arch/hppa/execregs.h
diff -u /dev/null src/tests/kernel/arch/hppa/execregs.h:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/hppa/execregs.h	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,89 @@
+/*	$NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	TESTS_KERNEL_ARCH_HPPA_EXECREGS_H
+#define	TESTS_KERNEL_ARCH_HPPA_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define	NEXECREGS	31
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct struct trapframe in sys/arch/hppa/include/frame.h
+ * for convenience of auditing.  Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+	"t1",
+	"t2",
+	/* sp: stack pointer */
+	"t3",
+	/* various privileged stuff */
+	"sar",
+	"r1",
+	"rp",
+	/* r3: frame pointer (set to initial stack pointer) */
+	"r4",
+	"r5",
+	"r6",
+	"r70",
+	"r8",
+	"r9",
+	"r10",
+	"r11",
+	"r12",
+	"r13",
+	"r14",
+	"r15",
+	"r16",
+	"r17",
+	"r18",
+	"t4",
+	"arg3",
+	"arg2",
+	"arg1",
+	/* arg0: ps_strings */
+	"dp",
+	"ret0",
+	"ret1",
+	"r31",
+	"cr27",
+	"cr28",
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int	execregschild(char *);
+pid_t	spawnregschild(char *, int);
+
+#endif	/* _LOCORE */
+
+#endif	/* TESTS_KERNEL_ARCH_HPPA_EXECREGS_H */
Index: src/tests/kernel/arch/hppa/h_execregs.S
diff -u /dev/null src/tests/kernel/arch/hppa/h_execregs.S:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/hppa/h_execregs.S	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,129 @@
+/*	$NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define	_LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+#include <machine/vmparam.h>
+
+#include "execregs.h"
+
+_ENTRY(execregs_start)
+	.callinfo frame=(NEXECREGS*4), calls
+	.entry
+
+	ldo	(NEXECREGS*4)(%sp), %sp		/* space for NEXECREGS */
+	stw	%t1, (4*(0 - NEXECREGS))(%sp)	/* order matches execregs.h */
+	stw	%t2, (4*(1 - NEXECREGS))(%sp)
+	/* sp: stack pointer */
+	stw	%t3, (4*(2 - NEXECREGS))(%sp)
+	/* cr17/iisq_head: privileged */
+	/* cr17/iisq_tail: privileged */
+	/* cr18/iioq_head: privileged */
+	/* cr18/iioq_tail: privileged */
+	/* cr15/eiem: privileged */
+	/* cr22/ipsw: privileged */
+	/* sr3: privileged(?) */
+	/* cr8/pidr1: privileged */
+	/* cr20/isr: privileged */
+	/* cr21/ior: privileged */
+	/* cr19/iir: privileged */
+	/* flags: N/A(?) */
+	stw	%sar, (4*(3 - NEXECREGS))(%sp)
+	stw	%r1, (4*(4 - NEXECREGS))(%sp)
+	stw	%rp, (4*(5 - NEXECREGS))(%sp)
+	/* r3: frame pointer (set to initial stack pointer) */
+	stw	%r4, (4*(6 - NEXECREGS))(%sp)
+	stw	%r5, (4*(7 - NEXECREGS))(%sp)
+	stw	%r6, (4*(8 - NEXECREGS))(%sp)
+	stw	%r7, (4*(9 - NEXECREGS))(%sp)
+	stw	%r8, (4*(10 - NEXECREGS))(%sp)
+	stw	%r9, (4*(11 - NEXECREGS))(%sp)
+	stw	%r10, (4*(12 - NEXECREGS))(%sp)
+	stw	%r11, (4*(13 - NEXECREGS))(%sp)
+	stw	%r12, (4*(14 - NEXECREGS))(%sp)
+	stw	%r13, (4*(15 - NEXECREGS))(%sp)
+	stw	%r14, (4*(16 - NEXECREGS))(%sp)
+	stw	%r15, (4*(17 - NEXECREGS))(%sp)
+	stw	%r16, (4*(18 - NEXECREGS))(%sp)
+	stw	%r17, (4*(19 - NEXECREGS))(%sp)
+	stw	%r18, (4*(20 - NEXECREGS))(%sp)
+	stw	%t4, (4*(21 - NEXECREGS))(%sp)
+	stw	%arg3, (4*(22 - NEXECREGS))(%sp)
+	stw	%arg2, (4*(23 - NEXECREGS))(%sp)
+	stw	%arg1, (4*(24 - NEXECREGS))(%sp)
+	/* arg0: ps_strings */
+	stw	%dp, (4*(25 - NEXECREGS))(%sp)
+	stw	%ret0, (4*(26 - NEXECREGS))(%sp)
+	stw	%ret1, (4*(27 - NEXECREGS))(%sp)
+	stw	%r31, (4*(28 - NEXECREGS))(%sp)
+	/* sr0-sr7: space registers initialized by kernel */
+	/* cr9/pidr2: privileged */
+	/* cr12/pidr3: privileged */
+	/* cr13/pidr4: privileged */
+	/* cr0/rctr: privileged */
+	/* cr10/ccr: privileged */
+	/* cr23/eirr: privileged */
+	/* cr24: privileged */
+	/* cr25/vtop: privileged */
+	/* cr26: ??? */
+	stw	%cr27, (4*(29 - NEXECREGS))(%sp)
+	stw	%cr28, (4*(30 - NEXECREGS))(%sp)
+	/* cr30/fpregs: privileged */
+	/* cr31: privileged */
+
+	/* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+	ldi	1, %arg0			/* arg0 := STDOUT_FILENO */
+	ldo	-(4*NEXECREGS)(%sp), %arg1	/* arg1 := regs */
+	ldi	(4*NEXECREGS), %arg2		/* arg2 := sizeof(regs) */
+	ldil	L%SYSCALLGATE, %r1
+	ble	4(%sr2, %r1)
+	 ldi	SYS_write, %t1
+
+	comb,<>,n	%r0, %t1, 2f		/* bail if write failed */
+	ldi		(4*NEXECREGS), %t1	/* bail if wrong # bytes */
+	comb,<>,n	%ret0, %t1, 2f
+
+	/* call exit(0) */
+	ldi	0, %arg0
+1:	ldil	L%SYSCALLGATE, %r1
+	ble	4(%sr2, %r1)
+	 ldi	SYS_exit, %t1
+	break	0, 0				/* paranoia */
+
+2:	/* call exit(127) */
+	b	1b
+	 ldi	127, %arg0
+EXIT(execregs_start)
+
+/* main stub to simplify linking */
+LEAF_ENTRY(main)
+	break	0, 0				/* paranoia */
+EXIT(main)

Index: src/tests/kernel/arch/i386/execregs.c
diff -u /dev/null src/tests/kernel/arch/i386/execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/i386/execregs.c	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,132 @@
+/*	$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+	x |= x << 8;
+	x |= x << 16;
+	return x;
+}
+
+int
+execregschild(char *path)
+{
+	register long edi __asm("edi") = nonnull('d');
+	register long esi __asm("esi") = nonnull('s');
+	/* ebp: frame pointer, can't touch that here, but it'll be nonnull */
+	/* ebx: ps_strings, passed to child */
+	register long edx __asm("edx") = nonnull('x');
+	register long ecx __asm("ecx") = nonnull('c');
+	register long eax __asm("eax") = nonnull('a');
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(edi),
+	    "+r"(esi),
+	    "+r"(edx),
+	    "+r"(ecx),
+	    "+r"(eax)
+	    :: "memory");
+
+	return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+	register long edi __asm("edi") = nonnull('d');
+	register long esi __asm("esi") = nonnull('s');
+	/* ebp: frame pointer, can't touch that here, but it'll be nonnull */
+	/* ebx: ps_strings, passed to child */
+	register long edx __asm("edx") = nonnull('x');
+	register long ecx __asm("ecx") = nonnull('c');
+	register long eax __asm("eax") = nonnull('a');
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+	posix_spawn_file_actions_t fileacts;
+	posix_spawnattr_t attr;
+	pid_t pid;
+	int error;
+
+	error = posix_spawn_file_actions_init(&fileacts);
+	if (error)
+		goto out;
+	error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+	if (error)
+		goto out;
+	error = posix_spawnattr_init(&attr);
+	if (error)
+		goto out;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(edi),
+	    "+r"(esi),
+	    "+r"(edx),
+	    "+r"(ecx),
+	    "+r"(eax)
+	    :: "memory");
+
+	error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+	if (error)
+		goto out;
+
+out:	posix_spawnattr_destroy(&attr);
+	posix_spawn_file_actions_destroy(&fileacts);
+	if (error) {
+		errno = error;
+		return -1;
+	}
+	return 0;
+}
Index: src/tests/kernel/arch/i386/execregs.h
diff -u /dev/null src/tests/kernel/arch/i386/execregs.h:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/i386/execregs.h	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,69 @@
+/*	$NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	TESTS_KERNEL_ARCH_I386_EXECREGS_H
+#define	TESTS_KERNEL_ARCH_I386_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define	NEXECREGS	6
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by struct trapframe in sys/arch/i386/include/frame.h for
+ * convenience of auditing.  Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+	/* gs/fs/es/ds: segment registers, not really registers */
+	"edi",
+	"esi",
+	"ebp",
+	/* ebx: ps_strings */
+	"edx",
+	"ecx",
+	"eax",
+	/* trapno: not a register */
+	/* err: not a register */
+	/* eip: instruction pointer */
+	/* cs: segment register */
+	/* eflags */
+	/* esp: stack pointer */
+	/* ss: stack selector */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int	execregschild(char *);
+pid_t	spawnregschild(char *, int);
+
+#endif	/* _LOCORE */
+
+#endif	/* TESTS_KERNEL_ARCH_I386_EXECREGS_H */
Index: src/tests/kernel/arch/i386/h_execregs.S
diff -u /dev/null src/tests/kernel/arch/i386/h_execregs.S:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/i386/h_execregs.S	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,85 @@
+/*	$NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define	_LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+	andl	$-0x4,%esp		/* align stack to 4-byte boundary */
+
+	/* store registers to a buffer on stack */
+	subl	$(NEXECREGS*4),%esp	/* space for NEXECREGS registers */
+	movl	%edi,0*4(%esp)		/* order matches execregs.h */
+	movl	%esi,1*4(%esp)
+	movl	%ebp,2*4(%esp)
+	movl	%edx,3*4(%esp)
+	movl	%ecx,4*4(%esp)
+	movl	%eax,5*4(%esp)
+
+	/* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+	movl	%esp,%eax		/* eax := regs */
+	pushl	$(NEXECREGS*4)		/* arg2 := sizeof(regs) */
+	pushl	%eax			/* arg1 := regs */
+	pushl	$0x1			/* arg0 := STDOUT_FILENO */
+	call	execregs_write
+
+	jb	2f			/* bail if write failed */
+	cmpl	$(NEXECREGS*4),%eax	/* bail if wrote wrong # of bytes */
+	jne	2f
+
+	/* call exit(0) */
+	pushl	$0			/* arg0 := 0 */
+1:	call	execregs_exit
+	hlt				/* paranoia */
+
+2:	/* call exit(127) */
+	pushl	$127			/* arg0 := 127 */
+	jmp	1b
+END(execregs_start)
+
+ENTRY(execregs_write)
+	movl	$SYS_write,%eax		/* syscall number */
+	int	$0x80
+	retl
+END(execregs_write)
+
+ENTRY(execregs_exit)
+	movl	$SYS_exit,%eax		/* syscall number */
+	int	$0x80
+	hlt
+END(execregs_exit)
+
+/* main stub to simplify linking */
+ENTRY(main)
+	hlt
+END(main)

Index: src/tests/kernel/arch/vax/execregs.c
diff -u /dev/null src/tests/kernel/arch/vax/execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/vax/execregs.c	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,166 @@
+/*	$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+	x |= x << 8;
+	x |= x << 16;
+	return x;
+}
+
+int
+execregschild(char *path)
+{
+	/* fp: frame pointer, nonnull */
+	/* ap: argument pointer, on user stack, nonnull */
+	/* sp: stack pointer, nonnull */
+	register long r0 __asm("r0") = nonnull(0x10);
+	register long r1 __asm("r1") = nonnull(1);
+	register long r2 __asm("r2") = nonnull(2);
+	register long r3 __asm("r3") = nonnull(3);
+	register long r4 __asm("r4") = nonnull(4);
+	register long r5 __asm("r5") = nonnull(5);
+	register long r6 __asm("r6") = nonnull(6);
+	register long r7 __asm("r7") = nonnull(7);
+	register long r8 __asm("r8") = nonnull(8);
+	register long r9 __asm("r9") = nonnull(9);
+	register long r10 __asm("r10") = nonnull(10);
+	register long r11 __asm("r11") = nonnull(11);
+	/* pc: user PC, will be nonnull */
+	/* psl: processor status longword, will be nonnull */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(r0),
+	    "+r"(r1),
+	    "+r"(r2),
+	    "+r"(r3),
+	    "+r"(r4),
+	    "+r"(r5),
+	    "+r"(r6),
+	    "+r"(r7),
+	    "+r"(r8),
+	    "+r"(r9),
+	    "+r"(r10),
+	    "+r"(r11)
+	    :: "memory");
+
+	return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+	/* fp: frame pointer, nonnull */
+	/* ap: argument pointer, on user stack, nonnull */
+	/* sp: stack pointer, nonnull */
+	register long r0 __asm("r0") = nonnull(0x10);
+	register long r1 __asm("r1") = nonnull(1);
+	register long r2 __asm("r2") = nonnull(2);
+	register long r3 __asm("r3") = nonnull(3);
+	register long r4 __asm("r4") = nonnull(4);
+	register long r5 __asm("r5") = nonnull(5);
+	register long r6 __asm("r6") = nonnull(6);
+	register long r7 __asm("r7") = nonnull(7);
+	register long r8 __asm("r8") = nonnull(8);
+	register long r9 __asm("r9") = nonnull(9);
+	register long r10 __asm("r10") = nonnull(10);
+	register long r11 __asm("r11") = nonnull(11);
+	/* pc: user PC, will be nonnull */
+	/* psl: processor status longword, will be nonnull */
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+	posix_spawn_file_actions_t fileacts;
+	posix_spawnattr_t attr;
+	pid_t pid;
+	int error;
+
+	error = posix_spawn_file_actions_init(&fileacts);
+	if (error)
+		goto out;
+	error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+	if (error)
+		goto out;
+	error = posix_spawnattr_init(&attr);
+	if (error)
+		goto out;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(r0),
+	    "+r"(r1),
+	    "+r"(r2),
+	    "+r"(r3),
+	    "+r"(r4),
+	    "+r"(r5),
+	    "+r"(r6),
+	    "+r"(r7),
+	    "+r"(r8),
+	    "+r"(r9),
+	    "+r"(r10),
+	    "+r"(r11)
+	    :: "memory");
+
+	error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+	if (error)
+		goto out;
+
+out:	posix_spawnattr_destroy(&attr);
+	posix_spawn_file_actions_destroy(&fileacts);
+	if (error) {
+		errno = error;
+		return -1;
+	}
+	return 0;
+}
Index: src/tests/kernel/arch/vax/execregs.h
diff -u /dev/null src/tests/kernel/arch/vax/execregs.h:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/vax/execregs.h	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,77 @@
+/*	$NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	TESTS_KERNEL_ARCH_VAX_EXECREGS_H
+#define	TESTS_KERNEL_ARCH_VAX_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define	NEXECREGS	12
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * The order matches that in struct trapframe in
+ * sys/arch/vax/include/trap.h
+ *
+ * Must match h_execregs.S.
+ *
+ * See also sys/arch/vax/vax/trap.c:setregs()
+ */
+static const char *const regname[] = {
+	"fp",	       /* Stack frame pointer */
+	"ap",	       /* Argument pointer on user stack */
+	/* sp: stack pointer */
+	"r0",	       /* General registers saved upon trap/syscall */
+	"r1",
+	"r2",
+	"r3",
+	"r4",
+	"r5",
+	/* r6: initial stack pointer */
+	"r7",
+	"r8",
+	/* r9: ps_strings */
+	"r10",
+	"r11",
+	/* trap: type of trap, not a register */
+	/* code: trap specific code, not a register */
+	/* pc: user PC */
+	/* psl: processor status longword */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int	execregschild(char *);
+pid_t	spawnregschild(char *, int);
+
+#endif	/* _LOCORE */
+
+#endif	/* TESTS_KERNEL_ARCH_VAX_EXECREGS_H */
Index: src/tests/kernel/arch/vax/h_execregs.S
diff -u /dev/null src/tests/kernel/arch/vax/h_execregs.S:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/vax/h_execregs.S	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,87 @@
+/*	$NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define	_LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+#define REGSIZE 4
+#define BUFSIZE (NEXECREGS * REGSIZE)
+#define SLOT(n)	(n)*REGSIZE(%sp)
+
+ENTRY(execregs_start, 0)
+	/* store registers to a buffer on stack */
+	subl2	$BUFSIZE,%sp		/* space for NEXECREGS registers */
+	movl	%fp,SLOT(0)		/* order matches execregs.h */
+	movl	%ap,SLOT(1)
+	/* sp: stack pointer */
+	movl	%r0,SLOT(2)
+	movl	%r1,SLOT(3)
+	movl	%r2,SLOT(4)
+	movl	%r3,SLOT(5)
+	movl	%r4,SLOT(6)
+	movl	%r5,SLOT(7)
+	/* r6: initial stack pointer */
+	movl	%r7,SLOT(8)
+	movl	%r8,SLOT(9)
+	/* r9: ps_strings */
+	movl	%r10,SLOT(10)
+	movl	%r11,SLOT(11)
+
+	/* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+	pushl	$BUFSIZE		/* arg2 := sizeof(regs) */
+	pushal	4(%sp)			/* arg1 := regs */
+	pushl	$1			/* arg0 := STDOUT_FILENO */
+	pushl	$3			/* number of arguments */
+	movl	%sp,%ap			/* argument pointer */
+	chmk	$SYS_write
+
+	bcs	2f			/* bail if write failed */
+	cmpl	$BUFSIZE,%r0		/* bail if wrote wrong # of bytes */
+	bneq	2f
+
+	/* call exit(0) */
+	pushl	$0			/* arg0 := 0 */
+1:	pushl	$1			/* number of arguments */
+	movl	%sp,%ap			/* argument pointer */
+	chmk	$SYS_exit
+	.word	0xffff			/* paranoia -- illegal opcode */
+
+2:	/* call exit(127) */
+	pushl	$127			/* arg0 := 127 */
+	jmp	1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main, 0)
+	.word	0xffff			/* illegal opcode */
+END(main)

Index: src/tests/kernel/arch/x86_64/execregs.c
diff -u /dev/null src/tests/kernel/arch/x86_64/execregs.c:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/x86_64/execregs.c	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,156 @@
+/*	$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: execregs.c,v 1.1 2025/02/27 00:55:32 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+	x |= x << 8;
+	x |= x << 16;
+	x |= x << 32;
+	return x;
+}
+
+int
+execregschild(char *path)
+{
+	/* rdi: used to pass exec arg0, nonnull anyway (path) */
+	/* rsi: used to pass exec arg1, nonnull anyway (argv) */
+	/* rdx: used to pass exec arg2, nonnull anyway (environ) */
+	register long r10 __asm("r10") = nonnull(10);
+	register long r8 __asm("r8") = nonnull(8);
+	register long r9 __asm("r9") = nonnull(9);
+	register long rcx __asm("rcx") = nonnull('c');
+	register long r11 __asm("r11") = nonnull(11);
+	register long r12 __asm("r12") = nonnull(12);
+	register long r13 __asm("r13") = nonnull(13);
+	register long r14 __asm("r14") = nonnull(14);
+	register long r15 __asm("r15") = nonnull(15);
+	/* rbp: frame pointer, can't touch that here, but it'll be nonnull */
+	/* rbx: ps_strings, passed to child */
+	register long rax __asm("rax") = nonnull('a');
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(r10),
+	    "+r"(r8),
+	    "+r"(r9),
+	    "+r"(rcx),
+	    "+r"(r11),
+	    "+r"(r12),
+	    "+r"(r13),
+	    "+r"(r14),
+	    "+r"(r15),
+	    "+r"(rax)
+	    :: "memory");
+
+	return execve(path, argv, envp);
+}
+
+pid_t
+spawnregschild(char *path, int fd)
+{
+	/* rdi: used to pass posix_spawn arg0, nonnull anyway (&pid) */
+	/* rsi: used to pass posix_spawn arg1, nonnull anyway (path) */
+	/* rdx: used to pass posix_spawn arg2, nonnull anyway (&fileacts) */
+	register long r10 __asm("r10") = nonnull(10);
+	/* r8: used to pass posix_spawn arg4, nonnull anyway (argv) */
+	/* r9: used to pass posix_spawn arg5, nonnull anyway (environ) */
+	/* rcx: used to pass posix_spawn arg3, nonnull anyway (&attr) */
+	register long r11 __asm("r11") = nonnull(11);
+	register long r12 __asm("r12") = nonnull(12);
+	register long r13 __asm("r13") = nonnull(13);
+	register long r14 __asm("r14") = nonnull(14);
+	register long r15 __asm("r15") = nonnull(15);
+	/* rbp: frame pointer, can't touch that here, but it'll be nonnull */
+	/* rbx: ps_strings, passed to child */
+	register long rax __asm("rax") = nonnull('a');
+
+	char *argv[] = {path, NULL};
+	char **envp = environ;
+	posix_spawn_file_actions_t fileacts;
+	posix_spawnattr_t attr;
+	pid_t pid;
+	int error;
+
+	error = posix_spawn_file_actions_init(&fileacts);
+	if (error)
+		goto out;
+	error = posix_spawn_file_actions_adddup2(&fileacts, fd, STDOUT_FILENO);
+	if (error)
+		goto out;
+	error = posix_spawnattr_init(&attr);
+	if (error)
+		goto out;
+
+	/*
+	 * Not perfect -- compiler might use some registers for
+	 * stack/argument transfers, but all the arguments are nonnull
+	 * so this is probably a good test anyway.
+	 */
+	__asm volatile("" :
+	    "+r"(r10),
+	    "+r"(r11),
+	    "+r"(r12),
+	    "+r"(r13),
+	    "+r"(r14),
+	    "+r"(r15),
+	    "+r"(rax)
+	    :: "memory");
+
+	error = posix_spawn(&pid, path, &fileacts, &attr, argv, envp);
+	if (error)
+		goto out;
+
+out:	posix_spawnattr_destroy(&attr);
+	posix_spawn_file_actions_destroy(&fileacts);
+	if (error) {
+		errno = error;
+		return -1;
+	}
+	return 0;
+}
Index: src/tests/kernel/arch/x86_64/execregs.h
diff -u /dev/null src/tests/kernel/arch/x86_64/execregs.h:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/x86_64/execregs.h	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,81 @@
+/*	$NetBSD: execregs.h,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef	TESTS_KERNEL_ARCH_X86_64_EXECREGS_H
+#define	TESTS_KERNEL_ARCH_X86_64_EXECREGS_H
+
+#include <sys/cdefs.h>
+
+#define	NEXECREGS	14
+
+#ifndef _LOCORE
+
+#include <unistd.h>
+
+/*
+ * Ordered by _FRAME_REG in sys/arch/amd64/include/frame_regs.h for
+ * convenience of auditing.  Must match h_execregs.S.
+ */
+static const char *const regname[] = {
+	"rdi",
+	"rsi",
+	"rdx",
+	"r10",
+	"r8",
+	"r9",
+	/* arg6: syscall arg from stack, not a real register */
+	/* arg7: syscall arg from stack, not a real register */
+	/* arg8: syscall arg from stack, not a real register */
+	/* arg9: syscall arg from stack, not a real register */
+	"rcx",
+	"r11",
+	"r12",
+	"r13",
+	"r14",
+	"r15",
+	"rbp",
+	/* rbx: ps_strings */
+	"rax",
+	/* gs/fs/es/ds: segment registers, not really registers */
+	/* trapno: not a register */
+	/* err: not a register */
+	/* rip: instruction pointer */
+	/* cs: segment register */
+	/* rflags */
+	/* rsp: stack pointer */
+	/* ss: stack selector */
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int	execregschild(char *);
+pid_t	spawnregschild(char *, int);
+
+#endif	/* _LOCORE */
+
+#endif	/* TESTS_KERNEL_ARCH_X86_64_EXECREGS_H */
Index: src/tests/kernel/arch/x86_64/h_execregs.S
diff -u /dev/null src/tests/kernel/arch/x86_64/h_execregs.S:1.1
--- /dev/null	Thu Feb 27 00:55:33 2025
+++ src/tests/kernel/arch/x86_64/h_execregs.S	Thu Feb 27 00:55:32 2025
@@ -0,0 +1,82 @@
+/*	$NetBSD: h_execregs.S,v 1.1 2025/02/27 00:55:32 riastradh Exp $	*/
+
+/*-
+ * Copyright (c) 2025 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define	_LOCORE
+
+#include <sys/syscall.h>
+
+#include <machine/asm.h>
+
+#include "execregs.h"
+
+ENTRY(execregs_start)
+	andq	$-0x10,%rsp		/* align stack to 16-byte boundary */
+
+	/* store registers to a buffer on stack */
+	subq	$(NEXECREGS*8),%rsp	/* space for NEXECREGS registers */
+	movq	%rdi,0*8(%rsp)		/* order matches execregs.h */
+	movq	%rsi,1*8(%rsp)
+	movq	%rdx,2*8(%rsp)
+	movq	%r10,3*8(%rsp)
+	movq	%r8,4*8(%rsp)
+	movq	%r9,5*8(%rsp)
+	movq	%rcx,6*8(%rsp)
+	movq	%r11,7*8(%rsp)
+	movq	%r12,8*8(%rsp)
+	movq	%r13,9*8(%rsp)
+	movq	%r14,10*8(%rsp)
+	movq	%r15,11*8(%rsp)
+	movq	%rbp,12*8(%rsp)
+	movq	%rax,13*8(%rsp)
+
+	/* call write(STDOUT_FILENO, regs, sizeof(regs)) */
+	movl	$0x1,%edi		/* arg0 := STDOUT_FILENO */
+	movq	%rsp,%rsi		/* arg1 := regs */
+	movl	$(NEXECREGS*8),%edx	/* arg2 := sizeof(regs) */
+	movl	$SYS_write,%eax		/* syscall number */
+	syscall
+
+	jb	2f			/* bail if write failed */
+	cmpq	$(NEXECREGS*8),%rax	/* bail if wrote wrong # of bytes */
+	jne	2f
+
+	/* call exit(0) */
+	xorl	%edi,%edi		/* arg0 := 0 */
+1:	movl	$SYS_exit,%eax		/* syscall number */
+	syscall
+	hlt				/* paranoia */
+
+2:	/* call exit(127) */
+	movl	$127,%edi		/* arg0 := 127 */
+	jmp	1b
+END(execregs_start)
+
+/* main stub to simplify linking */
+ENTRY(main)
+	hlt
+END(main)

Reply via email to