Linux Documentation
 help / color / mirror / Atom feed
From: "Thomas Weißschuh" <thomas.weissschuh@linutronix.de>
To: "Nathan Chancellor" <nathan@kernel.org>,
	"Andrew Morton" <akpm@linux-foundation.org>,
	"Willy Tarreau" <w@1wt.eu>,
	"Thomas Weißschuh" <linux@weissschuh.net>,
	"Brendan Higgins" <brendan.higgins@linux.dev>,
	"Shuah Khan" <shuah@kernel.org>,
	"Jonathan Corbet" <corbet@lwn.net>,
	"Nicolas Schier" <nicolas.schier@linux.dev>,
	"Kees Cook" <kees@kernel.org>,
	"Alexander Viro" <viro@zeniv.linux.org.uk>,
	"Christian Brauner" <brauner@kernel.org>,
	"Jan Kara" <jack@suse.cz>, "Christoph Hellwig" <hch@lst.de>,
	"Luis Chamberlain" <mcgrof@kernel.org>,
	"David Gow" <david@davidgow.net>,
	"Rae Moar" <raemoar63@gmail.com>
Cc: linux-kbuild@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-kselftest@vger.kernel.org, kunit-dev@googlegroups.com,
	linux-doc@vger.kernel.org, workflows@vger.kernel.org,
	linux-mm@kvack.org, linux-fsdevel@vger.kernel.org,
	"Thomas Weißschuh" <thomas.weissschuh@linutronix.de>,
	"Christophe Leroy" <chleroy@kernel.org>,
	"Nicolas Schier" <nsc@kernel.org>
Subject: [PATCH v6 07/11] kunit: Introduce UAPI testing framework
Date: Mon, 04 May 2026 11:33:23 +0200	[thread overview]
Message-ID: <20260504-kunit-kselftests-v6-7-712d3d526d97@linutronix.de> (raw)
In-Reply-To: <20260504-kunit-kselftests-v6-0-712d3d526d97@linutronix.de>

Enable running UAPI tests as part of kunit.
The selftests are embedded into the kernel image and their output is
forwarded to kunit for unified reporting.

The implementation reuses parts of usermode drivers and usermode
helpers. However these frameworks are not used directly as they make it
impossible to retrieve a thread's exit code.

Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
---
 Documentation/dev-tools/kunit/api/index.rst |   5 +
 Documentation/dev-tools/kunit/api/uapi.rst  |  14 ++
 MAINTAINERS                                 |   6 +
 include/kunit/uapi.h                        |  77 +++++++++++
 lib/kunit/Kconfig                           |  16 +++
 lib/kunit/Makefile                          |   2 +
 lib/kunit/kunit-uapi.c                      | 193 ++++++++++++++++++++++++++++
 7 files changed, 313 insertions(+)

diff --git a/Documentation/dev-tools/kunit/api/index.rst b/Documentation/dev-tools/kunit/api/index.rst
index 5cdb552a0808..34d8fee9a970 100644
--- a/Documentation/dev-tools/kunit/api/index.rst
+++ b/Documentation/dev-tools/kunit/api/index.rst
@@ -9,6 +9,7 @@ API Reference
 	test
 	resource
 	functionredirection
+	uapi
 	clk
 	of
 	platformdevice
@@ -32,6 +33,10 @@ Documentation/dev-tools/kunit/api/functionredirection.rst
 
  - Documents the KUnit Function Redirection API
 
+Documentation/dev-tools/kunit/api/uapi.rst
+
+ - Documents the KUnit Userspace testing API
+
 Driver KUnit API
 ================
 
diff --git a/Documentation/dev-tools/kunit/api/uapi.rst b/Documentation/dev-tools/kunit/api/uapi.rst
new file mode 100644
index 000000000000..1f01b5c6c9db
--- /dev/null
+++ b/Documentation/dev-tools/kunit/api/uapi.rst
@@ -0,0 +1,14 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================
+Userspace Test API
+==================
+
+This file documents all of the userspace testing API.
+Userspace tests are built as :ref:`kbuild userprogs <kbuild_userprogs>`,
+linked statically and without any external dependencies.
+
+For the widest platform compatibility they should use nolibc, as provided by `init/Makefile.nolibc`.
+
+.. kernel-doc:: include/kunit/uapi.h
+   :internal:
diff --git a/MAINTAINERS b/MAINTAINERS
index be4f6242b3fc..e29ca56f7334 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14395,6 +14395,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/leds/backlight/kinetic,ktz8866.yaml
 F:	drivers/video/backlight/ktz8866.c
 
