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       |  6 +++++
 lib/kunit/kunit-uapi.c   |  9 +++++--
 lib/kunit/uapi-preinit.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 77 insertions(+), 2 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 
b1405f0a0e638d1654d9dc9e51d784ddc838cf5b..e81dfa180ab374ef91c7a45e546e6e9a8f454fa7
 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13546,6 +13546,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 
1bba7965613e36e26939d6b31e1d65acf5bad0dc..b50f3bc8bc7f3ade03be4900d9163d7a0d96863c
 100644
--- a/lib/kunit/Makefile
+++ b/lib/kunit/Makefile
@@ -14,8 +14,14 @@ kunit-objs +=                                test.o \
                                        device.o \
                                        platform.o
 
+userprogs +=                           uapi-preinit
+uapi-preinit-userccflags +=            -static $(NOLIBC_USERCFLAGS) \
+                                       -include include/generated/autoconf.h \
+                                       -include 
$(srctree)/tools/include/linux/kconfig.h
 obj-$(CONFIG_KUNIT_UAPI) +=            kunit-uapi.o
 
+$(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 
cfe8440e16fde942a5f0fa7ac9d8ab90a737215b..7c87605b9ded9dbeb3968af8a8f4650ab5938887
 100644
--- a/lib/kunit/kunit-uapi.c
+++ b/lib/kunit/kunit-uapi.c
@@ -25,6 +25,8 @@
 #define KSFT_XPASS     3
 #define KSFT_SKIP      4
 
+KUNIT_UAPI_EMBED_BLOB(kunit_uapi_preinit, "uapi-preinit");
+
 static struct vfsmount *kunit_uapi_mount_ramfs(void)
 {
        struct file_system_type *type;
@@ -146,7 +148,7 @@ static int kunit_uapi_user_mode_thread_init(void *data)
        kernel_sigaction(SIGABRT, SIG_DFL);
 
        complete(&ctx->setup_done);
-       ctx->exec_err = kernel_execve(ctx->executable, argv, NULL);
+       ctx->exec_err = kernel_execve(kbasename(kunit_uapi_preinit.path), argv, 
NULL);
        if (!ctx->exec_err)
                return 0;
        do_exit(0);
@@ -255,7 +257,10 @@ static int kunit_uapi_run_executable(struct kunit *test,
        if (IS_ERR(mnt))
                return PTR_ERR(mnt);
 
-       err = kunit_uapi_write_executable(mnt, executable);
+       err = kunit_uapi_write_executable(mnt, &kunit_uapi_preinit);
+
+       if (!err)
+               err = kunit_uapi_write_executable(mnt, executable);
 
        if (!err)
                err = kunit_uapi_run_executable_in_mount(test, exe_name, mnt);
diff --git a/lib/kunit/uapi-preinit.c b/lib/kunit/uapi-preinit.c
new file mode 100644
index 
0000000000000000000000000000000000000000..81182039965a8c93aebb2d5d76f4113bfef277a6
--- /dev/null
+++ b/lib/kunit/uapi-preinit.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * KUnit Userspace environment setup.
+ *
+ * Copyright (C) 2025, 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"
+
+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 = 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);
+
+       if (IS_ENABLED(CONFIG_DEVTMPFS)) {
+               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.50.0


Reply via email to