Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Documentation/dev-tools/kunit/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ API Reference
test
resource
functionredirection
uapi
clk
of
platformdevice
Expand All @@ -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
================

Expand Down
14 changes: 14 additions & 0 deletions Documentation/dev-tools/kunit/api/uapi.rst
Original file line number Diff line number Diff line change
@@ -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:
2 changes: 2 additions & 0 deletions Documentation/kbuild/makefiles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -891,6 +891,8 @@ This is possible in two ways:
This will tell kbuild to build lxdialog even if not referenced in
any rule.

.. _kbuild_userprogs:

Userspace Program support
=========================

Expand Down
11 changes: 11 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -14321,6 +14321,15 @@ 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-example-uapi.c
F: lib/kunit/kunit-test-uapi.c
F: lib/kunit/kunit-uapi.c
F: lib/kunit/uapi-preinit.c

KVM PARAVIRT (KVM/paravirt)
M: Paolo Bonzini <pbonzini@redhat.com>
R: Vitaly Kuznetsov <vkuznets@redhat.com>
Expand Down Expand Up @@ -18811,6 +18820,8 @@ M: Willy Tarreau <w@1wt.eu>
M: Thomas Weißschuh <linux@weissschuh.net>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/nolibc/linux-nolibc.git
F: init/Kconfig.nolibc
F: init/Makefile.nolibc
F: tools/include/nolibc/
F: tools/testing/selftests/nolibc/