+KUNIT UAPI TESTING FRAMEWORK (in addition to KERNEL UNIT TESTING FRAMEWORK)
+M:	Thomas Weißschuh <thomas.weissschuh@linutronix.de>
+S:	Maintained
+F:	include/kunit/uapi.h
+F:	lib/kunit/kunit-uapi.c
+
 KVM PARAVIRT (KVM/paravirt)
 M:	Paolo Bonzini <pbonzini@redhat.com>
 R:	Vitaly Kuznetsov <vkuznets@redhat.com>
diff --git a/include/kunit/uapi.h b/include/kunit/uapi.h
new file mode 100644
index 000000000000..1e0585355124
--- /dev/null
+++ b/include/kunit/uapi.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * KUnit Userspace testing API.
+ *
+ * Copyright (C) 2026, Linutronix GmbH.
+ * Author: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
+ */
+
+#ifndef _KUNIT_UAPI_H
+#define _KUNIT_UAPI_H
+
+#include <linux/types.h>
+
+struct kunit;
+
+/**
+ * struct kunit_uapi_blob - Blob embedded build artifact
+ * @path: Path of the embedded artifact.
+ * @data: Start of the embedded data in memory.
+ * @end: End of the embedded data in memory.
+ */
+struct kunit_uapi_blob {
+	const char *const path;
+	const u8 *data;
+	const u8 *end;
+};
+
+#if IS_ENABLED(CONFIG_KUNIT_UAPI)
+
+/**
+ * KUNIT_UAPI_EMBED_BLOB() - Embed another build artifact into the kernel
+ * @_name: The name of symbol under which the artifact is embedded.
+ * @_path: Path to the artifact on disk.
+ *
+ * Embeds a build artifact like a userspace executable into the kernel or current module.
+ * The build artifact is read from disk and needs to be already built.
+ */
+#define KUNIT_UAPI_EMBED_BLOB(_name, _path)					\
+	asm (									\
+	"	.pushsection .rodata, \"a\"				\n"	\
+	"	.global " __stringify(CONCATENATE(_name, _data)) "	\n"	\
+	__stringify(CONCATENATE(_name, _data)) ":			\n"	\
+	"	.incbin " __stringify(_path) "				\n"	\
+	"	.size " __stringify(CONCATENATE(_name, _data)) ", "		\
+			". - " __stringify(CONCATENATE(_name, _data)) "	\n"	\
+	"	.global " __stringify(CONCATENATE(_name, _end)) "	\n"	\
+	__stringify(CONCATENATE(_name, _end)) ":			\n"	\
+	"	.popsection						\n"	\
+	);									\
+										\
+	extern const char CONCATENATE(_name, _data)[];				\
+	extern const char CONCATENATE(_name, _end)[];				\
+										\
+	static const struct kunit_uapi_blob _name = {				\
+		.path	= _path,						\
+		.data	= CONCATENATE(_name, _data),				\
+		.end	= CONCATENATE(_name, _end),				\
+	}									\
+
+#else /* !CONFIG_KUNIT_UAPI */
+
+/* Unresolved external reference, to be optimized away */
+#define KUNIT_UAPI_EMBED_BLOB(_name, _path)					\
+	extern const struct kunit_uapi_blob _name
+
+#endif /* CONFIG_KUNIT_UAPI */
+
+/**
+ * kunit_uapi_run_kselftest() - Run a userspace kselftest as part of kunit
+ * @test: The test context object.
+ * @executable: kselftest executable to run
+ *
+ * Runs the kselftest and forwards its TAP output and exit status to kunit.
+ */
+void kunit_uapi_run_kselftest(struct kunit *test, const struct kunit_uapi_blob *executable);
+
+#endif /* _KUNIT_UAPI_H */
diff --git a/lib/kunit/Kconfig b/lib/kunit/Kconfig
index 498cc51e493d..f3dc9fac811a 100644
--- a/lib/kunit/Kconfig
+++ b/lib/kunit/Kconfig
@@ -141,4 +141,20 @@ config KUNIT_UML_PCI
 
 	  If unsure, say N.
 
+config KUNIT_UAPI
+	tristate "KUnit UAPI testing framework"
+	depends on KUNIT
+	depends on ARCH_HAS_NOLIBC
+	depends on !STATIC_USERMODEHELPER
+	depends on !LTO_CLANG # https://github.com/llvm/llvm-project/issues/112920
+	select HEADERS_INSTALL
+	select DEVTMPFS
+	default KUNIT
+	help
+	  Enables support for building and running userspace selftests as part of kunit.
+	  These tests should be statically linked and use kselftest.h or kselftest_harness.h
+	  for status reporting.
+
+	  In most cases this should be left as its default.
+
 endif # KUNIT
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 656f1fa35abc..6059621a2d32 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -12,6 +12,8 @@ kunit-objs +=				test.o \
 					device.o \
 					platform.o
 
