diff --git a/tools/testing/selftests/kselftest-nolibc.h b/tools/testing/selftests/kselftest-nolibc.h
new file mode 100644
index 000000000000..3dea10cc5490
--- /dev/null
+++ b/tools/testing/selftests/kselftest-nolibc.h
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * kselftest-nolibc	Cut down nolibc based kselftest output functions
+ *
+ * Copyright (c) 2014 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __KSELFTEST_H
+#error This file should never be included directly, always include kselftest.h
+#endif
+
+static inline void ksft_print_msg(const char *msg)
+{
+	printf("# %s", msg);
+}
+
+static inline void ksft_test_result_pass(const char *msg)
+{
+	ksft_cnt.ksft_pass++;
+
+	printf("ok %d %s", ksft_test_num(), msg);
+}
+
+static inline void ksft_test_result_fail(const char *msg)
+{
+	ksft_cnt.ksft_fail++;
+
+	printf("not ok %d %s", ksft_test_num(), msg);
+}
+
+/**
+ * ksft_test_result() - Report test success based on truth of condition
+ *
+ * @condition: if true, report test success, otherwise failure.
+ */
+#define ksft_test_result(condition, fmt) do {	\
+	if (!!(condition))				\
+		ksft_test_result_pass(fmt);\
+	else						\
+		ksft_test_result_fail(fmt);\
+	} while (0)
+
+static inline void ksft_test_result_xfail(const char *msg)
+{
+	ksft_cnt.ksft_xfail++;
+
+	printf("ok %d # XFAIL %s", ksft_test_num(), msg);
+}
+
+static inline void ksft_test_result_skip(const char *msg)
+{
+	ksft_cnt.ksft_xskip++;
+
+	printf("ok %d # SKIP %s", ksft_test_num(), msg);
+}
+
+static inline void ksft_test_result_error(const char *msg)
+{
+	ksft_cnt.ksft_error++;
+
+	printf("not ok %d # error %s", ksft_test_num(), msg);
+}
+
+static inline int ksft_exit_fail_msg(const char *msg)
+{
+	printf("Bail out! %s", msg);
+
+	ksft_print_cnts();
+	exit(KSFT_FAIL);
+}
+
+static inline int ksft_exit_skip(const char *msg)
+{
+	/*
+	 * FIXME: several tests misuse ksft_exit_skip so produce
+	 * something sensible if some tests have already been run
+	 * or a plan has been printed.  Those tests should use
+	 * ksft_test_result_skip or ksft_exit_fail_msg instead.
+	 */
+	if (ksft_plan || ksft_test_num()) {
+		ksft_cnt.ksft_xskip++;
+		printf("ok %d # SKIP ", 1 + ksft_test_num());
+	} else {
+		printf("1..0 # SKIP ");
+	}
+	if (msg)
+		printf("%s", msg);
+	if (ksft_test_num())
+		ksft_print_cnts();
+	exit(KSFT_SKIP);
+}
diff --git a/tools/testing/selftests/kselftest-std.h b/tools/testing/selftests/kselftest-std.h
new file mode 100644
index 000000000000..b57c515f6dda
--- /dev/null
+++ b/tools/testing/selftests/kselftest-std.h
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * kselftest-std.h:	Full stdio based kselftest output functions
+ *
+ * Copyright (c) 2014 Shuah Khan <shuahkh@osg.samsung.com>
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *
+ */
+
+#ifndef __KSELFTEST_H
+#error This file should never be included directly, always include kselftest.h
+#endif
+
+static inline void ksft_print_msg(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	va_start(args, msg);
+	printf("# ");
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+static inline void ksft_test_result_pass(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	ksft_cnt.ksft_pass++;
+
+	va_start(args, msg);
+	printf("ok %d ", ksft_test_num());
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+static inline void ksft_test_result_fail(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	ksft_cnt.ksft_fail++;
+
+	va_start(args, msg);
+	printf("not ok %d ", ksft_test_num());
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+/**
+ * ksft_test_result() - Report test success based on truth of condition
+ *
+ * @condition: if true, report test success, otherwise failure.
+ */
+#define ksft_test_result(condition, fmt, ...) do {	\
+	if (!!(condition))				\
+		ksft_test_result_pass(fmt, ##__VA_ARGS__);\
+	else						\
+		ksft_test_result_fail(fmt, ##__VA_ARGS__);\
+	} while (0)
+
+static inline void ksft_test_result_xfail(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	ksft_cnt.ksft_xfail++;
+
+	va_start(args, msg);
+	printf("ok %d # XFAIL ", ksft_test_num());
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+static inline void ksft_test_result_skip(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	ksft_cnt.ksft_xskip++;
+
+	va_start(args, msg);
+	printf("ok %d # SKIP ", ksft_test_num());
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+/* TODO: how does "error" differ from "fail" or "skip"? */
+static inline void ksft_test_result_error(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	ksft_cnt.ksft_error++;
+
+	va_start(args, msg);
+	printf("not ok %d # error ", ksft_test_num());
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+}
+
+static inline int ksft_exit_fail_msg(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	va_start(args, msg);
+	printf("Bail out! ");
+	errno = saved_errno;
+	vprintf(msg, args);
+	va_end(args);
+
+	ksft_print_cnts();
+	exit(KSFT_FAIL);
+}
+
+static inline int ksft_exit_skip(const char *msg, ...)
+{
+	int saved_errno = errno;
+	va_list args;
+
+	va_start(args, msg);
+
+	/*
+	 * FIXME: several tests misuse ksft_exit_skip so produce
+	 * something sensible if some tests have already been run
+	 * or a plan has been printed.  Those tests should use
+	 * ksft_test_result_skip or ksft_exit_fail_msg instead.
+	 */
+	if (ksft_plan || ksft_test_num()) {
+		ksft_cnt.ksft_xskip++;
+		printf("ok %d # SKIP ", 1 + ksft_test_num());
+	} else {
+		printf("1..0 # SKIP ");
+	}
+	if (msg) {
+		errno = saved_errno;
+		vprintf(msg, args);
+		va_end(args);
+	}
+	if (ksft_test_num())
+		ksft_print_cnts();
+	exit(KSFT_SKIP);
+}
diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h
index 33a0dbd26bd3..10fb6f7c4ae2 100644
--- a/tools/testing/selftests/kselftest.h
+++ b/tools/testing/selftests/kselftest.h
@@ -26,6 +26,10 @@
  *     ksft_test_result_xfail(fmt, ...);
  *     ksft_test_result_error(fmt, ...);
  *
+ * If building with nolibc then only a string may be provided, va_args
+ * arre not supported and format characters within the string will not be
+ * interpreted.
+ *
  * When all tests are finished, clean up and exit the program with one of:
  *
  *    ksft_finished();
@@ -43,11 +47,13 @@
 #ifndef __KSELFTEST_H
 #define __KSELFTEST_H
 
+#ifndef NOLIBC
 #include <errno.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <stdarg.h>
 #include <stdio.h>
+#endif
 
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
@@ -132,100 +138,11 @@ static inline void ksft_print_cnts(void)
 		ksft_cnt.ksft_xskip, ksft_cnt.ksft_error);
 }
 
-static inline void ksft_print_msg(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	va_start(args, msg);
-	printf("# ");
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
-
-static inline void ksft_test_result_pass(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	ksft_cnt.ksft_pass++;
-
-	va_start(args, msg);
-	printf("ok %d ", ksft_test_num());
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
-
-static inline void ksft_test_result_fail(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	ksft_cnt.ksft_fail++;
-
-	va_start(args, msg);
-	printf("not ok %d ", ksft_test_num());
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
-
-/**
- * ksft_test_result() - Report test success based on truth of condition
- *
- * @condition: if true, report test success, otherwise failure.
- */
-#define ksft_test_result(condition, fmt, ...) do {	\
-	if (!!(condition))				\
-		ksft_test_result_pass(fmt, ##__VA_ARGS__);\
-	else						\
-		ksft_test_result_fail(fmt, ##__VA_ARGS__);\
-	} while (0)
-
-static inline void ksft_test_result_xfail(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	ksft_cnt.ksft_xfail++;
-
-	va_start(args, msg);
-	printf("ok %d # XFAIL ", ksft_test_num());
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
-
-static inline void ksft_test_result_skip(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	ksft_cnt.ksft_xskip++;
-
-	va_start(args, msg);
-	printf("ok %d # SKIP ", ksft_test_num());
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
-
-/* TODO: how does "error" differ from "fail" or "skip"? */
-static inline void ksft_test_result_error(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	ksft_cnt.ksft_error++;
-
-	va_start(args, msg);
-	printf("not ok %d # error ", ksft_test_num());
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-}
+#ifdef NOLIBC
+#include "kselftest-nolibc.h"
+#else
+#include "kselftest-std.h"
+#endif
 
 static inline int ksft_exit_pass(void)
 {
@@ -260,21 +177,6 @@ static inline int ksft_exit_fail(void)
 		  ksft_cnt.ksft_xfail +	\
 		  ksft_cnt.ksft_xskip)
 
-static inline int ksft_exit_fail_msg(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	va_start(args, msg);
-	printf("Bail out! ");
-	errno = saved_errno;
-	vprintf(msg, args);
-	va_end(args);
-
-	ksft_print_cnts();
-	exit(KSFT_FAIL);
-}
-
 static inline int ksft_exit_xfail(void)
 {
 	ksft_print_cnts();
@@ -287,33 +189,4 @@ static inline int ksft_exit_xpass(void)
 	exit(KSFT_XPASS);
 }
 
-static inline int ksft_exit_skip(const char *msg, ...)
-{
-	int saved_errno = errno;
-	va_list args;
-
-	va_start(args, msg);
-
-	/*
-	 * FIXME: several tests misuse ksft_exit_skip so produce
-	 * something sensible if some tests have already been run
-	 * or a plan has been printed.  Those tests should use
-	 * ksft_test_result_skip or ksft_exit_fail_msg instead.
-	 */
-	if (ksft_plan || ksft_test_num()) {
-		ksft_cnt.ksft_xskip++;
-		printf("ok %d # SKIP ", 1 + ksft_test_num());
-	} else {
-		printf("1..0 # SKIP ");
-	}
-	if (msg) {
-		errno = saved_errno;
-		vprintf(msg, args);
-		va_end(args);
-	}
-	if (ksft_test_num())
-		ksft_print_cnts();
-	exit(KSFT_SKIP);
-}
-
 #endif /* __KSELFTEST_H */