Expand Down
2 changes: 1 addition & 1 deletion fs/coredump.c
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,7 @@ static bool coredump_pipe(struct core_name *cn, struct coredump_params *cprm,
helper_argv[argi] = cn->corename + argv[argi];
helper_argv[argi] = NULL;

sub_info = call_usermodehelper_setup(helper_argv[0], helper_argv, NULL,
sub_info = call_usermodehelper_setup(AT_FDCWD, helper_argv[0], helper_argv, NULL,
GFP_KERNEL, umh_coredump_setup,
NULL, cprm);
if (!sub_info)
Expand Down
4 changes: 2 additions & 2 deletions fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1846,7 +1846,7 @@ static int do_execveat_common(int fd, struct filename *filename,
return bprm_execve(bprm);
}

int kernel_execve(const char *kernel_filename,
int kernel_execve(int dirfd, const char *kernel_filename,
const char *const *argv, const char *const *envp)
{
int retval;
Expand All @@ -1856,7 +1856,7 @@ int kernel_execve(const char *kernel_filename,
return -EINVAL;

CLASS(filename_kernel, filename)(kernel_filename);
CLASS(bprm, bprm)(AT_FDCWD, filename, 0);
CLASS(bprm, bprm)(dirfd, filename, 0);
if (IS_ERR(bprm))
return PTR_ERR(bprm);

Expand Down
77 changes: 77 additions & 0 deletions include/kunit/uapi.h
Original file line number Diff line number Diff line change
@@ -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 */
2 changes: 1 addition & 1 deletion include/linux/binfmts.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm);
extern void set_binfmt(struct linux_binfmt *new);
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);

int kernel_execve(const char *filename,
int kernel_execve(int dirfd, const char *filename,
const char *const *argv, const char *const *envp);

#endif /* _LINUX_BINFMTS_H */
2 changes: 2 additions & 0 deletions include/linux/mount.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#ifndef _LINUX_MOUNT_H
#define _LINUX_MOUNT_H

#include <linux/cleanup.h>
#include <linux/types.h>
#include <asm/barrier.h>

Expand Down Expand Up @@ -99,6 +100,7 @@ extern bool our_mnt(struct vfsmount *mnt);

extern struct vfsmount *kern_mount(struct file_system_type *);
extern void kern_unmount(struct vfsmount *mnt);
DEFINE_FREE(kern_unmount, struct vfsmount *, if (_T) kern_unmount(_T));
extern int may_umount_tree(struct vfsmount *);
extern int may_umount(struct vfsmount *);
int do_mount(const char *, const char __user *,
Expand Down
3 changes: 2 additions & 1 deletion include/linux/umh.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ struct file;
struct subprocess_info {
struct work_struct work;
struct completion *complete;
int dirfd;
const char *path;
char **argv;
char **envp;
Expand All @@ -34,7 +35,7 @@ extern int
call_usermodehelper(const char *path, char **argv, char **envp, int wait);

extern struct subprocess_info *
call_usermodehelper_setup(const char *path, char **argv, char **envp,
call_usermodehelper_setup(int dirfd, const char *path, char **argv, char **envp,
gfp_t gfp_mask,
int (*init)(struct subprocess_info *info, struct cred *new),
void (*cleanup)(struct subprocess_info *), void *data);
Expand Down
2 changes: 2 additions & 0 deletions init/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ config CC_CAN_LINK
default $(cc_can_link_user,$(m64-flag)) if 64BIT
default $(cc_can_link_user,$(m32-flag))

source "init/Kconfig.nolibc"

# Fixed in GCC 14, 13.3, 12.4 and 11.5
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113921
config GCC_ASM_GOTO_OUTPUT_BROKEN
Expand Down
16 changes: 16 additions & 0 deletions init/Kconfig.nolibc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: GPL-2.0

config ARCH_HAS_NOLIBC
bool
default y if ARM
default y if ARM64
default y if LOONGARCH
default y if M68K
default y if MIPS
default y if PPC
default y if RISCV
default y if S390
default y if SPARC
default y if SUPERH
default y if UML_X86
default y if X86
13 changes: 13 additions & 0 deletions init/Makefile.nolibc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-License-Identifier: GPL-2.0
# Compiler flags, which are necessary to build userspace applications with the
# in-kernel libc "nolibc".

ifeq ($(and $(CONFIG_ARCH_HAS_NOLIBC),$(CONFIG_HEADERS_INSTALL)),y)

NOLIBC_USERCFLAGS := -nostdlib -nostdinc -static -ffreestanding \
-fno-asynchronous-unwind-tables -fno-stack-protector \
-I$(objtree)/usr/include -I$(srctree)/tools/include/nolibc/

NOLIBC_USERLDFLAGS := -nostdlib -nostdinc -static

endif # CONFIG_ARCH_HAS_NOLIBC && CONFIG_HEADERS_INSTALL
2 changes: 1 addition & 1 deletion init/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1500,7 +1500,7 @@ static int run_init_process(const char *init_filename)
pr_debug(" with environment:\n");
for (p = envp_init; *p; p++)
pr_debug(" %s\n", *p);
return kernel_execve(init_filename, argv_init, envp_init);
return kernel_execve(AT_FDCWD, init_filename, argv_init, envp_init);
}

static int try_to_run_init_process(const char *init_filename)
Expand Down
2 changes: 1 addition & 1 deletion kernel/module/kmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ static int call_modprobe(char *orig_module_name, int wait)
argv[3] = module_name; /* check free_modprobe_argv() */
argv[4] = NULL;

info = call_usermodehelper_setup(modprobe_path, argv, envp, GFP_KERNEL,
info = call_usermodehelper_setup(AT_FDCWD, modprobe_path, argv, envp, GFP_KERNEL,
NULL, free_modprobe_argv, NULL);
if (!info)
goto free_module_name;
Expand Down
9 changes: 6 additions & 3 deletions kernel/umh.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ static int call_usermodehelper_exec_async(void *data)
commit_creds(new);

wait_for_initramfs();
retval = kernel_execve(sub_info->path,
retval = kernel_execve(sub_info->dirfd, sub_info->path,
(const char *const *)sub_info->argv,
(const char *const *)sub_info->envp);
out:
Expand Down Expand Up @@ -331,6 +331,7 @@ static void helper_unlock(void)

/**
* call_usermodehelper_setup - prepare to call a usermode helper
* @dirfd: directory to resolve path against
* @path: path to usermode executable
* @argv: arg vector for process
* @envp: environment for process
Expand All @@ -352,7 +353,7 @@ static void helper_unlock(void)
* Function must be runnable in either a process context or the
* context in which call_usermodehelper_exec is called.
*/
struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
struct subprocess_info *call_usermodehelper_setup(int dirfd, const char *path, char **argv,
char **envp, gfp_t gfp_mask,
int (*init)(struct subprocess_info *info, struct cred *new),
void (*cleanup)(struct subprocess_info *info),
Expand All @@ -366,8 +367,10 @@ struct subprocess_info *call_usermodehelper_setup(const char *path, char **argv,
INIT_WORK(&sub_info->work, call_usermodehelper_exec_work);

#ifdef CONFIG_STATIC_USERMODEHELPER
sub_info->dirfd = AT_FDCWD;
sub_info->path = CONFIG_STATIC_USERMODEHELPER_PATH;
#else
sub_info->dirfd = dirfd;
sub_info->path = path;
#endif
sub_info->argv = argv;
Expand Down Expand Up @@ -484,7 +487,7 @@ int call_usermodehelper(const char *path, char **argv, char **envp, int wait)
struct subprocess_info *info;
gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;

info = call_usermodehelper_setup(path, argv, envp, gfp_mask,
info = call_usermodehelper_setup(AT_FDCWD, path, argv, envp, gfp_mask,
NULL, NULL, NULL);
if (info == NULL)
return -ENOMEM;
Expand Down
2 changes: 1 addition & 1 deletion lib/kobject_uevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
goto exit;

retval = -ENOMEM;
info = call_usermodehelper_setup(env->argv[0], env->argv,
info = call_usermodehelper_setup(AT_FDCWD, env->argv[0], env->argv,
env->envp, GFP_KERNEL,
NULL, cleanup_uevent_env, env);
if (info) {
Expand Down
16 changes: 16 additions & 0 deletions lib/kunit/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
26 changes: 26 additions & 0 deletions lib/kunit/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include $(srctree)/init/Makefile.nolibc

obj-$(CONFIG_KUNIT) += kunit.o

kunit-objs += test.o \
Expand All @@ -12,6 +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
Expand All @@ -20,6 +29,15 @@ endif
obj-$(if $(CONFIG_KUNIT),y) += hooks.o

obj-$(CONFIG_KUNIT_TEST) += kunit-test.o

userprogs += kunit-test-uapi
kunit-test-uapi-userccflags := -static $(NOLIBC_USERCFLAGS)

ifdef CONFIG_KUNIT_UAPI
CFLAGS_kunit-test.o := -Wa,-I$(obj)
$(obj)/kunit-test.o: $(obj)/kunit-test-uapi
endif

obj-$(CONFIG_KUNIT_TEST) += platform-test.o

# string-stream-test compiles built-in only.
Expand All @@ -29,3 +47,11 @@ obj-$(CONFIG_KUNIT_TEST) += assert_test.o
endif

obj-$(CONFIG_KUNIT_EXAMPLE_TEST) += kunit-example-test.o

userprogs += kunit-example-uapi
kunit-example-uapi-userccflags := -static $(NOLIBC_USERCFLAGS)

ifdef CONFIG_KUNIT_UAPI
CFLAGS_kunit-example-test.o := -Wa,-I$(obj)
$(obj)/kunit-example-test.o: $(obj)/kunit-example-uapi
endif
Loading
Loading