aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorEnji Cooper <ngie@FreeBSD.org>2026-02-15 01:57:42 +0000
committerEnji Cooper <ngie@FreeBSD.org>2026-02-15 02:12:44 +0000
commite8dbf2b6df199526a660f81de07d17925cfd8518 (patch)
treecd0c09449bea5df56ef67059e797737d70587070 /kernel
parent56a7ce8416d181a2060d7a428aed9c3c6a431e6d (diff)
Diffstat (limited to 'kernel')
-rw-r--r--kernel/arch/aarch64/contextspfunc.S46
-rw-r--r--kernel/arch/aarch64/execregs.c226
-rw-r--r--kernel/arch/aarch64/execregs.h86
-rw-r--r--kernel/arch/aarch64/execsp.S99
-rw-r--r--kernel/arch/aarch64/h_execregs.S84
-rw-r--r--kernel/arch/aarch64/signalsphandler.S46
-rw-r--r--kernel/arch/aarch64/stack_pointer.h42
-rw-r--r--kernel/arch/aarch64/threadspfunc.S43
-rw-r--r--kernel/arch/alpha/contextspfunc.S50
-rw-r--r--kernel/arch/alpha/execsp.S106
-rw-r--r--kernel/arch/alpha/signalsphandler.S49
-rw-r--r--kernel/arch/alpha/stack_pointer.h35
-rw-r--r--kernel/arch/alpha/threadspfunc.S46
-rw-r--r--kernel/arch/arm/contextspfunc.S51
-rw-r--r--kernel/arch/arm/execsp.S119
-rw-r--r--kernel/arch/arm/signalsphandler.S51
-rw-r--r--kernel/arch/arm/stack_pointer.h35
-rw-r--r--kernel/arch/arm/threadspfunc.S43
-rw-r--r--kernel/arch/hppa/contextspfunc.S48
-rw-r--r--kernel/arch/hppa/execregs.c387
-rw-r--r--kernel/arch/hppa/execregs.h157
-rw-r--r--kernel/arch/hppa/execsp.S130
-rw-r--r--kernel/arch/hppa/h_execregs.S168
-rw-r--r--kernel/arch/hppa/signalsphandler.S46
-rw-r--r--kernel/arch/hppa/stack_pointer.h35
-rw-r--r--kernel/arch/hppa/threadspfunc.S43
-rw-r--r--kernel/arch/i386/contextspfunc.S56
-rw-r--r--kernel/arch/i386/execregs.c132
-rw-r--r--kernel/arch/i386/execregs.h69
-rw-r--r--kernel/arch/i386/execsp.S112
-rw-r--r--kernel/arch/i386/h_execregs.S85
-rw-r--r--kernel/arch/i386/signalsphandler.S56
-rw-r--r--kernel/arch/i386/stack_pointer.h35
-rw-r--r--kernel/arch/i386/threadspfunc.S43
-rw-r--r--kernel/arch/mips/contextspfunc.S53
-rw-r--r--kernel/arch/mips/execsp.S118
-rw-r--r--kernel/arch/mips/signalsphandler.S53
-rw-r--r--kernel/arch/mips/stack_pointer.h40
-rw-r--r--kernel/arch/mips/threadspfunc.S46
-rw-r--r--kernel/arch/riscv/contextspfunc.S45
-rw-r--r--kernel/arch/riscv/execsp.S100
-rw-r--r--kernel/arch/riscv/signalsphandler.S45
-rw-r--r--kernel/arch/riscv/stack_pointer.h35
-rw-r--r--kernel/arch/riscv/threadspfunc.S43
-rw-r--r--kernel/arch/sh3/asm.h58
-rw-r--r--kernel/arch/sh3/contextspfunc.S55
-rw-r--r--kernel/arch/sh3/execsp.S146
-rw-r--r--kernel/arch/sh3/h_execregs.S86
-rw-r--r--kernel/arch/sh3/signalsphandler.S61
-rw-r--r--kernel/arch/sh3/stack_pointer.h35
-rw-r--r--kernel/arch/sh3/threadspfunc.S41
-rw-r--r--kernel/arch/sparc/contextspfunc.S64
-rw-r--r--kernel/arch/sparc/execsp.S123
-rw-r--r--kernel/arch/sparc/signalsphandler.S62
-rw-r--r--kernel/arch/sparc/stack_pointer.h35
-rw-r--r--kernel/arch/sparc/threadspfunc.S45
-rw-r--r--kernel/arch/vax/execregs.c166
-rw-r--r--kernel/arch/vax/execregs.h77
-rw-r--r--kernel/arch/vax/h_execregs.S87
-rw-r--r--kernel/arch/x86_64/contextspfunc.S55
-rw-r--r--kernel/arch/x86_64/execregs.c156
-rw-r--r--kernel/arch/x86_64/execregs.h81
-rw-r--r--kernel/arch/x86_64/execsp.S111
-rw-r--r--kernel/arch/x86_64/h_execregs.S82
-rw-r--r--kernel/arch/x86_64/signalsphandler.S55
-rw-r--r--kernel/arch/x86_64/stack_pointer.h35
-rw-r--r--kernel/arch/x86_64/threadspfunc.S54
-rw-r--r--kernel/h_cloexec.c48
-rw-r--r--kernel/h_execregs_unimpl.c37
-rw-r--r--kernel/h_execsp.c68
-rw-r--r--kernel/h_execsp.h47
-rw-r--r--kernel/setjmp_tester/Makefile12
-rw-r--r--kernel/setjmp_tester/setjmp_tester.c147
-rw-r--r--kernel/t_cloexec.c457
-rw-r--r--kernel/t_clofork.c445
-rw-r--r--kernel/t_execregs.c197
-rw-r--r--kernel/t_fdrestart.c274
-rw-r--r--kernel/t_nanosleep.c246
-rw-r--r--kernel/t_semtimedop.c279
-rw-r--r--kernel/t_setjmp.sh63
-rw-r--r--kernel/t_signal_and_sp.c628
-rw-r--r--kernel/t_time_arith.c1224
-rw-r--r--kernel/t_unmount.c106
83 files changed, 9485 insertions, 0 deletions
diff --git a/kernel/arch/aarch64/contextspfunc.S b/kernel/arch/aarch64/contextspfunc.S
new file mode 100644
index 000000000000..9a4e3059a8e0
--- /dev/null
+++ b/kernel/arch/aarch64/contextspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/06/08 18:55:35 christos 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/06/08 18:55:35 christos Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ mov x0, sp
+ adrp x1, _C_LABEL(contextsp)
+ str x0, [x1, :lo12:_C_LABEL(contextsp)]
+ b _C_LABEL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/aarch64/execregs.c b/kernel/arch/aarch64/execregs.c
new file mode 100644
index 000000000000..4c7697fe98c7
--- /dev/null
+++ b/kernel/arch/aarch64/execregs.c
@@ -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;
+}
diff --git a/kernel/arch/aarch64/execregs.h b/kernel/arch/aarch64/execregs.h
new file mode 100644
index 000000000000..471a3859c4d1
--- /dev/null
+++ b/kernel/arch/aarch64/execregs.h
@@ -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 */
diff --git a/kernel/arch/aarch64/execsp.S b/kernel/arch/aarch64/execsp.S
new file mode 100644
index 000000000000..52c1472afef6
--- /dev/null
+++ b/kernel/arch/aarch64/execsp.S
@@ -0,0 +1,99 @@
+/* $NetBSD: execsp.S,v 1.3 2025/06/04 19:25:45 christos 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.3 2025/06/04 19:25:45 christos Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@x0)(void), void *obj_main@x1,
+ * struct ps_strings *ps_strings@x2)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ mov x16, sp
+ adrp x17, _C_LABEL(startsp)
+ str x16, [x17, :lo12:_C_LABEL(startsp)]
+ b _C_LABEL(__start)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ mov x16, sp
+ adrp x17, _C_LABEL(ctorsp)
+ str x16, [x17, :lo12:_C_LABEL(ctorsp)]
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw",%init_array
+ .p2align 3
+ .xword _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@x0, char **argv@x1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ mov x16, sp
+ adrp x17, _C_LABEL(mainsp)
+ str x16, [x17, :lo12:_C_LABEL(mainsp)]
+ mov x0, #0
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ mov x16, sp
+ adrp x17, _C_LABEL(dtorsp)
+ str x16, [x17, :lo12:_C_LABEL(dtorsp)]
+ b _C_LABEL(execsp_main)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw",%fini_array
+ .p2align 3
+ .xword _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/aarch64/h_execregs.S b/kernel/arch/aarch64/h_execregs.S
new file mode 100644
index 000000000000..f060911067f4
--- /dev/null
+++ b/kernel/arch/aarch64/h_execregs.S
@@ -0,0 +1,84 @@
+/* $NetBSD: h_execregs.S,v 1.2 2025/04/25 12:58:40 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, aligned to 16-byte */
+ stp fp, lr, [sp, #-(16 + ((NEXECREGS*8 + 15)/16)*16)]!
+
+ /* 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)
diff --git a/kernel/arch/aarch64/signalsphandler.S b/kernel/arch/aarch64/signalsphandler.S
new file mode 100644
index 000000000000..246ed383273d
--- /dev/null
+++ b/kernel/arch/aarch64/signalsphandler.S
@@ -0,0 +1,46 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/06/08 18:55:35 christos 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/06/08 18:55:35 christos Exp $")
+
+/*
+ * signalsphandler(signo@x0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ mov x0, sp
+ adrp x1, _C_LABEL(signalsp)
+ str x0, [x1, :lo12:_C_LABEL(signalsp)]
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/aarch64/stack_pointer.h b/kernel/arch/aarch64/stack_pointer.h
new file mode 100644
index 000000000000..3bd7e6f65335
--- /dev/null
+++ b/kernel/arch/aarch64/stack_pointer.h
@@ -0,0 +1,42 @@
+/* $NetBSD: stack_pointer.h,v 1.2 2025/04/20 22:31:00 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2024 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_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_AARCH64_STACK_POINTER_H
+
+#define MISALIGN_SP \
+ __asm__ volatile ( \
+ "sub sp, sp, #8" \
+ )
+
+#define FIX_SP \
+ __asm__ volatile ( \
+ "add sp, sp, #8" \
+ )
+
+#endif /* TESTS_KERNEL_ARCH_AARCH64_STACK_POINTER_H */
diff --git a/kernel/arch/aarch64/threadspfunc.S b/kernel/arch/aarch64/threadspfunc.S
new file mode 100644
index 000000000000..90f779d5e961
--- /dev/null
+++ b/kernel/arch/aarch64/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@x0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov x0, sp
+ ret
+END(threadspfunc)
diff --git a/kernel/arch/alpha/contextspfunc.S b/kernel/arch/alpha/contextspfunc.S
new file mode 100644
index 000000000000..da198f089124
--- /dev/null
+++ b/kernel/arch/alpha/contextspfunc.S
@@ -0,0 +1,50 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF(contextspfunc, 1)
+ LDGP(pv)
+ ldq at_reg, contextsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, contextdone(gp) !literal
+ jmp (pv), contextdone
+END(contextspfunc)
diff --git a/kernel/arch/alpha/execsp.S b/kernel/arch/alpha/execsp.S
new file mode 100644
index 000000000000..08eaa521fa03
--- /dev/null
+++ b/kernel/arch/alpha/execsp.S
@@ -0,0 +1,106 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/21 02:31:22 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/21 02:31:22 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * void execsp_start(void *stackpointer@a0, void (*cleanup@a1)(void),
+ * void *obj_main@a2, struct ps_strings *ps_strings@a3)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF(execsp_start, 4)
+ LDGP(pv)
+ ldq at_reg, startsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, __start(gp) !literal
+ jmp (pv), __start
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF(execsp_ctor, 0)
+ LDGP(pv)
+ ldq at_reg, ctorsp(gp) !literal
+ stq sp, 0(at_reg)
+ RET
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align 3
+ .quad execsp_ctor
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF(main, 2)
+ LDGP(pv)
+ ldq at_reg, mainsp(gp) !literal
+ stq sp, 0(at_reg)
+ mov zero, v0
+ RET
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF(execsp_dtor, 0)
+ LDGP(pv)
+ ldq at_reg, dtorsp(gp) !literal
+ stq sp, 0(at_reg)
+ ldq pv, execsp_main(gp) !literal
+ jmp (pv), execsp_main
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align 3
+ .quad execsp_dtor
+ .popsection
diff --git a/kernel/arch/alpha/signalsphandler.S b/kernel/arch/alpha/signalsphandler.S
new file mode 100644
index 000000000000..eaa3f2cdb654
--- /dev/null
+++ b/kernel/arch/alpha/signalsphandler.S
@@ -0,0 +1,49 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:57 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:57 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF(signalsphandler, 1)
+ LDGP(pv)
+ ldq at_reg, signalsp(gp) !literal
+ stq sp, 0(at_reg)
+ RET
+END(signalsphandler)
diff --git a/kernel/arch/alpha/stack_pointer.h b/kernel/arch/alpha/stack_pointer.h
new file mode 100644
index 000000000000..58c581ffde0a
--- /dev/null
+++ b/kernel/arch/alpha/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:31:57 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_ALPHA_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_ALPHA_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("lda $sp,-1($sp)" ::: "memory")
+#define FIX_SP __asm __volatile("lda $sp,1($sp)" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_ALPHA_STACK_POINTER_H */
diff --git a/kernel/arch/alpha/threadspfunc.S b/kernel/arch/alpha/threadspfunc.S
new file mode 100644
index 000000000000..413387bb0867
--- /dev/null
+++ b/kernel/arch/alpha/threadspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+ .set noat
+ .text
+
+/*
+ * void *threadspfunc(void *cookie@a0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF(threadspfunc, 1)
+ mov sp, v0
+ RET
+END(threadspfunc)
diff --git a/kernel/arch/arm/contextspfunc.S b/kernel/arch/arm/contextspfunc.S
new file mode 100644
index 000000000000..cf7fec19fb2a
--- /dev/null
+++ b/kernel/arch/arm/contextspfunc.S
@@ -0,0 +1,51 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/05/07 16:26:47 uwe 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+0: GOT_INIT(r0, .Lgot)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lcontextsp)
+ str r1, [r2]
+ b PLT_SYM(_C_LABEL(contextdone))
+
+ GOT_INITSYM(.Lgot, 0b)
+.Lcontextsp:
+ .word GOT_SYM(contextsp)
+END(contextspfunc)
diff --git a/kernel/arch/arm/execsp.S b/kernel/arch/arm/execsp.S
new file mode 100644
index 000000000000..b02587ef2b33
--- /dev/null
+++ b/kernel/arch/arm/execsp.S
@@ -0,0 +1,119 @@
+/* $NetBSD: execsp.S,v 1.4 2025/05/07 16:26:47 uwe 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.4 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@r0, void *obj_main@r1,
+ * void (*cleanup@r2)(void))
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+0: GOT_INIT(r3, .Lgot.execsp_start)
+ mov r4, sp
+ GOT_GET(r5, r3, .Lstartsp)
+ str r4, [r5]
+ b PLT_SYM(_C_LABEL(__start))
+
+ GOT_INITSYM(.Lgot.execsp_start, 0b)
+.Lstartsp:
+ .word GOT_SYM(startsp)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+0: GOT_INIT(r0, .Lgot.execsp_ctor)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lctorsp)
+ str r1, [r2]
+ RET
+
+ GOT_INITSYM(.Lgot.execsp_ctor, 0b)
+.Lctorsp:
+ .word GOT_SYM(ctorsp)
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw",%init_array
+ .p2align 2
+ .word _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@r0, char **argv@r1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+0: GOT_INIT(r0, .Lgot.main)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lmainsp)
+ str r1, [r2]
+ mov r0, #0
+ RET
+
+ GOT_INITSYM(.Lgot.main, 0b)
+.Lmainsp:
+ .word GOT_SYM(mainsp)
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+0: GOT_INIT(r0, .Lgot.execsp_dtor)
+ mov r1, sp
+ GOT_GET(r2, r0, .Ldtorsp)
+ str r1, [r2]
+ b PLT_SYM(_C_LABEL(execsp_main))
+
+ GOT_INITSYM(.Lgot.execsp_dtor, 0b)
+.Ldtorsp:
+ .word GOT_SYM(dtorsp)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw",%fini_array
+ .p2align 2
+ .word _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/arm/signalsphandler.S b/kernel/arch/arm/signalsphandler.S
new file mode 100644
index 000000000000..21b7d26a53b9
--- /dev/null
+++ b/kernel/arch/arm/signalsphandler.S
@@ -0,0 +1,51 @@
+/* $NetBSD: signalsphandler.S,v 1.3 2025/05/07 16:26:47 uwe 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.3 2025/05/07 16:26:47 uwe Exp $")
+
+/*
+ * signalsphandler(signo@r0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+0: GOT_INIT(r0, .Lgot)
+ mov r1, sp
+ GOT_GET(r2, r0, .Lsignalsp)
+ str r1, [r2]
+ RET
+
+ GOT_INITSYM(.Lgot, 0b)
+.Lsignalsp:
+ .word GOT_SYM(signalsp)
+END(signalsphandler)
diff --git a/kernel/arch/arm/stack_pointer.h b/kernel/arch/arm/stack_pointer.h
new file mode 100644
index 000000000000..73d10c341138
--- /dev/null
+++ b/kernel/arch/arm/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/25 02:24:01 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_ARM_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_ARM_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("sub sp, sp, #1")
+#define FIX_SP __asm __volatile("add sp, sp, #1")
+
+#endif /* TESTS_KERNEL_ARCH_ARM_STACK_POINTER_H */
diff --git a/kernel/arch/arm/threadspfunc.S b/kernel/arch/arm/threadspfunc.S
new file mode 100644
index 000000000000..5f34c547cadf
--- /dev/null
+++ b/kernel/arch/arm/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/25 02:24:01 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/25 02:24:01 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@r0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov r0, sp
+ RET
+END(threadspfunc)
diff --git a/kernel/arch/hppa/contextspfunc.S b/kernel/arch/hppa/contextspfunc.S
new file mode 100644
index 000000000000..683f59808c95
--- /dev/null
+++ b/kernel/arch/hppa/contextspfunc.S
@@ -0,0 +1,48 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/04/21 12:06:08 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF_ENTRY(contextspfunc)
+ addil LT%_C_LABEL(contextdone), %r19 /* r20 := contextdone */
+ ldw RT%_C_LABEL(contextdone)(%r1), %r20
+ addil LT%_C_LABEL(contextsp), %r19 /* r21 := &contextsp */
+ ldw RT%_C_LABEL(contextsp)(%r1), %r21
+ bv %r0(%r20) /* jump to contextdone */
+ stw %sp, 0(%r21) /* contextsp := sp */
+EXIT(contextspfunc)
diff --git a/kernel/arch/hppa/execregs.c b/kernel/arch/hppa/execregs.c
new file mode 100644
index 000000000000..c064d3a36e37
--- /dev/null
+++ b/kernel/arch/hppa/execregs.c
@@ -0,0 +1,387 @@
+/* $NetBSD: execregs.c,v 1.2 2025/02/28 16:08:19 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.2 2025/02/28 16:08:19 riastradh Exp $");
+
+#include "execregs.h"
+
+#include <errno.h>
+#include <spawn.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <unistd.h>
+
+extern char **environ;
+
+static unsigned long
+nonnull(unsigned long x)
+{
+
+ x |= x << 8;
+ x |= x << 16;
+ return x;
+}
+
+/*
+ * setfpregs()
+ *
+ * Set up all the floating-point registers with something nonzero
+ * in each one. We initialize the floating-point status register
+ * to set various bits so it's not all zero, but nothing that
+ * would trigger traps.
+ */
+static void
+setfpregs(void)
+{
+ static const uint64_t fpe[] = {
+ (__BITS(63,59) /* all exception flags (VZOUI) set */
+ | __BIT(58) /* C bit (comparison) set */
+ | __BITS(54,43) /* CQ (comparison queue) all set */
+ | __SHIFTIN(1, __BITS(42,41)) /* round toward zero */
+ | __SHIFTIN(0, __BIT(38)) /* no delayed trap */
+ | __SHIFTIN(1, __BIT(37)) /* Denormalized As Zero */
+ | __SHIFTIN(0, __BITS(36,32)) /* exceptions masked */
+ | 0x10101010),
+ 0x9191919111111111,
+ 0x9292929212121212,
+ 0x9393939313131313,
+ };
+ const uint64_t *fpep = fpe;
+
+ static const double fr[28] = {
+ 0x1.04p0, 0x1.05p0, 0x1.06p0, 0x1.07p0,
+ 0x1.08p0, 0x1.09p0, 0x1.0ap0, 0x1.0bp0,
+ 0x1.0cp0, 0x1.0dp0, 0x1.0ep0, 0x1.0fp0,
+ 0x1.10p0, 0x1.11p0, 0x1.12p0, 0x1.13p0,
+ 0x1.14p0, 0x1.15p0, 0x1.16p0, 0x1.17p0,
+ 0x1.18p0, 0x1.19p0, 0x1.1ap0, 0x1.1bp0,
+ 0x1.1cp0, 0x1.1dp0, 0x1.1ep0, 0x1.1fp0,
+ };
+ const double *frp = fr;
+
+ __asm volatile(
+ "fldds,ma 8(%0), %%fr0\n\t"
+ "fldds,ma 8(%0), %%fr1\n\t"
+ "fldds,ma 8(%0), %%fr2\n\t"
+ "fldds 0(%0), %%fr3"
+ : "+r"(fpep)
+ : "m"(fpe));
+
+ __asm volatile(
+ "fldds,ma 8(%0), %%fr4\n\t"
+ "fldds,ma 8(%0), %%fr5\n\t"
+ "fldds,ma 8(%0), %%fr6\n\t"
+ "fldds,ma 8(%0), %%fr7\n\t"
+ "fldds,ma 8(%0), %%fr8\n\t"
+ "fldds,ma 8(%0), %%fr9\n\t"
+ "fldds,ma 8(%0), %%fr10\n\t"
+ "fldds,ma 8(%0), %%fr11\n\t"
+ "fldds,ma 8(%0), %%fr12\n\t"
+ "fldds,ma 8(%0), %%fr13\n\t"
+ "fldds,ma 8(%0), %%fr14\n\t"
+ "fldds,ma 8(%0), %%fr15\n\t"
+ "fldds,ma 8(%0), %%fr16\n\t"
+ "fldds,ma 8(%0), %%fr17\n\t"
+ "fldds,ma 8(%0), %%fr18\n\t"
+ "fldds,ma 8(%0), %%fr19\n\t"
+ "fldds,ma 8(%0), %%fr20\n\t"
+ "fldds,ma 8(%0), %%fr21\n\t"
+ "fldds,ma 8(%0), %%fr22\n\t"
+ "fldds,ma 8(%0), %%fr23\n\t"
+ "fldds,ma 8(%0), %%fr24\n\t"
+ "fldds,ma 8(%0), %%fr25\n\t"
+ "fldds,ma 8(%0), %%fr26\n\t"
+ "fldds,ma 8(%0), %%fr27\n\t"
+ "fldds,ma 8(%0), %%fr28\n\t"
+ "fldds,ma 8(%0), %%fr29\n\t"
+ "fldds,ma 8(%0), %%fr30\n\t"
+ "fldds 0(%0), %%fr31"
+ : "+r"(frp)
+ : "m"(fr));
+}
+
+/*
+ * setpsw()
+ *
+ * Set some bits in PSW, the processor status word.
+ */
+static void
+setpsw(void)
+{
+ uint32_t x = 0xe0000000, y = 0xffffffff, sum;
+
+ /*
+ * Trigger some arithmetic that causes the carry/borrow
+ * (PSW[C/B]) bits to be set.
+ *
+ * XXX Also set PSW[V].
+ */
+ __asm volatile("sh3add %[sum], %[x], %[y]"
+ : /* outputs */ [sum] "=r"(sum)
+ : /* inputs */ [x] "r"(x), [y] "r"(y));
+}
+
+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;
+
+ setfpregs();
+ setpsw();
+
+ /*
+ * 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;
+
+ setfpregs();
+ setpsw();
+
+ /*
+ * 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;
+}
diff --git a/kernel/arch/hppa/execregs.h b/kernel/arch/hppa/execregs.h
new file mode 100644
index 000000000000..ae4dbb2e39c0
--- /dev/null
+++ b/kernel/arch/hppa/execregs.h
@@ -0,0 +1,157 @@
+/* $NetBSD: execregs.h,v 1.2 2025/02/28 16:08:19 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 96
+
+#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",
+
+ "psw", /* user-visible PSW bits: C/B and V */
+
+ /* Floating-point registers */
+ "fr0l",
+ "fr0r",
+ "fr1l",
+ "fr1r",
+ "fr2l",
+ "fr2r",
+ "fr3l",
+ "fr3r",
+ "fr4l",
+ "fr4r",
+ "fr5l",
+ "fr5r",
+ "fr6l",
+ "fr6r",
+ "fr7l",
+ "fr7r",
+ "fr8l",
+ "fr8r",
+ "fr9l",
+ "fr9r",
+ "fr10l",
+ "fr10r",
+ "fr11l",
+ "fr11r",
+ "fr12l",
+ "fr12r",
+ "fr13l",
+ "fr13r",
+ "fr14l",
+ "fr14r",
+ "fr15l",
+ "fr15r",
+ "fr16l",
+ "fr16r",
+ "fr17l",
+ "fr17r",
+ "fr18l",
+ "fr18r",
+ "fr19l",
+ "fr19r",
+ "fr20l",
+ "fr20r",
+ "fr21l",
+ "fr21r",
+ "fr22l",
+ "fr22r",
+ "fr23l",
+ "fr23r",
+ "fr24l",
+ "fr24r",
+ "fr25l",
+ "fr25r",
+ "fr26l",
+ "fr26r",
+ "fr27l",
+ "fr27r",
+ "fr28l",
+ "fr28r",
+ "fr29l",
+ "fr29r",
+ "fr30l",
+ "fr30r",
+ "fr31l",
+ "fr31r",
+};
+
+__CTASSERT(NEXECREGS == __arraycount(regname));
+
+int execregschild(char *);
+pid_t spawnregschild(char *, int);
+
+#endif /* _LOCORE */
+
+#endif /* TESTS_KERNEL_ARCH_HPPA_EXECREGS_H */
diff --git a/kernel/arch/hppa/execsp.S b/kernel/arch/hppa/execsp.S
new file mode 100644
index 000000000000..520421a1f1fc
--- /dev/null
+++ b/kernel/arch/hppa/execsp.S
@@ -0,0 +1,130 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:32:49 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $")
+
+ .import _GLOBAL_OFFSET_TABLE_
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@arg0,
+ * void (*cleanup@arg1)(void), void *obj_main@arg2)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF_ENTRY(execsp_start)
+ /*
+ * Set up the data pointer (r19) and linkage table register
+ * (r27) like the real startup routine so we can get at the
+ * global symbols startsp and __start.
+ *
+ * XXX Not really sure why we need to set up r27, since we only
+ * use r19 here and the real startup routine, __start, will set
+ * up both r19 and r27 anyway. But this crashes with SIGSEGV
+ * shortly after startup if we don't set up r27, and gdb
+ * crashes on my attempts to single-step, so I'll just leave
+ * the initialization of r27 here for now until someone is
+ * motivated by the potential for a single-instruction
+ * micro-optimization in this test program to find out why r27
+ * is needed too.
+ */
+ bl L$lpc, %r27
+ depi 0, 31, 2, %r27
+L$lpc: addil L'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 8), %r27
+ ldo R'_GLOBAL_OFFSET_TABLE_ - ($PIC_pcrel$0 - 12)(%r1), %r27
+ copy %r27, %r19
+
+ addil LT%_C_LABEL(startsp), %r19 /* r20 := &startsp */
+ ldw RT%_C_LABEL(startsp)(%r1), %r20
+
+ /* PIC_TAILCALL(__start), if we had it */
+ addil LT%_C_LABEL(__start), %r19 /* r1 := __start */
+ ldw RT%_C_LABEL(__start)(%r1), %r1
+ bv %r0(%r1) /* jump to __start */
+ stw %sp, 0(%r20) /* startsp := sp */
+EXIT(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF_ENTRY(execsp_ctor)
+ addil LT%_C_LABEL(ctorsp), %r19 /* r1 := &ctorsp */
+ ldw RT%_C_LABEL(ctorsp)(%r1), %r1
+ bv %r0(%rp) /* return */
+ stw %sp, 0(%r1) /* ctorsp := sp */
+EXIT(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 2
+ .word _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@arg0, char **argv@arg1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF_ENTRY(main)
+ addil LT%_C_LABEL(mainsp), %r19 /* r1 := &mainsp */
+ ldw RT%_C_LABEL(mainsp)(%r1), %r1
+ stw %sp, 0(%r1) /* ctorsp := sp */
+ bv %r0(%rp) /* return... */
+ copy %r0, %ret0 /* ...zero */
+EXIT(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF_ENTRY(execsp_dtor)
+ addil LT%_C_LABEL(dtorsp), %r19 /* r20 := &dtorsp */
+ ldw RT%_C_LABEL(dtorsp)(%r1), %r20
+
+ /* PIC_TAILCALL(__start), if we had it */
+ addil LT%_C_LABEL(execsp_main), %r19 /* r1 := execsp_main */
+ ldw RT%_C_LABEL(execsp_main)(%r1), %r1
+ bv %r0(%r1) /* jump to execsp_main */
+ stw %sp, 0(%r20) /* startsp := sp */
+EXIT(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 2
+ .word _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/hppa/h_execregs.S b/kernel/arch/hppa/h_execregs.S
new file mode 100644
index 000000000000..ad7a50a5b932
--- /dev/null
+++ b/kernel/arch/hppa/h_execregs.S
@@ -0,0 +1,168 @@
+/* $NetBSD: h_execregs.S,v 1.2 2025/02/28 16:08:19 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 */
+
+ addc %t1, %r0, %r0 /* t1 := PSW[C/B]{0} */
+ zdep %t1, 23, 8, %t1 /* t1 := PSW */
+ stw %t1, (4*(31 - NEXECREGS))(%sp)
+
+ /* store the fp registers */
+ ldo (4*(32 - NEXECREGS))(%sp), %t1
+ fstd,ma %fr0, 8(%t1)
+ fstd,ma %fr1, 8(%t1)
+ fstd,ma %fr2, 8(%t1)
+ fstd,ma %fr3, 8(%t1)
+ fstd,ma %fr4, 8(%t1)
+ fstd,ma %fr5, 8(%t1)
+ fstd,ma %fr6, 8(%t1)
+ fstd,ma %fr7, 8(%t1)
+ fstd,ma %fr8, 8(%t1)
+ fstd,ma %fr9, 8(%t1)
+ fstd,ma %fr10, 8(%t1)
+ fstd,ma %fr11, 8(%t1)
+ fstd,ma %fr12, 8(%t1)
+ fstd,ma %fr13, 8(%t1)
+ fstd,ma %fr14, 8(%t1)
+ fstd,ma %fr15, 8(%t1)
+ fstd,ma %fr16, 8(%t1)
+ fstd,ma %fr17, 8(%t1)
+ fstd,ma %fr18, 8(%t1)
+ fstd,ma %fr19, 8(%t1)
+ fstd,ma %fr20, 8(%t1)
+ fstd,ma %fr21, 8(%t1)
+ fstd,ma %fr22, 8(%t1)
+ fstd,ma %fr23, 8(%t1)
+ fstd,ma %fr24, 8(%t1)
+ fstd,ma %fr25, 8(%t1)
+ fstd,ma %fr26, 8(%t1)
+ fstd,ma %fr27, 8(%t1)
+ fstd,ma %fr28, 8(%t1)
+ fstd,ma %fr29, 8(%t1)
+ fstd,ma %fr30, 8(%t1)
+ fstd %fr31, 0(%t1)
+
+ /* 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)
diff --git a/kernel/arch/hppa/signalsphandler.S b/kernel/arch/hppa/signalsphandler.S
new file mode 100644
index 000000000000..01633d99ff8b
--- /dev/null
+++ b/kernel/arch/hppa/signalsphandler.S
@@ -0,0 +1,46 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:32:49 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:32:49 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@arg0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF_ENTRY(signalsphandler)
+ addil LT%_C_LABEL(signalsp), %r19 /* r1 := &signalsp */
+ ldw RT%_C_LABEL(signalsp)(%r1), %r1
+ bv %r0(%rp) /* return */
+ stw %sp, 0(%r1) /* signalsp := sp */
+EXIT(signalsphandler)
diff --git a/kernel/arch/hppa/stack_pointer.h b/kernel/arch/hppa/stack_pointer.h
new file mode 100644
index 000000000000..acb73443621c
--- /dev/null
+++ b/kernel/arch/hppa/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:32:49 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_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_HPPA_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("ldo 1(%%sp),%%sp" ::: "memory")
+#define FIX_SP __asm __volatile("ldo -1(%%sp),%%sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_HPPA_STACK_POINTER_H */
diff --git a/kernel/arch/hppa/threadspfunc.S b/kernel/arch/hppa/threadspfunc.S
new file mode 100644
index 000000000000..f1b1d21fadfe
--- /dev/null
+++ b/kernel/arch/hppa/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@arg0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF_ENTRY(threadspfunc)
+ bv %r0(%rp) /* return to caller */
+ copy %sp, %ret0 /* return sp */
+EXIT(threadspfunc)
diff --git a/kernel/arch/i386/contextspfunc.S b/kernel/arch/i386/contextspfunc.S
new file mode 100644
index 000000000000..cc097cc61325
--- /dev/null
+++ b/kernel/arch/i386/contextspfunc.S
@@ -0,0 +1,56 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(contextsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(contextdone)@PLT
+END(contextspfunc)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/execregs.c b/kernel/arch/i386/execregs.c
new file mode 100644
index 000000000000..477cd82989e9
--- /dev/null
+++ b/kernel/arch/i386/execregs.c
@@ -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;
+}
diff --git a/kernel/arch/i386/execregs.h b/kernel/arch/i386/execregs.h
new file mode 100644
index 000000000000..f512bbabc1c7
--- /dev/null
+++ b/kernel/arch/i386/execregs.h
@@ -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 */
diff --git a/kernel/arch/i386/execsp.S b/kernel/arch/i386/execsp.S
new file mode 100644
index 000000000000..133af37715ad
--- /dev/null
+++ b/kernel/arch/i386/execsp.S
@@ -0,0 +1,112 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:33:13 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@edx)(void), void *obj_main@ecx,
+ * struct ps_strings *ps_strings@ebx)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(startsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(__start)@PLT
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(ctorsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@rdi, char **argv@rsi, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(mainsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ xorl %eax,%eax
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(dtorsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ jmp _C_LABEL(execsp_main)@PLT
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/h_execregs.S b/kernel/arch/i386/h_execregs.S
new file mode 100644
index 000000000000..ac9b843f9d86
--- /dev/null
+++ b/kernel/arch/i386/h_execregs.S
@@ -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)
diff --git a/kernel/arch/i386/signalsphandler.S b/kernel/arch/i386/signalsphandler.S
new file mode 100644
index 000000000000..418825e78ab6
--- /dev/null
+++ b/kernel/arch/i386/signalsphandler.S
@@ -0,0 +1,56 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:13 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:13 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@esp[4])
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ call getpc_eax
+ addl $_GLOBAL_OFFSET_TABLE_,%eax
+ movl _C_LABEL(signalsp)@GOT(%eax),%eax
+ movl %esp,(%eax)
+ ret
+END(signalsphandler)
+
+ .text
+ _ALIGN_TEXT
+ .local getpc_eax
+ .type getpc_eax,@function
+getpc_eax:
+ movl (%esp),%eax
+ ret
+END(getpc_eax)
diff --git a/kernel/arch/i386/stack_pointer.h b/kernel/arch/i386/stack_pointer.h
new file mode 100644
index 000000000000..6c95a28ab7b6
--- /dev/null
+++ b/kernel/arch/i386/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:33:13 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_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_I386_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addl $-1,%esp")
+#define FIX_SP __asm __volatile("addl $1,%esp")
+
+#endif /* TESTS_KERNEL_ARCH_I386_STACK_POINTER_H */
diff --git a/kernel/arch/i386/threadspfunc.S b/kernel/arch/i386/threadspfunc.S
new file mode 100644
index 000000000000..af03ff4c550e
--- /dev/null
+++ b/kernel/arch/i386/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@esp[4])
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mov %esp,%eax
+ ret
+END(threadspfunc)
diff --git a/kernel/arch/mips/contextspfunc.S b/kernel/arch/mips/contextspfunc.S
new file mode 100644
index 000000000000..bee0a610dc86
--- /dev/null
+++ b/kernel/arch/mips/contextspfunc.S
@@ -0,0 +1,53 @@
+/* $NetBSD: contextspfunc.S,v 1.2 2025/04/27 16:49:54 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+LEAF(contextspfunc)
+ PIC_PROLOGUE(contextspfunc) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(contextsp) /* load t1 := &contextsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store contextsp := stack pointer */
+
+ PIC_TAILCALL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/mips/execsp.S b/kernel/arch/mips/execsp.S
new file mode 100644
index 000000000000..b4f2dcfd6e2c
--- /dev/null
+++ b/kernel/arch/mips/execsp.S
@@ -0,0 +1,118 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/27 16:49:54 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * void execsp_start(void *stackpointer@a0, void (*cleanup@a1)(void),
+ * void *obj_main@a2, struct ps_strings *ps_strings@a3)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+LEAF(execsp_start)
+ PIC_PROLOGUE(execsp_start) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(startsp) /* load t1 := &startsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store startsp := stack pointer */
+
+ PIC_TAILCALL(__start) /* gp restore, uses t3 */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+LEAF(execsp_ctor)
+ PIC_PROLOGUE(execsp_ctor) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(ctorsp) /* load t1 := &ctorsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store ctorsp := stack pointer */
+
+ PIC_RETURN() /* gp restore, uses t3 */
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_ctor)
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+LEAF(main)
+ PIC_PROLOGUE(main) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(mainsp) /* load t1 := &mainsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store mainsp := stack pointer */
+
+ move v0, zero /* return 0 */
+
+ PIC_RETURN() /* gp restore, uses t3 */
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+LEAF(execsp_dtor)
+ PIC_PROLOGUE(execsp_dtor) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(dtorsp) /* load t1 := &dtorsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store dtorsp := stack pointer */
+
+ PIC_TAILCALL(execsp_main) /* gp restore, uses t3 */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/mips/signalsphandler.S b/kernel/arch/mips/signalsphandler.S
new file mode 100644
index 000000000000..e742c2c95701
--- /dev/null
+++ b/kernel/arch/mips/signalsphandler.S
@@ -0,0 +1,53 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/04/27 16:49:54 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/04/27 16:49:54 riastradh Exp $")
+
+#include "SYS.h"
+
+ .text
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+LEAF(signalsphandler)
+ PIC_PROLOGUE(signalsphandler) /* gp setup, sets t3 */
+
+ PTR_LA t1, _C_LABEL(signalsp) /* load t1 := &signalsp */
+ NOP_L /* load hazard */
+ PTR_S sp, 0(t1) /* store signalsp := stack pointer */
+
+ PIC_RETURN()
+END(signalsphandler)
diff --git a/kernel/arch/mips/stack_pointer.h b/kernel/arch/mips/stack_pointer.h
new file mode 100644
index 000000000000..288c58928526
--- /dev/null
+++ b/kernel/arch/mips/stack_pointer.h
@@ -0,0 +1,40 @@
+/* $NetBSD: stack_pointer.h,v 1.2 2025/04/21 14:17:38 rin 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_MIPS_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_MIPS_STACK_POINTER_H
+
+#ifdef __mips_o32
+#define MISALIGN_SP __asm __volatile("addiu $sp,$sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("addiu $sp,$sp,1" ::: "memory")
+#else
+#define MISALIGN_SP __asm __volatile("daddiu $sp,$sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("daddiu $sp,$sp,1" ::: "memory")
+#endif
+
+#endif /* TESTS_KERNEL_ARCH_MIPS_STACK_POINTER_H */
diff --git a/kernel/arch/mips/threadspfunc.S b/kernel/arch/mips/threadspfunc.S
new file mode 100644
index 000000000000..a42362fee67d
--- /dev/null
+++ b/kernel/arch/mips/threadspfunc.S
@@ -0,0 +1,46 @@
+/* $NetBSD: threadspfunc.S,v 1.2 2025/04/21 03:47: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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.2 2025/04/21 03:47:32 riastradh Exp $")
+
+ .text
+ .set noreorder
+
+/*
+ * void *threadspfunc(void *cookie@a0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+LEAF(threadspfunc)
+ j ra
+ move v0, sp
+END(threadspfunc)
diff --git a/kernel/arch/riscv/contextspfunc.S b/kernel/arch/riscv/contextspfunc.S
new file mode 100644
index 000000000000..7f405123deb3
--- /dev/null
+++ b/kernel/arch/riscv/contextspfunc.S
@@ -0,0 +1,45 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ PTR_LA t0, _C_LABEL(contextsp) /* t0 := &contextsp */
+ PTR_S sp, 0(t0) /* contextsp := sp */
+ tail _C_LABEL(contextdone) /* tail call contextdone */
+END(contextspfunc)
diff --git a/kernel/arch/riscv/execsp.S b/kernel/arch/riscv/execsp.S
new file mode 100644
index 000000000000..d045cbf0d3af
--- /dev/null
+++ b/kernel/arch/riscv/execsp.S
@@ -0,0 +1,100 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:34:07 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@a0)(void),
+ * struct ps_strings *ps_strings@a1)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ .option push
+ .option norelax /* Don't optimize this into `mv gp, gp'! */
+ lla gp, __global_pointer$
+ .option pop
+
+ PTR_LA t0, _C_LABEL(startsp) /* t0 := &startsp */
+ PTR_S sp, 0(t0) /* startsp := sp */
+ tail _C_LABEL(__start) /* tail call __start */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ PTR_LA t0, _C_LABEL(ctorsp) /* t0 := &ctorsp */
+ PTR_S sp, 0(t0) /* ctorsp := sp */
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .init_array,"aw"
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ PTR_LA t0, _C_LABEL(mainsp) /* t0 := &mainsp */
+ PTR_S sp, 0(t0) /* mainsp := sp */
+ li a0, 0 /* return 0 */
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ PTR_LA t0, _C_LABEL(dtorsp) /* t0 := &dtorsp */
+ PTR_S sp, 0(t0) /* dtorsp := sp */
+ tail _C_LABEL(execsp_main) /* tail call execsp_main */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .fini_array,"aw"
+ .p2align PTR_SCALESHIFT
+ PTR_WORD _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/riscv/signalsphandler.S b/kernel/arch/riscv/signalsphandler.S
new file mode 100644
index 000000000000..6a5d2f752ee2
--- /dev/null
+++ b/kernel/arch/riscv/signalsphandler.S
@@ -0,0 +1,45 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:34:07 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:34:07 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@a0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ PTR_LA t0, _C_LABEL(signalsp) /* t0 := &signalsp */
+ PTR_S sp, 0(t0) /* signalsp := sp */
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/riscv/stack_pointer.h b/kernel/arch/riscv/stack_pointer.h
new file mode 100644
index 000000000000..22c246e4a86a
--- /dev/null
+++ b/kernel/arch/riscv/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:34:07 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_RISCV_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_RISCV_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addi sp,sp,-1" ::: "memory")
+#define FIX_SP __asm __volatile("addi sp,sp,1" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_RISCV_STACK_POINTER_H */
diff --git a/kernel/arch/riscv/threadspfunc.S b/kernel/arch/riscv/threadspfunc.S
new file mode 100644
index 000000000000..30291ad2eb73
--- /dev/null
+++ b/kernel/arch/riscv/threadspfunc.S
@@ -0,0 +1,43 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:44 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@o0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ mv a0, sp /* return sp */
+ ret /* return to caller */
+END(threadspfunc)
diff --git a/kernel/arch/sh3/asm.h b/kernel/arch/sh3/asm.h
new file mode 100644
index 000000000000..16389b5c299e
--- /dev/null
+++ b/kernel/arch/sh3/asm.h
@@ -0,0 +1,58 @@
+/* $NetBSD: asm.h,v 1.1 2025/04/26 22:34:52 uwe 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_SH3_ASM_H
+#define TESTS_KERNEL_ARCH_SH3_ASM_H
+
+#include <machine/asm.h>
+
+/*
+ * We define convenience macros to hide the difference between direct
+ * and PIC function calls, but with variable accesses it gets a bit
+ * more unwieldy, so don't impose this on the general audience
+ */
+
+#ifdef __PIC__
+
+#define VAR_DATUM(v) .long v@GOT
+
+#define MOVL_VAR(label, reg) \
+ mov.l label, r0; \
+ mov.l @(r0, r12), reg
+
+#else
+
+#define VAR_DATUM(v) .long v
+
+#define MOVL_VAR(label, reg) \
+ mov.l label, reg; \
+
+#endif
+
+
+#endif /* TESTS_KERNEL_ARCH_SH3_ASM_H */
diff --git a/kernel/arch/sh3/contextspfunc.S b/kernel/arch/sh3/contextspfunc.S
new file mode 100644
index 000000000000..51e91133417e
--- /dev/null
+++ b/kernel/arch/sh3/contextspfunc.S
@@ -0,0 +1,55 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/26 22:34:52 uwe 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 "asm.h"
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $")
+
+
+/*
+ * void contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ mov sp, r1
+ PIC_PROLOGUE(.L_GOT)
+
+ MOVL_VAR(.L_contextsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L_contextdone, r0
+1: JUMP r0
+ PIC_EPILOGUE_SLOT
+
+ .p2align 2
+.L_GOT: PIC_GOT_DATUM
+.L_contextsp: VAR_DATUM(contextsp)
+.L_contextdone: CALL_DATUM(contextdone, 1b)
+ SET_ENTRY_SIZE(contextspfunc)
diff --git a/kernel/arch/sh3/execsp.S b/kernel/arch/sh3/execsp.S
new file mode 100644
index 000000000000..639ed4e6487b
--- /dev/null
+++ b/kernel/arch/sh3/execsp.S
@@ -0,0 +1,146 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/27 00:03:46 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 "asm.h"
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/27 00:03:46 riastradh Exp $")
+
+
+/*
+ * void execsp_start()
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ *
+ * See sys/arch/sh3/sh3/sh3_machdep.c setregs()
+ */
+ENTRY(execsp_start)
+ mov sp, r1 // for consistency, don't need to
+ PIC_PROLOGUE_NOSAVE(.L_start_GOT)
+
+ MOVL_VAR(.L_startsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L___start, r0
+1: JUMP r0
+ nop
+
+ .p2align 2
+.L_start_GOT: PIC_GOT_DATUM
+.L_startsp: VAR_DATUM(startsp)
+.L___start: CALL_DATUM(__start, 1b)
+ SET_ENTRY_SIZE(execsp_start)
+
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ mov sp, r1
+ PIC_PROLOGUE(.L_ctor_GOT)
+
+ MOVL_VAR(.L_ctorsp, r0)
+ mov.l r1, @r0
+
+ rts
+ PIC_EPILOGUE_SLOT
+
+ .p2align 2
+.L_ctor_GOT: PIC_GOT_DATUM
+.L_ctorsp: VAR_DATUM(ctorsp)
+ SET_ENTRY_SIZE(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors, "aw", @progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+ .popsection
+
+
+/*
+ * int main(int argc, char **argv, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ mov sp, r1
+ PIC_PROLOGUE(.L_main_GOT)
+
+ MOVL_VAR(.L_mainsp, r0)
+ mov.l r1, @r0
+
+ PIC_EPILOGUE
+ rts
+ mov #0, r0
+
+ .p2align 2
+.L_main_GOT: PIC_GOT_DATUM
+.L_mainsp: VAR_DATUM(mainsp)
+ SET_ENTRY_SIZE(main)
+
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ mov sp, r1
+ sts.l pr, @-sp
+ PIC_PROLOGUE(.L_dtor_GOT)
+
+ MOVL_VAR(.L_dtorsp, r0)
+ mov.l r1, @r0
+
+ mov.l .L_execsp_main, r0
+1: CALL r0
+ nop
+
+ PIC_EPILOGUE
+ lds.l @sp+, pr
+ rts
+ nop
+
+ .p2align 2
+.L_dtor_GOT: PIC_GOT_DATUM
+.L_dtorsp: VAR_DATUM(dtorsp)
+.L_execsp_main: CALL_DATUM(execsp_main, 1b)
+ SET_ENTRY_SIZE(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors, "aw", @progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/sh3/h_execregs.S b/kernel/arch/sh3/h_execregs.S
new file mode 100644
index 000000000000..d7c91161c34a
--- /dev/null
+++ b/kernel/arch/sh3/h_execregs.S
@@ -0,0 +1,86 @@
+/* $NetBSD: h_execregs.S,v 1.1 2025/04/27 02:24:07 uwe 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 <machine/asm.h>
+#include <machine/mcontext.h>
+#include <sys/syscall.h>
+
+
+// ELF entry point
+ENTRY(execregs_start)
+ // __gregset_t r; // mcontext (but general regs only)
+ mov.l sp, @-sp
+ mov.l r0, @-sp
+ mov.l r1, @-sp
+ mov.l r2, @-sp
+ mov.l r3, @-sp
+ mov.l r4, @-sp
+ mov.l r5, @-sp
+ mov.l r6, @-sp
+ mov.l r7, @-sp
+ mov.l r8, @-sp
+ mov.l r9, @-sp
+ mov.l r10, @-sp
+ mov.l r11, @-sp
+ mov.l r12, @-sp
+ mov.l r13, @-sp
+ mov.l r14, @-sp
+ sts.l pr, @-sp
+ sts.l mach, @-sp
+ sts.l macl, @-sp
+ mov #0, r0
+ mov.l r0, @-sp // _REG_SR is privileged
+ mova .Lend, r0 // _REG_PC
+ mov.l @r0, r1
+ sub r1, r0
+ mov.l r0, @-sp
+ stc.l gbr, @-sp
+
+ // write(STDOUT_FILENO, &r, sizeof(__gregset_t))
+ mov #1, r4
+ mov sp, r5
+ mov #(_NGREG * 4), r6
+ mov #SYS_write, r0
+ trapa #0x80
+
+ // _exit(0)
+ mov #0, r4
+ mov #SYS_exit, r0
+ trapa #0x80
+
+ .p2align 2
+.Lend: .long .Lend - execregs_start
+ SET_ENTRY_SIZE(execregs_start)
+
+
+// main stub to simplify linking
+ENTRY(main)
+ rts
+ mov #0, r0
+ SET_ENTRY_SIZE(main)
diff --git a/kernel/arch/sh3/signalsphandler.S b/kernel/arch/sh3/signalsphandler.S
new file mode 100644
index 000000000000..e0a86ae40558
--- /dev/null
+++ b/kernel/arch/sh3/signalsphandler.S
@@ -0,0 +1,61 @@
+/* $NetBSD: signalsphandler.S,v 1.2 2025/04/26 23:49:55 uwe 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 "asm.h"
+RCSID("$NetBSD: signalsphandler.S,v 1.2 2025/04/26 23:49:55 uwe Exp $")
+
+
+/*
+ * void signalsphandler(int signo)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ mov sp, r1
+#ifdef __PIC__
+ // PR kern/59327: don't touch stack as SP may be misaligned
+ // and as SuperH is a strict alignment architecture, we will
+ // get SIGBUS if we try to save registers on the stack
+ mov r12, r2
+#endif
+ PIC_PROLOGUE_NOSAVE(.L_GOT)
+
+ MOVL_VAR(.L_signalsp, r0)
+ mov.l r1, @r0
+
+#ifdef __PIC__
+ mov r2, r12
+#endif
+ rts
+ nop
+
+ .p2align 2
+.L_GOT: PIC_GOT_DATUM
+.L_signalsp: VAR_DATUM(signalsp)
+ SET_ENTRY_SIZE(signalsphandler)
diff --git a/kernel/arch/sh3/stack_pointer.h b/kernel/arch/sh3/stack_pointer.h
new file mode 100644
index 000000000000..ed3366602f56
--- /dev/null
+++ b/kernel/arch/sh3/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/26 22:34:52 uwe 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_SH3_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_SH3_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("add #-1, sp" ::: "memory")
+#define FIX_SP __asm __volatile("add #+1, sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_SH3_STACK_POINTER_H */
diff --git a/kernel/arch/sh3/threadspfunc.S b/kernel/arch/sh3/threadspfunc.S
new file mode 100644
index 000000000000..de065e54191b
--- /dev/null
+++ b/kernel/arch/sh3/threadspfunc.S
@@ -0,0 +1,41 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/26 22:34:52 uwe 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 "asm.h"
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/26 22:34:52 uwe Exp $")
+
+
+/*
+ * void *threadspfunc(void *cookie)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ rts
+ mov sp, r0
+ SET_ENTRY_SIZE(threadspfunc)
diff --git a/kernel/arch/sparc/contextspfunc.S b/kernel/arch/sparc/contextspfunc.S
new file mode 100644
index 000000000000..a24185cabd73
--- /dev/null
+++ b/kernel/arch/sparc/contextspfunc.S
@@ -0,0 +1,64 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(contextsp), %g1, %o5) /* o5 := &contextsp */
+ call _C_LABEL(contextdone) /* jump to contextdone */
+ st %sp, [LO(_C_LABEL(contextsp), %o5)] /* contextsp := sp */
+ /* don't care what happens here, caller must never return */
+ ta 1 /* Tcc, trap always */
+END(contextspfunc)
diff --git a/kernel/arch/sparc/execsp.S b/kernel/arch/sparc/execsp.S
new file mode 100644
index 000000000000..4b225dd6d550
--- /dev/null
+++ b/kernel/arch/sparc/execsp.S
@@ -0,0 +1,123 @@
+/* $NetBSD: execsp.S,v 1.1 2025/04/20 22:33:41 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * void execsp_start(struct ps_strings *ps_strings@g1,
+ * void *obj_main@g2, void (*cleanup@g3)(void))
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ PIC_PROLOGUE(%o1, %o2) /* o1 := GOT, clobber o2 */
+ SETHI(_C_LABEL(startsp), %o1, %o2) /* o2 := &startup */
+ call _C_LABEL(__start) /* jump to start via PLT */
+ st %sp, [LO(_C_LABEL(startsp), %o2)] /* startsp := sp */
+ /* don't care what happens here, caller must never return */
+ ta 1 /* Tcc, trap always */
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(ctorsp), %g1, %o5) /* o5 := &ctorsp */
+ retl /* return to caller */
+ st %sp, [LO(_C_LABEL(ctorsp), %o5)] /* ctorsp := sp */
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .pushsection .ctors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_ctor)
+ .popsection
+
+/*
+ * int main(int argc@a0, char **argv@a1, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(mainsp), %g1, %o5) /* o5 := &mainsp */
+ st %sp, [LO(_C_LABEL(mainsp), %o5)] /* mainsp := sp */
+ retl /* return to caller */
+ mov 0, %o0 /* return 0 */
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(dtorsp), %g1, %o5) /* o5 := &dtorsp - &GOT */
+ st %sp, [LO(_C_LABEL(dtorsp), %o5)] /* dtorsp := sp */
+ mov %o7, %o5 /* save return address */
+ call _C_LABEL(execsp_main) /* tail call to execsp_main */
+ mov %o5, %o7 /* restore return address */
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .pushsection .dtors,"aw",@progbits
+ .p2align 2
+ .long _C_LABEL(execsp_dtor)
+ .popsection
diff --git a/kernel/arch/sparc/signalsphandler.S b/kernel/arch/sparc/signalsphandler.S
new file mode 100644
index 000000000000..187091d23c01
--- /dev/null
+++ b/kernel/arch/sparc/signalsphandler.S
@@ -0,0 +1,62 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:41 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:33:41 riastradh Exp $")
+
+#ifdef __PIC__
+#define SETHI(label, got, reg) \
+ set label, reg; /* reg := &label - &GOT */ \
+ ld [got + reg], reg /* reg := &label */
+#define LO(label, reg) \
+ reg
+#else
+#define SETHI(label, got, reg) \
+ sethi %hi(label), reg /* reg := &label - %lo(label) */
+#define LO(label, reg) \
+ reg + %lo(label)
+#endif
+
+ .text
+
+/*
+ * signalsphandler(signo@o0)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ /* Reminder: o6 is frame pointer, o7 + 8 is return address. */
+ PIC_PROLOGUE(%g1, %o5) /* g1 := &GOT, clobber o5 */
+ SETHI(_C_LABEL(signalsp), %g1, %o5) /* o5 := &signalsp */
+ retl /* return to caller */
+ st %sp, [LO(_C_LABEL(signalsp), %o5)] /* signalsp := sp */
+END(signalsphandler)
diff --git a/kernel/arch/sparc/stack_pointer.h b/kernel/arch/sparc/stack_pointer.h
new file mode 100644
index 000000000000..20c06bec77cd
--- /dev/null
+++ b/kernel/arch/sparc/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:33:41 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_SPARC_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_SPARC_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("dec %%sp" ::: "memory")
+#define FIX_SP __asm __volatile("inc %%sp" ::: "memory")
+
+#endif /* TESTS_KERNEL_ARCH_SPARC_STACK_POINTER_H */
diff --git a/kernel/arch/sparc/threadspfunc.S b/kernel/arch/sparc/threadspfunc.S
new file mode 100644
index 000000000000..794804d85741
--- /dev/null
+++ b/kernel/arch/sparc/threadspfunc.S
@@ -0,0 +1,45 @@
+/* $NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:45 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+ .text
+
+/*
+ * void *threadspfunc(void *cookie@o0)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ retl /* return to caller */
+ mov %sp, %o0 /* return sp */
+END(threadspfunc)
diff --git a/kernel/arch/vax/execregs.c b/kernel/arch/vax/execregs.c
new file mode 100644
index 000000000000..414b810336d8
--- /dev/null
+++ b/kernel/arch/vax/execregs.c
@@ -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;
+}
diff --git a/kernel/arch/vax/execregs.h b/kernel/arch/vax/execregs.h
new file mode 100644
index 000000000000..882bcd1b6702
--- /dev/null
+++ b/kernel/arch/vax/execregs.h
@@ -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 */
diff --git a/kernel/arch/vax/h_execregs.S b/kernel/arch/vax/h_execregs.S
new file mode 100644
index 000000000000..27c5c2052867
--- /dev/null
+++ b/kernel/arch/vax/h_execregs.S
@@ -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)
diff --git a/kernel/arch/x86_64/contextspfunc.S b/kernel/arch/x86_64/contextspfunc.S
new file mode 100644
index 000000000000..d01e1dcdcaf3
--- /dev/null
+++ b/kernel/arch/x86_64/contextspfunc.S
@@ -0,0 +1,55 @@
+/* $NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 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 <machine/asm.h>
+
+RCSID("$NetBSD: contextspfunc.S,v 1.1 2025/04/21 02:33:45 riastradh Exp $")
+
+/*
+ * contextspfunc()
+ *
+ * makecontext(3) function. Store the stack pointer on entry at
+ * the global variable contextsp and call contextdone.
+ */
+ENTRY(contextspfunc)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(contextsp)(%rip)
+ addq $-8,_C_LABEL(contextsp)(%rip)
+ call _C_LABEL(contextdone)
+END(contextspfunc)
diff --git a/kernel/arch/x86_64/execregs.c b/kernel/arch/x86_64/execregs.c
new file mode 100644
index 000000000000..fad1b40daa5e
--- /dev/null
+++ b/kernel/arch/x86_64/execregs.c
@@ -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;
+}
diff --git a/kernel/arch/x86_64/execregs.h b/kernel/arch/x86_64/execregs.h
new file mode 100644
index 000000000000..904991777483
--- /dev/null
+++ b/kernel/arch/x86_64/execregs.h
@@ -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 */
diff --git a/kernel/arch/x86_64/execsp.S b/kernel/arch/x86_64/execsp.S
new file mode 100644
index 000000000000..d351dfb2d9e8
--- /dev/null
+++ b/kernel/arch/x86_64/execsp.S
@@ -0,0 +1,111 @@
+/* $NetBSD: execsp.S,v 1.2 2025/04/20 22:31:25 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 <machine/asm.h>
+
+RCSID("$NetBSD: execsp.S,v 1.2 2025/04/20 22:31:25 riastradh Exp $")
+
+/*
+ * void execsp_start(void (*cleanup@rbx)(void), void *obj_main@rcx,
+ * struct ps_strings *ps_strings@rbx)
+ *
+ * ELF entry point. Saves the stack pointer in startsp and defers
+ * to the usual csu __start routine.
+ */
+ENTRY(execsp_start)
+ movq %rsp,_C_LABEL(startsp)(%rip)
+ /*
+ * No adjustment like in main because entry point is special
+ * and the amd64 csu __start routine takes care of it.
+ *
+ * XXX Why don't we just arrange to align it in the kernel
+ * anyway?
+ */
+ jmp _C_LABEL(__start)
+END(execsp_start)
+
+/*
+ * void execsp_ctor(void)
+ *
+ * ELF constructor. Saves the stack pointer in ctorsp and
+ * returns.
+ */
+ENTRY(execsp_ctor)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(ctorsp)(%rip)
+ addq $-8,_C_LABEL(ctorsp)(%rip)
+ ret
+END(execsp_ctor)
+
+ /* Make execsp_ctor a constructor. */
+ .section .ctors,"aw",@progbits
+ .p2align 3
+ .quad _C_LABEL(execsp_ctor)
+
+/*
+ * int main(int argc@rdi, char **argv@rsi, ...)
+ *
+ * Main function. Saves the stack pointer in mainsp and returns
+ * zero. We will call execsp_main in execsp_dtor once dtorsp has
+ * been initialized.
+ */
+ENTRY(main)
+ movq %rsp,_C_LABEL(mainsp)(%rip)
+ addq $-8,_C_LABEL(mainsp)(%rip)
+ xorl %eax,%eax
+ ret
+END(main)
+
+/*
+ * void execsp_dtor(void)
+ *
+ * ELF destructor. Saves the stack pointer in dtorsp and defers
+ * to the C execsp_main in h_execsp.c to report the stack pointers
+ * back to the t_signal_and_sp parent.
+ */
+ENTRY(execsp_dtor)
+ movq %rsp,_C_LABEL(dtorsp)(%rip)
+ addq $-8,_C_LABEL(dtorsp)(%rip)
+ jmp _C_LABEL(execsp_main)
+END(execsp_dtor)
+
+ /* Make execsp_ctor a destructor. */
+ .section .dtors,"aw",@progbits
+ .p2align 3
+ .quad _C_LABEL(execsp_dtor)
diff --git a/kernel/arch/x86_64/h_execregs.S b/kernel/arch/x86_64/h_execregs.S
new file mode 100644
index 000000000000..638f73940f06
--- /dev/null
+++ b/kernel/arch/x86_64/h_execregs.S
@@ -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)
diff --git a/kernel/arch/x86_64/signalsphandler.S b/kernel/arch/x86_64/signalsphandler.S
new file mode 100644
index 000000000000..b53cb005d0b0
--- /dev/null
+++ b/kernel/arch/x86_64/signalsphandler.S
@@ -0,0 +1,55 @@
+/* $NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:01 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 <machine/asm.h>
+
+RCSID("$NetBSD: signalsphandler.S,v 1.1 2025/04/20 22:31:01 riastradh Exp $")
+
+/*
+ * signalsphandler(signo@rdi)
+ *
+ * Signal handler. Store the stack pointer on entry at the global
+ * variable signalsp and return.
+ */
+ENTRY(signalsphandler)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,_C_LABEL(signalsp)(%rip)
+ addq $-8,_C_LABEL(signalsp)(%rip)
+ ret
+END(signalsphandler)
diff --git a/kernel/arch/x86_64/stack_pointer.h b/kernel/arch/x86_64/stack_pointer.h
new file mode 100644
index 000000000000..6ea297cf97d9
--- /dev/null
+++ b/kernel/arch/x86_64/stack_pointer.h
@@ -0,0 +1,35 @@
+/* $NetBSD: stack_pointer.h,v 1.1 2025/04/20 22:31:01 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_STACK_POINTER_H
+#define TESTS_KERNEL_ARCH_X86_64_STACK_POINTER_H
+
+#define MISALIGN_SP __asm __volatile("addq $-1,%rsp")
+#define FIX_SP __asm __volatile("addq $1,%rsp")
+
+#endif /* TESTS_KERNEL_ARCH_X86_64_STACK_POINTER_H */
diff --git a/kernel/arch/x86_64/threadspfunc.S b/kernel/arch/x86_64/threadspfunc.S
new file mode 100644
index 000000000000..c939f36b408a
--- /dev/null
+++ b/kernel/arch/x86_64/threadspfunc.S
@@ -0,0 +1,54 @@
+/* $NetBSD: threadspfunc.S,v 1.2 2025/04/21 12:06:08 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 <machine/asm.h>
+
+RCSID("$NetBSD: threadspfunc.S,v 1.2 2025/04/21 12:06:08 riastradh Exp $")
+
+/*
+ * void *threadspfunc(void *cookie@rdi)
+ *
+ * pthread_create(3) function. Return the stack pointer on entry.
+ */
+ENTRY(threadspfunc)
+ /*
+ * `The end of the input argument area shall be aligned on a
+ * [16-byte] boundary. In other words, the value of (%rsp + 8)
+ * is always a multiple of 16 when control is transferred to
+ * the function entry point.'
+ *
+ * To make it convenient for t_signal_and_sp.c, we subtract 8
+ * from %rsp in order to get something congruent to zero modulo
+ * the stack alignemnt.
+ */
+ movq %rsp,%rax
+ addq $-8,%rax
+ ret
+END(threadspfunc)
diff --git a/kernel/h_cloexec.c b/kernel/h_cloexec.c
new file mode 100644
index 000000000000..55ca4978497f
--- /dev/null
+++ b/kernel/h_cloexec.c
@@ -0,0 +1,48 @@
+/* $NetBSD: h_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 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_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $");
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int
+main(int argc, char **argv)
+{
+ int fd = atoi(argv[1]);
+ int flags;
+
+ if (fcntl(fd, F_GETFL, &flags) == 0)
+ return 1;
+ if (errno != EBADF)
+ return 2;
+ return 0;
+}
diff --git a/kernel/h_execregs_unimpl.c b/kernel/h_execregs_unimpl.c
new file mode 100644
index 000000000000..606781a2f745
--- /dev/null
+++ b/kernel/h_execregs_unimpl.c
@@ -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;
+}
diff --git a/kernel/h_execsp.c b/kernel/h_execsp.c
new file mode 100644
index 000000000000..dac1919e4417
--- /dev/null
+++ b/kernel/h_execsp.c
@@ -0,0 +1,68 @@
+/* $NetBSD: h_execsp.c,v 1.2 2025/04/20 22:31:25 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_execsp.c,v 1.2 2025/04/20 22:31:25 riastradh Exp $");
+
+#include <err.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "h_execsp.h"
+
+/*
+ * The machine-dependent execsp.S assembly routines will initialize
+ * startsp, ctorsp, mainsp, and dtorsp, and then call execsp_main on
+ * program startup.
+ */
+void *startsp;
+void *ctorsp;
+void *mainsp;
+void *dtorsp;
+
+int execsp_main(void);
+int
+execsp_main(void)
+{
+ struct execsp execsp;
+ ssize_t nwrit;
+
+ memset(&execsp, 0, sizeof(execsp));
+ execsp.startsp = startsp;
+ execsp.ctorsp = ctorsp;
+ execsp.mainsp = mainsp;
+ execsp.dtorsp = dtorsp;
+
+ nwrit = write(STDOUT_FILENO, &execsp, sizeof(execsp));
+ if (nwrit == -1)
+ err(1, "write");
+ if ((size_t)nwrit != sizeof(execsp))
+ errx(1, "wrote %zu != %zu", (size_t)nwrit, sizeof(execsp));
+
+ return 0;
+}
diff --git a/kernel/h_execsp.h b/kernel/h_execsp.h
new file mode 100644
index 000000000000..e94ceaab9c1b
--- /dev/null
+++ b/kernel/h_execsp.h
@@ -0,0 +1,47 @@
+/* $NetBSD: h_execsp.h,v 1.2 2025/04/20 22:31:25 riastradh Exp $ */
+
+/*
+ * Copyright (c) 2024 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_H_EXECSP_H
+#define TESTS_KERNEL_H_EXECSP_H
+
+/*
+ * struct execsp
+ *
+ * Structure passed from the h_execsp_* programs to the
+ * t_signal_and_sp test, giving the stack pointer as it was at the
+ * ELF entry point, an ELF constructor, the main function, and an
+ * ELF destructor.
+ */
+struct execsp {
+ void *startsp;
+ void *ctorsp;
+ void *mainsp;
+ void *dtorsp;
+};
+
+#endif /* TESTS_KERNEL_H_EXECSP_H */
diff --git a/kernel/setjmp_tester/Makefile b/kernel/setjmp_tester/Makefile
new file mode 100644
index 000000000000..18492f35fd4a
--- /dev/null
+++ b/kernel/setjmp_tester/Makefile
@@ -0,0 +1,12 @@
+# $NetBSD: Makefile,v 1.1 2025/04/27 16:22:26 riastradh Exp $
+
+KMOD= setjmp_tester
+KMODULEDIR= ${TESTSBASE}/kernel/${KMOD}
+
+SRCS= setjmp_tester.c
+
+ATFFILE= no
+NOMAN= # defined
+
+.include <bsd.test.mk>
+.include <bsd.kmodule.mk>
diff --git a/kernel/setjmp_tester/setjmp_tester.c b/kernel/setjmp_tester/setjmp_tester.c
new file mode 100644
index 000000000000..8b15b840effe
--- /dev/null
+++ b/kernel/setjmp_tester/setjmp_tester.c
@@ -0,0 +1,147 @@
+/* $NetBSD: setjmp_tester.c,v 1.1 2025/04/27 16:22:26 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>
+__KERNEL_RCSID(0, "$NetBSD: setjmp_tester.c,v 1.1 2025/04/27 16:22:26 riastradh Exp $");
+
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+
+MODULE(MODULE_CLASS_MISC, setjmp_tester, NULL);
+
+static struct sysctllog *setjmp_tester_sysctllog;
+static const struct sysctlnode *setjmp_tester_sysctlnode;
+static kmutex_t setjmp_tester_lock;
+static bool setjmp_tester_done;
+static label_t setjmp_tester_label;
+
+__noinline
+static void
+setjmp_tester_subroutine(void)
+{
+
+ printf("%s: call longjmp\n", __func__);
+ setjmp_tester_done = true;
+ longjmp(&setjmp_tester_label);
+ printf("%s: unreachable\n", __func__);
+}
+
+static int
+setjmp_tester_test(void)
+{
+ int result;
+
+ mutex_enter(&setjmp_tester_lock);
+
+ setjmp_tester_done = false;
+ result = setjmp(&setjmp_tester_label);
+ if (!setjmp_tester_done) {
+ printf("%s: setjmp returned %d at first\n", __func__, result);
+ if (result != 0) {
+ result = -1;
+ goto out;
+ }
+ setjmp_tester_subroutine();
+ /*NOTREACHED*/
+ printf("%s: setjmp_tester_subroutine returned\n", __func__);
+ result = -1;
+ } else {
+ printf("%s: setjmp returned %d at second\n", __func__, result);
+ if (result == 0) {
+ result = -2;
+ goto out;
+ }
+ }
+
+out: mutex_exit(&setjmp_tester_lock);
+ return result;
+}
+
+static int
+setjmp_tester_test_sysctl(SYSCTLFN_ARGS)
+{
+ struct sysctlnode node = *rnode;
+ int v = 0;
+ int error;
+
+ if (newp == NULL) {
+ error = ENOENT;
+ goto out;
+ }
+ error = sysctl_copyin(curlwp, newp, &v, sizeof(v));
+ if (error)
+ goto out;
+ switch (v) {
+ case 1:
+ v = setjmp_tester_test();
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ node.sysctl_data = &v;
+ error = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+out: return error;
+}
+
+static int
+setjmp_tester_modcmd(modcmd_t cmd, void *arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+ mutex_init(&setjmp_tester_lock, MUTEX_DEFAULT, IPL_NONE);
+ error = sysctl_createv(&setjmp_tester_sysctllog, 0,
+ NULL, &setjmp_tester_sysctlnode,
+ CTLFLAG_PERMANENT, CTLTYPE_NODE, "setjmp_tester",
+ SYSCTL_DESCR("setjmp/longjmp testing interface"),
+ NULL, 0, NULL, 0,
+ CTL_KERN, CTL_CREATE, CTL_EOL);
+ if (error)
+ goto fini;
+ error = sysctl_createv(&setjmp_tester_sysctllog, 0,
+ &setjmp_tester_sysctlnode, NULL,
+ CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "test",
+ SYSCTL_DESCR("setjmp/longjmp test trigger"),
+ &setjmp_tester_test_sysctl, 0, NULL, 0,
+ CTL_CREATE, CTL_EOL);
+ if (error)
+ goto fini;
+ return error;
+ case MODULE_CMD_FINI:
+ fini:
+ sysctl_teardown(&setjmp_tester_sysctllog);
+ mutex_destroy(&setjmp_tester_lock);
+ return error;
+ default:
+ return ENOTTY;
+ }
+}
diff --git a/kernel/t_cloexec.c b/kernel/t_cloexec.c
new file mode 100644
index 000000000000..c0150af8ca38
--- /dev/null
+++ b/kernel/t_cloexec.c
@@ -0,0 +1,457 @@
+/* $NetBSD: t_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024 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_cloexec.c,v 1.1 2024/11/10 15:57:32 riastradh Exp $");
+
+#include <sys/types.h>
+
+#include <sys/bitops.h>
+#include <sys/event.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+/*
+ * Test close-on-exec as set in various ways
+ */
+
+static int
+open_via_accept4(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOEXEC.
+ */
+ RL(saccept = accept4(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ SOCK_CLOEXEC));
+ return saccept;
+}
+
+static int
+open_via_clonedev(void)
+{
+ int fd;
+
+ RL(fd = open("/dev/drvctl", O_RDONLY|O_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_dup3(void)
+{
+ int fd3;
+
+ RL(fd3 = dup3(STDIN_FILENO, 3, O_CLOEXEC));
+ ATF_REQUIRE_EQ_MSG(fd3, 3, "dup3(STDIN_FILENO, 3, ...)"
+ " failed to return 3: %d", fd3);
+
+ return fd3;
+}
+
+static int
+open_via_fcntldupfd(void)
+{
+ int fd;
+
+ RL(fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0));
+
+ return fd;
+}
+
+static int
+open_via_kqueue(void)
+{
+ int fd;
+
+ RL(fd = kqueue1(O_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_opencloexec(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT|O_CLOEXEC, 0644));
+
+ return fd;
+}
+
+static int
+open_via_openfcntlcloexec(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(fcntl(fd, F_SETFD, FD_CLOEXEC));
+
+ return fd;
+}
+
+static int
+open_via_openioctlfioclex(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(ioctl(fd, FIOCLEX));
+
+ return fd;
+}
+
+static int
+open_via_pipe2rd(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOEXEC));
+
+ return fd[0];
+}
+
+static int
+open_via_pipe2wr(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOEXEC));
+
+ return fd[1];
+}
+
+static int
+open_via_paccept(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOEXEC.
+ */
+ RL(saccept = paccept(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ /*sigmask*/NULL, SOCK_CLOEXEC));
+ return saccept;
+}
+
+static int
+open_via_socket(void)
+{
+ int fd;
+
+ RL(fd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0));
+
+ return fd;
+}
+
+static int
+open_via_socketpair0(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, fd));
+
+ return fd[0];
+}
+
+static int
+open_via_socketpair1(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOEXEC, 0, fd));
+
+ return fd[1];
+}
+
+/*
+ * XXX Close-on-exec paths still missing:
+ * XXX
+ * XXX compat_linux inotify
+ * XXX compat_linux close_range
+ * XXX drm i915_perf_open_ioctl
+ * XXX drm dma_buf
+ * XXX eventfd(2)
+ * XXX memfd(2)
+ * XXX timerfd(2)
+ * XXX recvmsg/recvmmsg with MSG_CMSG_CLOEXEC
+ */
+
+static void
+check_cloexec(const struct atf_tc *tc, int fd,
+ pid_t (*execfn)(char *, char *const[]))
+{
+ char h_cloexec[PATH_MAX];
+ char fdstr[(ilog2(INT_MAX) + 1)/(ilog2(10) - 1) + 1];
+ char *const argv[] = {__UNCONST("h_cloexec"), fdstr, NULL};
+ pid_t child, waitedpid;
+ int status;
+
+ /*
+ * Format the h_cloexec helper executable path, which lives in
+ * the test's directory (typically /usr/tests/kernel), and the
+ * argument of a file descriptor in decimal.
+ */
+ snprintf(h_cloexec, sizeof(h_cloexec), "%s/h_cloexec",
+ atf_tc_get_config_var(tc, "srcdir"));
+ snprintf(fdstr, sizeof(fdstr), "%d", fd);
+
+ /*
+ * Execute h_cloexec as a subprocess.
+ */
+ child = (*execfn)(h_cloexec, argv);
+
+ /*
+ * Wait for the child to complete.
+ */
+ RL(waitedpid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(child, waitedpid, "waited for %jd, got %jd",
+ (intmax_t)child, (intmax_t)waitedpid);
+
+ /*
+ * Verify the child exited normally.
+ */
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail("subprocess terminated on signal %d",
+ WTERMSIG(status));
+ return;
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail("subprocess failed to exit normally: status=0x%x",
+ status);
+ return;
+ }
+
+ /*
+ * h_cloexec is supposed to exit status 0 if an operation on
+ * the fd failed with EBADFD, 1 if it unexpectedly succeeded,
+ * 127 if exec returned, or something else if anything else
+ * happened.
+ */
+ switch (WEXITSTATUS(status)) {
+ case 0: /* success -- closed on exec */
+ return;
+ case 1: /* fail -- not closed on exec */
+ atf_tc_fail("fd was not closed on exec");
+ return;
+ case 127: /* exec failed */
+ atf_tc_fail("failed to exec h_cloexec");
+ return;
+ default: /* something else went wong */
+ atf_tc_fail("h_cloexec failed unexpectedly: %d",
+ WEXITSTATUS(status));
+ return;
+ }
+}
+
+static pid_t
+exec_via_forkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = fork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_vforkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_posixspawn(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RZ(posix_spawn(&pid, prog, /*file_actions*/NULL, /*attrp*/NULL, argv,
+ /*envp*/NULL));
+
+ return pid;
+}
+
+/*
+ * Full cartesian product is not really important here -- the paths for
+ * open and the paths for exec are independent. So we try
+ * pipe2(O_CLOEXEC) with each exec path, and we try each open path with
+ * posix_spawn.
+ */
+
+#define CLOEXEC_TEST(test, openvia, execvia, descr) \
+ATF_TC(test); \
+ATF_TC_HEAD(test, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", descr); \
+} \
+ATF_TC_BODY(test, tc) \
+{ \
+ check_cloexec(tc, openvia(), &execvia); \
+}
+
+CLOEXEC_TEST(pipe2rd_forkexecve, open_via_pipe2rd, exec_via_forkexecve,
+ "pipe2(O_CLOEXEC) reader is closed in child on fork/exec")
+CLOEXEC_TEST(pipe2rd_vforkexecve, open_via_pipe2rd, exec_via_vforkexecve,
+ "pipe2(O_CLOEXEC) reader is closed in child on vfork/exec")
+CLOEXEC_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) reader is closed in child on posix_spawn")
+
+CLOEXEC_TEST(accept4_posixspawn, open_via_accept4, exec_via_posixspawn,
+ "accept4(SOCK_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(clonedev_posixspawn, open_via_clonedev, exec_via_posixspawn,
+ "open(\"/dev/drvctl\") is closed in child on posix_spawn");
+CLOEXEC_TEST(dup3_posixspawn, open_via_dup3, exec_via_posixspawn,
+ "dup3(..., O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(fcntldupfd_posixspawn, open_via_fcntldupfd, exec_via_posixspawn,
+ "fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(kqueue_posixspawn, open_via_kqueue, exec_via_posixspawn,
+ "kqueue1(O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(opencloexec_posixspawn, open_via_opencloexec, exec_via_posixspawn,
+ "open(O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(openfcntlcloexec_posixspawn, open_via_openfcntlcloexec,
+ exec_via_posixspawn,
+ "fcntl(open(...), F_SETFD, O_CLOEXEC) is closed in child on posix_spawn");
+CLOEXEC_TEST(openioctlfioclex_posixspawn, open_via_openioctlfioclex,
+ exec_via_posixspawn,
+ "ioctl(open(...), FIOCLEX) is closed in child on posix_spawn");
+#if 0 /* already done above */
+CLOEXEC_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) reader is closed in child on posix_spawn")
+#endif
+CLOEXEC_TEST(pipe2wr_posixspawn, open_via_pipe2wr, exec_via_posixspawn,
+ "pipe2(O_CLOEXEC) writer is closed in child on posix_spawn")
+CLOEXEC_TEST(paccept_posixspawn, open_via_paccept, exec_via_posixspawn,
+ "paccept(..., SOCK_CLOEXEC) is closed in child on posix_spawn")
+CLOEXEC_TEST(socket_posixspawn, open_via_socket, exec_via_posixspawn,
+ "socket(SOCK_CLOEXEC) is closed in child on posix_spawn")
+CLOEXEC_TEST(socketpair0_posixspawn, open_via_socketpair0, exec_via_posixspawn,
+ "socketpair(SOCK_CLOEXEC) side 0 is closed in child on posix_spawn")
+CLOEXEC_TEST(socketpair1_posixspawn, open_via_socketpair1, exec_via_posixspawn,
+ "socketpair(SOCK_CLOEXEC) side 1 is closed in child on posix_spawn")
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, accept4_posixspawn);
+ ATF_TP_ADD_TC(tp, clonedev_posixspawn);
+ ATF_TP_ADD_TC(tp, dup3_posixspawn);
+ ATF_TP_ADD_TC(tp, fcntldupfd_posixspawn);
+ ATF_TP_ADD_TC(tp, kqueue_posixspawn);
+ ATF_TP_ADD_TC(tp, opencloexec_posixspawn);
+ ATF_TP_ADD_TC(tp, openfcntlcloexec_posixspawn);
+ ATF_TP_ADD_TC(tp, openioctlfioclex_posixspawn);
+ ATF_TP_ADD_TC(tp, paccept_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_forkexecve);
+ ATF_TP_ADD_TC(tp, pipe2rd_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_vforkexecve);
+ ATF_TP_ADD_TC(tp, pipe2wr_posixspawn);
+ ATF_TP_ADD_TC(tp, socket_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair0_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair1_posixspawn);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_clofork.c b/kernel/t_clofork.c
new file mode 100644
index 000000000000..9c1ba81c733a
--- /dev/null
+++ b/kernel/t_clofork.c
@@ -0,0 +1,445 @@
+/* $NetBSD: t_clofork.c,v 1.1 2025/07/17 19:50:40 kre Exp $ */
+
+/*-
+ * Copyright (c) 2024 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.
+ */
+
+/* Adapted from t_cloexec.c */
+
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+
+#include <sys/bitops.h>
+#include <sys/event.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <spawn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "h_macros.h"
+
+#if defined(O_CLOFORK) && O_CLOFORK != 0
+/*
+ * Test close-on-fork as set in various ways
+ */
+
+static int
+open_via_accept4(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOFORK.
+ */
+ RL(saccept = accept4(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ SOCK_CLOFORK));
+ return saccept;
+}
+
+static int
+open_via_clonedev(void)
+{
+ int fd;
+
+ RL(fd = open("/dev/drvctl", O_RDONLY|O_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_dup3(void)
+{
+ int fd3;
+
+ RL(fd3 = dup3(STDIN_FILENO, 3, O_CLOFORK));
+ ATF_REQUIRE_EQ_MSG(fd3, 3, "dup3(STDIN_FILENO, 3, ...)"
+ " failed to return 3: %d", fd3);
+
+ return fd3;
+}
+
+static int
+open_via_fcntldupfd(void)
+{
+ int fd;
+
+ RL(fd = fcntl(STDIN_FILENO, F_DUPFD_CLOFORK, 0));
+
+ return fd;
+}
+
+static int
+open_via_kqueue(void)
+{
+ int fd;
+
+ RL(fd = kqueue1(O_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_openclofork(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT|O_CLOFORK, 0644));
+
+ return fd;
+}
+
+static int
+open_via_openfcntlclofork(void)
+{
+ int fd;
+
+ RL(fd = open("file", O_RDWR|O_CREAT, 0644));
+ RL(fcntl(fd, F_SETFD, FD_CLOFORK));
+
+ return fd;
+}
+
+static int
+open_via_pipe2rd(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOFORK));
+
+ return fd[0];
+}
+
+static int
+open_via_pipe2wr(void)
+{
+ int fd[2];
+
+ RL(pipe2(fd, O_CLOFORK));
+
+ return fd[1];
+}
+
+static int
+open_via_paccept(void)
+{
+ static const union {
+ struct sockaddr sa;
+ struct sockaddr_un sun;
+ } name = { .sun = {
+ .sun_family = AF_LOCAL,
+ .sun_path = "socket",
+ } };
+ int slisten, saccept, c;
+
+ /*
+ * Create a listening server socket and bind it to the path.
+ */
+ RL(slisten = socket(PF_LOCAL, SOCK_STREAM, 0));
+ RL(bind(slisten, &name.sa, sizeof(name)));
+ RL(listen(slisten, SOMAXCONN));
+
+ /*
+ * Create an active client socket and connect it to the path --
+ * nonblocking, so we don't deadlock here. If connect doesn't
+ * succeed immediately, it had better fail immediately with
+ * EINPROGRESS.
+ */
+ RL(c = socket(PF_LOCAL, SOCK_STREAM|SOCK_NONBLOCK, 0));
+ if (connect(c, &name.sa, sizeof(name)) == -1) {
+ ATF_CHECK_EQ_MSG(errno, EINPROGRESS, "connect failed %d: %s",
+ errno, strerror(errno));
+ }
+
+ /*
+ * Accept a socket on the server side with SOCK_CLOFORK.
+ */
+ RL(saccept = paccept(slisten, /*addr*/NULL, /*addrlen*/NULL,
+ /*sigmask*/NULL, SOCK_CLOFORK));
+ return saccept;
+}
+
+static int
+open_via_socket(void)
+{
+ int fd;
+
+ RL(fd = socket(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0));
+
+ return fd;
+}
+
+static int
+open_via_socketpair0(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0, fd));
+
+ return fd[0];
+}
+
+static int
+open_via_socketpair1(void)
+{
+ int fd[2];
+
+ RL(socketpair(PF_LOCAL, SOCK_STREAM|SOCK_CLOFORK, 0, fd));
+
+ return fd[1];
+}
+
+static void
+check_clofork(const struct atf_tc *tc, int fd,
+ pid_t (*execfn)(char *, char *const[]))
+{
+ char h_clofork[PATH_MAX];
+ char fdstr[(ilog2(INT_MAX) + 1)/(ilog2(10) - 1) + 1];
+ char *const argv[] = {__UNCONST("h_cloexec"), fdstr, NULL};
+ pid_t child, waitedpid;
+ int status;
+
+ /*
+ * Format the h_clofork helper executable path, which lives in
+ * the test's directory (typically /usr/tests/kernel), and the
+ * argument of a file descriptor in decimal.
+ */
+ snprintf(h_clofork, sizeof(h_clofork), "%s/h_cloexec",
+ atf_tc_get_config_var(tc, "srcdir"));
+ snprintf(fdstr, sizeof(fdstr), "%d", fd);
+
+ /*
+ * Execute h_clofork as a subprocess.
+ */
+ child = (*execfn)(h_clofork, argv);
+
+ /*
+ * Wait for the child to complete.
+ */
+ RL(waitedpid = waitpid(child, &status, 0));
+ ATF_CHECK_EQ_MSG(child, waitedpid, "waited for %jd, got %jd",
+ (intmax_t)child, (intmax_t)waitedpid);
+
+ /*
+ * Verify the child exited normally.
+ */
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail("subprocess terminated on signal %d",
+ WTERMSIG(status));
+ return;
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail("subprocess failed to exit normally: status=0x%x",
+ status);
+ return;
+ }
+
+ /*
+ * h_clofork is supposed to exit status 0 if an operation on
+ * the fd failed with EBADFD, 1 if it unexpectedly succeeded,
+ * 127 if exec returned, or something else if anything else
+ * happened.
+ */
+ switch (WEXITSTATUS(status)) {
+ case 0: /* success -- closed on exec */
+ return;
+ case 1: /* fail -- not closed on exec */
+ atf_tc_fail("fd was not closed on exec");
+ return;
+ case 127: /* exec failed */
+ atf_tc_fail("failed to exec h_cloexec");
+ return;
+ default: /* something else went wong */
+ atf_tc_fail("h_cloexec failed unexpectedly: %d",
+ WEXITSTATUS(status));
+ return;
+ }
+}
+
+static pid_t
+exec_via_forkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = fork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_vforkexecve(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ if (execve(prog, argv, /*envp*/NULL) == -1)
+ _exit(127);
+ abort();
+ }
+
+ /* parent */
+ return pid;
+}
+
+static pid_t
+exec_via_posixspawn(char *prog, char *const argv[])
+{
+ pid_t pid;
+
+ RZ(posix_spawn(&pid, prog, /*file_actions*/NULL, /*attrp*/NULL, argv,
+ /*envp*/NULL));
+
+ return pid;
+}
+
+/*
+ * Full cartesian product is not really important here -- the paths for
+ * open and the paths for exec are independent. So we try
+ * pipe2(O_CLOFORK) with each exec path, and we try each open path with
+ * posix_spawn.
+ */
+
+#define CLOFORK_TEST(test, openvia, execvia, descr) \
+ATF_TC(test); \
+ATF_TC_HEAD(test, tc) \
+{ \
+ atf_tc_set_md_var(tc, "descr", descr); \
+} \
+ATF_TC_BODY(test, tc) \
+{ \
+ check_clofork(tc, openvia(), &execvia); \
+}
+
+CLOFORK_TEST(pipe2rd_forkexecve, open_via_pipe2rd, exec_via_forkexecve,
+ "pipe2(O_CLOFORK) reader is closed in child on fork/exec")
+CLOFORK_TEST(pipe2rd_vforkexecve, open_via_pipe2rd, exec_via_vforkexecve,
+ "pipe2(O_CLOFORK) reader is closed in child on vfork/exec")
+CLOFORK_TEST(pipe2rd_posixspawn, open_via_pipe2rd, exec_via_posixspawn,
+ "pipe2(O_CLOFORK) reader is closed in child on posix_spawn")
+
+CLOFORK_TEST(accept4_posixspawn, open_via_accept4, exec_via_posixspawn,
+ "accept4(SOCK_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(clonedev_posixspawn, open_via_clonedev, exec_via_posixspawn,
+ "open(\"/dev/drvctl\") is closed in child on posix_spawn");
+CLOFORK_TEST(dup3_posixspawn, open_via_dup3, exec_via_posixspawn,
+ "dup3(..., O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(fcntldupfd_posixspawn, open_via_fcntldupfd, exec_via_posixspawn,
+ "fcntl(STDIN_FILENO, F_DUPFD_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(kqueue_posixspawn, open_via_kqueue, exec_via_posixspawn,
+ "kqueue1(O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(openclofork_posixspawn, open_via_openclofork, exec_via_posixspawn,
+ "open(O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(openfcntlclofork_posixspawn, open_via_openfcntlclofork,
+ exec_via_posixspawn,
+ "fcntl(open(...), F_SETFD, O_CLOFORK) is closed in child on posix_spawn");
+CLOFORK_TEST(pipe2wr_posixspawn, open_via_pipe2wr, exec_via_posixspawn,
+ "pipe2(O_CLOFORK) writer is closed in child on posix_spawn")
+CLOFORK_TEST(paccept_posixspawn, open_via_paccept, exec_via_posixspawn,
+ "paccept(..., SOCK_CLOFORK) is closed in child on posix_spawn")
+CLOFORK_TEST(socket_posixspawn, open_via_socket, exec_via_posixspawn,
+ "socket(SOCK_CLOFORK) is closed in child on posix_spawn")
+CLOFORK_TEST(socketpair0_posixspawn, open_via_socketpair0, exec_via_posixspawn,
+ "socketpair(SOCK_CLOFORK) side 0 is closed in child on posix_spawn")
+CLOFORK_TEST(socketpair1_posixspawn, open_via_socketpair1, exec_via_posixspawn,
+ "socketpair(SOCK_CLOFORK) side 1 is closed in child on posix_spawn")
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, accept4_posixspawn);
+ ATF_TP_ADD_TC(tp, clonedev_posixspawn);
+ ATF_TP_ADD_TC(tp, dup3_posixspawn);
+ ATF_TP_ADD_TC(tp, fcntldupfd_posixspawn);
+ ATF_TP_ADD_TC(tp, kqueue_posixspawn);
+ ATF_TP_ADD_TC(tp, openclofork_posixspawn);
+ ATF_TP_ADD_TC(tp, openfcntlclofork_posixspawn);
+ ATF_TP_ADD_TC(tp, paccept_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_forkexecve);
+ ATF_TP_ADD_TC(tp, pipe2rd_posixspawn);
+ ATF_TP_ADD_TC(tp, pipe2rd_vforkexecve);
+ ATF_TP_ADD_TC(tp, pipe2wr_posixspawn);
+ ATF_TP_ADD_TC(tp, socket_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair0_posixspawn);
+ ATF_TP_ADD_TC(tp, socketpair1_posixspawn);
+
+ return atf_no_error();
+}
+
+#else /* No O_CLOFORK */
+
+ATF_TC(not_implemented);
+ATF_TC_HEAD(not_implemented, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Unimplemented O_CLOFORK");
+}
+ATF_TC_BODY(not_implemented, tc)
+{
+ atf_tc_skip("close-on-fork not yet available");
+}
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, not_implemented);
+
+ return atf_no_error();
+}
+#endif
diff --git a/kernel/t_execregs.c b/kernel/t_execregs.c
new file mode 100644
index 000000000000..076a8de9547c
--- /dev/null
+++ b/kernel/t_execregs.c
@@ -0,0 +1,197 @@
+/* $NetBSD: t_execregs.c,v 1.7 2025/04/27 14:30:03 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.7 2025/04/27 14:30:03 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 "isqemu.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_CHECK_EQ_MSG(n, 0,
+ "truncated read, missing %zu of %zu bytes",
+ n, NEXECREGS*sizeof(regs[0]));
+}
+
+static void
+checkregs(const register_t regs[static NEXECREGS])
+{
+ unsigned i;
+
+#ifdef __hppa__
+ if (isQEMU()) {
+ atf_tc_expect_fail("PR port-hppa/59114: hppa:"
+ " eager fpu switching for qemu and/or spectre mitigation");
+ }
+#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));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child terminated on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child terminated mysteriously,"
+ " status=0x%x",
+ status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d", WEXITSTATUS(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();
+}
diff --git a/kernel/t_fdrestart.c b/kernel/t_fdrestart.c
new file mode 100644
index 000000000000..5449fcdaf18d
--- /dev/null
+++ b/kernel/t_fdrestart.c
@@ -0,0 +1,274 @@
+/* $NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2023 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 _KMEMUSER /* ERESTART */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_fdrestart.c,v 1.4 2023/11/18 19:46:55 riastradh Exp $");
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <rump/rump.h>
+#include <rump/rump_syscalls.h>
+
+#include "h_macros.h"
+
+struct fdrestart {
+ void (*op)(struct fdrestart *);
+ int fd;
+ pthread_barrier_t barrier;
+};
+
+static void
+waitforbarrier(struct fdrestart *F, const char *caller)
+{
+ int error;
+
+ error = pthread_barrier_wait(&F->barrier);
+ switch (error) {
+ case 0:
+ case PTHREAD_BARRIER_SERIAL_THREAD:
+ break;
+ default:
+ atf_tc_fail("%s: pthread_barrier_wait: %d, %s", caller, error,
+ strerror(error));
+ }
+}
+
+static void
+doread(struct fdrestart *F)
+{
+ char c;
+ ssize_t nread;
+ int error;
+
+ /*
+ * Wait for the other thread to be ready.
+ */
+ waitforbarrier(F, "reader");
+
+ /*
+ * Start a read. This should block, and then, when the other
+ * thread closes the fd, should be woken to fail with ERESTART.
+ */
+ nread = rump_sys_read(F->fd, &c, sizeof(c));
+ ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error,
+ strerror(error));
+
+ /*
+ * Now further attempts at I/O should fail with EBADF because
+ * the fd has been closed.
+ */
+ nread = rump_sys_read(F->fd, &c, sizeof(c));
+ ATF_REQUIRE_EQ_MSG(nread, -1, "nread=%zd", nread);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error,
+ strerror(error));
+}
+
+static void
+dowrite(struct fdrestart *F)
+{
+ static const char buf[1024*1024]; /* XXX >BIG_PIPE_SIZE */
+ ssize_t nwrit;
+ int error;
+
+ /*
+ * Make sure the pipe's buffer is full first.
+ */
+ for (;;) {
+ int nspace;
+
+ RL(rump_sys_ioctl(F->fd, FIONSPACE, &nspace));
+ ATF_REQUIRE_MSG(nspace >= 0, "nspace=%d", nspace);
+ if (nspace == 0)
+ break;
+ RL(rump_sys_write(F->fd, buf, (size_t)nspace));
+ }
+
+ /*
+ * Wait for the other thread to be ready.
+ */
+ waitforbarrier(F, "writer");
+
+ /*
+ * Start a write. This should block, and then, when the other
+ * thread closes the fd, should be woken to fail with ERESTART.
+ */
+ nwrit = rump_sys_write(F->fd, buf, sizeof(buf));
+ ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, ERESTART, "errno=%d (%s)", error,
+ strerror(error));
+
+ /*
+ * Now further attempts at I/O should fail with EBADF because
+ * the fd has been closed.
+ */
+ nwrit = rump_sys_write(F->fd, buf, sizeof(buf));
+ ATF_REQUIRE_EQ_MSG(nwrit, -1, "nwrit=%zd", nwrit);
+ error = errno;
+ ATF_REQUIRE_EQ_MSG(error, EBADF, "errno=%d (%s)", error,
+ strerror(error));
+}
+
+static void *
+doit(void *cookie)
+{
+ struct fdrestart *F = cookie;
+
+ (*F->op)(F);
+
+ return NULL;
+}
+
+static void
+on_sigalrm(int signo)
+{
+
+ atf_tc_fail("timed out");
+}
+
+static void
+testfdrestart(struct fdrestart *F)
+{
+ pthread_t t;
+
+ ATF_REQUIRE_MSG(signal(SIGALRM, &on_sigalrm) != SIG_ERR,
+ "errno=%d (%s)", errno, strerror(errno));
+
+ RZ(pthread_barrier_init(&F->barrier, NULL, 2));
+ RZ(pthread_create(&t, NULL, &doit, F));
+ waitforbarrier(F, "closer"); /* wait for thread to start */
+ (void)sleep(1); /* wait for op to start */
+ (void)alarm(1); /* set a deadline */
+ RL(rump_sys_close(F->fd)); /* wake op in other thread */
+ RZ(pthread_join(t, NULL)); /* wait for op to wake and fail */
+ (void)alarm(0); /* clear the deadline */
+}
+
+ATF_TC(pipe_read);
+ATF_TC_HEAD(pipe_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pipe read fails on close");
+}
+ATF_TC_BODY(pipe_read, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_pipe(fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &doread;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TC(pipe_write);
+ATF_TC_HEAD(pipe_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test pipe write fails on close");
+}
+ATF_TC_BODY(pipe_write, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_pipe(fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &dowrite;
+ F->fd = fd[1];
+ atf_tc_expect_fail("PR kern/57659");
+ testfdrestart(F);
+}
+
+ATF_TC(socketpair_read);
+ATF_TC_HEAD(socketpair_read, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test socketpair read fails on close");
+}
+ATF_TC_BODY(socketpair_read, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &doread;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TC(socketpair_write);
+ATF_TC_HEAD(socketpair_write, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Test socketpair write fails on close");
+}
+ATF_TC_BODY(socketpair_write, tc)
+{
+ struct fdrestart fdrestart, *F = &fdrestart;
+ int fd[2];
+
+ rump_init();
+
+ RL(rump_sys_socketpair(AF_LOCAL, SOCK_STREAM, 0, fd));
+
+ memset(F, 0, sizeof(*F));
+ F->op = &dowrite;
+ F->fd = fd[0];
+ testfdrestart(F);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, pipe_read);
+ ATF_TP_ADD_TC(tp, pipe_write);
+ ATF_TP_ADD_TC(tp, socketpair_read);
+ ATF_TP_ADD_TC(tp, socketpair_write);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_nanosleep.c b/kernel/t_nanosleep.c
new file mode 100644
index 000000000000..9ef752b37e60
--- /dev/null
+++ b/kernel/t_nanosleep.c
@@ -0,0 +1,246 @@
+/* $NetBSD: t_nanosleep.c,v 1.2 2025/10/07 20:09:27 andvar Exp $ */
+
+/*-
+ * Copyright (c) 2024 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>
+__COPYRIGHT("@(#) Copyright (c) 2024\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_nanosleep.c,v 1.2 2025/10/07 20:09:27 andvar Exp $");
+
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+static void
+sacrifice(void)
+{
+ pause();
+}
+
+static void
+tester(pid_t victim, clockid_t clock, int flags)
+{
+ /*
+ * we need this sleep to be long enough that we
+ * can accurately detect when the sleep finishes
+ * early, but not so long that when there's no
+ * bug and things actually sleep this long, that
+ * the execution of a sleep this long, several
+ * times, won't slow down the overall testing
+ * process too much. Trial and error...
+ */
+ struct timespec to_sleep = { 4, 0 };
+
+ struct timespec before, after;
+ struct timespec *ts;
+ int e;
+
+ if (clock_gettime(clock, &before) != 0)
+ exit(1);
+
+ if (flags & TIMER_ABSTIME) {
+ timespecadd(&to_sleep, &before, &after);
+ ts = &after;
+ } else
+ ts = &to_sleep;
+
+ printf("Test: Clock=%d Flags=%x, starting at %jd.%.9ld\n",
+ (int)clock, flags, (intmax_t)before.tv_sec, before.tv_nsec);
+ if (flags & TIMER_ABSTIME)
+ printf("Sleeping until %jd.%.9ld\n",
+ (intmax_t)ts->tv_sec, ts->tv_nsec);
+ else
+ printf("Sleeping for %jd.%.9ld\n",
+ (intmax_t)ts->tv_sec, ts->tv_nsec);
+
+ /* OK, we're ready */
+
+ /* these next two steps need to be as close together as possible */
+ if (kill(victim, SIGKILL) == -1)
+ exit(2);
+ if ((e = clock_nanosleep(clock, flags, ts, &after)) != 0)
+ exit(20 + e);
+
+ if (!(flags & TIMER_ABSTIME)) {
+ printf("Remaining to sleep: %jd.%.9ld\n",
+ (intmax_t)after.tv_sec, after.tv_nsec);
+
+ if (after.tv_sec != 0 || after.tv_nsec != 0)
+ exit(3);
+ }
+
+ if (clock_gettime(clock, &after) != 0)
+ exit(4);
+
+ printf("Sleep ended at: %jd.%.9ld\n",
+ (intmax_t)after.tv_sec, after.tv_nsec);
+
+ timespecadd(&before, &to_sleep, &before);
+ if (timespeccmp(&before, &after, >))
+ exit(5);
+
+ exit(0);
+}
+
+/*
+ * The parent of the masochist/victim above, controls everything.
+ */
+static void
+runit(clockid_t clock, int flags)
+{
+ pid_t v, m, x;
+ int status;
+ struct timespec brief = { 0, 3 * 100 * 1000 * 1000 }; /* 300 ms */
+
+ ATF_REQUIRE((v = fork()) != -1);
+ if (v == 0)
+ sacrifice();
+
+ ATF_REQUIRE((m = fork()) != -1);
+ if (m == 0)
+ tester(v, clock, flags);
+
+ ATF_REQUIRE((x = wait(&status)) != -1);
+
+ if (x == m) {
+ /*
+ * This is bad, the murderer shouldn't die first
+ */
+ fprintf(stderr, "M exited first, status %#x\n", status);
+ (void)kill(v, SIGKILL); /* just in case */
+ atf_tc_fail("2nd child predeceased first");
+ }
+ if (x != v) {
+ fprintf(stderr, "Unknown exit from %d (status: %#x)"
+ "(M=%d V=%d)\n", x, status, m, v);
+ (void)kill(m, SIGKILL);
+ (void)kill(v, SIGKILL);
+ atf_tc_fail("Strange child died");
+ }
+
+ /*
+ * OK, the victim died, we don't really care why,
+ * (it should have been because of a SIGKILL, maybe
+ * test for that someday).
+ *
+ * Now we get to proceed to the real test.
+ *
+ * But we want to wait a short while to try and be sure
+ * that m (the child still running) has a chance to
+ * fall asleep.
+ */
+ (void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+
+ /*
+ * This is the test, for PR kern/58733
+ * - stop a process while in clock_nanosleep()
+ * - resume it again
+ * - see if it still sleeps as long as was requested (or longer)
+ */
+ ATF_REQUIRE(kill(m, SIGSTOP) == 0);
+ (void) clock_nanosleep(CLOCK_MONOTONIC, TIMER_RELTIME, &brief, NULL);
+ ATF_REQUIRE(kill(m, SIGCONT) == 0);
+
+ ATF_REQUIRE((x = wait(&status)) != -1);
+
+ if (x != m) {
+ fprintf(stderr, "Unknown exit from %d (status: %#x)"
+ "(M=%d V=%d)\n", x, status, m, v);
+ (void) kill(m, SIGKILL);
+ atf_tc_fail("Strange child died");
+ }
+
+ if (status == 0)
+ atf_tc_pass();
+
+ /*
+ * Here we should decode the status, and give a better
+ * clue what really went wrong. Later...
+ */
+ fprintf(stderr, "Test failed: status from M: %#x\n", status);
+ atf_tc_fail("M exited with non-zero status. PR kern/58733");
+}
+
+
+ATF_TC(nanosleep_monotonic_absolute);
+ATF_TC_HEAD(nanosleep_monotonic_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, ABS)");
+}
+ATF_TC_BODY(nanosleep_monotonic_absolute, tc)
+{
+ runit(CLOCK_MONOTONIC, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_monotonic_relative);
+ATF_TC_HEAD(nanosleep_monotonic_relative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(MONO, REL)");
+}
+ATF_TC_BODY(nanosleep_monotonic_relative, tc)
+{
+ runit(CLOCK_MONOTONIC, TIMER_RELTIME);
+}
+
+ATF_TC(nanosleep_realtime_absolute);
+ATF_TC_HEAD(nanosleep_realtime_absolute, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, ABS)");
+}
+ATF_TC_BODY(nanosleep_realtime_absolute, tc)
+{
+ runit(CLOCK_REALTIME, TIMER_ABSTIME);
+}
+
+ATF_TC(nanosleep_realtime_relative);
+ATF_TC_HEAD(nanosleep_realtime_relative, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Checks clock_nanosleep(REAL, REL)");
+}
+ATF_TC_BODY(nanosleep_realtime_relative, tc)
+{
+ runit(CLOCK_REALTIME, TIMER_RELTIME);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, nanosleep_monotonic_absolute);
+ ATF_TP_ADD_TC(tp, nanosleep_monotonic_relative);
+ ATF_TP_ADD_TC(tp, nanosleep_realtime_absolute);
+ ATF_TP_ADD_TC(tp, nanosleep_realtime_relative);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_semtimedop.c b/kernel/t_semtimedop.c
new file mode 100644
index 000000000000..daeaac6b3dab
--- /dev/null
+++ b/kernel/t_semtimedop.c
@@ -0,0 +1,279 @@
+/* $NetBSD: t_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $ */
+
+/*-
+ * Copyright (c) 2024 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_semtimedop.c,v 1.2 2024/10/10 07:45:02 martin Exp $");
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <atf-c.h>
+#include <sys/wait.h>
+
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_{STAT,SET} */
+ u_short *array; /* array for GETALL & SETALL */
+};
+
+ATF_TC(semtimedop_basic);
+ATF_TC_HEAD(semtimedop_basic, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "Basic semtimedop functionality");
+}
+
+ATF_TC_BODY(semtimedop_basic, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s",
+ strerror(errno));
+ }
+
+ /* Define semtimedop operation: increment semaphore */
+ sops.sem_num = 0;
+ sops.sem_op = 1;
+ sops.sem_flg = 0;
+
+ /* Define timeout */
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ /* Perform semtimedop */
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ ATF_REQUIRE_MSG(0, "semtimedop failed: %s", strerror(errno));
+ }
+
+ /* Check semaphore value */
+ int val = semctl(semid, 0, GETVAL);
+ ATF_REQUIRE_MSG(val == 1,
+ "Semaphore value incorrect: got %d, expected 1", val);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop blocks until timeout expires */
+ATF_TC(semtimedop_timeout);
+ATF_TC_HEAD(semtimedop_timeout, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop blocks until timeout expires");
+}
+
+ATF_TC_BODY(semtimedop_timeout, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+ pid_t pid;
+ int status;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ pid = fork();
+ ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
+
+ if (pid == 0) {
+ /*
+ * Child: perform semtimedop with negative sem_op, should
+ * block until timeout
+ */
+ sops.sem_num = 0;
+ sops.sem_op = -1;
+ sops.sem_flg = 0;
+
+ timeout.tv_sec = 2;
+ timeout.tv_nsec = 0;
+
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ if (errno == EAGAIN || errno == EINTR) {
+ exit(0); /* Expected */
+ }
+ }
+ exit(1); /* Unexpected failure/success */
+ }
+
+ /* Parent: wait for child to finish */
+ waitpid(pid, &status, 0);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE_EQ(WEXITSTATUS(status), 0);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop with SEM_UNDO adjusts semaphore on exit */
+ATF_TC(semtimedop_semundo);
+ATF_TC_HEAD(semtimedop_semundo, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop with SEM_UNDO adjusts semaphore on exit");
+}
+
+ATF_TC_BODY(semtimedop_semundo, tc)
+{
+ key_t key = IPC_PRIVATE;
+ int semid;
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+ pid_t pid;
+ int val;
+
+ /* Create semaphore set with 1 semaphore */
+ semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ pid = fork();
+ ATF_REQUIRE_MSG(pid != -1, "fork failed: %s", strerror(errno));
+
+ if (pid == 0) {
+ /* Child: perform semtimedop with SEM_UNDO */
+ sops.sem_num = 0;
+ sops.sem_op = 1;
+ sops.sem_flg = SEM_UNDO;
+
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ if (semtimedop(semid, &sops, 1, &timeout) == -1) {
+ exit(1); /* Unexpected failure */
+ }
+
+ exit(0); /* Exit normally, SEM_UNDO should be triggered */
+ }
+
+ /* Parent: wait for child to exit */
+ waitpid(pid, NULL, 0);
+
+ /* Check semaphore value; should be 0 after SEM_UNDO */
+ val = semctl(semid, 0, GETVAL);
+ ATF_REQUIRE_MSG(val == 0,
+ "Semaphore value incorrect after SEM_UNDO: got %d, "
+ "expected 0", val);
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+/* semtimedop handles invalid parameters correctly */
+ATF_TC(semtimedop_invalid);
+ATF_TC_HEAD(semtimedop_invalid, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "semtimedop handles invalid parameters correctly");
+}
+
+ATF_TC_BODY(semtimedop_invalid, tc)
+{
+ struct sembuf sops;
+ union semun sun;
+ struct timespec timeout;
+
+ /* Invalid semaphore id */
+ sops.sem_num = 0;
+ sops.sem_op = -1;
+ sops.sem_flg = 0;
+
+ timeout.tv_sec = 1;
+ timeout.tv_nsec = 0;
+
+ /* Attempt to perform semtimedop on invalid semid */
+ ATF_REQUIRE_MSG(semtimedop(-1, &sops, 1, &timeout) == -1
+ && errno == EINVAL, "semtimedop did not fail on invalid semid");
+
+ /* Create semaphore set */
+ key_t key = IPC_PRIVATE;
+ int semid = semget(key, 1, IPC_CREAT | 0600);
+ ATF_REQUIRE_MSG(semid != -1, "semget failed: %s", strerror(errno));
+
+ /* Initialize semaphore to 0 */
+ sun.val = 0;
+ if (semctl(semid, 0, SETVAL, sun) == -1) {
+ ATF_REQUIRE_MSG(0, "semctl SETVAL failed: %s", strerror(errno));
+ }
+
+ /* Set an invalid semaphore number */
+ sops.sem_num = 1; /* Only 1 semaphore in set, index 0 */
+ sops.sem_op = 1;
+ sops.sem_flg = 0;
+
+ /* Attempt semtimedop with invalid sem_num */
+ ATF_REQUIRE_MSG(semtimedop(semid, &sops, 1, &timeout) == -1
+ && errno == EFBIG, "semtimedop did not fail on invalid sem_num");
+
+ /* Clean up */
+ ATF_REQUIRE_MSG(semctl(semid, 0, IPC_RMID) != -1,
+ "semctl IPC_RMID failed: %s", strerror(errno));
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, semtimedop_basic);
+ ATF_TP_ADD_TC(tp, semtimedop_timeout);
+ ATF_TP_ADD_TC(tp, semtimedop_semundo);
+ ATF_TP_ADD_TC(tp, semtimedop_invalid);
+
+ return atf_no_error();
+}
diff --git a/kernel/t_setjmp.sh b/kernel/t_setjmp.sh
new file mode 100644
index 000000000000..1dbbd8504234
--- /dev/null
+++ b/kernel/t_setjmp.sh
@@ -0,0 +1,63 @@
+# $NetBSD: t_setjmp.sh,v 1.5 2025/04/29 10:57:17 martin 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.
+#
+
+module_loaded=no
+atf_test_case setjmp cleanup
+setjmp_head()
+{
+ atf_set "descr" "Test setjmp(9)/longjmp(9)"
+}
+setjmp_body()
+{
+ case `uname -p` in
+ vax)
+ atf_expect_fail "PR port-vax/59308:" \
+ " kernel longjmp(9) fails to make setjmp(9) return 1"
+ ;;
+ esac
+
+ err=$( modstat -e 2>&1 )
+ if [ $? -gt 0 ]; then
+ atf_skip "${err##modstat:}"
+ fi
+
+ module_loaded="yes"
+ modload "$(atf_get_srcdir)/setjmp_tester/setjmp_tester.kmod"
+ atf_check -s exit:0 -o inline:'1\n' \
+ sysctl -n -w kern.setjmp_tester.test=1
+}
+setjmp_cleanup()
+{
+ if [ "${module_loaded}" != "no" ]; then
+ modunload setjmp_tester
+ fi
+}
+
+atf_init_test_cases()
+{
+ atf_add_test_case setjmp
+}
diff --git a/kernel/t_signal_and_sp.c b/kernel/t_signal_and_sp.c
new file mode 100644
index 000000000000..1cef56cd3041
--- /dev/null
+++ b/kernel/t_signal_and_sp.c
@@ -0,0 +1,628 @@
+/* $NetBSD: t_signal_and_sp.c,v 1.21 2025/04/26 23:49:55 uwe Exp $ */
+
+/*
+ * Copyright (c) 2024 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 __EXPOSE_STACK /* <sys/param.h>: expose STACK_ALIGNBYTES */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: t_signal_and_sp.c,v 1.21 2025/04/26 23:49:55 uwe Exp $");
+
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <limits.h>
+#include <poll.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <ucontext.h>
+#include <unistd.h>
+
+#include "h_execsp.h"
+#include "h_macros.h"
+
+#ifdef HAVE_STACK_POINTER_H
+# include "stack_pointer.h"
+#endif
+
+#define PR_59327 "PR kern/59327: user stack pointer is not aligned properly"
+
+#ifdef HAVE_SIGNALSPHANDLER
+void signalsphandler(int); /* signalsphandler.S assembly routine */
+#endif
+
+void *volatile signalsp;
+
+static void
+test_execsp(const struct atf_tc *tc, const char *prog)
+{
+#ifdef STACK_ALIGNBYTES
+ char h_execsp[PATH_MAX];
+ struct execsp execsp;
+ int fd[2];
+ pid_t pid;
+ struct pollfd pollfd;
+ int nfds;
+ ssize_t nread;
+ int status;
+
+ /*
+ * Determine the full path to the helper program.
+ */
+ RL(snprintf(h_execsp, sizeof(h_execsp), "%s/%s",
+ atf_tc_get_config_var(tc, "srcdir"), prog));
+
+ /*
+ * Create a pipe to read a bundle of stack pointer samples from
+ * the child, and fork the child.
+ */
+ RL(pipe(fd));
+ RL(pid = vfork());
+ if (pid == 0) { /* child */
+ char *const argv[] = {h_execsp, NULL};
+
+ if (dup2(fd[1], STDOUT_FILENO) == -1)
+ _exit(1);
+ if (closefrom(STDERR_FILENO + 1) == -1)
+ _exit(2);
+ if (execve(argv[0], argv, NULL) == -1)
+ _exit(3);
+ _exit(4);
+ }
+
+ /*
+ * Close the writing end so, if something goes wrong in the
+ * child, we don't hang indefinitely waiting for output.
+ */
+ RL(close(fd[1]));
+
+ /*
+ * Wait up to 5sec for the child to return an answer. Any more
+ * than that, and we kill it. The child is mostly hand-written
+ * assembly routines where lots can go wrong, so don't bother
+ * waiting if it gets stuck in a loop.
+ */
+ pollfd.fd = fd[0];
+ pollfd.events = POLLIN;
+ RL(nfds = poll(&pollfd, 1, 5*1000/*ms*/));
+ if (nfds == 0) {
+ fprintf(stderr, "child hung, killing\n");
+ RL(kill(pid, SIGKILL));
+ }
+
+ /*
+ * Read a bundle of stack pointer samples from the child.
+ */
+ RL(nread = read(fd[0], &execsp, sizeof(execsp)));
+ ATF_CHECK_MSG((size_t)nread == sizeof(execsp),
+ "nread=%zu sizeof(execsp)=%zu",
+ (size_t)nread, sizeof(execsp));
+
+ /*
+ * Wait for the child to terminate and report failure if it
+ * didn't exit cleanly.
+ */
+ RL(waitpid(pid, &status, 0));
+ if (WIFSIGNALED(status)) {
+ atf_tc_fail_nonfatal("child exited on signal %d (%s)",
+ WTERMSIG(status), strsignal(WTERMSIG(status)));
+ } else if (!WIFEXITED(status)) {
+ atf_tc_fail_nonfatal("child exited status=0x%x", status);
+ } else {
+ ATF_CHECK_MSG(WEXITSTATUS(status) == 0,
+ "child exited with code %d",
+ WEXITSTATUS(status));
+ }
+
+ /*
+ * Now that we have reaped the child, stop here if the stack
+ * pointer samples are bogus; otherwise verify they are all
+ * aligned.
+ */
+ if ((size_t)nread != sizeof(execsp))
+ return; /* failed already */
+
+ printf("start sp @ %p\n", execsp.startsp);
+ printf("ctor sp @ %p\n", execsp.ctorsp);
+ printf("main sp @ %p\n", execsp.mainsp);
+ printf("dtor sp @ %p\n", execsp.dtorsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.startsp & STACK_ALIGNBYTES) == 0,
+ "elf entry point was called with misaligned sp: %p",
+ execsp.startsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.ctorsp & STACK_ALIGNBYTES) == 0,
+ "elf constructor was called with misaligned sp: %p",
+ execsp.ctorsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.mainsp & STACK_ALIGNBYTES) == 0,
+ "main function was called with misaligned sp: %p",
+ execsp.mainsp);
+
+ ATF_CHECK_MSG(((uintptr_t)execsp.dtorsp & STACK_ALIGNBYTES) == 0,
+ "elf destructor was called with misaligned sp: %p",
+ execsp.dtorsp);
+
+ /*
+ * Leave a reminder on architectures for which we haven't
+ * implemented execsp_start.S.
+ */
+ if (execsp.startsp == NULL ||
+ execsp.ctorsp == NULL ||
+ execsp.mainsp == NULL ||
+ execsp.dtorsp == NULL)
+ atf_tc_skip("Not fully supported on this architecture");
+#else
+ atf_tc_skip("Unknown STACK_ALIGNBYTES on this architecture");
+#endif
+}
+
+ATF_TC(execsp_dynamic);
+ATF_TC_HEAD(execsp_dynamic, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on dynamic program start");
+}
+ATF_TC_BODY(execsp_dynamic, tc)
+{
+ test_execsp(tc, "h_execsp_dynamic");
+}
+
+ATF_TC(execsp_static);
+ATF_TC_HEAD(execsp_static, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on static program start");
+}
+ATF_TC_BODY(execsp_static, tc)
+{
+ test_execsp(tc, "h_execsp_static");
+}
+
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+void *volatile contextsp; /* set by contextspfunc.S */
+static ucontext_t return_context;
+static volatile bool test_context_done;
+
+void contextspfunc(void); /* contextspfunc.S assembly routine */
+
+static void
+contextnoop(void)
+{
+
+ fprintf(stderr, "contextnoop\n");
+ /* control will return to contextspfunc via uc_link */
+}
+
+void contextdone(void); /* called by contextspfunc.S */
+void
+contextdone(void)
+{
+
+ fprintf(stderr, "contextdone\n");
+ ATF_REQUIRE(!test_context_done);
+ test_context_done = true;
+ RL(setcontext(&return_context));
+ atf_tc_fail("setcontext returned");
+}
+#endif
+
+ATF_TC(contextsp);
+ATF_TC_HEAD(contextsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on makecontext entry");
+}
+ATF_TC_BODY(contextsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+ ucontext_t uc;
+ char *stack;
+ unsigned i;
+
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p,%p)\n", stack,
+ stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc));
+ uc.uc_stack.ss_sp = stack;
+ uc.uc_stack.ss_size = SIGSTKSZ + i;
+ makecontext(&uc, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc));
+ uc.uc_stack.ss_sp = stack + i;
+ uc.uc_stack.ss_size = SIGSTKSZ;
+ makecontext(&uc, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(contextsplink);
+ATF_TC_HEAD(contextsplink, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on makecontext link entry");
+}
+ATF_TC_BODY(contextsplink, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_CONTEXTSPFUNC
+ ucontext_t uc1, uc2;
+ char *stack1, *stack2;
+ unsigned i;
+
+ REQUIRE_LIBC(stack1 = malloc(SIGSTKSZ), NULL);
+ fprintf(stderr, "stack1 @ [%p,%p)\n", stack1, stack1 + SIGSTKSZ);
+ REQUIRE_LIBC(stack2 = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack2 @ [%p,%p)\n",
+ stack2, stack2 + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc1));
+ uc1.uc_stack.ss_sp = stack1;
+ uc1.uc_stack.ss_size = SIGSTKSZ;
+ uc1.uc_link = &uc2;
+ makecontext(&uc1, &contextnoop, 0);
+
+ RL(getcontext(&uc2));
+ uc2.uc_stack.ss_sp = stack2;
+ uc2.uc_stack.ss_size = SIGSTKSZ + i;
+ makecontext(&uc2, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc1));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack2 <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack2 + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ contextsp = NULL;
+ test_context_done = false;
+
+ RL(getcontext(&uc1));
+ uc1.uc_stack.ss_sp = stack1;
+ uc1.uc_stack.ss_size = SIGSTKSZ;
+ uc1.uc_link = &uc2;
+ makecontext(&uc1, &contextnoop, 0);
+
+ RL(getcontext(&uc2));
+ uc2.uc_stack.ss_sp = stack2 + i;
+ uc2.uc_stack.ss_size = SIGSTKSZ;
+ makecontext(&uc2, &contextspfunc, 0);
+
+ fprintf(stderr, "[%u] swapcontext\n", i);
+ RL(swapcontext(&return_context, &uc1));
+
+ ATF_CHECK(contextsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack2 + i <= (uintptr_t)contextsp &&
+ (uintptr_t)contextsp <= (uintptr_t)stack2 + SIGSTKSZ + i,
+ "contextsp=%p", contextsp);
+ ATF_CHECK_MSG(((uintptr_t)contextsp & STACK_ALIGNBYTES) == 0,
+ "[%u] makecontext function called with misaligned sp %p",
+ i, contextsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(signalsp);
+ATF_TC_HEAD(signalsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on entry to signal handler");
+}
+ATF_TC_BODY(signalsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_SIGNALSPHANDLER
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+ RL(raise(SIGUSR1));
+
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "signal handler was called with a misaligned sp: %p",
+ signalsp);
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(signalsp_sigaltstack);
+ATF_TC_HEAD(signalsp_sigaltstack, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on entry to signal handler"
+ " with maximally misaligned sigaltstack");
+}
+ATF_TC_BODY(signalsp_sigaltstack, tc)
+{
+#if defined STACK_ALIGNBYTES && HAVE_SIGNALSPHANDLER
+#if defined(__sh__)
+ atf_tc_expect_fail(PR_59327);
+#endif
+ char *stack;
+ struct sigaction sa;
+ struct sigaltstack ss;
+ unsigned i;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ sa.sa_flags = SA_ONSTACK;
+ RL(sigaction(SIGUSR1, &sa, NULL));
+
+ /*
+ * Allocate a signal stack with enough slop to try all possible
+ * misalignments of the stack pointer. Print it to stderr so
+ * it always appears in atf output before shenanigans happen.
+ */
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p, %p)\n",
+ stack, stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ /*
+ * Try with all alignments of high addresses.
+ */
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ ss.ss_sp = stack;
+ ss.ss_size = SIGSTKSZ + i;
+ ss.ss_flags = 0;
+ RL(sigaltstack(&ss, NULL));
+
+ signalsp = NULL;
+ RL(raise(SIGUSR1));
+ ATF_CHECK(signalsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)signalsp &&
+ (uintptr_t)signalsp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "signalsp=%p", signalsp);
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "[%u] signal handler was called with a misaligned sp: %p",
+ i, signalsp);
+ }
+
+ /*
+ * Try with all alignments of low addresses.
+ */
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ ss.ss_sp = stack + i;
+ ss.ss_size = SIGSTKSZ;
+ ss.ss_flags = 0;
+ RL(sigaltstack(&ss, NULL));
+
+ signalsp = NULL;
+ RL(raise(SIGUSR1));
+ ATF_CHECK(signalsp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)signalsp &&
+ (uintptr_t)signalsp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "signalsp=%p", signalsp);
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "[%u] signal handler was called with a misaligned sp: %p",
+ i, signalsp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+#if defined STACK_ALIGNBYTES && defined HAVE_THREADSPFUNC
+void *threadspfunc(void *); /* threadspfunc.S assembly routine */
+#endif
+
+ATF_TC(threadsp);
+ATF_TC_HEAD(threadsp, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Verify stack pointer is aligned on thread start");
+}
+ATF_TC_BODY(threadsp, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_THREADSPFUNC
+ pthread_t t;
+ void *sp;
+ char *stack;
+ unsigned i;
+
+ REQUIRE_LIBC(stack = malloc(SIGSTKSZ + STACK_ALIGNBYTES), NULL);
+ fprintf(stderr, "stack @ [%p,%p)\n", stack,
+ stack + SIGSTKSZ + STACK_ALIGNBYTES);
+
+ RZ(pthread_create(&t, NULL, &threadspfunc, NULL));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "thread called with misaligned sp: %p", sp);
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ pthread_attr_t attr;
+
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setstack(&attr, stack, SIGSTKSZ + i));
+ RZ(pthread_create(&t, &attr, &threadspfunc, NULL));
+ RZ(pthread_attr_destroy(&attr));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack <= (uintptr_t)sp &&
+ (uintptr_t)sp <= (uintptr_t)stack + SIGSTKSZ + i,
+ "sp=%p", sp);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "[%u] thread called with misaligned sp: %p", i, sp);
+ }
+
+ for (i = 0; i <= STACK_ALIGNBYTES; i++) {
+ pthread_attr_t attr;
+
+ RZ(pthread_attr_init(&attr));
+ RZ(pthread_attr_setstack(&attr, stack + i, SIGSTKSZ));
+ RZ(pthread_create(&t, &attr, &threadspfunc, NULL));
+ RZ(pthread_attr_destroy(&attr));
+
+ alarm(1);
+ RZ(pthread_join(t, &sp));
+ alarm(0);
+
+ ATF_CHECK(sp != NULL);
+ ATF_CHECK_MSG((uintptr_t)stack + i <= (uintptr_t)sp &&
+ (uintptr_t)sp <= (uintptr_t)stack + i + SIGSTKSZ,
+ "sp=%p", sp);
+ ATF_CHECK_MSG(((uintptr_t)sp & STACK_ALIGNBYTES) == 0,
+ "[%u] thread called with misaligned sp: %p", i, sp);
+ }
+#else
+ atf_tc_skip("Not implemented on this platform");
+#endif
+}
+
+ATF_TC(misaligned_sp_and_signal);
+ATF_TC_HEAD(misaligned_sp_and_signal, tc)
+{
+ atf_tc_set_md_var(tc, "descr", "process can return from a signal"
+ " handler even if the stack pointer is misaligned when a signal"
+ " arrives");
+}
+ATF_TC_BODY(misaligned_sp_and_signal, tc)
+{
+#if defined STACK_ALIGNBYTES && defined HAVE_STACK_POINTER_H
+#if defined(__sh__)
+ atf_tc_expect_fail(PR_59327);
+#endif
+
+ /*
+ * Set up a handler for SIGALRM.
+ */
+ struct sigaction sa;
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signalsphandler;
+ RL(sigaction(SIGALRM, &sa, NULL));
+
+ /*
+ * Set up an interval timer so that we receive SIGALRM after 50 ms.
+ */
+ struct itimerval itv;
+ memset(&itv, 0, sizeof(itv));
+ itv.it_value.tv_usec = 1000 * 50;
+ RL(setitimer(ITIMER_MONOTONIC, &itv, NULL));
+
+ /*
+ * Now misalign the SP. Wait for the signal to arrive and see what
+ * happens. This should be fine as long as we don't use it to
+ * access memory.
+ */
+ MISALIGN_SP;
+ while (signalsp == NULL) {
+ /*
+ * Make sure the compiler does not optimize this busy loop
+ * away.
+ */
+ __asm__("" ::: "memory");
+ }
+ /*
+ * We could successfully return from a signal handler. Now we
+ * should fix the SP before calling any functions.
+ */
+ FIX_SP;
+
+ /*
+ * But was the stack pointer aligned when we were on the signal
+ * handler?
+ */
+ ATF_CHECK_MSG(((uintptr_t)signalsp & STACK_ALIGNBYTES) == 0,
+ "signal handler was called with a misaligned sp: %p",
+ signalsp);
+#else
+ atf_tc_skip("Not implemented for this platform");
+#endif
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, contextsp);
+ ATF_TP_ADD_TC(tp, contextsplink);
+ ATF_TP_ADD_TC(tp, execsp_dynamic);
+ ATF_TP_ADD_TC(tp, execsp_static);
+ ATF_TP_ADD_TC(tp, misaligned_sp_and_signal);
+ ATF_TP_ADD_TC(tp, signalsp);
+ ATF_TP_ADD_TC(tp, signalsp_sigaltstack);
+ ATF_TP_ADD_TC(tp, threadsp);
+ return atf_no_error();
+}
diff --git a/kernel/t_time_arith.c b/kernel/t_time_arith.c
new file mode 100644
index 000000000000..0f738bfbe19a
--- /dev/null
+++ b/kernel/t_time_arith.c
@@ -0,0 +1,1224 @@
+/* $NetBSD: t_time_arith.c,v 1.7 2025/10/06 12:05:04 riastradh Exp $ */
+
+/*-
+ * Copyright (c) 2024-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_time_arith.c,v 1.7 2025/10/06 12:05:04 riastradh Exp $");
+
+#include <sys/timearith.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <util.h>
+
+#include "h_macros.h"
+
+enum { HZ = 100 };
+
+int hz = HZ;
+int tick = 1000000/HZ;
+
+static sig_atomic_t jmp_en;
+static int jmp_sig;
+static jmp_buf jmp;
+
+static void
+handle_signal(int signo)
+{
+ const int errno_save = errno;
+ char buf[32];
+
+ snprintf_ss(buf, sizeof(buf), "signal %d\n", signo);
+ (void)write(STDERR_FILENO, buf, strlen(buf));
+
+ errno = errno_save;
+
+ if (jmp_en) {
+ jmp_sig = signo;
+ jmp_en = 0;
+ longjmp(jmp, 1);
+ } else {
+ raise_default_signal(signo);
+ }
+}
+
+const struct itimer_transition {
+ struct itimerspec it_time;
+ struct timespec it_now;
+ struct timespec it_next;
+ int it_overruns;
+ const char *it_xfail;
+} itimer_transitions[] = {
+ /*
+ * Fired more than one interval early -- treat clock as wound
+ * backwards, not counting overruns. Advance to the next
+ * integral multiple of it_interval starting from it_value.
+ */
+ [0] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,1}, {1,0}, 0,
+ NULL},
+ [1] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,500000000}, {1,0}, 0,
+ NULL},
+ [2] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {0,999999999}, {1,0}, 0,
+ NULL},
+ [3] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,0}, {2,0}, 0,
+ NULL},
+ [4] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,1}, {2,0}, 0,
+ NULL},
+ [5] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,500000000}, {2,0}, 0,
+ NULL},
+ [6] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {1,999999999}, {2,0}, 0,
+ NULL},
+
+ /*
+ * Fired exactly one interval early. Treat this too as clock
+ * wound backwards.
+ */
+ [7] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,0}, {3,0}, 0,
+ NULL},
+
+ /*
+ * Fired less than one interval early -- callouts and real-time
+ * clock might not be perfectly synced, counted as zero
+ * overruns. Advance by one interval from the scheduled time.
+ */
+ [8] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,1}, {3,0}, 0,
+ NULL},
+ [9] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,500000000}, {3,0}, 0,
+ NULL},
+ [10] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {2,999999999}, {3,0}, 0,
+ NULL},
+
+ /*
+ * Fired exactly on time. Advance by one interval.
+ */
+ [11] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,0}, {4,0}, 0, NULL},
+
+ /*
+ * Fired late by less than one interval -- callouts and
+ * real-time clock might not be prefectly synced, counted as
+ * zero overruns. Advance by one interval from the scheduled
+ * time (even if it's very close to a full interval).
+ */
+ [12] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,1}, {4,0}, 0, NULL},
+ [14] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,500000000}, {4,0}, 0, NULL},
+ [15] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {3,999999999}, {4,0}, 0, NULL},
+
+ /*
+ * Fired late by exactly one interval -- treat it as overrun.
+ */
+ [16] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,0}, {5,0}, 1,
+ NULL},
+
+ /*
+ * Fired late by more than one interval but less than two --
+ * overrun.
+ */
+ [17] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,1}, {5,0}, 1,
+ NULL},
+ [18] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,500000000}, {5,0}, 1,
+ NULL},
+ [19] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {4,999999999}, {5,0}, 1,
+ NULL},
+
+ /*
+ * Fired late by exactly two intervals -- two overruns.
+ */
+ [20] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {5,0}, {6,0}, 2,
+ NULL},
+
+ /*
+ * Fired late by more intervals plus slop, up to 32.
+ *
+ * XXX Define DELAYTIMER_MAX so we can write it in terms of
+ * that.
+ */
+ [21] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {13,123456789}, {14,0}, 10,
+ NULL},
+ [22] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {34,999999999}, {35,0}, 31,
+ NULL},
+
+ /*
+ * Fired late by roughly INT_MAX intervals.
+ */
+ [23] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX - 1, 0},
+ {(time_t)3 + INT_MAX, 0},
+ INT_MAX - 1,
+ NULL},
+ [24] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX, 0},
+ {(time_t)3 + INT_MAX + 1, 0},
+ INT_MAX,
+ NULL},
+ [25] = {{.it_value = {3,0}, .it_interval = {1,0}},
+ {(time_t)3 + INT_MAX + 1, 0},
+ {(time_t)3 + INT_MAX + 2, 0},
+ INT_MAX,
+ NULL},
+
+ /* (2^63 - 1) ns */
+ [26] = {{.it_value = {3,0}, .it_interval = {9223372036,854775807}},
+ {3,1}, {9223372039,854775807}, 0, NULL},
+ /* 2^63 ns */
+ [27] = {{.it_value = {3,0}, .it_interval = {9223372036,854775808}},
+ {3,1}, {9223372039,854775808}, 0, NULL},
+ /* (2^63 + 1) ns */
+ [28] = {{.it_value = {3,0}, .it_interval = {9223372036,854775809}},
+ {3,1}, {9223372039,854775809}, 0, NULL},
+
+ /*
+ * Overflows -- we should (XXX but currently don't) reject
+ * intervals of at least 2^64 nanoseconds up front, since this
+ * is more time than it is reasonable to wait (more than 584
+ * years).
+ */
+
+ /* (2^64 - 1) ns */
+ [29] = {{.it_value = {3,0}, .it_interval = {18446744073,709551615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 ns */
+ [30] = {{.it_value = {3,0}, .it_interval = {18446744073,709551616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) ns */
+ [31] = {{.it_value = {3,0}, .it_interval = {18446744073,709551617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^63 - 1) us */
+ [32] = {{.it_value = {3,0}, .it_interval = {9223372036854,775807}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^63 us */
+ [33] = {{.it_value = {3,0}, .it_interval = {9223372036854,775808}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^63 + 1) us */
+ [34] = {{.it_value = {3,0}, .it_interval = {9223372036854,775809}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^64 - 1) us */
+ [35] = {{.it_value = {3,0}, .it_interval = {18446744073709,551615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 us */
+ [36] = {{.it_value = {3,0}, .it_interval = {18446744073709,551616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) us */
+ [37] = {{.it_value = {3,0}, .it_interval = {18446744073709,551617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^63 - 1) ms */
+ [38] = {{.it_value = {3,0}, .it_interval = {9223372036854775,807}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^63 ms */
+ [39] = {{.it_value = {3,0}, .it_interval = {9223372036854775,808}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^63 + 1) ms */
+ [40] = {{.it_value = {3,0}, .it_interval = {9223372036854775,809}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* (2^64 - 1) ms */
+ [41] = {{.it_value = {3,0}, .it_interval = {18446744073709551,615}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* 2^64 ms */
+ [42] = {{.it_value = {3,0}, .it_interval = {18446744073709551,616}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+ /* (2^64 + 1) ms */
+ [43] = {{.it_value = {3,0}, .it_interval = {18446744073709551,617}},
+ {2,999999999}, {0,0}, 0,
+ NULL},
+
+ /* invalid intervals */
+ [44] = {{.it_value = {3,0}, .it_interval = {-1,0}},
+ {3,1}, {0,0}, 0, NULL},
+ [45] = {{.it_value = {3,0}, .it_interval = {0,-1}},
+ {3,1}, {0,0}, 0, NULL},
+ [46] = {{.it_value = {3,0}, .it_interval = {0,1000000000}},
+ {3,1}, {0,0}, 0, NULL},
+
+ /*
+ * Overflow nanosecond arithmetic. The magic interval number
+ * here is ceiling(INT64_MAX/2) nanoseconds. The interval
+ * start value will be rounded to an integral number of ticks,
+ * so rather than write exactly `4611686018,427387905', just
+ * round up the `now' value to the next second. This forces an
+ * overrun _and_ triggers int64_t arithmetic overflow.
+ */
+ [47] = {{.it_value = {0,1},
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {4611686019,0}, {0,0}, 1,
+ NULL},
+
+ /* interval ~ 1/4 * (2^63 - 1) ns, now ~ 3/4 * (2^63 - 1) ns */
+ [48] = {{.it_value = {0,1},
+ .it_interval = {2305843009,213693952}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 3,
+ NULL},
+ [49] = {{.it_value = {6917529027,0},
+ .it_interval = {2305843009,213693952}},
+ {6917529028,0}, {9223372036,213693952}, 0, NULL},
+ [50] = {{.it_value = {6917529029,0},
+ .it_interval = {2305843009,213693952}},
+ {6917529028,0}, {6917529029,0}, 0,
+ NULL},
+
+ /* interval ~ 1/2 * (2^63 - 1) ns, now ~ 3/4 * (2^63 - 1) ns */
+ [51] = {{.it_value = {0,1},
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 1,
+ NULL},
+ [52] = {{.it_value = {2305843009,213693951}, /* ~1/4 * (2^63 - 1) */
+ .it_interval = {4611686018,427387904}},
+ /* XXX needless overflow */
+ {6917529028,0}, {0,0}, 1,
+ NULL},
+ [54] = {{.it_value = {6917529027,0},
+ .it_interval = {4611686018,427387904}},
+ {6917529028,0}, {11529215045,427387904}, 0, NULL},
+ [55] = {{.it_value = {6917529029,0},
+ .it_interval = {4611686018,427387904}},
+ {6917529028,0}, {6917529029,0}, 0,
+ NULL},
+
+ [56] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ /* XXX needless overflow */
+ {INT64_MAX - 2,999999999}, {0,0}, 0,
+ NULL},
+ [57] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,0}, {INT64_MAX,0}, 0, NULL},
+ [58] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,1}, {INT64_MAX,0}, 0, NULL},
+ [59] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,999999999}, {INT64_MAX,0}, 0, NULL},
+ [60] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,0}, {0,0}, 0,
+ NULL},
+ [61] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,1}, {0,0}, 0,
+ NULL},
+ [62] = {{.it_value = {INT64_MAX - 1,0}, .it_interval = {1,0}},
+ {INT64_MAX,999999999}, {0,0}, 0,
+ NULL},
+
+ [63] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,1}, {0,0}, 0,
+ NULL},
+ [64] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX - 1,999999999}, {0,0}, 0,
+ NULL},
+ [65] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,0}, {0,0}, 0,
+ NULL},
+ [66] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,1}, {0,0}, 0,
+ NULL},
+ [67] = {{.it_value = {INT64_MAX,0}, .it_interval = {1,0}},
+ {INT64_MAX,999999999}, {0,0}, 0,
+ NULL},
+};
+
+ATF_TC(itimer_transitions);
+ATF_TC_HEAD(itimer_transitions, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Tests interval timer transitions");
+}
+ATF_TC_BODY(itimer_transitions, tc)
+{
+ volatile unsigned i;
+
+ REQUIRE_LIBC(signal(SIGFPE, handle_signal), SIG_ERR);
+ REQUIRE_LIBC(signal(SIGABRT, handle_signal), SIG_ERR);
+
+ for (i = 0; i < __arraycount(itimer_transitions); i++) {
+ struct itimer_transition it = itimer_transitions[i];
+ struct timespec next;
+ int overruns;
+ volatile bool aborted = true;
+ volatile bool expect_abort = false;
+
+ fprintf(stderr, "case %u\n", i);
+
+ if (it.it_xfail)
+ atf_tc_expect_fail("%s", it.it_xfail);
+
+ if (itimespecfix(&it.it_time.it_value) != 0 ||
+ itimespecfix(&it.it_time.it_interval) != 0) {
+ fprintf(stderr, "rejected by itimerspecfix\n");
+ expect_abort = true;
+ }
+
+ if (setjmp(jmp) == 0) {
+ jmp_en = 1;
+ itimer_transition(&it.it_time, &it.it_now,
+ &next, &overruns);
+ jmp_en = 0;
+ aborted = false;
+ }
+ ATF_CHECK(!jmp_en);
+ jmp_en = 0; /* paranoia */
+ if (expect_abort) {
+ fprintf(stderr, "expected abort\n");
+ ATF_CHECK_MSG(aborted,
+ "[%u] missing invariant assertion", i);
+ ATF_CHECK_MSG(jmp_sig == SIGABRT,
+ "[%u] missing invariant assertion", i);
+ } else {
+ ATF_CHECK_MSG(!aborted, "[%u] raised signal %d: %s", i,
+ jmp_sig, strsignal(jmp_sig));
+ }
+
+ ATF_CHECK_MSG((next.tv_sec == it.it_next.tv_sec &&
+ next.tv_nsec == it.it_next.tv_nsec),
+ "[%u] periodic intervals of %lld.%09d from %lld.%09d"
+ " last expired at %lld.%09d:"
+ " next expiry at %lld.%09d, expected %lld.%09d", i,
+ (long long)it.it_time.it_interval.tv_sec,
+ (int)it.it_time.it_interval.tv_nsec,
+ (long long)it.it_time.it_value.tv_sec,
+ (int)it.it_time.it_value.tv_nsec,
+ (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec,
+ (long long)next.tv_sec, (int)next.tv_nsec,
+ (long long)it.it_next.tv_sec, (int)it.it_next.tv_nsec);
+ ATF_CHECK_EQ_MSG(overruns, it.it_overruns,
+ "[%u] periodic intervals of %lld.%09d from %lld.%09d"
+ " last expired at %lld.%09d:"
+ " overruns %d, expected %d", i,
+ (long long)it.it_time.it_interval.tv_sec,
+ (int)it.it_time.it_interval.tv_nsec,
+ (long long)it.it_time.it_value.tv_sec,
+ (int)it.it_time.it_value.tv_nsec,
+ (long long)it.it_now.tv_sec, (int)it.it_now.tv_nsec,
+ overruns, it.it_overruns);
+
+ if (it.it_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+/*
+ * { 0, if t <= 0;
+ * tstohz(t sec) @ f Hz = { ceil(t/(1/f)), if that's below INT_MAX;
+ * { INT_MAX, otherwise.
+ */
+
+#define TSTOHZ_ROUND_XFAIL \
+ "PR kern/59691: tstohz(9) rounding errors"
+
+const struct tstohz_case {
+ int ts_hz;
+ struct timespec ts_ts;
+ int ts_ticks;
+ const char *ts_xfail;
+} tstohz_cases[] = {
+ /*
+ * hz = 10
+ */
+
+ /* negative inputs yield 0 ticks */
+ [0] = {10, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [1] = {10, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+
+ /* zero input yields 0 ticks */
+ [2] = {10, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+
+ /*
+ * Nonzero input always yields >=2 ticks, because the time from
+ * now until the next tick may be arbitrarily short, and we
+ * need to wait one full tick, so we have to wait for two
+ * ticks.
+ */
+ [3] = {10, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [4] = {10, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [5] = {10, {.tv_sec = 0, .tv_nsec = 99999999}, 2, NULL},
+ [6] = {10, {.tv_sec = 0, .tv_nsec = 100000000}, 2, NULL},
+ [7] = {10, {.tv_sec = 0, .tv_nsec = 100000001}, 3, NULL},
+ [8] = {10, {.tv_sec = 0, .tv_nsec = 100000002}, 3, NULL},
+ [9] = {10, {.tv_sec = 0, .tv_nsec = 199999999}, 3, NULL},
+ [10] = {10, {.tv_sec = 0, .tv_nsec = 200000000}, 3, NULL},
+ [11] = {10, {.tv_sec = 0, .tv_nsec = 200000001}, 4, NULL},
+ [12] = {10, {.tv_sec = 0, .tv_nsec = 200000002}, 4, NULL},
+ [13] = {10, {.tv_sec = 0, .tv_nsec = 999999999}, 11, NULL},
+ [14] = {10, {.tv_sec = 1, .tv_nsec = 0}, 11, NULL},
+ [15] = {10, {.tv_sec = 1, .tv_nsec = 1}, 12, NULL},
+ [16] = {10, {.tv_sec = 1, .tv_nsec = 2}, 12, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [17] = {10, {.tv_sec = 2147, .tv_nsec = 999999999}, 21481, NULL},
+ [18] = {10, {.tv_sec = 2148, .tv_nsec = 0}, 21481, NULL},
+ [19] = {10, {.tv_sec = 2148, .tv_nsec = 1}, 21482, NULL},
+ [20] = {10, {.tv_sec = 2148, .tv_nsec = 2}, 21482, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [21] = {10, {.tv_sec = 214748364, .tv_nsec = 499999999}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [22] = {10, {.tv_sec = 214748364, .tv_nsec = 500000000}, 2147483646,
+ NULL},
+ [23] = {10, {.tv_sec = 214748364, .tv_nsec = 500000001}, 2147483647,
+ NULL},
+ [24] = {10, {.tv_sec = 214748364, .tv_nsec = 500000002}, 2147483647,
+ NULL},
+ [25] = {10, {.tv_sec = 214748364, .tv_nsec = 599999999}, 2147483647,
+ NULL},
+ [26] = {10, {.tv_sec = 214748364, .tv_nsec = 600000000}, 2147483647,
+ NULL},
+ [27] = {10, {.tv_sec = 214748364, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ [28] = {10, {.tv_sec = 214748365, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [29] = {10, {.tv_sec = 214748365, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [30] = {10, {.tv_sec = 214748365, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [31] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [32] = {10, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [33] = {10, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [34] = {10, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [35] = {10, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [36] = {10, {.tv_sec = 922337203685477580, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [37] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [38] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [39] = {10, {.tv_sec = 922337203685477581, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [40] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 100
+ */
+
+ [41] = {100, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [42] = {100, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [43] = {100, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [44] = {100, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [45] = {100, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [46] = {100, {.tv_sec = 0, .tv_nsec = 9999999}, 2, NULL},
+ [47] = {100, {.tv_sec = 0, .tv_nsec = 10000000}, 2, NULL},
+ [48] = {100, {.tv_sec = 0, .tv_nsec = 10000001}, 3, NULL},
+ [49] = {100, {.tv_sec = 0, .tv_nsec = 10000002}, 3, NULL},
+ [50] = {100, {.tv_sec = 0, .tv_nsec = 19999999}, 3, NULL},
+ [51] = {100, {.tv_sec = 0, .tv_nsec = 20000000}, 3, NULL},
+ [52] = {100, {.tv_sec = 0, .tv_nsec = 20000001}, 4, NULL},
+ [53] = {100, {.tv_sec = 0, .tv_nsec = 20000002}, 4, NULL},
+ [54] = {100, {.tv_sec = 0, .tv_nsec = 99999999}, 11, NULL},
+ [55] = {100, {.tv_sec = 0, .tv_nsec = 100000000}, 11, NULL},
+ [56] = {100, {.tv_sec = 0, .tv_nsec = 100000001}, 12, NULL},
+ [57] = {100, {.tv_sec = 0, .tv_nsec = 100000002}, 12, NULL},
+ [58] = {100, {.tv_sec = 0, .tv_nsec = 999999999}, 101, NULL},
+ [59] = {100, {.tv_sec = 1, .tv_nsec = 0}, 101, NULL},
+ [60] = {100, {.tv_sec = 1, .tv_nsec = 1}, 102, NULL},
+ [61] = {100, {.tv_sec = 1, .tv_nsec = 2}, 102, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [62] = {100, {.tv_sec = 2147, .tv_nsec = 999999999}, 214801, NULL},
+ [63] = {100, {.tv_sec = 2148, .tv_nsec = 0}, 214801, NULL},
+ [64] = {100, {.tv_sec = 2148, .tv_nsec = 1}, 214802, NULL},
+ [65] = {100, {.tv_sec = 2148, .tv_nsec = 2}, 214802, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [66] = {100, {.tv_sec = 21474836, .tv_nsec = 439999999}, 2147483645,
+ NULL},
+ [67] = {100, {.tv_sec = 21474836, .tv_nsec = 440000000}, 2147483645,
+ NULL},
+ [68] = {100, {.tv_sec = 21474836, .tv_nsec = 440000001}, 2147483646,
+ NULL},
+ [69] = {100, {.tv_sec = 21474836, .tv_nsec = 440000002}, 2147483646,
+ NULL},
+ [70] = {100, {.tv_sec = 21474836, .tv_nsec = 449999999}, 2147483646,
+ NULL},
+ [71] = {100, {.tv_sec = 21474836, .tv_nsec = 450000000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [72] = {100, {.tv_sec = 21474836, .tv_nsec = 450000001}, 2147483647,
+ NULL},
+ [73] = {100, {.tv_sec = 21474836, .tv_nsec = 450000002}, 2147483647,
+ NULL},
+ [74] = {100, {.tv_sec = 21474836, .tv_nsec = 459999999}, 2147483647,
+ NULL},
+ [75] = {100, {.tv_sec = 21474836, .tv_nsec = 460000000}, 2147483647,
+ NULL},
+ [76] = {100, {.tv_sec = 21474836, .tv_nsec = 460000001}, 2147483647,
+ NULL},
+ [77] = {100, {.tv_sec = 21474836, .tv_nsec = 460000002}, 2147483647,
+ NULL},
+ [78] = {100, {.tv_sec = 21474836, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ [79] = {100, {.tv_sec = 21474837, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [80] = {100, {.tv_sec = 21474837, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [81] = {100, {.tv_sec = 21474837, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [82] = {100, {.tv_sec = 21474837, .tv_nsec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000 */
+ [83] = {100, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [84] = {100, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [85] = {100, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [86] = {100, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [87] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [88] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [89] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [90] = {100, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [91] = {100, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 1000
+ */
+
+ [92] = {1000, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [93] = {1000, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [94] = {1000, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [95] = {1000, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [96] = {1000, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [97] = {1000, {.tv_sec = 0, .tv_nsec = 999999}, 2, NULL},
+ [98] = {1000, {.tv_sec = 0, .tv_nsec = 1000000}, 2, NULL},
+ [99] = {1000, {.tv_sec = 0, .tv_nsec = 1000001}, 3, NULL},
+ [100] = {1000, {.tv_sec = 0, .tv_nsec = 1000002}, 3, NULL},
+ [101] = {1000, {.tv_sec = 0, .tv_nsec = 1999999}, 3, NULL},
+ [102] = {1000, {.tv_sec = 0, .tv_nsec = 2000000}, 3, NULL},
+ [103] = {1000, {.tv_sec = 0, .tv_nsec = 2000001}, 4, NULL},
+ [104] = {1000, {.tv_sec = 0, .tv_nsec = 2000002}, 4, NULL},
+ [105] = {1000, {.tv_sec = 0, .tv_nsec = 999999999}, 1001, NULL},
+ [106] = {1000, {.tv_sec = 1, .tv_nsec = 0}, 1001, NULL},
+ [107] = {1000, {.tv_sec = 1, .tv_nsec = 1}, 1002, NULL},
+ [108] = {1000, {.tv_sec = 1, .tv_nsec = 2}, 1002, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [109] = {1000, {.tv_sec = 2147, .tv_nsec = 999999999}, 2148001, NULL},
+ [110] = {1000, {.tv_sec = 2148, .tv_nsec = 0}, 2148001, NULL},
+ [111] = {1000, {.tv_sec = 2148, .tv_nsec = 1}, 2148002, NULL},
+ [112] = {1000, {.tv_sec = 2148, .tv_nsec = 2}, 2148002, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [113] = {1000, {.tv_sec = 2147483, .tv_nsec = 643999999}, 2147483645,
+ NULL},
+ [114] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000000}, 2147483645,
+ NULL},
+ [115] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000001}, 2147483646,
+ NULL},
+ [116] = {1000, {.tv_sec = 2147483, .tv_nsec = 644000002}, 2147483646,
+ NULL},
+ [117] = {1000, {.tv_sec = 2147483, .tv_nsec = 644999999}, 2147483646,
+ NULL},
+ [118] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [119] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000001}, 2147483647,
+ NULL},
+ [120] = {1000, {.tv_sec = 2147483, .tv_nsec = 645000002}, 2147483647,
+ NULL},
+ [121] = {1000, {.tv_sec = 2147483, .tv_nsec = 645999999}, 2147483647,
+ NULL},
+ [122] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000000}, 2147483647,
+ NULL},
+ [123] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000001}, 2147483647,
+ NULL},
+ [124] = {1000, {.tv_sec = 2147483, .tv_nsec = 646000002}, 2147483647,
+ NULL},
+ [125] = {1000, {.tv_sec = 2147483, .tv_nsec = 699999999}, 2147483647,
+ NULL},
+ [126] = {1000, {.tv_sec = 2147484, .tv_nsec = 0}, 2147483647,
+ NULL},
+ [127] = {1000, {.tv_sec = 2147484, .tv_nsec = 1}, 2147483647,
+ NULL},
+ [128] = {1000, {.tv_sec = 2147484, .tv_nsec = 2}, 2147483647,
+ NULL},
+ [129] = {1000, {.tv_sec = 2147484, .tv_nsec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [130] = {1000, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [131] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [132] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [133] = {1000, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [134] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [135] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [136] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [137] = {1000, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [138] = {1000, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 8191, prime non-divisor of 10^k or 2^k
+ */
+
+ [139] = {8191, {.tv_sec = -1, .tv_nsec = 0}, 0, NULL},
+ [140] = {8191, {.tv_sec = -1, .tv_nsec = 999999999}, 0, NULL},
+ [141] = {8191, {.tv_sec = 0, .tv_nsec = 0}, 0, NULL},
+ [142] = {8191, {.tv_sec = 0, .tv_nsec = 1}, 2, NULL},
+ [143] = {8191, {.tv_sec = 0, .tv_nsec = 2}, 2, NULL},
+ [144] = {8191, {.tv_sec = 0, .tv_nsec = 122084}, 2,
+ TSTOHZ_ROUND_XFAIL},
+ [145] = {8191, {.tv_sec = 0, .tv_nsec = 122085}, 2,
+ TSTOHZ_ROUND_XFAIL},
+ [146] = {8191, {.tv_sec = 0, .tv_nsec = 122086}, 3, NULL},
+ [147] = {8191, {.tv_sec = 0, .tv_nsec = 244168}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [148] = {8191, {.tv_sec = 0, .tv_nsec = 244169}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [149] = {8191, {.tv_sec = 0, .tv_nsec = 244170}, 3,
+ TSTOHZ_ROUND_XFAIL},
+ [150] = {8191, {.tv_sec = 0, .tv_nsec = 244171}, 4, NULL},
+ [151] = {8191, {.tv_sec = 0, .tv_nsec = 244172}, 4, NULL},
+ [152] = {8191, {.tv_sec = 0, .tv_nsec = 999999999}, 8192, NULL},
+ [153] = {8191, {.tv_sec = 1, .tv_nsec = 0}, 8192, NULL},
+ [154] = {8191, {.tv_sec = 1, .tv_nsec = 1}, 8193, NULL},
+ [155] = {8191, {.tv_sec = 1, .tv_nsec = 2}, 8193, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [156] = {8191, {.tv_sec = 2147, .tv_nsec = 999999999}, 17594269, NULL},
+ [157] = {8191, {.tv_sec = 2148, .tv_nsec = 0}, 17594269, NULL},
+ [158] = {8191, {.tv_sec = 2148, .tv_nsec = 1}, 17594270, NULL},
+ [159] = {8191, {.tv_sec = 2148, .tv_nsec = 2}, 17594270, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [160] = {8191, {.tv_sec = 262176, .tv_nsec = 3540471}, 2147483646,
+ NULL},
+ [161] = {8191, {.tv_sec = 262176, .tv_nsec = 3540472}, 2147483647,
+ NULL},
+ [162] = {8191, {.tv_sec = 262176, .tv_nsec = 3540473}, 2147483647,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [163] = {8191, {.tv_sec = 262176, .tv_nsec = 3662556}, 2147483647,
+ NULL},
+ [164] = {8191, {.tv_sec = 262176, .tv_nsec = 3662557}, 2147483647,
+ NULL},
+ [165] = {8191, {.tv_sec = 262176, .tv_nsec = 3662558}, 2147483647,
+ NULL},
+ [166] = {8191, {.tv_sec = 262176, .tv_nsec = 999999999}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [167] = {8191, {.tv_sec = 9223372036854, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [168] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [169] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [170] = {8191, {.tv_sec = 9223372036855, .tv_nsec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [171] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 999999999},
+ INT_MAX, NULL},
+ [172] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 0},
+ INT_MAX, NULL},
+ [173] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 1},
+ INT_MAX, NULL},
+ [174] = {8191, {.tv_sec = 92233720368547758, .tv_nsec = 2},
+ INT_MAX, NULL},
+ [175] = {8191, {.tv_sec = (time_t)INT_MAX + 1, .tv_nsec = 123456789},
+ INT_MAX, NULL},
+};
+
+ATF_TC(tstohz);
+ATF_TC_HEAD(tstohz, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test tstohz(9)");
+}
+ATF_TC_BODY(tstohz, tc)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(tstohz_cases); i++) {
+ const struct tstohz_case *ts = &tstohz_cases[i];
+ int ticks;
+
+ /* set system parameters */
+ hz = ts->ts_hz;
+ tick = 1000000/hz;
+
+ ticks = tstohz(&ts->ts_ts);
+ if (ts->ts_xfail)
+ atf_tc_expect_fail("%s", ts->ts_xfail);
+
+ /*
+ * Allow some slop of one part per thousand in the
+ * arithmetic, but ensure we round up, not down.
+ */
+ ATF_CHECK_MSG(((unsigned)(ticks - ts->ts_ticks) <=
+ (unsigned)ts->ts_ticks/1000),
+ "[%zu] tstohz(%lld.%09ld sec) @ %d Hz:"
+ " expected %d, got %d",
+ i,
+ (long long)ts->ts_ts.tv_sec,
+ (long)ts->ts_ts.tv_nsec,
+ ts->ts_hz,
+ ts->ts_ticks,
+ ticks);
+ if (ts->ts_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+/*
+ * { 0, if t <= 0;
+ * tvtohz(t sec) @ f Hz = { ceil(t/(1/f)), if that's below INT_MAX;
+ * { INT_MAX, otherwise.
+ */
+
+const struct tvtohz_case {
+ int tv_hz;
+ struct timeval tv_tv;
+ int tv_ticks;
+ const char *tv_xfail;
+} tvtohz_cases[] = {
+ /*
+ * hz = 10
+ */
+
+ /* negative inputs yield 0 ticks */
+ [0] = {10, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [1] = {10, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+
+ /* zero input yields 0 ticks */
+ [2] = {10, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+
+ /*
+ * Nonzero input always yields >=2 ticks, because the time from
+ * now until the next tick may be arbitrarily short, and we
+ * need to wait one full tick, so we have to wait for two
+ * ticks.
+ */
+ [3] = {10, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [4] = {10, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [5] = {10, {.tv_sec = 0, .tv_usec = 99999}, 2, NULL},
+ [6] = {10, {.tv_sec = 0, .tv_usec = 100000}, 2, NULL},
+ [7] = {10, {.tv_sec = 0, .tv_usec = 100001}, 3, NULL},
+ [8] = {10, {.tv_sec = 0, .tv_usec = 100002}, 3, NULL},
+ [9] = {10, {.tv_sec = 0, .tv_usec = 199999}, 3, NULL},
+ [10] = {10, {.tv_sec = 0, .tv_usec = 200000}, 3, NULL},
+ [11] = {10, {.tv_sec = 0, .tv_usec = 200001}, 4, NULL},
+ [12] = {10, {.tv_sec = 0, .tv_usec = 200002}, 4, NULL},
+ [13] = {10, {.tv_sec = 0, .tv_usec = 999999}, 11, NULL},
+ [14] = {10, {.tv_sec = 1, .tv_usec = 0}, 11, NULL},
+ [15] = {10, {.tv_sec = 1, .tv_usec = 1}, 12, NULL},
+ [16] = {10, {.tv_sec = 1, .tv_usec = 2}, 12, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [17] = {10, {.tv_sec = 2147, .tv_usec = 999999}, 21481, NULL},
+ [18] = {10, {.tv_sec = 2148, .tv_usec = 0}, 21481, NULL},
+ [19] = {10, {.tv_sec = 2148, .tv_usec = 1}, 21482, NULL},
+ [20] = {10, {.tv_sec = 2148, .tv_usec = 2}, 21482, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [21] = {10, {.tv_sec = 214748364, .tv_usec = 499999}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [22] = {10, {.tv_sec = 214748364, .tv_usec = 500000}, 2147483646,
+ NULL},
+ [23] = {10, {.tv_sec = 214748364, .tv_usec = 500001}, 2147483647,
+ NULL},
+ [24] = {10, {.tv_sec = 214748364, .tv_usec = 500002}, 2147483647,
+ NULL},
+ [25] = {10, {.tv_sec = 214748364, .tv_usec = 599999}, 2147483647,
+ NULL},
+ [26] = {10, {.tv_sec = 214748364, .tv_usec = 600000}, 2147483647,
+ NULL},
+ [27] = {10, {.tv_sec = 214748364, .tv_usec = 999999}, 2147483647,
+ NULL},
+ [28] = {10, {.tv_sec = 214748365, .tv_usec = 0}, 2147483647,
+ NULL},
+ [29] = {10, {.tv_sec = 214748365, .tv_usec = 1}, 2147483647,
+ NULL},
+ [30] = {10, {.tv_sec = 214748365, .tv_usec = 2}, 2147483647,
+ NULL},
+ [31] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [32] = {10, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [33] = {10, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [34] = {10, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [35] = {10, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [36] = {10, {.tv_sec = 922337203685477580, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [37] = {10, {.tv_sec = 922337203685477581, .tv_usec = 0},
+ INT_MAX, NULL},
+ [38] = {10, {.tv_sec = 922337203685477581, .tv_usec = 1},
+ INT_MAX, NULL},
+ [39] = {10, {.tv_sec = 922337203685477581, .tv_usec = 2},
+ INT_MAX, NULL},
+ [40] = {10, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 100
+ */
+
+ [41] = {100, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [42] = {100, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [43] = {100, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [44] = {100, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [45] = {100, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [46] = {100, {.tv_sec = 0, .tv_usec = 9999}, 2, NULL},
+ [47] = {100, {.tv_sec = 0, .tv_usec = 10000}, 2, NULL},
+ [48] = {100, {.tv_sec = 0, .tv_usec = 10001}, 3, NULL},
+ [49] = {100, {.tv_sec = 0, .tv_usec = 10002}, 3, NULL},
+ [50] = {100, {.tv_sec = 0, .tv_usec = 19999}, 3, NULL},
+ [51] = {100, {.tv_sec = 0, .tv_usec = 20000}, 3, NULL},
+ [52] = {100, {.tv_sec = 0, .tv_usec = 20001}, 4, NULL},
+ [53] = {100, {.tv_sec = 0, .tv_usec = 20002}, 4, NULL},
+ [54] = {100, {.tv_sec = 0, .tv_usec = 99999}, 11, NULL},
+ [55] = {100, {.tv_sec = 0, .tv_usec = 100000}, 11, NULL},
+ [56] = {100, {.tv_sec = 0, .tv_usec = 100001}, 12, NULL},
+ [57] = {100, {.tv_sec = 0, .tv_usec = 100002}, 12, NULL},
+ [58] = {100, {.tv_sec = 0, .tv_usec = 999999}, 101, NULL},
+ [59] = {100, {.tv_sec = 1, .tv_usec = 0}, 101, NULL},
+ [60] = {100, {.tv_sec = 1, .tv_usec = 1}, 102, NULL},
+ [61] = {100, {.tv_sec = 1, .tv_usec = 2}, 102, NULL},
+ /* .tv_sec ~ INT32_MAX/1000000 */
+ [62] = {100, {.tv_sec = 2147, .tv_usec = 999999}, 214801, NULL},
+ [63] = {100, {.tv_sec = 2148, .tv_usec = 0}, 214801, NULL},
+ [64] = {100, {.tv_sec = 2148, .tv_usec = 1}, 214802, NULL},
+ [65] = {100, {.tv_sec = 2148, .tv_usec = 2}, 214802, NULL},
+ /* .tv_sec ~ INT32_MAX/hz */
+ [66] = {100, {.tv_sec = 21474836, .tv_usec = 439999}, 2147483645,
+ NULL},
+ [67] = {100, {.tv_sec = 21474836, .tv_usec = 440000}, 2147483645,
+ NULL},
+ [68] = {100, {.tv_sec = 21474836, .tv_usec = 440001}, 2147483646,
+ NULL},
+ [69] = {100, {.tv_sec = 21474836, .tv_usec = 440002}, 2147483646,
+ NULL},
+ [70] = {100, {.tv_sec = 21474836, .tv_usec = 449999}, 2147483646,
+ NULL},
+ [71] = {100, {.tv_sec = 21474836, .tv_usec = 450000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [72] = {100, {.tv_sec = 21474836, .tv_usec = 450001}, 2147483647,
+ NULL},
+ [73] = {100, {.tv_sec = 21474836, .tv_usec = 450002}, 2147483647,
+ NULL},
+ [74] = {100, {.tv_sec = 21474836, .tv_usec = 459999}, 2147483647,
+ NULL},
+ [75] = {100, {.tv_sec = 21474836, .tv_usec = 460000}, 2147483647,
+ NULL},
+ [76] = {100, {.tv_sec = 21474836, .tv_usec = 460001}, 2147483647,
+ NULL},
+ [77] = {100, {.tv_sec = 21474836, .tv_usec = 460002}, 2147483647,
+ NULL},
+ [78] = {100, {.tv_sec = 21474836, .tv_usec = 999999}, 2147483647,
+ NULL},
+ [79] = {100, {.tv_sec = 21474837, .tv_usec = 0}, 2147483647,
+ NULL},
+ [80] = {100, {.tv_sec = 21474837, .tv_usec = 1}, 2147483647,
+ NULL},
+ [81] = {100, {.tv_sec = 21474837, .tv_usec = 2}, 2147483647,
+ NULL},
+ [82] = {100, {.tv_sec = 21474837, .tv_usec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000 */
+ [83] = {100, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [84] = {100, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [85] = {100, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [86] = {100, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [87] = {100, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [88] = {100, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [89] = {100, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [90] = {100, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [91] = {100, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 1000
+ */
+
+ [92] = {1000, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [93] = {1000, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [94] = {1000, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [95] = {1000, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [96] = {1000, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [97] = {1000, {.tv_sec = 0, .tv_usec = 999}, 2, NULL},
+ [98] = {1000, {.tv_sec = 0, .tv_usec = 1000}, 2, NULL},
+ [99] = {1000, {.tv_sec = 0, .tv_usec = 1001}, 3, NULL},
+ [100] = {1000, {.tv_sec = 0, .tv_usec = 1002}, 3, NULL},
+ [101] = {1000, {.tv_sec = 0, .tv_usec = 1999}, 3, NULL},
+ [102] = {1000, {.tv_sec = 0, .tv_usec = 2000}, 3, NULL},
+ [103] = {1000, {.tv_sec = 0, .tv_usec = 2001}, 4, NULL},
+ [104] = {1000, {.tv_sec = 0, .tv_usec = 2002}, 4, NULL},
+ [105] = {1000, {.tv_sec = 0, .tv_usec = 999999}, 1001, NULL},
+ [106] = {1000, {.tv_sec = 1, .tv_usec = 0}, 1001, NULL},
+ [107] = {1000, {.tv_sec = 1, .tv_usec = 1}, 1002, NULL},
+ [108] = {1000, {.tv_sec = 1, .tv_usec = 2}, 1002, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [109] = {1000, {.tv_sec = 2147, .tv_usec = 999999}, 2148001, NULL},
+ [110] = {1000, {.tv_sec = 2148, .tv_usec = 0}, 2148001, NULL},
+ [111] = {1000, {.tv_sec = 2148, .tv_usec = 1}, 2148002, NULL},
+ [112] = {1000, {.tv_sec = 2148, .tv_usec = 2}, 2148002, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [113] = {1000, {.tv_sec = 2147483, .tv_usec = 643999}, 2147483645,
+ NULL},
+ [114] = {1000, {.tv_sec = 2147483, .tv_usec = 644000}, 2147483645,
+ NULL},
+ [115] = {1000, {.tv_sec = 2147483, .tv_usec = 644001}, 2147483646,
+ NULL},
+ [116] = {1000, {.tv_sec = 2147483, .tv_usec = 644002}, 2147483646,
+ NULL},
+ [117] = {1000, {.tv_sec = 2147483, .tv_usec = 644999}, 2147483646,
+ NULL},
+ [118] = {1000, {.tv_sec = 2147483, .tv_usec = 645000}, 2147483646,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [119] = {1000, {.tv_sec = 2147483, .tv_usec = 645001}, 2147483647,
+ NULL},
+ [120] = {1000, {.tv_sec = 2147483, .tv_usec = 645002}, 2147483647,
+ NULL},
+ [121] = {1000, {.tv_sec = 2147483, .tv_usec = 645999}, 2147483647,
+ NULL},
+ [122] = {1000, {.tv_sec = 2147483, .tv_usec = 646000}, 2147483647,
+ NULL},
+ [123] = {1000, {.tv_sec = 2147483, .tv_usec = 646001}, 2147483647,
+ NULL},
+ [124] = {1000, {.tv_sec = 2147483, .tv_usec = 646002}, 2147483647,
+ NULL},
+ [125] = {1000, {.tv_sec = 2147483, .tv_usec = 699999}, 2147483647,
+ NULL},
+ [126] = {1000, {.tv_sec = 2147484, .tv_usec = 0}, 2147483647,
+ NULL},
+ [127] = {1000, {.tv_sec = 2147484, .tv_usec = 1}, 2147483647,
+ NULL},
+ [128] = {1000, {.tv_sec = 2147484, .tv_usec = 2}, 2147483647,
+ NULL},
+ [129] = {1000, {.tv_sec = 2147484, .tv_usec = 2}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [130] = {1000, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [131] = {1000, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [132] = {1000, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [133] = {1000, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [134] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [135] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [136] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [137] = {1000, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [138] = {1000, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+
+ /*
+ * hz = 8191, prime non-divisor of 10^k or 2^k
+ */
+
+ [139] = {8191, {.tv_sec = -1, .tv_usec = 0}, 0, NULL},
+ [140] = {8191, {.tv_sec = -1, .tv_usec = 999999}, 0, NULL},
+ [141] = {8191, {.tv_sec = 0, .tv_usec = 0}, 0, NULL},
+ [142] = {8191, {.tv_sec = 0, .tv_usec = 1}, 2, NULL},
+ [143] = {8191, {.tv_sec = 0, .tv_usec = 2}, 2, NULL},
+ [144] = {8191, {.tv_sec = 0, .tv_usec = 121}, 2, NULL},
+ [145] = {8191, {.tv_sec = 0, .tv_usec = 122}, 2, NULL},
+ [146] = {8191, {.tv_sec = 0, .tv_usec = 123}, 3, NULL},
+ [147] = {8191, {.tv_sec = 0, .tv_usec = 242}, 3, NULL},
+ [148] = {8191, {.tv_sec = 0, .tv_usec = 243}, 3, NULL},
+ [149] = {8191, {.tv_sec = 0, .tv_usec = 244}, 3, NULL},
+ [150] = {8191, {.tv_sec = 0, .tv_usec = 245}, 4, NULL},
+ [151] = {8191, {.tv_sec = 0, .tv_usec = 246}, 4, NULL},
+ [152] = {8191, {.tv_sec = 0, .tv_usec = 999999}, 8192, NULL},
+ [153] = {8191, {.tv_sec = 1, .tv_usec = 0}, 8192, NULL},
+ [154] = {8191, {.tv_sec = 1, .tv_usec = 1}, 8193, NULL},
+ [155] = {8191, {.tv_sec = 1, .tv_usec = 2}, 8193, NULL},
+ /* .tv_sec ~ INT_MAX/1000000 */
+ [156] = {8191, {.tv_sec = 2147, .tv_usec = 999999}, 17594269, NULL},
+ [157] = {8191, {.tv_sec = 2148, .tv_usec = 0}, 17594269, NULL},
+ [158] = {8191, {.tv_sec = 2148, .tv_usec = 1}, 17594270, NULL},
+ [159] = {8191, {.tv_sec = 2148, .tv_usec = 2}, 17594270, NULL},
+ /* .tv_sec ~ INT_MAX/hz */
+ [160] = {8191, {.tv_sec = 262176, .tv_usec = 3540}, 2147483646,
+ NULL},
+ [161] = {8191, {.tv_sec = 262176, .tv_usec = 3541}, 2147483647,
+ NULL},
+ [162] = {8191, {.tv_sec = 262176, .tv_usec = 3542}, 2147483647,
+ NULL},
+ /* saturate at INT_MAX = 2^31 - 1 ticks */
+ [163] = {8191, {.tv_sec = 262176, .tv_usec = 3662}, 2147483647,
+ NULL},
+ [164] = {8191, {.tv_sec = 262176, .tv_usec = 3663}, 2147483647,
+ NULL},
+ [165] = {8191, {.tv_sec = 262176, .tv_usec = 3664}, 2147483647,
+ NULL},
+ [166] = {8191, {.tv_sec = 262176, .tv_usec = 999999}, 2147483647,
+ NULL},
+ /* .tv_sec ~ INT64_MAX/1000000, overflows to INT_MAX ticks */
+ [167] = {8191, {.tv_sec = 9223372036854, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [168] = {8191, {.tv_sec = 9223372036855, .tv_usec = 0},
+ INT_MAX, NULL},
+ [169] = {8191, {.tv_sec = 9223372036855, .tv_usec = 1},
+ INT_MAX, NULL},
+ [170] = {8191, {.tv_sec = 9223372036855, .tv_usec = 2},
+ INT_MAX, NULL},
+ /* .tv_sec ~ INT64_MAX/hz, overflows to INT_MAX ticks */
+ [171] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 999999},
+ INT_MAX, NULL},
+ [172] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 0},
+ INT_MAX, NULL},
+ [173] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 1},
+ INT_MAX, NULL},
+ [174] = {8191, {.tv_sec = 92233720368547758, .tv_usec = 2},
+ INT_MAX, NULL},
+ [175] = {8191, {.tv_sec = (time_t)INT_MAX + 1, .tv_usec = 123456},
+ INT_MAX, NULL},
+};
+
+ATF_TC(tvtohz);
+ATF_TC_HEAD(tvtohz, tc)
+{
+ atf_tc_set_md_var(tc, "descr",
+ "Test tvtohz(9)");
+}
+ATF_TC_BODY(tvtohz, tc)
+{
+ size_t i;
+
+ for (i = 0; i < __arraycount(tvtohz_cases); i++) {
+ const struct tvtohz_case *tv = &tvtohz_cases[i];
+ int ticks;
+
+ /* set system parameters */
+ hz = tv->tv_hz;
+ tick = 1000000/hz;
+
+ ticks = tvtohz(&tv->tv_tv);
+ if (tv->tv_xfail)
+ atf_tc_expect_fail("%s", tv->tv_xfail);
+
+ /*
+ * Allow some slop of one part per thousand in the
+ * arithmetic, but ensure we round up, not down.
+ *
+ * XXX Analytically determine error bounds on the
+ * formulae we use and assess them.
+ */
+ ATF_CHECK_MSG(((unsigned)(ticks - tv->tv_ticks) <=
+ (unsigned)tv->tv_ticks/1000),
+ "[%zu] tvtohz(%lld.%06ld sec) @ %d Hz:"
+ " expected %d, got %d",
+ i,
+ (long long)tv->tv_tv.tv_sec,
+ (long)tv->tv_tv.tv_usec,
+ tv->tv_hz,
+ tv->tv_ticks,
+ ticks);
+ if (tv->tv_xfail)
+ atf_tc_expect_pass();
+ }
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+
+ ATF_TP_ADD_TC(tp, itimer_transitions);
+ ATF_TP_ADD_TC(tp, tstohz);
+ ATF_TP_ADD_TC(tp, tvtohz);
+
+ return atf_no_error();
+}
+
diff --git a/kernel/t_unmount.c b/kernel/t_unmount.c
new file mode 100644
index 000000000000..41ed39082e61
--- /dev/null
+++ b/kernel/t_unmount.c
@@ -0,0 +1,106 @@
+/* $NetBSD: t_unmount.c,v 1.4 2024/10/02 17:16:32 bad Exp $ */
+
+/*-
+ * Copyright (c) 2024 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>
+__COPYRIGHT("@(#) Copyright (c) 2024\
+ The NetBSD Foundation, inc. All rights reserved.");
+__RCSID("$NetBSD: t_unmount.c,v 1.4 2024/10/02 17:16:32 bad Exp $");
+
+#include <sys/types.h>
+#include <sys/mount.h>
+#include <sys/vnode.h>
+
+#include <rump/rump.h>
+#include <rump/rumpvnode_if.h>
+#include <rump/rump_syscalls.h>
+
+#include <fs/tmpfs/tmpfs_args.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "h_macros.h"
+
+ATF_TC(async);
+ATF_TC_HEAD(async, tc)
+{
+ atf_tc_set_md_var(tc,
+ "descr", "failed unmount of async fs should stay async");
+}
+
+#define MP "/mnt"
+
+ATF_TC_BODY(async, tc)
+{
+ struct tmpfs_args args;
+ struct vnode *vp;
+ extern struct vnode *rumpns_rootvnode;
+
+ RZ(rump_init());
+
+ memset(&args, 0, sizeof(args));
+ args.ta_version = TMPFS_ARGS_VERSION;
+ args.ta_root_mode = 0777;
+
+ /* create mount point and mount a tmpfs on it */
+ RL(rump_sys_mkdir(MP, 0777));
+ RL(rump_sys_mount(MOUNT_TMPFS, MP, MNT_ASYNC, &args, sizeof(args)));
+
+ /* make sure the tmpfs is busy */
+ RL(rump_sys_chdir(MP));
+
+ /* need a stable lwp for componentname */
+ RZ(rump_pub_lwproc_rfork(RUMP_RFCFDG));
+
+ /* get vnode of MP, unlocked */
+ RZ(rump_pub_namei(RUMP_NAMEI_LOOKUP, 0, MP, NULL, &vp, NULL));
+
+ /* make sure we didn't just get the root vnode */
+ ATF_REQUIRE_MSG((rumpns_rootvnode != vp), "drat! got the root vnode");
+
+ printf("mnt_iflag & IMNT_ONWORKLIST == %d\n",
+ (vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) != 0);
+
+ /* can't unmount a busy file system */
+ ATF_REQUIRE_ERRNO(EBUSY, rump_sys_unmount(MP, 0) == -1);
+
+ printf("mnt_iflag & IMNT_ONWORKLIST == %d\n",
+ (vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) != 0);
+
+
+ ATF_REQUIRE_MSG(((vp->v_mount->mnt_iflag & IMNT_ONWORKLIST) == 0),
+ "mount point on syncer work list");
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, async);
+
+ return atf_no_error();
+}