UAPI selftests may expect a "normal" userspace environment.
For example the normal kernel API pseudo-filesystems should be mounted.
This could be done from kernel code but it is non-idiomatic.

Add a preinit userspace executable which performs these setup steps
before running the final test executable.
This preinit executable is only ever run from the kernel.
Give it access to autoconf.h and kconfig.h to adapt itself to the
tested kernel.

Signed-off-by: Thomas Weißschuh <[email protected]>
Reviewed-by: David Gow <[email protected]>
---
 MAINTAINERS              |  1 +
 lib/kunit/Makefile       |  5 ++++
 lib/kunit/kunit-uapi.c   | 11 +++++---
 lib/kunit/uapi-preinit.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 82 insertions(+), 3 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index b7358d89df70..4e8cf9fa7aa9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14401,6 +14401,7 @@ S:      Maintained
 F:     include/kunit/uapi.h
 F:     lib/kunit/kunit-example-uapi.c
 F:     lib/kunit/kunit-uapi.c
+F:     lib/kunit/uapi-preinit.c
 
 KVM PARAVIRT (KVM/paravirt)
 M:     Paolo Bonzini <[email protected]>
diff --git a/lib/kunit/Makefile b/lib/kunit/Makefile
index 2434470e9985..05991d69aa70 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -14,8 +14,13 @@ kunit-objs +=                                test.o \
                                        device.o \
                                        platform.o
 
+userprogs +=                           uapi-preinit
+uapi-preinit-userccflags +=            -static $(NOLIBC_USERCFLAGS)
 obj-$(CONFIG_KUNIT_UAPI) +=            kunit-uapi.o
 
+CFLAGS_kunit-uapi.o :=                 -Wa,-I$(obj)
+$(obj)/kunit-uapi.o: $(obj)/uapi-preinit
+
 ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
 kunit-objs +=                          debugfs.o
 endif
diff --git a/lib/kunit/kunit-uapi.c b/lib/kunit/kunit-uapi.c
index 7f0309a827a5..702d26878ccd 100644
--- a/lib/kunit/kunit-uapi.c
+++ b/lib/kunit/kunit-uapi.c
@@ -33,6 +33,8 @@ enum {
        KSFT_SKIP       = 4,
 };
 
+KUNIT_UAPI_EMBED_BLOB(kunit_uapi_preinit, "uapi-preinit");
+
 static struct vfsmount *kunit_uapi_mount_fs(const char *name)
 {
        struct file_system_type *type;
@@ -158,18 +160,17 @@ 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,
+               kunit_uapi_executable_target(executable),
                NULL
        };
 
-       info = call_usermodehelper_setup(AT_FDCWD, executable_target, (char 
**)argv, NULL,
+       info = call_usermodehelper_setup(AT_FDCWD, kunit_uapi_preinit.path, 
(char **)argv, NULL,
                                         GFP_KERNEL, 
kunit_uapi_usermodehelper_init, NULL, &ctx);
        if (!info)
                return -ENOMEM;
@@ -192,6 +193,10 @@ static int kunit_uapi_run_executable(struct kunit *test, 
const struct kunit_uapi
        if (err)
                return err;
 
+       err = kunit_uapi_write_executable(mnt, &kunit_uapi_preinit);
+       if (err)
+               return err;
+
        err = kunit_uapi_run_executable_in_mount(test, executable, mnt);
        if (err)
                return err;
diff --git a/lib/kunit/uapi-preinit.c b/lib/kunit/uapi-preinit.c
new file mode 100644
index 000000000000..686737ea3c76
--- /dev/null
+++ b/lib/kunit/uapi-preinit.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit Userspace environment setup.
+ *
+ * Copyright (C) 2026, Linutronix GmbH.
+ * Author: Thomas Weißschuh <[email protected]>
+ *
+ * This is *userspace* code.
+ */
+
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include "../../tools/testing/selftests/kselftest.h"
+
+#define KUNIT_UAPI_CHDIR_FD 3
+
+static int setup_api_mount(const char *target, const char *fstype)
+{
+       int ret;
+
+       ret = mkdir(target, 0755);
+       if (ret && errno != EEXIST)
+               return -errno;
+
+       ret = mount("none", target, fstype, 0, NULL);
+       if (ret && errno != EBUSY)
+               return -errno;
+
+       return 0;
+}
+
+static void exit_failure(const char *stage, int err)
+{
+       /* If preinit fails synthesize a failed test report. */
+       ksft_print_header();
+       ksft_set_plan(1);
+       ksft_test_result_fail("Failed during test setup: %s: %s\n", stage, 
strerror(-err));
+       ksft_finished();
+}
+
+int main(int argc, char **argv, char **envp)
+{
+       int ret;
+
+       ret = fchdir(KUNIT_UAPI_CHDIR_FD);
+       close(KUNIT_UAPI_CHDIR_FD);
+       if (ret)
+               exit_failure("fchdir", ret);
+
+       ret = setup_api_mount("/proc", "proc");
+       if (ret)
+               exit_failure("mount /proc", ret);
+
+       ret = setup_api_mount("/sys", "sysfs");
+       if (ret)
+               exit_failure("mount /sys", ret);
+
+       ret = setup_api_mount("/dev", "devtmpfs");
+       if (ret)
+               exit_failure("mount /dev", ret);
+
+       ret = execve(argv[0], argv, envp);
+       if (ret)
+               exit_failure("execve", ret);
+
+       return 0;
+}

-- 
2.53.0


Reply via email to