+obj-$(CONFIG_KUNIT_UAPI) +=		kunit-uapi.o
+
 ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
 kunit-objs +=				debugfs.o
 endif
diff --git a/lib/kunit/kunit-uapi.c b/lib/kunit/kunit-uapi.c
new file mode 100644
index 000000000000..485b79fd193d
--- /dev/null
+++ b/lib/kunit/kunit-uapi.c
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit Userspace testing API.
+ *
+ * Copyright (C) 2026, Linutronix GmbH.
+ * Author: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
+ */
+
+#include <linux/binfmts.h>
+#include <linux/export.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/pid.h>
+#include <linux/pipe_fs_i.h>
+#include <linux/sched/task.h>
+#include <linux/seq_buf.h>
+#include <linux/types.h>
+#include <linux/umh.h>
+
+#include <kunit/test-bug.h>
+#include <kunit/test.h>
+#include <kunit/uapi.h>
+
+enum {
+	KSFT_PASS	= 0,
+	KSFT_FAIL	= 1,
+	KSFT_XFAIL	= 2,
+	KSFT_XPASS	= 3,
+	KSFT_SKIP	= 4,
+};
+
+static struct vfsmount *kunit_uapi_mount_fs(const char *name)
+{
+	struct file_system_type *type;
+
+	type = get_fs_type(name);
+	if (!type)
+		return ERR_PTR(-ENODEV);
+
+	return kern_mount(type);
+}
+
+static int kunit_uapi_write_file(struct vfsmount *mnt, const char *name, mode_t mode,
+				 const u8 *data, size_t size)
+{
+	struct file *file;
+	ssize_t written;
+
+	file = file_open_root_mnt(mnt, name, O_CREAT | O_WRONLY, mode);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
+	written = kernel_write(file, data, size, NULL);
+	filp_close(file, NULL);
+	if (written != size) {
+		if (written >= 0)
+			return -ENOMEM;
+		return written;
+	}
+
+	return 0;
+}
+
+static const char *kunit_uapi_executable_target(const struct kunit_uapi_blob *executable)
+{
+	return kbasename(executable->path);
+}
+
+static int kunit_uapi_write_executable(struct vfsmount *mnt,
+				       const struct kunit_uapi_blob *executable)
+{
+	return kunit_uapi_write_file(mnt, kunit_uapi_executable_target(executable), 0755,
+				     executable->data, executable->end - executable->data);
+}
+
+struct kunit_uapi_usermodehelper_ctx {
+	struct vfsmount *mnt;
+	struct kunit *test;
+};
+
+static int kunit_uapi_get_cwd(struct vfsmount *mnt)
+{
+	CLASS(get_unused_fd, fd)(O_RDONLY);
+	if (fd < 0)
+		return fd;
+
+	struct file *file __free(fput) = file_open_root_mnt(mnt, "/", O_DIRECTORY, 0);
+	if (IS_ERR(file))
+		return PTR_ERR(file);
+
+	fd_install(fd, no_free_ptr(file));
+
+	return take_fd(fd);
+}
+
+static int kunit_uapi_usermodehelper_init(struct subprocess_info *info, struct cred *new)
+{
+	struct kunit_uapi_usermodehelper_ctx *ctx = info->data;
+	int dirfd;
+
+	dirfd = kunit_uapi_get_cwd(ctx->mnt);
+	if (dirfd < 0)
+		return dirfd;
+
+	kernel_sigaction(SIGKILL, SIG_DFL);
+	kernel_sigaction(SIGABRT, SIG_DFL);
+
+	current->kunit_test = ctx->test;
+
+	info->dirfd = dirfd;
+
+	return 0;
+}
+
+static int kunit_uapi_run_executable_in_mount(struct kunit *test,
+					      const struct kunit_uapi_blob *executable,
+					      struct vfsmount *mnt)
+{
+	const char *executable_target = kunit_uapi_executable_target(executable);
+	struct kunit_uapi_usermodehelper_ctx ctx = {
+		.test	= test,
+		.mnt	= mnt,
+	};
+	struct subprocess_info *info;
+	const char *const argv[] = {
+		executable_target,
+		NULL
+	};
+
+	info = call_usermodehelper_setup(AT_FDCWD, executable_target, (char **)argv, NULL,
+					 GFP_KERNEL, kunit_uapi_usermodehelper_init, NULL, &ctx);
+	if (!info)
+		return -ENOMEM;
+
+	/* Flush delayed fput so exec can open the file read-only */
+	flush_delayed_fput();
+
+	return call_usermodehelper_exec(info, UMH_WAIT_PROC);
+}
+
+static int kunit_uapi_run_executable(struct kunit *test, const struct kunit_uapi_blob *executable)
+{
+	int err;
+
+	struct vfsmount *mnt __free(kern_unmount) = kunit_uapi_mount_fs("ramfs");
+	if (IS_ERR(mnt))
+		return PTR_ERR(mnt);
+
+	err = kunit_uapi_write_executable(mnt, executable);
+	if (err)
+		return err;
+
+	err = kunit_uapi_run_executable_in_mount(test, executable, mnt);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+void kunit_uapi_run_kselftest(struct kunit *test, const struct kunit_uapi_blob *executable)
+{
+	u8 exit_code, exit_signal;
+	int err;
+
+	err = kunit_uapi_run_executable(test, executable);
+	if (err < 0)
+		KUNIT_FAIL_AND_ABORT(test, "Could not run test executable: %pe\n", ERR_PTR(err));
+
+	exit_code = err >> 8;
+	exit_signal = err & 0xff;
+
+	if (exit_signal)
+		KUNIT_FAIL_AND_ABORT(test, "kselftest exited with signal: %d\n", exit_signal);
+	else if (exit_code == KSFT_PASS)
+		; /* Noop */
+	else if (exit_code == KSFT_FAIL)
+		KUNIT_FAIL_AND_ABORT(test, "kselftest exited with code KSFT_FAIL\n");
+	else if (exit_code == KSFT_XPASS)
+		KUNIT_FAIL_AND_ABORT(test, "kselftest exited with code KSFT_XPASS\n");
+	else if (exit_code == KSFT_XFAIL)
+		; /* Noop */
+	else if (exit_code == KSFT_SKIP)
+		kunit_mark_skipped(test, "kselftest exited with code KSFT_SKIP\n");
+	else
+		KUNIT_FAIL_AND_ABORT(test, "kselftest exited with unknown exit code: %d\n",
+				     exit_code);
+}
+EXPORT_SYMBOL_GPL(kunit_uapi_run_kselftest);
+
+MODULE_DESCRIPTION("KUnit UAPI testing framework");
+MODULE_AUTHOR("Thomas Weißschuh <thomas.weissschuh@linutronix.de");
+MODULE_LICENSE("GPL");

-- 
2.53.0


  parent reply	other threads:[~2026-05-04  9:33 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-04  9:33 [PATCH v6 00/11] kunit: Introduce UAPI testing framework Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 01/11] kbuild: doc: add label for userprogs section Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 02/11] exec: add dirfd parameter to kernel_execve() Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 03/11] umh: add dirfd parameter Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 04/11] mount: add support for __free(kern_unmount) Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 05/11] init: add nolibc build support Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 06/11] kunit: qemu_configs: loongarch: Enable LSX/LSAX Thomas Weißschuh
