summaryrefslogtreecommitdiffstats
path: root/test/self
diff options
context:
space:
mode:
Diffstat (limited to 'test/self')
-rw-r--r--test/self/Kconfig66
-rw-r--r--test/self/Makefile24
-rw-r--r--test/self/core.c26
-rw-r--r--test/self/digest.c213
-rw-r--r--test/self/dirfd.c129
-rw-r--r--test/self/envvar.c82
-rw-r--r--test/self/idr.c119
-rw-r--r--test/self/json.c121
-rw-r--r--test/self/jwt.c156
-rw-r--r--test/self/jwt_test.pem37
-rw-r--r--test/self/jwt_test.pem.c_shipped49
-rw-r--r--test/self/malloc.c92
-rw-r--r--test/self/mmu.c271
-rw-r--r--test/self/of_manipulation.c23
-rw-r--r--test/self/of_manipulation.dts11
-rw-r--r--test/self/printf.c17
-rw-r--r--test/self/ramfs.c213
-rw-r--r--test/self/regulator.c190
-rw-r--r--test/self/setjmp.c98
-rw-r--r--test/self/string.c203
-rw-r--r--test/self/test_command.c64
-rw-r--r--test/self/test_regulator.dtso43
22 files changed, 2219 insertions, 28 deletions
diff --git a/test/self/Kconfig b/test/self/Kconfig
index cf11efe544..33e478aee8 100644
--- a/test/self/Kconfig
+++ b/test/self/Kconfig
@@ -10,6 +10,7 @@ if SELFTEST
config CMD_SELFTEST
bool "selftest command"
depends on COMMAND_SUPPORT
+ default y
help
Command to run enabled barebox self-tests.
If run without arguments, all tests are run
@@ -28,7 +29,22 @@ config SELFTEST_AUTORUN
config SELFTEST_ENABLE_ALL
bool "Enable all self-tests"
select SELFTEST_PRINTF
+ select SELFTEST_MALLOC
select SELFTEST_PROGRESS_NOTIFIER
+ select SELFTEST_OF_MANIPULATION
+ select SELFTEST_ENVIRONMENT_VARIABLES if ENVIRONMENT_VARIABLES
+ select SELFTEST_FS_RAMFS if FS_RAMFS
+ select SELFTEST_DIRFD if FS_RAMFS && FS_DEVFS
+ select SELFTEST_TFTP if FS_TFTP
+ select SELFTEST_JSON if JSMN
+ select SELFTEST_JWT if JWT
+ select SELFTEST_DIGEST if DIGEST
+ select SELFTEST_MMU if MMU
+ select SELFTEST_STRING
+ select SELFTEST_SETJMP if ARCH_HAS_SJLJ
+ select SELFTEST_REGULATOR if REGULATOR_FIXED
+ select SELFTEST_TEST_COMMAND if CMD_TEST
+ select SELFTEST_IDR
help
Selects all self-tests compatible with current configuration
@@ -51,4 +67,54 @@ config SELFTEST_OF_MANIPULATION
config SELFTEST_PROGRESS_NOTIFIER
bool "progress notifier selftest"
+config SELFTEST_ENVIRONMENT_VARIABLES
+ bool "environment variable selftest"
+
+config SELFTEST_FS_RAMFS
+ bool "ramfs selftest"
+ depends on FS_RAMFS
+
+config SELFTEST_DIRFD
+ bool "dirfd selftest"
+ depends on FS_RAMFS && FS_DEVFS
+
+config SELFTEST_JSON
+ bool "JSON selftest"
+ depends on JSMN
+
+config SELFTEST_JWT
+ bool "JSON Web Token selftest"
+ depends on JWT
+
+config SELFTEST_MMU
+ bool "MMU remapping selftest"
+ select MEMTEST
+ depends on MMU
+
+config SELFTEST_DIGEST
+ bool "Digest selftest"
+ depends on DIGEST
+ select PRINTF_HEXSTR
+
+config SELFTEST_STRING
+ bool "String library selftest"
+ select VERSION_CMP
+
+config SELFTEST_SETJMP
+ bool "setjmp/longjmp library selftest"
+ depends on ARCH_HAS_SJLJ
+
+config SELFTEST_REGULATOR
+ bool "Regulator selftest"
+ depends on REGULATOR_FIXED
+ select OF_OVERLAY
+
+config SELFTEST_TEST_COMMAND
+ bool "test command selftest"
+ depends on CMD_TEST
+
+config SELFTEST_IDR
+ bool "idr selftest"
+ select IDR
+
endif
diff --git a/test/self/Makefile b/test/self/Makefile
index 65d01596b8..fbc1867254 100644
--- a/test/self/Makefile
+++ b/test/self/Makefile
@@ -3,5 +3,29 @@
obj-$(CONFIG_SELFTEST) += core.o
obj-$(CONFIG_SELFTEST_MALLOC) += malloc.o
obj-$(CONFIG_SELFTEST_PRINTF) += printf.o
+CFLAGS_printf.o += -Wno-format-security -Wno-format
obj-$(CONFIG_SELFTEST_PROGRESS_NOTIFIER) += progress-notifier.o
obj-$(CONFIG_SELFTEST_OF_MANIPULATION) += of_manipulation.o of_manipulation.dtb.o
+obj-$(CONFIG_SELFTEST_ENVIRONMENT_VARIABLES) += envvar.o
+obj-$(CONFIG_SELFTEST_FS_RAMFS) += ramfs.o
+obj-$(CONFIG_SELFTEST_DIRFD) += dirfd.o
+obj-$(CONFIG_SELFTEST_JSON) += json.o
+obj-$(CONFIG_SELFTEST_JWT) += jwt.o jwt_test.pem.o
+obj-$(CONFIG_SELFTEST_DIGEST) += digest.o
+obj-$(CONFIG_SELFTEST_MMU) += mmu.o
+obj-$(CONFIG_SELFTEST_STRING) += string.o
+obj-$(CONFIG_SELFTEST_SETJMP) += setjmp.o
+obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o
+obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o
+obj-$(CONFIG_SELFTEST_IDR) += idr.o
+
+ifdef REGENERATE_RSATOC
+
+$(obj)/jwt_test.pem.c_shipped: $(src)/jwt_test.pem FORCE
+ $(call if_changed,rsa_keys,$(basename $(target-stem)):$<,-s)
+
+endif
+
+clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z
+clean-files += *.dtbo *.dtbo.S .*.dtso
+clean-files += *.pem.c
diff --git a/test/self/core.c b/test/self/core.c
index caa4c27f6d..40f5ee842d 100644
--- a/test/self/core.c
+++ b/test/self/core.c
@@ -7,6 +7,30 @@
LIST_HEAD(selftests);
+int selftest_run(struct selftest *test)
+{
+ int err;
+
+ test->running = true;
+ err = test->func();
+ test->running = false;
+
+ return err;
+}
+
+bool selftest_is_running(struct selftest *test)
+{
+ if (test)
+ return test->running;
+
+ list_for_each_entry(test, &selftests, list) {
+ if (selftest_is_running(test))
+ return true;
+ }
+
+ return false;
+}
+
void selftests_run(void)
{
struct selftest *test;
@@ -15,7 +39,7 @@ void selftests_run(void)
pr_notice("Configured tests will run now\n");
list_for_each_entry(test, &selftests, list)
- err |= test->func();
+ err |= selftest_run(test);
if (err)
pr_err("Some selftests failed\n");
diff --git a/test/self/digest.c b/test/self/digest.c
new file mode 100644
index 0000000000..4cda5b0963
--- /dev/null
+++ b/test/self/digest.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <clock.h>
+#include <digest.h>
+
+BSELFTEST_GLOBALS();
+
+struct digest_test_case {
+ const char *name;
+ const void *buf;
+ size_t buf_size;
+ const void *digest_str;
+ u64 time_ns;
+};
+
+#define TEST_CASE(buf, digest_str) \
+ { #buf, buf, sizeof(buf), digest_str }
+
+#define test_digest(option, algo, ...) do { \
+ struct digest_test_case *t, cases[] = { __VA_ARGS__, { /* sentinel */ } }; \
+ for (t = cases; t->buf; t++) \
+ __test_digest((option), (algo), t, __func__, __LINE__); \
+ if (!__is_defined(DEBUG)) \
+ break; \
+ printf("%s:\t", algo); \
+ for (t = cases; t->buf; t++) \
+ printf(" digest(%zu bytes) = %10lluns", t->buf_size, t->time_ns); \
+ printf("\n"); \
+} while (0)
+
+static inline const char *digest_suffix(const char *str, const char *suffix)
+{
+ static char buf[32];
+
+ if (!*suffix)
+ return str;
+
+ WARN_ON(snprintf(buf, sizeof(buf), "%s-%s", str, suffix) >= sizeof(buf));
+ return buf;
+}
+
+static void __test_digest(bool option,
+ const char *algo, struct digest_test_case *t,
+ const char *func, int line)
+{
+ unsigned char *output, *digest;
+ struct digest *d;
+ int hash_len, digest_len;
+ u64 start;
+ int ret;
+
+ total_tests++;
+
+ if (!option) {
+ skipped_tests++;
+ return;
+ }
+
+ d = digest_alloc(algo);
+ if (!d) {
+ printf("%s:%d: failed to allocate %s digest\n", func, line, algo);
+ goto fail;
+ }
+
+ hash_len = digest_length(d);
+ digest_len = strlen(t->digest_str) / 2;
+ if (hash_len != digest_len) {
+ printf("%s:%d: %s digests have length %u, but %u expected\n",
+ func, line, algo, hash_len, digest_len);
+ goto fail;
+ }
+
+ output = calloc(hash_len, 1);
+ if (WARN_ON(!output))
+ goto fail;
+
+ digest = calloc(digest_len, 1);
+ if (WARN_ON(!digest))
+ goto fail;
+
+ ret = hex2bin(digest, t->digest_str, digest_len);
+ if (WARN_ON(ret))
+ goto fail;
+
+ start = get_time_ns();
+
+ ret = digest_digest(d, t->buf, t->buf_size, output);
+ if (ret) {
+ printf("%s:%d: error calculating %s(%s): %pe\n",
+ func, line, algo, t->name, ERR_PTR(ret));
+ goto fail;
+ }
+
+ t->time_ns = get_time_ns() - start;
+
+ if (memcmp(output, digest, hash_len)) {
+ printf("%s:%d: mismatch calculating %s(%s):\n\tgot: %*phN\n\tbut: %*phN expected\n",
+ func, line, algo, t->name, hash_len, output, hash_len, digest);
+ goto fail;
+ }
+
+ return;
+fail:
+ failed_tests++;
+}
+
+static const u8 zeroes7[7] = {};
+static const u8 one32[32] = { 1 };
+static u8 inc4097[4097];
+
+static void test_digest_md5(const char *suffix)
+{
+ bool cond;
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_MD5_GENERIC) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_MD5);
+
+ test_digest(cond, digest_suffix("md5", suffix),
+ TEST_CASE(zeroes7, "d310a40483f9399dd7ed1712e0fdd702"),
+ TEST_CASE(one32, "b39ac6e2aa7e375c38ba7ae921b5ba89"),
+ TEST_CASE(inc4097, "70410aad262cd11e63ae854804c8024b"));
+}
+
+static void test_digests_sha12(const char *suffix)
+{
+ bool cond;
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA1_GENERIC) :
+ !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA1_ARM) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_SHA1);
+
+ test_digest(cond, digest_suffix("sha1", suffix),
+ TEST_CASE(zeroes7, "77ce0377defbd11b77b1f4ad54ca40ea5ef28490"),
+ TEST_CASE(one32, "cbd9cbfc20182e4b71e593e7ad598fc383cc6058"),
+ TEST_CASE(inc4097, "c627e736efd8bb0dff1778335c9c79cb1f27e396"));
+
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA224_GENERIC) :
+ !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM) :
+ !strcmp(suffix, "ce") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM64_CE) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_SHA224);
+
+ test_digest(cond, digest_suffix("sha224", suffix),
+ TEST_CASE(zeroes7, "fbf6df85218ac5632461a8a17c6f294e6f35264cbfc0a9774a4f665b"),
+ TEST_CASE(one32, "343cb3950305e6e6331e294b0a4925739d09ecbd2b43a2fc87c09941"),
+ TEST_CASE(inc4097, "6596b5dcfbd857f4246d6b94508b8a1a5b715a4f644a0c1e7d54c4f7"));
+
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA256_GENERIC) :
+ !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM) :
+ !strcmp(suffix, "ce") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM64_CE) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_SHA256);
+
+ test_digest(cond, digest_suffix("sha256", suffix),
+ TEST_CASE(zeroes7, "837885c8f8091aeaeb9ec3c3f85a6ff470a415e610b8ba3e49f9b33c9cf9d619"),
+ TEST_CASE(one32, "01d0fabd251fcbbe2b93b4b927b26ad2a1a99077152e45ded1e678afa45dbec5"),
+ TEST_CASE(inc4097, "1e973d029df2b2c66cb42a942c5edb45966f02abaff29fe99410e44d271d0efc"));
+}
+
+
+static void test_digests_sha35(const char *suffix)
+{
+ bool cond;
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA384_GENERIC) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_SHA384);
+
+ test_digest(cond, digest_suffix("sha384", suffix),
+ TEST_CASE(zeroes7, "b56705a73cf280f06d3a6b482c441a3d280c930d0c44b04f364dcdcedcfbc47c"
+ "f3645a71da7b97f9e5d3a0924f6b9634"),
+ TEST_CASE(one32, "dd606b49d7658a5eae905d593271c280819f92eb1a9a4986057aedc0a5f2eaea"
+ "99052904718f6d83f16ad209d793f253"),
+ TEST_CASE(inc4097, "f76046b90890f20ae94066a3ad33010f5b3b2fd46977414636bbc634898b06fd"
+ "4cb8f85e0926e8817e518300a930529e"));
+
+
+ cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA512_GENERIC) :
+ IS_ENABLED(CONFIG_HAVE_DIGEST_SHA512);
+
+ test_digest(cond, digest_suffix("sha512", suffix),
+ TEST_CASE(zeroes7, "76afca18a9b81ffb967ffcf0460ed221c3605d3820057214d785fa88259bb5cb"
+ "729576178e6edb0134f645d2e2e92cbabf1333462f3b9058692c950f51c64a92"),
+ TEST_CASE(one32, "ce0c265ecc82dd8cee6e56ce44e45dafd7a0c5750df914b253a1fb7a8af66ddb"
+ "99763607f0a85d0bd43669194a3a40577a528af395f4f17e06f1defcc6deb2a5"),
+ TEST_CASE(inc4097, "42eb09aca460d79b0c0aeac28187ed055a92e33602b69428461697680ff9f48f"
+ "60a5a68aa0017e3446433349b42592b74713d7787628a58e400b7f588b9bd69b"));
+}
+
+static void test_digests(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(inc4097); i++)
+ inc4097[i] = i;
+
+ test_digest_md5("generic");
+
+ test_digests_sha12("generic");
+ if (IS_ENABLED(CONFIG_CPU_32))
+ test_digests_sha12("asm");
+
+ test_digests_sha35("generic");
+
+ test_digest_md5("");
+ test_digests_sha12("");
+ test_digests_sha35("");
+
+}
+bselftest(core, test_digests);
diff --git a/test/self/dirfd.c b/test/self/dirfd.c
new file mode 100644
index 0000000000..20b5425871
--- /dev/null
+++ b/test/self/dirfd.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <string.h>
+#include <linux/bitfield.h>
+#include <unistd.h>
+#include <bselftest.h>
+
+BSELFTEST_GLOBALS();
+
+#define expect(cond, res, fmt, ...) ({ \
+ int __cond = (cond); \
+ int __res = (res); \
+ total_tests++; \
+ if (__cond != __res) { \
+ failed_tests++; \
+ printf("%s:%d failed: %s == %d: " fmt "\n", \
+ __func__, __LINE__, #cond, __cond, ##__VA_ARGS__); \
+ } \
+ __cond == __res; \
+})
+
+static void check_statat(const char *at, int dirfd, const char *prefix, unsigned expected)
+{
+ static const char *paths[] = { ".", "..", "zero", "dev" };
+ struct stat s;
+
+ for (int i = 0; i < ARRAY_SIZE(paths); i++) {
+ const char *path = paths[i];
+ char *fullpath = NULL, *testpath = basprintf("%s%s", prefix, path);
+ struct fs_device *fsdev1, *fsdev2;
+ int ret;
+
+ ret = statat(dirfd, testpath, &s);
+ if (!expect(ret == 0, FIELD_GET(BIT(2), expected),
+ "statat(%s, %s): %m", at, testpath))
+ goto next;
+
+ fullpath = canonicalize_path(dirfd, testpath);
+ if (!expect(fullpath != NULL, FIELD_GET(BIT(1), expected),
+ "canonicalize_path(%s, %s): %m", at, testpath))
+ goto next;
+
+ if (!fullpath)
+ goto next;
+
+ fsdev1 = get_fsdevice_by_path(AT_FDCWD, fullpath);
+ if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(AT_FDCWD, %s)",
+ fullpath))
+ goto next;
+
+ fsdev2 = get_fsdevice_by_path(dirfd, testpath);
+ if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(%s, %s)",
+ at, testpath))
+ goto next;
+
+ if (!expect(fsdev1 == fsdev2, true,
+ "get_fsdevice_by_path(%s, %s) != get_fsdevice_by_path(AT_FDCWD, %s)",
+ fullpath, at, testpath))
+ goto next;
+
+ ret = strcmp_ptr(fsdev1->path, "/dev");
+ if (!expect(ret == 0, FIELD_GET(BIT(0), expected),
+ "fsdev_of(%s)->path = %s != /dev", fullpath, fsdev1->path))
+ goto next;
+
+next:
+ expected >>= 3;
+ free(testpath);
+ free(fullpath);
+ }
+}
+
+static void do_test_dirfd(const char *at, int dirfd,
+ unsigned expected1, unsigned expected2,
+ unsigned expected3, unsigned expected4)
+{
+ if (dirfd < 0 && dirfd != AT_FDCWD)
+ return;
+
+ check_statat(at, dirfd, "", expected1);
+ check_statat(at, dirfd, "./", expected1);
+ check_statat(at, dirfd, "/dev/", expected2);
+ check_statat(at, dirfd, "/dev/./", expected2);
+ check_statat(at, dirfd, "/dev/../dev/", expected2);
+ check_statat(at, dirfd, "/", expected3);
+ check_statat(at, dirfd, "../", expected4);
+
+ if (dirfd >= 0)
+ close(dirfd);
+}
+
+
+static void test_dirfd(void)
+{
+ int fd;
+
+ fd = open("/", O_PATH);
+ if (expect(fd < 0, false, "open(/, O_PATH) = %d", fd))
+ close(fd);
+
+#define B(dot, dotdot, zero, dev) 0b##dev##zero##dotdot##dot
+ /* We do fiften tests for every configuration
+ * for dir in ./ /dev / ../ ; do
+ * for file in . .. zero dev ; do
+ * test if file exists
+ * test if file can be canonicalized
+ * test if parent FS is mounted at /dev
+ * done
+ * done
+ *
+ * The bits belows correspond to whether a test fails in the above loop
+ */
+
+ do_test_dirfd("AT_FDCWD", AT_FDCWD,
+ B(110,110,000,111), B(111,110,111,000),
+ B(110,110,000,111), B(110,110,000,111));
+ do_test_dirfd("/dev", open("/dev", O_PATH),
+ B(111,110,111,000), B(111,110,111,000),
+ B(110,110,000,111), B(110,110,000,111));
+ do_test_dirfd("/dev O_CHROOT", open("/dev", O_PATH | O_CHROOT),
+ B(111,111,111,000), B(000,000,000,000),
+ B(111,111,111,000), B(111,111,111,000));
+}
+bselftest(core, test_dirfd);
diff --git a/test/self/envvar.c b/test/self/envvar.c
new file mode 100644
index 0000000000..a4620f0437
--- /dev/null
+++ b/test/self/envvar.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <environment.h>
+#include <bselftest.h>
+#include <linux/sizes.h>
+
+BSELFTEST_GLOBALS();
+
+static int strequal(const char *a, const char *b)
+{
+ if (!a || !b)
+ return a == b;
+
+ return !strcmp(a, b);
+}
+
+static void __expect_getenv(const char *var, const char *expect,
+ const char *func, int line)
+{
+ const char *val;
+
+ total_tests++;
+
+ val = getenv(var);
+ if (!IS_ENABLED(CONFIG_ENVIRONMENT_VARIABLES)) {
+ if (val == NULL) {
+ skipped_tests++;
+ return;
+ }
+ }
+
+ if (!strequal(val, expect)) {
+ failed_tests++;
+ printf("%s:%d: failure: getenv(%s) == \"%s\", but \"%s\" expected\n",
+ func, line, var, val ?: "<NULL>", expect ?: "<NULL>");
+ }
+}
+
+#define expect_getenv(v, e) __expect_getenv(v, e, __func__, __LINE__)
+
+static void test_envvar(void)
+{
+
+ if (!IS_ENABLED(CONFIG_ENVIRONMENT_VARIABLES))
+ pr_info("built without environment variable support: Skipping tests\n");
+
+ expect_getenv("__TEST_VAR1", NULL);
+
+ setenv("__TEST_VAR1", "VALUE1");
+
+ expect_getenv("__TEST_VAR1", "VALUE1");
+
+ unsetenv("__TEST_VAR1");
+
+ expect_getenv("__TEST_VAR1", NULL);
+
+ setenv("__TEST_VAR1", "VALUE1");
+
+ expect_getenv("__TEST_VAR1", "VALUE1");
+
+ setenv("__TEST_VAR1", "VALUE2");
+
+ expect_getenv("__TEST_VAR1", "VALUE2");
+
+ setenv("__TEST_VAR1", "1337");
+
+ expect_getenv("__TEST_VAR1", "1337");
+
+ pr_setenv("__TEST_VAR1", "0x%s", "1337");
+
+ expect_getenv("__TEST_VAR1", "0x1337");
+
+ pr_setenv("__TEST_VAR1", "%ux1%c%x", 0, '3', 0x37);
+
+ expect_getenv("__TEST_VAR1", "0x1337");
+
+ unsetenv("__TEST_VAR1");
+}
+bselftest(core, test_envvar);
diff --git a/test/self/idr.c b/test/self/idr.c
new file mode 100644
index 0000000000..3d23141e0f
--- /dev/null
+++ b/test/self/idr.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <printk.h>
+#include <linux/idr.h>
+#include <bselftest.h>
+
+BSELFTEST_GLOBALS();
+
+#define __expect(cond, fmt, ...) ({ \
+ bool __cond = (cond); \
+ total_tests++; \
+ \
+ if (!__cond) { \
+ failed_tests++; \
+ printf("%s failed at %s:%d " fmt "\n", \
+ #cond, __func__, __LINE__, ##__VA_ARGS__); \
+ } \
+ __cond; \
+})
+
+#define expect(ret, ...) __expect((ret), __VA_ARGS__)
+
+static int cmp[3] = { 7, 1, 2};
+static int sorted_cmp[3] = { 1, 2, 7};
+
+static int test_idr_for_each(int id, void *p, void *data)
+{
+ expect(data == &cmp[2]);
+ expect(*(int *)p == id);
+
+ return id == 1 ? 0 : -1;
+}
+
+static int count_idr(int id, void *p, void *data)
+{
+ int *count = data;
+
+ ++*count;
+
+ return 0;
+}
+
+static void test_idr(void)
+{
+ void *ptr;
+ int id, count;
+
+ DEFINE_IDR(idr);
+
+ expect(idr_is_empty(&idr));
+
+ expect(!idr_find(&idr, cmp[0]));
+
+ id = idr_alloc_one(&idr, &cmp[0], cmp[0]);
+ expect(id == cmp[0]);
+
+ expect(!idr_is_empty(&idr));
+
+ ptr = idr_find(&idr, cmp[0]);
+ expect(ptr);
+ expect(ptr == &cmp[0]);
+
+ id = idr_alloc_one(&idr, &cmp[1], cmp[1]);
+ expect(id == cmp[1]);
+
+ id = idr_alloc_one(&idr, &cmp[2], cmp[2]);
+ expect(id == cmp[2]);
+
+ count = 0;
+
+ idr_for_each_entry(&idr, ptr, id) {
+ expect(id == sorted_cmp[count]);
+ expect(*(int *)ptr == sorted_cmp[count]);
+
+ count++;
+
+ }
+
+ expect(count == 3);
+
+ expect(idr_for_each(&idr, test_idr_for_each, &cmp[2]) == -1);
+
+ count = 0;
+ expect(idr_for_each(&idr, count_idr, &count) == 0);
+ expect(count == 3);
+
+ idr_remove(&idr, 1);
+
+ count = 0;
+ expect(idr_for_each(&idr, count_idr, &count) == 0);
+ expect(count == 2);
+
+ idr_remove(&idr, 7);
+
+ count = 0;
+ expect(idr_for_each(&idr, count_idr, &count) == 0);
+ expect(count == 1);
+
+ idr_remove(&idr, 2);
+
+ count = 0;
+ expect(idr_for_each(&idr, count_idr, &count) == 0);
+ expect(count == 0);
+
+ expect(idr_is_empty(&idr));
+
+ idr_alloc_one(&idr, &cmp[0], cmp[0]);
+ idr_alloc_one(&idr, &cmp[1], cmp[1]);
+ idr_alloc_one(&idr, &cmp[2], cmp[2]);
+
+ expect(!idr_is_empty(&idr));
+
+ idr_destroy(&idr);
+
+ expect(idr_is_empty(&idr));
+}
+bselftest(core, test_idr);
diff --git a/test/self/json.c b/test/self/json.c
new file mode 100644
index 0000000000..4896717689
--- /dev/null
+++ b/test/self/json.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <jsmn.h>
+
+BSELFTEST_GLOBALS();
+
+static const jsmntok_t*
+__json_expect(const char *json, const jsmntok_t *token, const char **lookup,
+ jsmntype_t expected_type, const char *expected_value)
+{
+ bool success = true;
+
+ total_tests++;
+
+ if (token->type != expected_type) {
+ failed_tests++;
+ printf("%pJP: type mismatch: got %d, but %d expected\n",
+ lookup, token->type, expected_type);
+ success = false;
+ }
+
+ if (!expected_value)
+ goto out;
+
+ total_tests++;
+
+ if (!jsmn_eq(expected_value, json, token)) {
+ failed_tests++;
+ printf("%pJP: string mismatch: got %.*s, but %s expected\n",
+ lookup, (int)(token->end - token->start),
+ &json[token->start], expected_value);
+ success = false;
+ }
+
+out:
+ return success ? token : NULL;
+}
+
+static const jsmntok_t*
+json_expect(const char *json, const jsmntok_t *tokens, const char **lookup,
+ jsmntype_t expected_type, const char *expected_value)
+{
+ const jsmntok_t *token;
+
+ total_tests++;
+
+ token = jsmn_locate(lookup, json, tokens);
+ if (!token) {
+ failed_tests++;
+ printf("%pJP: couldn't be located\n", lookup);
+ return NULL;
+ }
+
+ return __json_expect(json, token, lookup, expected_type, expected_value);
+}
+
+#define JP(...) (const char *[]) { __VA_ARGS__, NULL }
+
+/* Wonky indentation is intended */
+static const char json[] =
+"{\n"
+" \"null\" :null,\"number\" : 0x42,\n"
+" \"object\": {\n"
+" \"a\": \"b\",\n"
+" \"C\": \"dE\","
+" \"e\": \"\"\n"
+" },"
+" \"array\": [ \"1\",\"2\",\t\t\"3\"],\n"
+" \"boolean\": true,\n"
+"\"string\": \"Hello World\n\"}\n";
+
+static void test_json(void)
+{
+ const jsmntok_t *token;
+ jsmntok_t *tokens;
+ char *string;
+
+ total_tests++;
+
+ /* Figure out how many tokens we need. */
+ tokens = jsmn_parse_alloc(json, sizeof(json), NULL);
+ if (!tokens) {
+ printf("failed to parse JSON\n");
+ failed_tests++;
+ return;
+ }
+
+ json_expect(json, tokens, JP("null"), JSMN_PRIMITIVE, "null");
+ json_expect(json, tokens, JP("number"), JSMN_PRIMITIVE, "0x42");
+ json_expect(json, tokens, JP("object"), JSMN_OBJECT, NULL);
+ json_expect(json, tokens, JP("object", "a"), JSMN_STRING, "b");
+ json_expect(json, tokens, JP("object", "C"), JSMN_STRING, "dE");
+ json_expect(json, tokens, JP("object", "e"), JSMN_STRING, "");
+
+ token = json_expect(json, tokens, JP("array"), JSMN_ARRAY, NULL);
+
+ token = jsmn_skip_value(token);
+ __json_expect(json, token, JP("boolean"), JSMN_STRING, "boolean");
+
+ token = jsmn_skip_value(token);
+ __json_expect(json, token, JP("boolean"), JSMN_PRIMITIVE, "true");
+
+ string = jsmn_strdup(JP("string"), json, tokens);
+ if (WARN_ON(!string))
+ goto out;
+
+ total_tests++;
+ if (strcmp(string, "Hello World\n")) {
+ failed_tests++;
+ printf("%pJP: string mismatch\n", JP("string"));
+ }
+
+ free(string);
+out:
+ free(tokens);
+}
+bselftest(parser, test_json);
diff --git a/test/self/jwt.c b/test/self/jwt.c
new file mode 100644
index 0000000000..015a0e4367
--- /dev/null
+++ b/test/self/jwt.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <rsa.h>
+#include <bselftest.h>
+#include <crypto/jwt.h>
+#include <console.h>
+
+BSELFTEST_GLOBALS();
+
+static const jsmntok_t *check_token(const jsmntok_t *token,
+ const char *claim,
+ const char *payload,
+ jsmntype_t expected_type,
+ const char *expected_value)
+{
+ total_tests++;
+
+ if (token->type != expected_type) {
+ failed_tests++;
+ printf("claim %s has type mismatch: got %d, but %d expected\n",
+ claim, token->type, expected_type);
+ return NULL;
+ }
+
+ total_tests++;
+
+ if (!jsmn_eq(expected_value, payload, token)) {
+ failed_tests++;
+ printf("claim %s: value has mismatch: got %.*s, but %s expected\n",
+ claim, (int)(token->end - token->start),
+ &payload[token->start], expected_value);
+ return NULL;
+ }
+
+ return token;
+}
+
+static const jsmntok_t *jwt_check_claim(const struct jwt *jwt,
+ const char *claim,
+ jsmntype_t expected_type,
+ const char *expected_value)
+{
+ const jsmntok_t *token;
+
+ total_tests++;
+
+ token = jwt_get_claim(jwt, claim);
+ if (!token) {
+ failed_tests++;
+ printf("claim %s couldn't be located\n", claim);
+ return NULL;
+ }
+
+ return check_token(token, claim, jwt_get_payload(jwt),
+ expected_type, expected_value);
+}
+
+static const char jwt_rs256[] =
+ " eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9."
+ "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0."
+ "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGf"
+ "fz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yW"
+ "ytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUF"
+ "KrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzI"
+ "uHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ\n \n";
+
+static void test_jwt(void)
+{
+ char *jwt_rs256_mangled, *ch;
+ struct jwt_key jwt_key;
+ struct jwt *jwt;
+ extern const struct rsa_public_key __key_jwt_test;
+ int old_loglevel;
+
+ jwt_key.alg = JWT_ALG_RS256;
+ jwt_key.material.rsa_pub = &__key_jwt_test;
+ total_tests++;
+
+ jwt = jwt_decode(jwt_rs256, &jwt_key);
+ if (IS_ERR(jwt)) {
+ printf("failed to parse jwt\n");
+ failed_tests++;
+ } else {
+ jwt_check_claim(jwt, "sub", JSMN_STRING, "1234567890");
+ jwt_check_claim(jwt, "name", JSMN_STRING, "John Doe");
+ jwt_check_claim(jwt, "admin", JSMN_PRIMITIVE, "true");
+ jwt_check_claim(jwt, "iat", JSMN_PRIMITIVE, "1516239022");
+
+ jwt_free(jwt);
+ }
+
+ /*
+ * Following tests intentionally fail and JWT failures are intentionally
+ * noisy, so we decrease logging a bit during their run
+ */
+
+ old_loglevel = barebox_set_loglevel(MSG_CRIT);
+
+ jwt_rs256_mangled = strdup(jwt_rs256);
+ ch = &jwt_rs256_mangled[strlen(jwt_rs256_mangled) - 1];
+ *ch = *ch == '_' ? '-' : '_';
+
+ total_tests++;
+
+ jwt = jwt_decode(jwt_rs256_mangled, &jwt_key);
+ if (!IS_ERR(jwt)) {
+ printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+ failed_tests++;
+ jwt_free(jwt);
+ }
+
+ free(jwt_rs256_mangled);
+
+ jwt_rs256_mangled = strdup(jwt_rs256);
+ ch = &jwt_rs256_mangled[0];
+ *ch = *ch == '_' ? '-' : '_';
+
+ total_tests++;
+
+ jwt = jwt_decode(jwt_rs256_mangled, &jwt_key);
+ if (!IS_ERR(jwt)) {
+ printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+ failed_tests++;
+ jwt_free(jwt);
+ }
+
+ free(jwt_rs256_mangled);
+
+ total_tests++;
+
+ jwt_key.alg = JWT_ALG_RS384;
+
+ jwt = jwt_decode(jwt_rs256, &jwt_key);
+ if (!IS_ERR(jwt)) {
+ printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+ failed_tests++;
+ jwt_free(jwt);
+ }
+
+ total_tests++;
+
+ jwt_key.alg = JWT_ALG_NONE;
+
+ jwt = jwt_decode(jwt_rs256, &jwt_key);
+ if (!IS_ERR(jwt)) {
+ printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__);
+ failed_tests++;
+ jwt_free(jwt);
+ }
+
+ barebox_set_loglevel(old_loglevel);
+}
+bselftest(parser, test_jwt);
diff --git a/test/self/jwt_test.pem b/test/self/jwt_test.pem
new file mode 100644
index 0000000000..349a5b6a47
--- /dev/null
+++ b/test/self/jwt_test.pem
@@ -0,0 +1,37 @@
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo
+4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u
++qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh
+kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ
+0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg
+cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc
+mwIDAQAB
+-----END PUBLIC KEY-----
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj
+MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu
+NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ
+qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg
+p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR
+ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi
+VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV
+laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8
+sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H
+mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY
+dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw
+ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ
+DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T
+N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t
+0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv
+t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU
+AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk
+48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL
+DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK
+xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA
+mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh
+2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz
+et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr
+VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD
+TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc
+dn/RsYEONbwQSjIfMPkvxF+8HQ==
+-----END PRIVATE KEY-----
diff --git a/test/self/jwt_test.pem.c_shipped b/test/self/jwt_test.pem.c_shipped
new file mode 100644
index 0000000000..2142ae15df
--- /dev/null
+++ b/test/self/jwt_test.pem.c_shipped
@@ -0,0 +1,49 @@
+#include <rsa.h>
+
+static uint32_t jwt_test_modulus[] = {
+ 0x5df6dc9b, 0xdc2256e3, 0xa786531a, 0x00012002,
+ 0x231a85db, 0xe95fe2b1, 0xd68d022d, 0x5df86ca7,
+ 0x2fbd6865, 0x559e1658, 0x4fd9d3f0, 0xa5938e90,
+ 0x22476070, 0x39516551, 0xf5c5c34d, 0x5c5834c7,
+ 0x71340c31, 0x483094bf, 0x75988a46, 0xf78efade,
+ 0x6eb855ff, 0xbb6ebd57, 0xb40d14d7, 0x24fdc024,
+ 0xca4909d2, 0x4960a763, 0x8c37c7e5, 0x166c59ce,
+ 0x84f00900, 0x1433b956, 0xf73fb8d1, 0xd5923468,
+ 0x64e0d272, 0x0cbe4069, 0x25bd6fd5, 0xddeaa861,
+ 0x7327a191, 0xe259aa0b, 0x63208519, 0xcec92e7a,
+ 0x0f2cf822, 0xd15ccc69, 0x2f1a12da, 0x209d3c4a,
+ 0x6664a75f, 0xb3e6cc63, 0x9f06cb48, 0xa2a16f02,
+ 0x127e6efa, 0x4bee34ca, 0xb424ae60, 0x5bbc9647,
+ 0xead3f233, 0x89cb467a, 0x5532f0ee, 0x2dd20ba7,
+ 0x357a7df0, 0x46078b7b, 0xf3366d2d, 0x580e11e3,
+ 0x1f6328e2, 0xc2a33331, 0xb7d52cf1, 0xbb5494d4,
+};
+
+static uint32_t jwt_test_rr[] = {
+ 0xec4954b7, 0x61f69199, 0x9e489481, 0x14f25ec8,
+ 0x712de1ab, 0x9c4ed93b, 0xcff16ec3, 0xb6e0c808,
+ 0x56551022, 0x1206f0dc, 0x72051e96, 0x6ab07919,
+ 0x8d29bea3, 0xa2a79109, 0x18a5e53d, 0x0a1ed2ae,
+ 0xae6544f4, 0x5fb16424, 0x5253250c, 0x3fc04654,
+ 0x9b9a3028, 0xf7219ed8, 0x8f9a7d60, 0x1020027e,
+ 0xa7bb0182, 0xca68b839, 0x86a507ca, 0x725d9efb,
+ 0xf43e09cd, 0xd373027e, 0x6c22f55c, 0x074bee70,
+ 0x49525052, 0x0506900e, 0xf51bde0d, 0xc8f82c0e,
+ 0x4a00d71e, 0x0a517ae2, 0x616e76fb, 0xb17b75d0,
+ 0x4bfcbb90, 0x3efd07cf, 0xaf30c7cb, 0xa18dee7f,
+ 0x02ed9615, 0x9185d985, 0x630a209e, 0xef23435c,
+ 0x46277653, 0x57d47ec5, 0x86e58fcf, 0x8f0ebe09,
+ 0x3b26c77e, 0xa3ef370d, 0xf83df63e, 0xa30a742e,
+ 0x49c2fb64, 0xea9fbed9, 0xb7471da7, 0x7a411345,
+ 0x303732ed, 0x6660f318, 0xe3a7df4c, 0x6a784bd5,
+};
+
+struct rsa_public_key __key_jwt_test;
+struct rsa_public_key __key_jwt_test = {
+ .len = 64,
+ .n0inv = 0x17d8566d,
+ .modulus = jwt_test_modulus,
+ .rr = jwt_test_rr,
+ .exponent = 0x10001,
+ .key_name_hint = "jwt_test",
+};
diff --git a/test/self/malloc.c b/test/self/malloc.c
index c25b416b97..0feec81018 100644
--- a/test/self/malloc.c
+++ b/test/self/malloc.c
@@ -7,26 +7,62 @@
#include <malloc.h>
#include <memory.h>
#include <linux/sizes.h>
+#include <linux/bitops.h>
BSELFTEST_GLOBALS();
-static void __expect(bool cond, bool expect,
+#define get_alignment(val) \
+ BIT(__builtin_constant_p(val) ? __builtin_ffsll(val) : __ffs64(val))
+
+static_assert(get_alignment(0x1) != 1);
+static_assert(get_alignment(0x2) != 2);
+static_assert(get_alignment(0x3) != 1);
+static_assert(get_alignment(0x4) != 4);
+static_assert(get_alignment(0x5) != 1);
+static_assert(get_alignment(0x6) != 2);
+static_assert(get_alignment(0x8) != 8);
+static_assert(get_alignment(0x99) != 0x1);
+static_assert(get_alignment(0xDEADBEE0) != 0x10);
+
+static bool __expect_cond(bool cond, bool expect,
+ const char *condstr, const char *func, int line)
+{
+ total_tests++;
+ if (cond == expect)
+ return true;
+
+ failed_tests++;
+ printf("%s:%d: %s to %s\n", func, line,
+ expect ? "failed" : "unexpectedly succeeded",
+ condstr);
+ return false;
+
+}
+
+static void *__expect(void *ptr, bool expect,
const char *condstr, const char *func, int line)
{
+ bool ok;
total_tests++;
- if (cond != expect) {
- failed_tests++;
- printf("%s:%d: %s to %s\n", func, line,
- expect ? "failed" : "unexpectedly succeeded",
- condstr);
+
+ ok = __expect_cond(ptr != NULL, expect, condstr, func, line);
+ if (ok && ptr) {
+ unsigned alignment = get_alignment((uintptr_t)ptr);
+ if (alignment < CONFIG_MALLOC_ALIGNMENT) {
+ failed_tests++;
+ printf("%s:%d: invalid alignment of %u in %s = %p\n", func, line,
+ alignment, condstr, ptr);
+ }
}
+
+ return ptr;
}
-#define expect_alloc_ok(cond) \
- __expect((cond), true, #cond, __func__, __LINE__)
+#define expect_alloc_ok(ptr) \
+ __expect((ptr), true, #ptr, __func__, __LINE__)
-#define expect_alloc_fail(cond) \
- __expect((cond), false, #cond, __func__, __LINE__)
+#define expect_alloc_fail(ptr) \
+ __expect((ptr), false, #ptr, __func__, __LINE__)
static void test_malloc(void)
{
@@ -45,37 +81,42 @@ static void test_malloc(void)
mem_malloc_size = 0;
}
- expect_alloc_ok(p = malloc(1));
+ p = expect_alloc_ok(malloc(1));
free(p);
if (mem_malloc_size) {
- expect_alloc_fail(malloc(SIZE_MAX));
+ tmp = expect_alloc_fail(malloc(RELOC_HIDE(SIZE_MAX, 0)));
+ free(tmp);
if (0xf0000000 > mem_malloc_size) {
- expect_alloc_fail((tmp = malloc(0xf0000000)));
+ tmp = expect_alloc_fail(malloc(RELOC_HIDE(0xf0000000, 0)));
free(tmp);
}
} else {
skipped_tests += 2;
}
- p = realloc(NULL, 1);
- expect_alloc_ok(p = realloc(NULL, 1));
+ free(realloc(NULL, 1));
+ p = expect_alloc_ok(realloc(NULL, 1));
*p = 0x42;
- expect_alloc_ok(tmp = realloc(p, 2));
+ tmp = expect_alloc_ok(realloc(p, 2));
p = tmp;
- __expect(*p == 0x42, true, "reread after realloc", __func__, __LINE__);
+ __expect_cond(*p == 0x42, true, "reread after realloc", __func__, __LINE__);
if (mem_malloc_size) {
- expect_alloc_fail(tmp = realloc(p, mem_malloc_size));
+ tmp = expect_alloc_fail(realloc(p, mem_malloc_size));
+ free(tmp);
- if (0xf0000000 > mem_malloc_size)
- expect_alloc_fail((tmp = realloc(p, 0xf0000000)));
+ if (0xf0000000 > mem_malloc_size) {
+ tmp = expect_alloc_fail(realloc(p, RELOC_HIDE(0xf0000000, 0)));
+ free(tmp);
+ }
- expect_alloc_fail(tmp = realloc(p, SIZE_MAX));
+ tmp = expect_alloc_fail(realloc(p, RELOC_HIDE(SIZE_MAX, 0)));
+ free(tmp);
} else {
skipped_tests += 3;
@@ -83,9 +124,12 @@ static void test_malloc(void)
free(p);
- expect_alloc_ok(p = malloc(0));
- expect_alloc_ok(tmp = malloc(0));
+ p = expect_alloc_ok(malloc(0));
+ tmp = expect_alloc_ok(malloc(0));
+
+ __expect_cond(p != tmp, true, "allocate distinct 0-size buffers", __func__, __LINE__);
- __expect(p != tmp, true, "allocate distinct 0-size buffers", __func__, __LINE__);
+ free(p);
+ free(tmp);
}
bselftest(core, test_malloc);
diff --git a/test/self/mmu.c b/test/self/mmu.c
new file mode 100644
index 0000000000..60bc9b38e8
--- /dev/null
+++ b/test/self/mmu.c
@@ -0,0 +1,271 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <mmu.h>
+#include <memtest.h>
+#include <abort.h>
+#include <zero_page.h>
+#include <linux/sizes.h>
+#include <efi/efi-mode.h>
+#include <memory.h>
+
+#define TEST_BUFFER_SIZE SZ_1M
+#define TEST_BUFFER_ALIGN SZ_4K
+
+BSELFTEST_GLOBALS();
+
+#define __expect(ret, cond, fmt, ...) do { \
+ bool __cond = (cond); \
+ int __ret = (ret); \
+ total_tests++; \
+ \
+ if (!__cond) { \
+ failed_tests++; \
+ printf("%s:%d error %pe: " fmt "\n", \
+ __func__, __LINE__, ERR_PTR(__ret), ##__VA_ARGS__); \
+ } \
+} while (0)
+
+#define expect_success(ret, ...) __expect((ret), ((ret) >= 0), __VA_ARGS__)
+
+static void memtest(void __iomem *start, size_t size, const char *desc)
+{
+ int ret;
+
+ ret = mem_test_bus_integrity((resource_size_t)start,
+ (resource_size_t)start + size - 1, 0);
+ expect_success(ret, "%s bus test", desc);
+
+ ret = mem_test_moving_inversions((resource_size_t)start,
+ (resource_size_t)start + size - 1, 0);
+ expect_success(ret, "%s moving inverstions test", desc);
+}
+
+static inline int __check_mirroring(void __iomem *a, void __iomem *b, bool is_mirror,
+ const char *func, int line)
+{
+ if ((readl(a) == readl(b)) == (is_mirror))
+ return 0;
+
+ printf("%s:%d: mirroring unexpectedly %s: (*%p = 0x%x) %s (*%p = 0x%x)\n", func, line,
+ is_mirror ? "failed" : "succeeded",
+ a, readl(a), is_mirror ? "!=" : "==", b, readl(b));
+
+ mmuinfo(a);
+ mmuinfo(b);
+
+ return -EILSEQ;
+}
+
+#define check_mirroring(a, b, is_mirror) \
+ __check_mirroring((a), (b), (is_mirror), __func__, __LINE__)
+
+static void test_remap(void)
+{
+ u8 __iomem *buffer = NULL, *mirror = NULL;
+ phys_addr_t buffer_phys;
+ int i, ret;
+
+ buffer = memalign(TEST_BUFFER_ALIGN, TEST_BUFFER_SIZE);
+ if (WARN_ON(!buffer))
+ goto out;
+
+ buffer_phys = virt_to_phys(buffer);
+
+ mirror = memalign(TEST_BUFFER_ALIGN, TEST_BUFFER_SIZE);
+ if (WARN_ON(!mirror))
+ goto out;
+
+ pr_debug("allocated buffer = 0x%p, mirror = 0x%p\n", buffer, mirror);
+
+ memtest(buffer, TEST_BUFFER_SIZE, "cached buffer");
+ memtest(mirror, TEST_BUFFER_SIZE, "cached mirror");
+
+ if (!arch_can_remap()) {
+ skipped_tests += 18;
+ goto out;
+ }
+
+ ret = remap_range(buffer, TEST_BUFFER_SIZE, MAP_UNCACHED);
+ memtest(buffer, TEST_BUFFER_SIZE, "uncached buffer");
+
+ ret = remap_range(mirror, TEST_BUFFER_SIZE, MAP_UNCACHED);
+ memtest(mirror, TEST_BUFFER_SIZE, "uncached mirror");
+
+ for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) {
+ int m = i, b = i;
+ writel(0xDEADBEEF, &mirror[m]);
+ writel(i, &buffer[b]);
+ ret = check_mirroring(&mirror[m], &buffer[b], false);
+ if (ret)
+ break;
+ }
+
+ expect_success(ret, "asserting no mirror before remap");
+
+ ret = arch_remap_range(mirror, buffer_phys, TEST_BUFFER_SIZE, MAP_UNCACHED);
+ expect_success(ret, "remapping with mirroring");
+
+ for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) {
+ int m = i, b = i;
+ writel(0xDEADBEEF, &mirror[m]);
+ writel(i, &buffer[b]);
+ ret = check_mirroring(&mirror[m], &buffer[b], true);
+ if (ret)
+ break;
+ }
+
+ expect_success(ret, "asserting mirroring after remap");
+
+ ret = arch_remap_range(mirror, buffer_phys + SZ_4K,
+ TEST_BUFFER_SIZE / 2, MAP_UNCACHED);
+ expect_success(ret, "remapping with mirroring (phys += 4K)");
+
+ for (i = 0; i < TEST_BUFFER_SIZE / 2; i += sizeof(u32)) {
+ int m = i, b = i + SZ_4K;
+ writel(0xDEADBEEF, &mirror[m]);
+ writel(i, &buffer[b]);
+ ret = check_mirroring(&mirror[m], &buffer[b], true);
+ if (ret)
+ break;
+ }
+
+ expect_success(ret, "asserting mirroring after remap (phys += 4K)");
+
+ ret = arch_remap_range(mirror + SZ_4K, buffer_phys,
+ TEST_BUFFER_SIZE / 2, MAP_UNCACHED);
+ expect_success(ret, "remapping with mirroring (virt += 4K)");
+
+ for (i = 0; i < TEST_BUFFER_SIZE / 2; i += sizeof(u32)) {
+ int m = i + SZ_4K, b = i;
+ writel(0xDEADBEEF, &mirror[m]);
+ writel(i, &buffer[b]);
+ ret = check_mirroring(&mirror[m], &buffer[b], true);
+ if (ret)
+ break;
+ }
+
+ expect_success(ret, "asserting mirroring after remap (virt += 4K)");
+
+ ret = remap_range(buffer, TEST_BUFFER_SIZE, MAP_DEFAULT);
+ expect_success(ret, "remapping buffer with default attrs");
+ memtest(buffer, TEST_BUFFER_SIZE, "newly cached buffer");
+
+ ret = remap_range(mirror, TEST_BUFFER_SIZE, MAP_DEFAULT);
+ expect_success(ret, "remapping mirror with default attrs");
+ memtest(mirror, TEST_BUFFER_SIZE, "newly cached mirror");
+
+ for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) {
+ int m = i, b = i;
+ writel(0xDEADBEEF, &mirror[m]);
+ writel(i, &buffer[b]);
+ ret = check_mirroring(&mirror[m], &buffer[b], false);
+ if (ret)
+ break;
+ }
+
+ expect_success(ret, "asserting no mirror after remap restore");
+out:
+ free(buffer);
+ free(mirror);
+}
+
+static bool zero_page_access_ok(void)
+{
+ struct memory_bank *bank;
+ const char *reason;
+ struct resource null_res = {
+ .name = "null",
+ .flags = IORESOURCE_MEM,
+ .start = 0,
+ .end = sizeof(int) - 1,
+ };
+
+ if (!IS_ENABLED(CONFIG_ARCH_HAS_ZERO_PAGE)) {
+ reason = "CONFIG_ARCH_HAS_ZERO_PAGE=n";
+ goto not_ok;
+ }
+
+ for_each_memory_bank(bank) {
+ if (resource_contains(bank->res, &null_res))
+ return true;
+ }
+
+ reason = "no memory at address zero";
+
+not_ok:
+ pr_info("skipping rest of zero page tests because %s\n", reason);
+ return false;
+}
+
+static void test_zero_page(void)
+{
+ void __iomem *null = NULL;
+
+ total_tests += 3;
+
+ if (!IS_ENABLED(CONFIG_ARCH_HAS_DATA_ABORT_MASK)) {
+ pr_info("skipping %s because CONFIG_ARCH_HAS_DATA_ABORT_MASK=n\n",
+ __func__);
+ skipped_tests += 3;
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(null);
+
+ /* Check if *NULL traps and data_abort_mask works */
+
+ data_abort_mask();
+
+ (void)readl(null);
+
+ if (!data_abort_unmask()) {
+ printf("%s: NULL pointer access did not trap\n", __func__);
+ failed_tests++;
+ }
+
+ if (!zero_page_access_ok()) {
+ skipped_tests += 2;
+ return;
+ }
+
+ /* Check if zero_page_access() works */
+
+ data_abort_mask();
+
+ zero_page_access();
+ (void)readl(null);
+ zero_page_faulting();
+
+ if (data_abort_unmask()) {
+ printf("%s: unexpected fault on zero page access\n", __func__);
+ failed_tests++;
+ }
+
+ /* Check if zero_page_faulting() works */
+
+ data_abort_mask();
+
+ (void)readl(null);
+
+ if (!data_abort_unmask()) {
+ printf("%s NULL pointer access did not trap\n", __func__);
+ failed_tests++;
+ }
+}
+
+static void test_mmu(void)
+{
+ if (efi_is_payload()) {
+ pr_info("MMU was not initialized by us\n");
+ skipped_tests += 23;
+ return;
+ }
+
+ test_zero_page();
+ test_remap();
+}
+bselftest(core, test_mmu);
diff --git a/test/self/of_manipulation.c b/test/self/of_manipulation.c
index 1bcd593c86..8d645b1137 100644
--- a/test/self/of_manipulation.c
+++ b/test/self/of_manipulation.c
@@ -57,16 +57,27 @@ static void test_of_basics(struct device_node *root)
of_property_write_bool(node1, "property1", true);
assert_equal(node1, node2);
+
+ of_property_write_bool(node2, "property1", false);
+ of_property_write_u32(node2, "property1", 1);
+ of_property_write_u32(node2, "property2", 2);
+
+ of_property_write_u32(node1, "property3", 1);
+ of_copy_property(node2, "property2", node1);
+ of_rename_property(node1, "property3", "property1");
+
+ assert_equal(node1, node2);
}
static void test_of_property_strings(struct device_node *root)
{
- struct device_node *np1, *np2, *np3;
+ struct device_node *np1, *np2, *np3, *np4;
char properties[] = "ayy\0bee\0sea";
np1 = of_new_node(root, "np1");
np2 = of_new_node(root, "np2");
np3 = of_new_node(root, "np3");
+ np4 = of_new_node(root, "np4");
of_property_sprintf(np1, "property-single", "%c%c%c", 'a', 'y', 'y');
@@ -89,6 +100,14 @@ static void test_of_property_strings(struct device_node *root)
of_set_property(np1, "property-multi", properties, sizeof(properties) - 1, 0);
assert_different(np1, np2, 1);
+
+ of_append_property(np4, "property-single", "ayy", 4);
+
+ of_append_property(np4, "property-multi", "bee", 4);
+ of_append_property(np4, "property-multi", "sea", 4);
+ of_prepend_property(np4, "property-multi", "ayy", 4);
+
+ assert_equal(np3, np4);
}
static void __init test_of_manipulation(void)
@@ -102,6 +121,8 @@ static void __init test_of_manipulation(void)
expected = of_unflatten_dtb(__dtb_of_manipulation_start,
__dtb_of_manipulation_end - __dtb_of_manipulation_start);
+ if (WARN_ON(IS_ERR(expected)))
+ return;
assert_equal(root, expected);
diff --git a/test/self/of_manipulation.dts b/test/self/of_manipulation.dts
index 3b690bb7f0..2cc6773fa9 100644
--- a/test/self/of_manipulation.dts
+++ b/test/self/of_manipulation.dts
@@ -4,12 +4,14 @@
/ {
node1 {
- property1;
+ property1 = <1>;
+ property2 = <2>;
node21 { };
};
node2 {
- property1;
+ property1 = <1>;
+ property2 = <2>;
node21 { };
};
@@ -27,4 +29,9 @@
property-single = "ayy";
property-multi = "ayy", "bee", "sea";
};
+
+ np4 {
+ property-single = "ayy";
+ property-multi = "ayy", "bee", "sea";
+ };
};
diff --git a/test/self/printf.c b/test/self/printf.c
index 7a74660868..eae40ed242 100644
--- a/test/self/printf.c
+++ b/test/self/printf.c
@@ -305,6 +305,22 @@ test_pointer(void)
errptr();
}
+static void __init
+test_jsonpath(void)
+{
+ if (!IS_ENABLED(CONFIG_JSMN)) {
+ pr_info("skipping jsonpath tests: CONFIG_JSMN disabled in config\n");
+ skipped_tests += 5;
+ return;
+ }
+
+ test("<NULL>", "%pJP", NULL);
+ test("$", "%pJP", &(char *[]){ NULL });
+ test("$.1", "%pJP", &(char *[]){ "1", NULL });
+ test("$.1.23", "%pJP", &(char *[]){ "1", "23", NULL });
+ test("$.1.23.456", "%pJP", &(char *[]){ "1", "23", "456", NULL });
+}
+
static void __init test_printf(void)
{
alloced_buffer = malloc(BUF_SIZE + 2*PAD_SIZE);
@@ -317,6 +333,7 @@ static void __init test_printf(void)
test_string();
test_pointer();
test_hexstr();
+ test_jsonpath();
free(alloced_buffer);
}
diff --git a/test/self/ramfs.c b/test/self/ramfs.c
new file mode 100644
index 0000000000..1bc2b3b068
--- /dev/null
+++ b/test/self/ramfs.c
@@ -0,0 +1,213 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <fcntl.h>
+#include <fs.h>
+#include <dirent.h>
+#include <libfile.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <bselftest.h>
+#include <linux/sizes.h>
+
+BSELFTEST_GLOBALS();
+
+#define __expect(ret, cond, fmt, ...) ({ \
+ bool __cond = (cond); \
+ int __ret = (ret); \
+ total_tests++; \
+ \
+ if (!__cond) { \
+ failed_tests++; \
+ printf("%s:%d error %pe: " fmt "\n", \
+ __func__, __LINE__, ERR_PTR(__ret), ##__VA_ARGS__); \
+ } \
+ __cond; \
+})
+
+static inline int ptr_to_err(const void *ptr)
+{
+ if (ptr)
+ return PTR_ERR_OR_ZERO(ptr);
+
+ return -errno ?: -EFAULT;
+}
+
+#define expect_success(ret, ...) __expect((ret), (ret) >= 0, __VA_ARGS__)
+#define expect_ptrok(ptr, ...) expect_success(ptr_to_err(ptr), __VA_ARGS__)
+#define expect_fail(ret, ...) __expect((ret), (ret) < 0, __VA_ARGS__)
+
+static inline int get_file_count(int i)
+{
+ if (40 <= i && i < 50)
+ return 40;
+
+ if (i >= 50)
+ i -= 10;
+
+ /* we create a file for i == 0 as well */
+ return i + 1;
+}
+
+static void test_ramfs(void)
+{
+ int files[] = { 1, 3, 5, 7, 11, 13, 17 };
+ char fname[128];
+ char *content = NULL;
+ char *oldpwd = NULL;
+ DIR *dir = NULL;
+ const char *dname;
+ struct stat st;
+ int i, j, ret, fd;
+ struct dirent *d;
+
+ dname = make_temp("ramfs-test");
+ ret = mkdir(dname, 0777);
+
+ if (!expect_success(ret, "creating directory"))
+ return;
+
+ ret = stat(dname, &st);
+ if (!expect_success(ret, "stating directory"))
+ goto out;
+
+ expect_success(S_ISDIR(st.st_mode) ? 0 : -ENOTDIR,
+ "directory check");
+
+ content = malloc(99);
+ if (WARN_ON(!content))
+ goto out;
+
+ for (i = 0; i < 99; i++) {
+ scnprintf(fname, sizeof(fname), "%s/file-%02d", dname, i);
+
+ fd = open(fname, O_RDWR | O_CREAT);
+ if (!expect_success(fd, "creating file"))
+ continue;
+
+ for (j = 0; j < i; j++)
+ content[j] = i;
+
+ ret = write(fd, content, i);
+ expect_success(ret, "writing file");
+ close(fd);
+
+ if (40 <= i && i < 50) {
+ ret = unlink(fname);
+ expect_success(ret, "unlinking file");
+ }
+
+ ret = stat(fname, &st);
+ if (40 <= i && i < 50) {
+ expect_fail(ret, "stating file");
+ } else {
+ expect_success(ret, "stating file");
+
+ expect_success(S_ISREG(st.st_mode) ? 0 : -EINVAL,
+ "stating file");
+ }
+
+ dir = opendir(dname);
+ if (!expect_ptrok(dir, "opening parent directory"))
+ continue;
+
+ j = 0;
+
+ while ((d = readdir(dir))) {
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ j++;
+ }
+
+ expect_success(j == get_file_count(i) ? 0 : -EINVAL,
+ "unexpected file count iterating directory");
+
+ closedir(dir);
+ }
+
+ oldpwd = pushd(dname);
+ if (!expect_ptrok(oldpwd, "pushd()"))
+ goto out;;
+
+ dir = opendir(".");
+ if (!expect_ptrok(dir, "opening parent directory"))
+ goto out;
+
+ i = 1;
+ j = 0;
+
+ while ((d = readdir(dir))) {
+ size_t size = 0;
+ char *buf;
+
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ buf = read_file(d->d_name, &size);
+ if (size) {
+ for (j = 0; j < size; j++) {
+ expect_success(buf[j] == size ? 0 : -EINVAL,
+ "unexpected file content");
+ }
+ }
+
+ scnprintf(fname, sizeof(fname), "file-%02zu", size);
+
+ expect_success(strcmp(d->d_name, fname) == 0 ? 0 : -EINVAL,
+ "unexpected file content");
+
+ free(buf);
+
+ /*
+ * For select files, we test unreaddir once and check if
+ * we read back same element on repeated readdir
+ */
+ for (j = 0; j < ARRAY_SIZE(files); j++) {
+ if (size == files[j]) {
+ ret = unreaddir(dir, d);
+ expect_success(ret, "unreaddir");
+ files[j] = -1;
+ break;
+ }
+ }
+ if (j == ARRAY_SIZE(files))
+ i++;
+ }
+
+ expect_success(i == 90 ? 0 : -EINVAL,
+ "unexpected file count iterating directory: %u", i);
+
+ ret = make_directory("test/a/b/c/d/e/f/g/h/i/j/k/l");
+ expect_success(ret, "make_directory()");
+
+ if (!ret) {
+ const char hello[] = "hello";
+ char *buf;
+
+ ret = write_file("test/a/b/c/d/e/f/g/h/i/j/k/l/FILE",
+ ARRAY_AND_SIZE(hello));
+ expect_success(ret, "write_file()");
+
+ buf = read_file("test/a/b/c/d/e/f/g/h/i/j/k/l/FILE", NULL);
+ if (expect_ptrok(buf, "read_file()")) {
+ expect_success(memcmp(buf, ARRAY_AND_SIZE(hello)),
+ "read_file() content");
+ }
+ }
+
+out:
+ popd(oldpwd);
+ free(content);
+
+ closedir(dir);
+
+ ret = unlink_recursive(dname, NULL);
+ expect_success(ret, "unlinking directory");
+
+ dir = opendir(dname);
+ expect_fail(dir ? 0 : -EISDIR, "opening removed directory");
+}
+bselftest(core, test_ramfs);
diff --git a/test/self/regulator.c b/test/self/regulator.c
new file mode 100644
index 0000000000..bcbcbe33e1
--- /dev/null
+++ b/test/self/regulator.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <driver.h>
+#include <of.h>
+#include <regulator.h>
+#include <linux/regulator/of_regulator.h>
+
+struct test_regulator {
+ struct device *dev;
+};
+
+struct test_regulator_cfg {
+ struct regulator_desc desc;
+ struct regulator_dev rdev;
+};
+
+BSELFTEST_GLOBALS();
+
+static bool __ok(bool cond, const char *func, int line)
+{
+ total_tests++;
+ if (!cond) {
+ failed_tests++;
+ printf("%s:%d: assertion failure\n", func, line);
+ return false;
+ }
+
+ return true;
+}
+
+#define ok(cond) \
+ __ok(cond, __func__, __LINE__)
+
+static void test_regulator_selfref_always_on(struct device *dev)
+{
+ ok(1 == 1);
+}
+
+static int test_regulator_enable_noop(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev->dev, "enabling %s-supply\n", rdev->desc->supply_name);
+ failed_tests--;
+ return 0;
+}
+
+static int test_regulator_disable_noop(struct regulator_dev *rdev)
+{
+ dev_dbg(rdev->dev, "disabling %s-supply\n", rdev->desc->supply_name);
+ failed_tests--;
+ return 0;
+}
+
+static const struct regulator_ops test_regulator_ops_range = {
+ .enable = test_regulator_enable_noop,
+ .disable = test_regulator_disable_noop,
+};
+
+enum {
+ /*
+ * Ordering LDO1 before TEST_BUCK currently fails. This needs to be fixed
+ */
+ TEST_BUCK,
+ TEST_LDO1,
+ TEST_LDO2,
+ TEST_REGULATORS_NUM
+};
+
+static struct test_regulator_cfg test_pmic_reg[] = {
+ [TEST_BUCK] = {{
+ .supply_name = "buck",
+ .ops = &test_regulator_ops_range,
+ }},
+ [TEST_LDO1] = {{
+ .supply_name = "ldo1",
+ .ops = &test_regulator_ops_range,
+ }},
+ [TEST_LDO2] = {{
+ .supply_name = "ldo2",
+ .ops = &test_regulator_ops_range,
+ }},
+};
+
+static struct of_regulator_match test_reg_matches[] = {
+ [TEST_BUCK] = { .name = "BUCK", .desc = &test_pmic_reg[TEST_BUCK].desc },
+ [TEST_LDO1] = { .name = "LDO1", .desc = &test_pmic_reg[TEST_LDO1].desc },
+ [TEST_LDO2] = { .name = "LDO2", .desc = &test_pmic_reg[TEST_LDO2].desc },
+};
+
+static int test_regulator_register(struct test_regulator *priv, int id,
+ struct of_regulator_match *match,
+ struct test_regulator_cfg *cfg)
+{
+ struct device *dev = priv->dev;
+ int ret;
+
+ if (!match->of_node) {
+ dev_warn(dev, "Skip missing DTB regulator %s\n", match->name);
+ return 0;
+ }
+
+ cfg->rdev.desc = &cfg->desc;
+ cfg->rdev.dev = dev;
+
+ dev_dbg(dev, "registering %s\n", match->name);
+
+ ret = of_regulator_register(&cfg->rdev, match->of_node);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to register %s regulator\n",
+ match->name);
+
+ return 0;
+}
+
+static int regulator_probe(struct device *dev)
+{
+ size_t nregulators = ARRAY_SIZE(test_pmic_reg);
+ struct device_node *np = dev->of_node;
+ struct test_regulator *priv;
+ int ret, i;
+
+ priv = xzalloc(sizeof(*priv));
+ priv->dev = dev;
+
+ total_tests += 2;
+ failed_tests += 2;
+
+ np = of_get_child_by_name(np, "regulators");
+ if (!np)
+ return -ENOENT;
+
+ ret = of_regulator_match(dev, np, test_reg_matches, nregulators);
+ if (ret < 0)
+ return ret;
+
+ ok(ret == TEST_REGULATORS_NUM);
+
+ for (i = 0; i < nregulators; i++) {
+ ret = test_regulator_register(priv, i, &test_reg_matches[i],
+ &test_pmic_reg[i]);
+ ok(ret == 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id test_regulator_of_match[] = {
+ { .compatible = "barebox,regulator-self-test" },
+ { /* sentintel */ },
+};
+MODULE_DEVICE_TABLE(of, test_regulator_of_match);
+
+static struct driver regulator_test_driver = {
+ .name = "regulator-test",
+ .probe = regulator_probe,
+ .of_match_table = test_regulator_of_match,
+};
+
+static struct device *dev;
+
+static void test_regulator(void)
+{
+ extern char __dtbo_test_regulator_start[];
+ struct device_node *overlay;
+ int ret;
+
+ if (!dev) {
+ ret = platform_driver_register(&regulator_test_driver);
+ if (ret)
+ return;
+
+ overlay = of_unflatten_dtb(__dtbo_test_regulator_start, INT_MAX);
+ if (WARN_ON(IS_ERR(overlay)))
+ return;
+ of_overlay_apply_tree(of_get_root_node(), overlay);
+ of_probe();
+
+ dev = of_find_device_by_node_path("/regulator-self-test-pmic");
+
+ ok(dev->driver != NULL);
+ }
+
+ test_regulator_selfref_always_on(dev);
+}
+bselftest(core, test_regulator);
diff --git a/test/self/setjmp.c b/test/self/setjmp.c
new file mode 100644
index 0000000000..1cbb44bdf9
--- /dev/null
+++ b/test/self/setjmp.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: Copyright (C) 2003 Hewlett-Packard
+/*
+ * Code contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+ * to libunwind - a platform-independent unwind library
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <string.h>
+#include <asm/setjmp.h>
+
+BSELFTEST_GLOBALS();
+
+static __noreturn void raise_longjmp(jmp_buf jbuf, int i, int n)
+{
+ while (i < n)
+ raise_longjmp(jbuf, i + 1, n);
+
+ longjmp(jbuf, n);
+}
+
+static jmp_buf jbuf;
+
+static void __noreturn initjmp_entry(void)
+{
+ volatile u32 arr[256];
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(arr); i++)
+ writel(i, &arr[i]);
+
+ /* ensure arr[] is allocated on stack */
+ OPTIMIZER_HIDE_VAR(i);
+ if (i == 0)
+ initjmp_entry();
+
+ longjmp(jbuf, 0x1337);
+}
+
+static void test_setjmp(void)
+{
+ void *stack;
+ jmp_buf ijbuf;
+ volatile int i;
+ int ret;
+
+ total_tests += 20;
+
+ for (i = 0; i < 10; ++i) {
+ if ((ret = setjmp(jbuf))) {
+ pr_debug("%s: secondary setjmp() return, ret=%d\n",
+ __func__, ret);
+
+ if (ret != i + 1) {
+ printf("%s: setjmp() returned %d, expected %d\n",
+ __func__, ret, i + 1);
+ failed_tests += 2;
+ }
+ continue;
+ }
+
+ pr_debug("%s.%d: done with setjmp(); calling children\n",
+ __func__, i + 1);
+
+ raise_longjmp(jbuf, 0, i + 1);
+
+ printf("%s: raise_longjmp() returned unexpectedly\n", __func__);
+ failed_tests++;
+ }
+
+ stack = memalign(16, CONFIG_STACK_SIZE);
+ if (WARN_ON(!stack)) {
+ skipped_tests++;
+ return;
+ }
+
+ pr_debug("%s: testing initjmp\n", __func__);
+
+ total_tests += 1;
+
+ /* initialize new context with fresh stack */
+ initjmp(ijbuf, initjmp_entry, stack + CONFIG_STACK_SIZE);
+
+ switch (setjmp(jbuf)) {
+ case 0x1337:
+ break;
+ case 0:
+ longjmp(ijbuf, 0x42);
+ default:
+ failed_tests++;
+ }
+
+ free(stack);
+}
+bselftest(core, test_setjmp);
diff --git a/test/self/string.c b/test/self/string.c
new file mode 100644
index 0000000000..d33e2d7918
--- /dev/null
+++ b/test/self/string.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <common.h>
+#include <bselftest.h>
+#include <string.h>
+
+BSELFTEST_GLOBALS();
+
+static const char *strverscmp_expect_str(int expect)
+{
+ switch (expect) {
+ case -1: return "<";
+ case 0: return "==";
+ case 1: return ">";
+ default: return "?!";
+ }
+}
+
+static int strverscmp_assert_one(const char *lhs, const char *rhs, int expect)
+{
+ int actual;
+
+ total_tests++;
+
+ actual = strverscmp(lhs, rhs);
+ if (actual != expect) {
+ failed_tests++;
+ printf("(%s %s %s), but (%s %s %s) expected\n",
+ lhs, strverscmp_expect_str(actual), rhs,
+ lhs, strverscmp_expect_str(expect), rhs);
+ }
+
+ return actual;
+}
+
+static int __strverscmp_assert(char *expr)
+{
+ const char *token, *tokens[3];
+ int expect = -42;
+ int i = 0;
+
+ while ((token = strsep_unescaped(&expr, " "))) {
+ if (i == 3) {
+ pr_err("invalid expression\n");
+ return -EILSEQ;
+ }
+
+ tokens[i++] = token;
+ }
+
+ if (!strcmp(tokens[1], "<"))
+ expect = -1;
+ else if (!strcmp(tokens[1], "=="))
+ expect = 0;
+ else if (!strcmp(tokens[1], ">"))
+ expect = 1;
+
+ return strverscmp_assert_one(tokens[0], tokens[2], expect);
+}
+
+#define strverscmp_assert(expr) ({ \
+ char __expr_mut[] = expr; \
+ __strverscmp_assert(__expr_mut); \
+})
+
+static void test_strverscmp_spec_examples(void)
+{
+ /*
+ * Taken from specification at
+ * https://uapi-group.org/specifications/specs/version_format_specification/#examples
+ */
+ strverscmp_assert("11 == 11");
+ strverscmp_assert("systemd-123 == systemd-123");
+ strverscmp_assert("bar-123 < foo-123");
+ strverscmp_assert("123a > 123");
+ strverscmp_assert("123.a > 123");
+ strverscmp_assert("123.a < 123.b");
+ strverscmp_assert("123a > 123.a");
+ strverscmp_assert("11α == 11β");
+ strverscmp_assert("A < a");
+ strverscmp_assert_one("", "0", -1);
+ strverscmp_assert("0. > 0");
+ strverscmp_assert("0.0 > 0");
+ strverscmp_assert("0 > ~");
+ strverscmp_assert_one("", "~", 1);
+ strverscmp_assert("1_ == 1");
+ strverscmp_assert("_1 == 1");
+ strverscmp_assert("1_ < 1.2");
+ strverscmp_assert("1_2_3 > 1.3.3");
+ strverscmp_assert("1+ == 1");
+ strverscmp_assert("+1 == 1");
+ strverscmp_assert("1+ < 1.2");
+ strverscmp_assert("1+2+3 > 1.3.3");
+}
+
+static void test_strverscmp_one(const char *newer, const char *older)
+{
+ strverscmp_assert_one(newer, newer, 0);
+ strverscmp_assert_one(newer, older, 1);
+ strverscmp_assert_one(older, newer, -1);
+ strverscmp_assert_one(older, older, 0);
+}
+
+static void test_strverscmp_spec_systemd(void)
+{
+ /*
+ * Taken from systemd tests at
+ * 87b7d9b6ff23ec10b66bf53efeabf16ad85d7ad8
+ */
+ static const char * const versions[] = {
+ "~1", "", "ab", "abb", "abc", "0001", "002", "12", "122", "122.9",
+ "123~rc1", "123", "123-a", "123-a.1", "123-a1", "123-a1.1", "123-3",
+ "123-3.1", "123^patch1", "123^1", "123.a-1" "123.1-1", "123a-1", "124",
+ NULL,
+ };
+ const char * const *p, * const *q;
+
+ for (p = versions; *p; p++)
+ for (q = p + 1; *q; q++)
+ test_strverscmp_one(*q, *p);
+
+ test_strverscmp_one("123.45-67.89", "123.45-67.88");
+ test_strverscmp_one("123.45-67.89a", "123.45-67.89");
+ test_strverscmp_one("123.45-67.89", "123.45-67.ab");
+ test_strverscmp_one("123.45-67.89", "123.45-67.9");
+ test_strverscmp_one("123.45-67.89", "123.45-67");
+ test_strverscmp_one("123.45-67.89", "123.45-66.89");
+ test_strverscmp_one("123.45-67.89", "123.45-9.99");
+ test_strverscmp_one("123.45-67.89", "123.42-99.99");
+ test_strverscmp_one("123.45-67.89", "123-99.99");
+
+ /* '~' : pre-releases */
+ test_strverscmp_one("123.45-67.89", "123~rc1-99.99");
+ test_strverscmp_one("123-45.67.89", "123~rc1-99.99");
+ test_strverscmp_one("123~rc2-67.89", "123~rc1-99.99");
+ test_strverscmp_one("123^aa2-67.89", "123~rc1-99.99");
+ test_strverscmp_one("123aa2-67.89", "123~rc1-99.99");
+
+ /* '-' : separator between version and release. */
+ test_strverscmp_one("123.45-67.89", "123-99.99");
+ test_strverscmp_one("123^aa2-67.89", "123-99.99");
+ test_strverscmp_one("123aa2-67.89", "123-99.99");
+
+ /* '^' : patch releases */
+ test_strverscmp_one("123.45-67.89", "123^45-67.89");
+ test_strverscmp_one("123^aa2-67.89", "123^aa1-99.99");
+ test_strverscmp_one("123aa2-67.89", "123^aa2-67.89");
+
+ /* '.' : point release */
+ test_strverscmp_one("123aa2-67.89", "123.aa2-67.89");
+ test_strverscmp_one("123.ab2-67.89", "123.aa2-67.89");
+
+ /* invalid characters */
+ strverscmp_assert_one("123_aa2-67.89", "123aa+2-67.89", 0);
+}
+
+static void test_strverscmp(void)
+{
+ test_strverscmp_spec_examples();
+ test_strverscmp_spec_systemd();
+
+ /* and now some corner cases */
+ strverscmp_assert_one(NULL, NULL, 0);
+ strverscmp_assert_one(NULL, "", 0);
+ strverscmp_assert_one("", NULL, 0);
+ strverscmp_assert_one("", "", 0);
+}
+
+static void __expect_streq(const char *func, int line,
+ char *is, const char *expect, bool free_is)
+{
+ total_tests++;
+ if (strcmp(is, expect)) {
+ failed_tests++;
+ printf("%s:%d: got %s, but %s expected\n", func, line, is, expect);
+ }
+
+ if (free_is)
+ free(is);
+}
+
+#define expect_dynstreq(args...) \
+ __expect_streq(__func__, __LINE__, args, true)
+
+static void test_strjoin(void)
+{
+ char *strs[] = { "ayy", "bee", "cee" };
+
+ expect_dynstreq(strjoin("", strs, ARRAY_SIZE(strs)), "ayybeecee");
+ expect_dynstreq(strjoin(" ", strs, ARRAY_SIZE(strs)), "ayy bee cee");
+ expect_dynstreq(strjoin(", ", strs, ARRAY_SIZE(strs)), "ayy, bee, cee");
+ expect_dynstreq(strjoin(" ", strs, 1), "ayy");
+ expect_dynstreq(strjoin(" ", NULL, 0), "");
+}
+
+static void test_string(void)
+{
+ test_strverscmp();
+ test_strjoin();
+}
+bselftest(parser, test_string);
diff --git a/test/self/test_command.c b/test/self/test_command.c
new file mode 100644
index 0000000000..358855d0f6
--- /dev/null
+++ b/test/self/test_command.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <bselftest.h>
+#include <command.h>
+
+BSELFTEST_GLOBALS();
+
+#if __LONG_MAX__ == 0x7fffffff
+#define LONG_MIN_STR "-2147483648"
+#define LONG_MIN_PLUS_1_STR "-2147483647"
+#define LONG_MAX_STR "2147483647"
+#define LONG_MAX_PLUS_1_STR "2147483648"
+#else
+#define LONG_MIN_STR "-9223372036854775808"
+#define LONG_MIN_PLUS_1_STR "-9223372036854775807"
+#define LONG_MAX_STR "9223372036854775807"
+#define LONG_MAX_PLUS_1_STR "9223372036854775808"
+#endif
+
+static void __assert_eq(const char *expr, bool result, const char *func, int line)
+{
+ int ret;
+
+ total_tests++;
+
+ ret = run_command(expr);
+ if ((result && ret != 0) || (!result && ret != 1)) {
+ failed_tests++;
+ printf("%s:%d: %s: assertion failure, ret=%d\n", func, line, expr, ret);
+ }
+}
+
+#define assert_eq(expr, result) __assert_eq(expr, result, __func__, __LINE__)
+
+static void test_test_command(void)
+{
+ assert_eq("[ 0 -eq 0 ]", true);
+ assert_eq("[ 0 -eq 1 ]", false);
+ assert_eq("[ -1 -le 1 ]", true);
+ assert_eq("[ -1 -ge -5 ]", true);
+
+ assert_eq("[ " LONG_MIN_STR " -lt " LONG_MAX_STR " ]", true);
+ assert_eq("[ " LONG_MIN_STR " -gt " LONG_MAX_STR " ]", false);
+
+ assert_eq("[ " LONG_MIN_STR " -eq " LONG_MIN_STR " ]", true);
+ assert_eq("[ " LONG_MAX_STR " -eq " LONG_MAX_STR " ]", true);
+ assert_eq("[ " LONG_MIN_STR " -ne " LONG_MIN_STR " ]", false);
+ assert_eq("[ " LONG_MAX_STR " -ne " LONG_MAX_STR " ]", false);
+
+ assert_eq("[ " LONG_MIN_PLUS_1_STR " -eq " LONG_MIN_PLUS_1_STR " ]", true);
+ assert_eq("[ " LONG_MAX_PLUS_1_STR " -eq " LONG_MAX_PLUS_1_STR " ]", true);
+ assert_eq("[ " LONG_MIN_PLUS_1_STR " -ne " LONG_MIN_PLUS_1_STR " ]", false);
+ assert_eq("[ " LONG_MAX_PLUS_1_STR " -ne " LONG_MAX_PLUS_1_STR " ]", false);
+
+ assert_eq("[ " LONG_MIN_PLUS_1_STR " -gt " LONG_MIN_STR " ]", true);
+ assert_eq("[ " LONG_MAX_PLUS_1_STR " -gt " LONG_MAX_STR " ]", true);
+ assert_eq("[ " LONG_MIN_PLUS_1_STR " -lt " LONG_MIN_STR " ]", false);
+ assert_eq("[ " LONG_MAX_PLUS_1_STR " -lt " LONG_MAX_STR " ]", false);
+ assert_eq("[ " LONG_MIN_STR " -le " LONG_MIN_PLUS_1_STR " ]", true);
+ assert_eq("[ " LONG_MAX_STR " -le " LONG_MAX_PLUS_1_STR " ]", true);
+}
+bselftest(core, test_test_command);
diff --git a/test/self/test_regulator.dtso b/test/self/test_regulator.dtso
new file mode 100644
index 0000000000..65d2b13098
--- /dev/null
+++ b/test/self/test_regulator.dtso
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ regulator_test_fixed: regulator-self-test-fixed {
+ compatible = "regulator-fixed";
+ regulator-name = "test_fixed";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ regulator-self-test-pmic {
+ compatible = "barebox,regulator-self-test";
+
+ buck-supply = <&regulator_test_fixed>;
+ ldo1-supply = <&test_pmic_buck>;
+ ldo2-supply = <&test_pmic_buck>;
+
+ regulators {
+ test_pmic_buck: BUCK {
+ regulator-name = "buck";
+ regulator-min-microvolt = <330000>;
+ regulator-max-microvolt = <330000>;
+ };
+
+ test_pmic_ldo1: LDO1 {
+ regulator-name = "ldo1";
+ regulator-always-on;
+ regulator-boot-on;
+ regulator-min-microvolt = <180000>;
+ regulator-max-microvolt = <180000>;
+ };
+
+ test_pmic_ldo2: LDO2 {
+ regulator-name = "ldo2";
+ regulator-min-microvolt = <180000>;
+ regulator-max-microvolt = <180000>;
+ };
+ };
+ };
+};