2026-05-04  9:33 ` Thomas Weißschuh [this message]
2026-05-04  9:33 ` [PATCH v6 08/11] kunit: uapi: Forward test executable output to KUnit log Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 09/11] kunit: uapi: Add example for UAPI tests Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 10/11] kunit: uapi: Introduce preinit executable Thomas Weißschuh
2026-05-04  9:33 ` [PATCH v6 11/11] kunit: uapi: Validate usability of /proc Thomas Weißschuh

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260504-kunit-kselftests-v6-7-712d3d526d97@linutronix.de \
    --to=thomas.weissschuh@linutronix.de \
    --cc=akpm@linux-foundation.org \
    --cc=brauner@kernel.org \
    --cc=brendan.higgins@linux.dev \
    --cc=chleroy@kernel.org \
    --cc=corbet@lwn.net \
    --cc=david@davidgow.net \
    --cc=hch@lst.de \
    --cc=jack@suse.cz \
    --cc=kees@kernel.org \
    --cc=kunit-dev@googlegroups.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kbuild@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-kselftest@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux@weissschuh.net \
    --cc=mcgrof@kernel.org \
    --cc=nathan@kernel.org \
    --cc=nicolas.schier@linux.dev \
    --cc=nsc@kernel.org \
    --cc=raemoar63@gmail.com \
    --cc=shuah@kernel.org \
    --cc=viro@zeniv.linux.org.uk \
    --cc=w@1wt.eu \
    --cc=workflows@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox