diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/test | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/test')
265 files changed, 41616 insertions, 0 deletions
diff --git a/roms/u-boot/test/Kconfig b/roms/u-boot/test/Kconfig new file mode 100644 index 000000000..ab3ac54a1 --- /dev/null +++ b/roms/u-boot/test/Kconfig @@ -0,0 +1,91 @@ +menuconfig UNIT_TEST + bool "Unit tests" + help + Select this to compile in unit tests for various parts of + U-Boot. Test suites will be subcommands of the "ut" command. + This does not require sandbox to be included, but it is most + often used there. + +config SPL_UNIT_TEST + bool "Unit tests in SPL" + # We need to be able to unbind devices for tests to work + select SPL_DM_DEVICE_REMOVE + help + Select this to enable unit tests in SPL. Most test are designed for + running in U-Boot proper, but some are intended for SPL, such as + of-platdata and SPL handover. To run these tests with the sandbox_spl + board, use the -u (unit test) option. + +config UT_LIB + bool "Unit tests for library functions" + depends on UNIT_TEST + default y + help + Enables the 'ut lib' command which tests library functions like + memcat(), memcyp(), memmove() and ASN1 compiler/decoder. + +if UT_LIB + +config UT_LIB_ASN1 + bool "Unit test for asn1 compiler and decoder function" + default y + imply ASYMMETRIC_KEY_TYPE + imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE + imply X509_CERTIFICATE_PARSER + imply PKCS7_MESSAGE_PARSER + imply RSA_PUBLIC_KEY_PARSER + help + Enables a test which exercises asn1 compiler and decoder function + via various parsers. + +config UT_LIB_RSA + bool "Unit test for rsa_verify() function" + depends on RSA + depends on RSA_VERIFY_WITH_PKEY + select IMAGE_SIGN_INFO + default y + help + Enables rsa_verify() test, currently rsa_verify_with_pkey only() + only, at the 'ut lib' command. + +endif + +config UT_COMPRESSION + bool "Unit test for compression" + depends on UNIT_TEST + depends on CMDLINE && GZIP_COMPRESSED && BZIP2 && LZMA && LZO && LZ4 + default y + help + Enables tests for compression and decompression routines for simple + sanity and for buffer overflow conditions. + +config UT_LOG + bool "Unit tests for logging functions" + depends on UNIT_TEST + default y + help + Enables the 'ut log' command which tests logging functions like + log_err(). + See also CONFIG_LOG_TEST which provides the 'log test' command. + +config UT_TIME + bool "Unit tests for time functions" + depends on UNIT_TEST + help + Enables the 'ut time' command which tests that the time functions + work correctly. The test is fairly simple and will not catch all + problems. But if you are having problems with udelay() and the like, + this is a good place to start. + +config UT_UNICODE + bool "Unit tests for Unicode functions" + depends on UNIT_TEST + default y + help + Enables the 'ut unicode' command which tests that the functions for + manipulating Unicode strings work correctly. + +source "test/dm/Kconfig" +source "test/env/Kconfig" +source "test/optee/Kconfig" +source "test/overlay/Kconfig" diff --git a/roms/u-boot/test/Makefile b/roms/u-boot/test/Makefile new file mode 100644 index 000000000..a26e915e0 --- /dev/null +++ b/roms/u-boot/test/Makefile @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2012 The Chromium Authors + +obj-y += test-main.o +obj-$(CONFIG_SANDBOX) += image/ + +ifneq ($(CONFIG_$(SPL_)BLOBLIST),) +obj-$(CONFIG_$(SPL_)CMDLINE) += bloblist.o +obj-$(CONFIG_$(SPL_)CMDLINE) += bootm.o +endif +obj-$(CONFIG_$(SPL_)CMDLINE) += cmd/ +obj-$(CONFIG_$(SPL_)CMDLINE) += cmd_ut.o +obj-$(CONFIG_$(SPL_)CMDLINE) += command_ut.o +obj-$(CONFIG_$(SPL_)UT_COMPRESSION) += compression.o +obj-y += dm/ +obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o +obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o +obj-$(CONFIG_UT_TIME) += time_ut.o +obj-y += ut.o + +ifeq ($(CONFIG_SPL_BUILD),) +obj-$(CONFIG_UNIT_TEST) += lib/ +obj-y += log/ +obj-$(CONFIG_$(SPL_)UT_UNICODE) += unicode_ut.o +endif diff --git a/roms/u-boot/test/bloblist.c b/roms/u-boot/test/bloblist.c new file mode 100644 index 000000000..d876b6391 --- /dev/null +++ b/roms/u-boot/test/bloblist.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2018, Google Inc. All rights reserved. + */ + +#include <common.h> +#include <bloblist.h> +#include <log.h> +#include <mapmem.h> +#include <asm/global_data.h> +#include <test/suites.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Declare a new compression test */ +#define BLOBLIST_TEST(_name, _flags) \ + UNIT_TEST(_name, _flags, bloblist_test) + +enum { + TEST_TAG = 1, + TEST_TAG2 = 2, + TEST_TAG_MISSING = 3, + + TEST_SIZE = 10, + TEST_SIZE2 = 20, + TEST_SIZE_LARGE = 0x3e0, + + TEST_ADDR = CONFIG_BLOBLIST_ADDR, + TEST_BLOBLIST_SIZE = 0x400, + + ERASE_BYTE = '\xff', +}; + +static struct bloblist_hdr *clear_bloblist(void) +{ + struct bloblist_hdr *hdr; + + /* + * Clear out any existing bloblist so we have a clean slate. Zero the + * header so that existing records are removed, but set everything else + * to 0xff for testing purposes. + */ + hdr = map_sysmem(CONFIG_BLOBLIST_ADDR, TEST_BLOBLIST_SIZE); + memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE); + memset(hdr, '\0', sizeof(*hdr)); + + return hdr; +} + +static int check_zero(void *data, int size) +{ + u8 *ptr; + int i; + + for (ptr = data, i = 0; i < size; i++, ptr++) { + if (*ptr) + return -EINVAL; + } + + return 0; +} + +static int bloblist_test_init(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + + hdr = clear_bloblist(); + ut_asserteq(-ENOENT, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + hdr->version++; + ut_asserteq(-EPROTONOSUPPORT, bloblist_check(TEST_ADDR, + TEST_BLOBLIST_SIZE)); + + ut_asserteq(-ENOSPC, bloblist_new(TEST_ADDR, 0x10, 0)); + ut_asserteq(-EFAULT, bloblist_new(1, TEST_BLOBLIST_SIZE, 0)); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + ut_assertok(bloblist_finish()); + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + hdr->flags++; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + + return 1; +} +BLOBLIST_TEST(bloblist_test_init, 0); + +static int bloblist_test_blob(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + struct bloblist_rec *rec, *rec2; + char *data; + + /* At the start there should be no records */ + hdr = clear_bloblist(); + ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE)); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + ut_asserteq(map_to_sysmem(hdr), TEST_ADDR); + + /* Add a record and check that we can find it */ + data = bloblist_add(TEST_TAG, TEST_SIZE, 0); + rec = (void *)(hdr + 1); + ut_asserteq_addr(rec + 1, data); + data = bloblist_find(TEST_TAG, TEST_SIZE); + ut_asserteq_addr(rec + 1, data); + + /* Check the data is zeroed */ + ut_assertok(check_zero(data, TEST_SIZE)); + + /* Check the 'ensure' method */ + ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE)); + ut_assertnull(bloblist_ensure(TEST_TAG, TEST_SIZE2)); + rec2 = (struct bloblist_rec *)(data + ALIGN(TEST_SIZE, BLOBLIST_ALIGN)); + ut_assertok(check_zero(data, TEST_SIZE)); + + /* Check for a non-existent record */ + ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE)); + ut_asserteq_addr(rec2 + 1, bloblist_ensure(TEST_TAG2, TEST_SIZE2)); + ut_assertnull(bloblist_find(TEST_TAG_MISSING, 0)); + + return 0; +} +BLOBLIST_TEST(bloblist_test_blob, 0); + +/* Check bloblist_ensure_size_ret() */ +static int bloblist_test_blob_ensure(struct unit_test_state *uts) +{ + void *data, *data2; + int size; + + /* At the start there should be no records */ + clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + + /* Test with an empty bloblist */ + size = TEST_SIZE; + ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data)); + ut_asserteq(TEST_SIZE, size); + ut_assertok(check_zero(data, TEST_SIZE)); + + /* Check that we get the same thing again */ + ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data2)); + ut_asserteq(TEST_SIZE, size); + ut_asserteq_addr(data, data2); + + /* Check that the size remains the same */ + size = TEST_SIZE2; + ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data)); + ut_asserteq(TEST_SIZE, size); + + /* Check running out of space */ + size = TEST_SIZE_LARGE; + ut_asserteq(-ENOSPC, bloblist_ensure_size_ret(TEST_TAG2, &size, &data)); + + return 0; +} +BLOBLIST_TEST(bloblist_test_blob_ensure, 0); + +static int bloblist_test_bad_blob(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + void *data; + + hdr = clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + data = hdr + 1; + data += sizeof(struct bloblist_rec); + ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE)); + ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE)); + + return 0; +} +BLOBLIST_TEST(bloblist_test_bad_blob, 0); + +static int bloblist_test_checksum(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + char *data, *data2; + + hdr = clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + ut_assertok(bloblist_finish()); + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + + /* + * Now change things amd make sure that the checksum notices. We cannot + * change the size or alloced fields, since that will crash the code. + * It has to rely on these being correct. + */ + hdr->flags--; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + hdr->flags++; + + hdr->size--; + ut_asserteq(-EFBIG, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + hdr->size++; + + hdr->spare++; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + hdr->spare--; + + hdr->chksum++; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + hdr->chksum--; + + /* Make sure the checksum changes when we add blobs */ + data = bloblist_add(TEST_TAG, TEST_SIZE, 0); + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + + data2 = bloblist_add(TEST_TAG2, TEST_SIZE2, 0); + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + ut_assertok(bloblist_finish()); + + /* It should also change if we change the data */ + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + *data += 1; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + *data -= 1; + + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + *data2 += 1; + ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + *data2 -= 1; + + /* + * Changing data outside the range of valid data should not affect + * the checksum. + */ + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + data[TEST_SIZE]++; + data2[TEST_SIZE2]++; + ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE)); + + return 0; +} +BLOBLIST_TEST(bloblist_test_checksum, 0); + +/* Test the 'bloblist info' command */ +static int bloblist_test_cmd_info(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + char *data, *data2; + + hdr = clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + data = bloblist_ensure(TEST_TAG, TEST_SIZE); + data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2); + + console_record_reset_enable(); + ut_silence_console(uts); + console_record_reset(); + run_command("bloblist info", 0); + ut_assert_nextline("base: %lx", (ulong)map_to_sysmem(hdr)); + ut_assert_nextline("size: 400 1 KiB"); + ut_assert_nextline("alloced: 70 112 Bytes"); + ut_assert_nextline("free: 390 912 Bytes"); + ut_assert_console_end(); + ut_unsilence_console(uts); + + return 0; +} +BLOBLIST_TEST(bloblist_test_cmd_info, 0); + +/* Test the 'bloblist list' command */ +static int bloblist_test_cmd_list(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + char *data, *data2; + + hdr = clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + data = bloblist_ensure(TEST_TAG, TEST_SIZE); + data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2); + + console_record_reset_enable(); + ut_silence_console(uts); + console_record_reset(); + run_command("bloblist list", 0); + ut_assert_nextline("Address Size Tag Name"); + ut_assert_nextline("%08lx %8x 1 EC host event", + (ulong)map_to_sysmem(data), TEST_SIZE); + ut_assert_nextline("%08lx %8x 2 SPL hand-off", + (ulong)map_to_sysmem(data2), TEST_SIZE2); + ut_assert_console_end(); + ut_unsilence_console(uts); + + return 0; +} +BLOBLIST_TEST(bloblist_test_cmd_list, 0); + +/* Test alignment of bloblist blobs */ +static int bloblist_test_align(struct unit_test_state *uts) +{ + struct bloblist_hdr *hdr; + ulong addr; + char *data; + int i; + + /* At the start there should be no records */ + hdr = clear_bloblist(); + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE)); + + /* Check the default alignment */ + for (i = 0; i < 3; i++) { + int size = i * 3; + ulong addr; + char *data; + int j; + + data = bloblist_add(i, size, 0); + ut_assertnonnull(data); + addr = map_to_sysmem(data); + ut_asserteq(0, addr & (BLOBLIST_ALIGN - 1)); + + /* Only the bytes in the blob data should be zeroed */ + for (j = 0; j < size; j++) + ut_asserteq(0, data[j]); + for (; j < BLOBLIST_ALIGN; j++) + ut_asserteq(ERASE_BYTE, data[j]); + } + + /* Check larger alignment */ + for (i = 0; i < 3; i++) { + int align = 32 << i; + + data = bloblist_add(3 + i, i * 4, align); + ut_assertnonnull(data); + addr = map_to_sysmem(data); + ut_asserteq(0, addr & (align - 1)); + } + + /* Check alignment with an bloblist starting on a smaller alignment */ + hdr = map_sysmem(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE); + memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE); + memset(hdr, '\0', sizeof(*hdr)); + ut_assertok(bloblist_new(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE, + 0)); + + data = bloblist_add(1, 5, BLOBLIST_ALIGN * 2); + ut_assertnonnull(data); + addr = map_to_sysmem(data); + ut_asserteq(0, addr & (BLOBLIST_ALIGN * 2 - 1)); + + return 0; +} +BLOBLIST_TEST(bloblist_test_align, 0); + +/* Test relocation of a bloblist */ +static int bloblist_test_reloc(struct unit_test_state *uts) +{ + const uint large_size = TEST_BLOBLIST_SIZE; + const uint small_size = 0x20; + void *old_ptr, *new_ptr; + void *blob1, *blob2; + ulong new_addr; + ulong new_size; + + ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0)); + old_ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE); + + /* Add one blob and then one that won't fit */ + blob1 = bloblist_add(TEST_TAG, small_size, 0); + ut_assertnonnull(blob1); + blob2 = bloblist_add(TEST_TAG2, large_size, 0); + ut_assertnull(blob2); + + /* Relocate the bloblist somewhere else, a bit larger */ + new_addr = TEST_ADDR + TEST_BLOBLIST_SIZE; + new_size = TEST_BLOBLIST_SIZE + 0x100; + new_ptr = map_sysmem(new_addr, TEST_BLOBLIST_SIZE); + bloblist_reloc(new_ptr, new_size, old_ptr, TEST_BLOBLIST_SIZE); + gd->bloblist = new_ptr; + + /* Check the old blob is there and that we can now add the bigger one */ + ut_assertnonnull(bloblist_find(TEST_TAG, small_size)); + ut_assertnull(bloblist_find(TEST_TAG2, small_size)); + blob2 = bloblist_add(TEST_TAG2, large_size, 0); + ut_assertnonnull(blob2); + + return 0; +} +BLOBLIST_TEST(bloblist_test_reloc, 0); + +int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(bloblist_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(bloblist_test); + + return cmd_ut_category("bloblist", "bloblist_test_", + tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/bootm.c b/roms/u-boot/test/bootm.c new file mode 100644 index 000000000..8528982ae --- /dev/null +++ b/roms/u-boot/test/bootm.c @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for bootm routines + * + * Copyright 2020 Google LLC + */ + +#include <common.h> +#include <bootm.h> +#include <asm/global_data.h> +#include <test/suites.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BOOTM_TEST(_name, _flags) UNIT_TEST(_name, _flags, bootm_test) + +enum { + BUF_SIZE = 1024, +}; + +#define CONSOLE_STR "console=/dev/ttyS0" + +/* Test cmdline processing where nothing happens */ +static int bootm_test_nop(struct unit_test_state *uts) +{ + char buf[BUF_SIZE]; + + *buf = '\0'; + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true)); + ut_asserteq_str("", buf); + + strcpy(buf, "test"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true)); + ut_asserteq_str("test", buf); + + return 0; +} +BOOTM_TEST(bootm_test_nop, 0); + +/* Test cmdline processing when out of space */ +static int bootm_test_nospace(struct unit_test_state *uts) +{ + char buf[BUF_SIZE]; + + /* Zero buffer size */ + *buf = '\0'; + ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 0, true)); + + /* Buffer string not terminated */ + memset(buf, 'a', BUF_SIZE); + ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, BUF_SIZE, true)); + + /* Not enough space to copy string */ + memset(buf, '\0', BUF_SIZE); + memset(buf, 'a', BUF_SIZE / 2); + ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, BUF_SIZE, true)); + + /* Just enough space */ + memset(buf, '\0', BUF_SIZE); + memset(buf, 'a', BUF_SIZE / 2 - 1); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true)); + + return 0; +} +BOOTM_TEST(bootm_test_nospace, 0); + +/* Test silent processing */ +static int bootm_test_silent(struct unit_test_state *uts) +{ + char buf[BUF_SIZE]; + + /* 'silent_linux' not set should do nothing */ + env_set("silent_linux", NULL); + strcpy(buf, CONSOLE_STR); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str(CONSOLE_STR, buf); + + ut_assertok(env_set("silent_linux", "no")); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str(CONSOLE_STR, buf); + + ut_assertok(env_set("silent_linux", "yes")); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str("console=", buf); + + /* Empty buffer should still add the string */ + *buf = '\0'; + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str("console=", buf); + + /* Check nothing happens when do_silent is false */ + *buf = '\0'; + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, 0)); + ut_asserteq_str("", buf); + + /* Not enough space */ + *buf = '\0'; + ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 8, BOOTM_CL_SILENT)); + + /* Just enough space */ + *buf = '\0'; + ut_assertok(bootm_process_cmdline(buf, 9, BOOTM_CL_SILENT)); + + /* add at end */ + strcpy(buf, "something"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str("something console=", buf); + + /* change at start */ + strcpy(buf, CONSOLE_STR " something"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT)); + ut_asserteq_str("console= something", buf); + + return 0; +} +BOOTM_TEST(bootm_test_silent, 0); + +/* Test substitution processing */ +static int bootm_test_subst(struct unit_test_state *uts) +{ + char buf[BUF_SIZE]; + + /* try with an unset variable */ + ut_assertok(env_set("var", NULL)); + strcpy(buf, "some${var}thing"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("something", buf); + + /* Replace with shorter string */ + ut_assertok(env_set("var", "bb")); + strcpy(buf, "some${var}thing"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("somebbthing", buf); + + /* Replace with same-length string */ + ut_assertok(env_set("var", "abc")); + strcpy(buf, "some${var}thing"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("someabcthing", buf); + + /* Replace with longer string */ + ut_assertok(env_set("var", "abcde")); + strcpy(buf, "some${var}thing"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("someabcdething", buf); + + /* Check it is case sensitive */ + ut_assertok(env_set("VAR", NULL)); + strcpy(buf, "some${VAR}thing"); + ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("something", buf); + + /* Check too long - need 12 bytes for each string */ + strcpy(buf, "some${var}thing"); + ut_asserteq(-ENOSPC, + bootm_process_cmdline(buf, 12 * 2 - 1, BOOTM_CL_SUBST)); + + /* Check just enough space */ + strcpy(buf, "some${var}thing"); + ut_assertok(bootm_process_cmdline(buf, 16 * 2, BOOTM_CL_SUBST)); + ut_asserteq_str("someabcdething", buf); + + /* + * Check the substition string being too long. This results in a string + * of 12 (13 bytes). We need enough space for that plus the original + * "a${var}c" string of 9 bytes. So 12 + 9 = 21 bytes. + */ + ut_assertok(env_set("var", "1234567890")); + strcpy(buf, "a${var}c"); + ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 21, BOOTM_CL_SUBST)); + + strcpy(buf, "a${var}c"); + ut_asserteq(0, bootm_process_cmdline(buf, 22, BOOTM_CL_SUBST)); + + /* Check multiple substitutions */ + ut_assertok(env_set("var", "abc")); + strcpy(buf, "some${var}thing${bvar}else"); + ut_asserteq(0, bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("someabcthingelse", buf); + + /* Check multiple substitutions */ + ut_assertok(env_set("bvar", "123")); + strcpy(buf, "some${var}thing${bvar}else"); + ut_asserteq(0, bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST)); + ut_asserteq_str("someabcthing123else", buf); + + return 0; +} +BOOTM_TEST(bootm_test_subst, 0); + +/* Test silent processing in the bootargs variable */ +static int bootm_test_silent_var(struct unit_test_state *uts) +{ + env_set("bootargs", NULL); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SUBST)); + ut_assertnull(env_get("bootargs")); + + ut_assertok(env_set("bootargs", "some${var}thing")); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SUBST)); + ut_asserteq_str("something", env_get("bootargs")); + + return 0; +} +BOOTM_TEST(bootm_test_silent_var, 0); + +/* Test substitution processing in the bootargs variable */ +static int bootm_test_subst_var(struct unit_test_state *uts) +{ + env_set("bootargs", NULL); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SILENT)); + ut_asserteq_str("console=", env_get("bootargs")); + + ut_assertok(env_set("var", "abc")); + ut_assertok(env_set("bootargs", "some${var}thing")); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SILENT)); + ut_asserteq_str("some${var}thing console=", env_get("bootargs")); + + return 0; +} +BOOTM_TEST(bootm_test_subst_var, 0); + +/* Test substitution and silent console processing in the bootargs variable */ +static int bootm_test_subst_both(struct unit_test_state *uts) +{ + ut_assertok(env_set("silent_linux", "yes")); + env_set("bootargs", NULL); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_ALL)); + ut_asserteq_str("console=", env_get("bootargs")); + + ut_assertok(env_set("bootargs", "some${var}thing " CONSOLE_STR)); + ut_assertok(env_set("var", "1234567890")); + ut_assertok(bootm_process_cmdline_env(BOOTM_CL_ALL)); + ut_asserteq_str("some1234567890thing console=", env_get("bootargs")); + + return 0; +} +BOOTM_TEST(bootm_test_subst_both, 0); + +int do_ut_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(bootm_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(bootm_test); + + return cmd_ut_category("bootm", "bootm_test_", tests, n_ents, + argc, argv); +} diff --git a/roms/u-boot/test/cmd/Makefile b/roms/u-boot/test/cmd/Makefile new file mode 100644 index 000000000..2cfe43a6b --- /dev/null +++ b/roms/u-boot/test/cmd/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2013 Google, Inc + +ifdef CONFIG_HUSH_PARSER +obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o +endif +obj-y += mem.o +obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o +obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o +obj-$(CONFIG_CMD_PWM) += pwm.o +obj-$(CONFIG_CMD_SETEXPR) += setexpr.o diff --git a/roms/u-boot/test/cmd/addrmap.c b/roms/u-boot/test/cmd/addrmap.c new file mode 100644 index 000000000..fb744485b --- /dev/null +++ b/roms/u-boot/test/cmd/addrmap.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for addrmap command + * + * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <console.h> +#include <test/suites.h> +#include <test/ut.h> + +/* Declare a new addrmap test */ +#define ADDRMAP_TEST(_name, _flags) UNIT_TEST(_name, _flags, addrmap_test) + +/* Test 'addrmap' command output */ +static int addrmap_test_basic(struct unit_test_state *uts) +{ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_command("addrmap", 0)); + ut_assert_nextline(" vaddr paddr size"); + ut_assert_nextline("================ ================ ================"); + /* There should be at least one entry */ + ut_assertok(!ut_check_console_end(uts)); + + return 0; +} +ADDRMAP_TEST(addrmap_test_basic, UT_TESTF_CONSOLE_REC); + +int do_ut_addrmap(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = ll_entry_start(struct unit_test, + addrmap_test); + const int n_ents = ll_entry_count(struct unit_test, addrmap_test); + + return cmd_ut_category("cmd_addrmap", "cmd_addrmap_", tests, n_ents, + argc, argv); +} diff --git a/roms/u-boot/test/cmd/mem.c b/roms/u-boot/test/cmd/mem.c new file mode 100644 index 000000000..d76f47cf3 --- /dev/null +++ b/roms/u-boot/test/cmd/mem.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Executes tests for memory-related commands + * + * Copyright 2020 Google LLC + */ + +#include <common.h> +#include <command.h> +#include <test/suites.h> +#include <test/test.h> + +int do_ut_mem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(mem_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(mem_test); + + return cmd_ut_category("cmd_mem", "mem_test_", tests, n_ents, argc, + argv); +} diff --git a/roms/u-boot/test/cmd/mem_search.c b/roms/u-boot/test/cmd/mem_search.c new file mode 100644 index 000000000..94942793a --- /dev/null +++ b/roms/u-boot/test/cmd/mem_search.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for memory commands + * + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <console.h> +#include <mapmem.h> +#include <dm/test.h> +#include <test/ut.h> + +#define BUF_SIZE 0x100 + +/* Declare a new mem test */ +#define MEM_TEST(_name, _flags) UNIT_TEST(_name, _flags, mem_test) + +/* Test 'ms' command with bytes */ +static int mem_test_ms_b(struct unit_test_state *uts) +{ + u8 *buf; + + buf = map_sysmem(0, BUF_SIZE + 1); + memset(buf, '\0', BUF_SIZE); + buf[0x0] = 0x12; + buf[0x31] = 0x12; + buf[0xff] = 0x12; + buf[0x100] = 0x12; + ut_assertok(console_record_reset_enable()); + run_command("ms.b 1 ff 12", 0); + ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................"); + ut_assert_nextline("--"); + ut_assert_nextline("000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 ................"); + ut_assert_nextline("2 matches"); + ut_assert_console_end(); + + ut_asserteq(2, env_get_hex("memmatches", 0)); + ut_asserteq(0xff, env_get_hex("memaddr", 0)); + ut_asserteq(0xfe, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_b, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with 16-bit values */ +static int mem_test_ms_w(struct unit_test_state *uts) +{ + u16 *buf; + + buf = map_sysmem(0, BUF_SIZE + 2); + memset(buf, '\0', BUF_SIZE); + buf[0x34 / 2] = 0x1234; + buf[BUF_SIZE / 2] = 0x1234; + ut_assertok(console_record_reset_enable()); + run_command("ms.w 0 80 1234", 0); + ut_assert_nextline("00000030: 0000 0000 1234 0000 0000 0000 0000 0000 ....4..........."); + ut_assert_nextline("1 match"); + ut_assert_console_end(); + + ut_asserteq(1, env_get_hex("memmatches", 0)); + ut_asserteq(0x34, env_get_hex("memaddr", 0)); + ut_asserteq(0x34 / 2, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_w, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with 32-bit values */ +static int mem_test_ms_l(struct unit_test_state *uts) +{ + u32 *buf; + + buf = map_sysmem(0, BUF_SIZE + 4); + memset(buf, '\0', BUF_SIZE); + buf[0x38 / 4] = 0x12345678; + buf[BUF_SIZE / 4] = 0x12345678; + ut_assertok(console_record_reset_enable()); + run_command("ms 0 40 12345678", 0); + ut_assert_nextline("00000030: 00000000 00000000 12345678 00000000 ........xV4....."); + ut_assert_nextline("1 match"); + ut_assert_console_end(); + + ut_asserteq(1, env_get_hex("memmatches", 0)); + ut_asserteq(0x38, env_get_hex("memaddr", 0)); + ut_asserteq(0x38 / 4, env_get_hex("mempos", 0)); + + ut_assertok(console_record_reset_enable()); + run_command("ms 0 80 12345679", 0); + ut_assert_nextline("0 matches"); + ut_assert_console_end(); + + ut_asserteq(0, env_get_hex("memmatches", 0)); + ut_asserteq(0, env_get_hex("memaddr", 0)); + ut_asserteq(0 / 4, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_l, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with continuation */ +static int mem_test_ms_cont(struct unit_test_state *uts) +{ + char *const args[] = {"ms.b", "0", "100", "34"}; + int repeatable; + u8 *buf; + int i; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\0', BUF_SIZE); + for (i = 5; i < 0x33; i += 3) + buf[i] = 0x34; + ut_assertok(console_record_reset_enable()); + run_command("ms.b 0 100 34", 0); + ut_assert_nextlinen("00000000: 00 00 00 00 00 34 00 00 34 00 00 34 00 00 34 00"); + ut_assert_nextline("--"); + ut_assert_nextlinen("00000010: 00 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00"); + ut_assert_nextline("--"); + ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34"); + ut_assert_nextlinen("10 matches (repeat command to check for more)"); + ut_assert_console_end(); + + ut_asserteq(10, env_get_hex("memmatches", 0)); + ut_asserteq(0x20, env_get_hex("memaddr", 0)); + ut_asserteq(0x20, env_get_hex("mempos", 0)); + + /* + * run_command() ignoes the repeatable flag when using hush, so call + * cmd_process() directly + */ + ut_assertok(console_record_reset_enable()); + cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL); + ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34"); + ut_assert_nextline("--"); + ut_assert_nextlinen("00000030: 00 00 34 00 00 00 00 00"); + ut_assert_nextlinen("6 matches"); + ut_assert_console_end(); + + ut_asserteq(6, env_get_hex("memmatches", 0)); + ut_asserteq(0x32, env_get_hex("memaddr", 0)); + + /* 0x32 less 0x21, where the second search started */ + ut_asserteq(0x11, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_cont, UT_TESTF_CONSOLE_REC); + +/* Test that an 'ms' command with continuation stops at the end of the range */ +static int mem_test_ms_cont_end(struct unit_test_state *uts) +{ + char *const args[] = {"ms.b", "1", "ff", "12"}; + int repeatable; + u8 *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\0', BUF_SIZE); + buf[0x0] = 0x12; + buf[0x31] = 0x12; + buf[0xff] = 0x12; + buf[0x100] = 0x12; + ut_assertok(console_record_reset_enable()); + run_command("ms.b 1 ff 12", 0); + ut_assert_nextlinen("00000030"); + ut_assert_nextlinen("--"); + ut_assert_nextlinen("000000f0"); + ut_assert_nextlinen("2 matches"); + ut_assert_console_end(); + + /* + * run_command() ignoes the repeatable flag when using hush, so call + * cmd_process() directly. + * + * This should produce no matches. + */ + ut_assertok(console_record_reset_enable()); + cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL); + ut_assert_nextlinen("0 matches"); + ut_assert_console_end(); + + /* One more time */ + ut_assertok(console_record_reset_enable()); + cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL); + ut_assert_nextlinen("0 matches"); + ut_assert_console_end(); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_cont_end, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with multiple values */ +static int mem_test_ms_mult(struct unit_test_state *uts) +{ + static const char str[] = "hello"; + char *buf; + + buf = map_sysmem(0, BUF_SIZE + 5); + memset(buf, '\0', BUF_SIZE); + strcpy(buf + 0x1e, str); + strcpy(buf + 0x63, str); + strcpy(buf + BUF_SIZE - strlen(str) + 1, str); + ut_assertok(console_record_reset_enable()); + run_command("ms.b 0 100 68 65 6c 6c 6f", 0); + ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65 ..............he"); + ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 llo............."); + ut_assert_nextline("--"); + ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 ...hello........"); + ut_assert_nextline("2 matches"); + ut_assert_console_end(); + unmap_sysmem(buf); + + ut_asserteq(2, env_get_hex("memmatches", 0)); + ut_asserteq(0x63, env_get_hex("memaddr", 0)); + ut_asserteq(0x63, env_get_hex("mempos", 0)); + + return 0; +} +MEM_TEST(mem_test_ms_mult, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with string */ +static int mem_test_ms_s(struct unit_test_state *uts) +{ + static const char str[] = "hello"; + static const char str2[] = "hellothere"; + char *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\0', BUF_SIZE); + strcpy(buf + 0x1e, str); + strcpy(buf + 0x63, str); + strcpy(buf + 0xa1, str2); + ut_assertok(console_record_reset_enable()); + run_command("ms.s 0 100 hello", 0); + ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65 ..............he"); + ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 llo............."); + ut_assert_nextline("--"); + ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 ...hello........"); + ut_assert_nextline("--"); + ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00 .hellothere....."); + ut_assert_nextline("3 matches"); + ut_assert_console_end(); + + ut_asserteq(3, env_get_hex("memmatches", 0)); + ut_asserteq(0xa1, env_get_hex("memaddr", 0)); + ut_asserteq(0xa1, env_get_hex("mempos", 0)); + + ut_assertok(console_record_reset_enable()); + run_command("ms.s 0 100 hello there", 0); + ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00 .hellothere....."); + ut_assert_nextline("1 match"); + ut_assert_console_end(); + + ut_asserteq(1, env_get_hex("memmatches", 0)); + ut_asserteq(0xa1, env_get_hex("memaddr", 0)); + ut_asserteq(0xa1, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_s, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command with limit */ +static int mem_test_ms_limit(struct unit_test_state *uts) +{ + u8 *buf; + + buf = map_sysmem(0, BUF_SIZE + 1); + memset(buf, '\0', BUF_SIZE); + buf[0x0] = 0x12; + buf[0x31] = 0x12; + buf[0x62] = 0x12; + buf[0x76] = 0x12; + ut_assertok(console_record_reset_enable()); + run_command("ms.b -l2 1 ff 12", 0); + ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................"); + ut_assert_nextline("--"); + ut_assert_nextlinen("00000060: 00 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00"); + ut_assert_nextline("2 matches (repeat command to check for more)"); + ut_assert_console_end(); + + ut_asserteq(2, env_get_hex("memmatches", 0)); + ut_asserteq(0x62, env_get_hex("memaddr", 0)); + ut_asserteq(0x61, env_get_hex("mempos", 0)); + + unmap_sysmem(buf); + + return 0; +} +MEM_TEST(mem_test_ms_limit, UT_TESTF_CONSOLE_REC); + +/* Test 'ms' command in quiet mode */ +static int mem_test_ms_quiet(struct unit_test_state *uts) +{ + u8 *buf; + + buf = map_sysmem(0, BUF_SIZE + 1); + memset(buf, '\0', BUF_SIZE); + buf[0x0] = 0x12; + buf[0x31] = 0x12; + buf[0x62] = 0x12; + buf[0x76] = 0x12; + ut_assertok(console_record_reset_enable()); + run_command("ms.b -q -l2 1 ff 12", 0); + ut_assert_console_end(); + unmap_sysmem(buf); + + ut_asserteq(2, env_get_hex("memmatches", 0)); + ut_asserteq(0x62, env_get_hex("memaddr", 0)); + ut_asserteq(0x61, env_get_hex("mempos", 0)); + + return 0; +} +MEM_TEST(mem_test_ms_quiet, UT_TESTF_CONSOLE_REC); diff --git a/roms/u-boot/test/cmd/pwm.c b/roms/u-boot/test/cmd/pwm.c new file mode 100644 index 000000000..5343af83f --- /dev/null +++ b/roms/u-boot/test/cmd/pwm.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for pwm command + * + * Copyright 2020 SiFive, Inc + * + * Authors: + * Pragnesh Patel <pragnesh.patel@sifive.com> + */ + +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of 'pwm' command */ +static int dm_test_pwm_cmd(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_PWM, 0, &dev)); + ut_assertnonnull(dev); + + ut_assertok(console_record_reset_enable()); + + /* pwm <invert> <pwm_dev_num> <channel> <polarity> */ + ut_assertok(run_command("pwm invert 0 0 1", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("pwm invert 0 0 0", 0)); + ut_assert_console_end(); + + /* pwm <config> <pwm_dev_num> <channel> <period_ns> <duty_ns> */ + ut_assertok(run_command("pwm config 0 0 10 50", 0)); + ut_assert_console_end(); + + /* pwm <enable/disable> <pwm_dev_num> <channel> */ + ut_assertok(run_command("pwm enable 0 0", 0)); + ut_assert_console_end(); + + ut_assertok(run_command("pwm disable 0 0", 0)); + ut_assert_console_end(); + + return 0; +} + +DM_TEST(dm_test_pwm_cmd, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC); diff --git a/roms/u-boot/test/cmd/setexpr.c b/roms/u-boot/test/cmd/setexpr.c new file mode 100644 index 000000000..c537e8935 --- /dev/null +++ b/roms/u-boot/test/cmd/setexpr.c @@ -0,0 +1,398 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for setexpr command + * + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <console.h> +#include <mapmem.h> +#include <dm/test.h> +#include <test/suites.h> +#include <test/ut.h> + +#define BUF_SIZE 0x100 + +/* Declare a new setexpr test */ +#define SETEXPR_TEST(_name, _flags) UNIT_TEST(_name, _flags, setexpr_test) + +/* Test 'setexpr' command with simply setting integers */ +static int setexpr_test_int(struct unit_test_state *uts) +{ + u8 *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\xff', BUF_SIZE); + + /* byte */ + buf[0x0] = 0x12; + ut_assertok(run_command("setexpr.b fred 0", 0)); + ut_asserteq_str("0", env_get("fred")); + ut_assertok(run_command("setexpr.b fred *0", 0)); + ut_asserteq_str("12", env_get("fred")); + + /* 16-bit */ + *(short *)buf = 0x2345; + ut_assertok(run_command("setexpr.w fred 0", 0)); + ut_asserteq_str("0", env_get("fred")); + ut_assertok(run_command("setexpr.w fred *0", 0)); + ut_asserteq_str("2345", env_get("fred")); + + /* 32-bit */ + *(u32 *)buf = 0x3456789a; + ut_assertok(run_command("setexpr.l fred 0", 0)); + ut_asserteq_str("0", env_get("fred")); + ut_assertok(run_command("setexpr.l fred *0", 0)); + ut_asserteq_str("3456789a", env_get("fred")); + + /* 64-bit */ + *(u64 *)buf = 0x456789abcdef0123; + ut_assertok(run_command("setexpr.q fred 0", 0)); + ut_asserteq_str("0", env_get("fred")); + ut_assertok(run_command("setexpr.q fred *0", 0)); + ut_asserteq_str("456789abcdef0123", env_get("fred")); + + /* default */ + ut_assertok(run_command("setexpr fred 0", 0)); + ut_asserteq_str("0", env_get("fred")); + ut_assertok(run_command("setexpr fred *0", 0)); + ut_asserteq_str("cdef0123", env_get("fred")); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_int, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with + operator */ +static int setexpr_test_plus(struct unit_test_state *uts) +{ + char *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\xff', BUF_SIZE); + + /* byte */ + buf[0x0] = 0x12; + buf[0x10] = 0x34; + ut_assertok(run_command("setexpr.b fred *0 + *10", 0)); + ut_asserteq_str("46", env_get("fred")); + + /* 16-bit */ + *(short *)buf = 0x2345; + *(short *)(buf + 0x10) = 0xf012; + ut_assertok(run_command("setexpr.w fred *0 + *10", 0)); + ut_asserteq_str("11357", env_get("fred")); + + /* 32-bit */ + *(u32 *)buf = 0x3456789a; + *(u32 *)(buf + 0x10) = 0xc3384235; + ut_assertok(run_command("setexpr.l fred *0 + *10", 0)); + ut_asserteq_str("f78ebacf", env_get("fred")); + + /* 64-bit */ + *(u64 *)buf = 0x456789abcdef0123; + *(u64 *)(buf + 0x10) = 0x4987328372849283; + ut_assertok(run_command("setexpr.q fred *0 + *10", 0)); + ut_asserteq_str("8eeebc2f407393a6", env_get("fred")); + + /* default */ + ut_assertok(run_command("setexpr fred *0 + *10", 0)); + ut_asserteq_str("1407393a6", env_get("fred")); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_plus, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with other operators */ +static int setexpr_test_oper(struct unit_test_state *uts) +{ + char *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\xff', BUF_SIZE); + + *(u32 *)buf = 0x1234; + *(u32 *)(buf + 0x10) = 0x560000; + + /* Quote | to avoid confusing hush */ + ut_assertok(run_command("setexpr fred *0 \"|\" *10", 0)); + ut_asserteq_str("561234", env_get("fred")); + + *(u32 *)buf = 0x561200; + *(u32 *)(buf + 0x10) = 0x1234; + + /* Quote & to avoid confusing hush */ + ut_assertok(run_command("setexpr.l fred *0 \"&\" *10", 0)); + ut_asserteq_str("1200", env_get("fred")); + + ut_assertok(run_command("setexpr.l fred *0 ^ *10", 0)); + ut_asserteq_str("560034", env_get("fred")); + + ut_assertok(run_command("setexpr.l fred *0 - *10", 0)); + ut_asserteq_str("55ffcc", env_get("fred")); + + ut_assertok(run_command("setexpr.l fred *0 * *10", 0)); + ut_asserteq_str("61ebfa800", env_get("fred")); + + ut_assertok(run_command("setexpr.l fred *0 / *10", 0)); + ut_asserteq_str("4ba", env_get("fred")); + + ut_assertok(run_command("setexpr.l fred *0 % *10", 0)); + ut_asserteq_str("838", env_get("fred")); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_oper, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with regex */ +static int setexpr_test_regex(struct unit_test_state *uts) +{ + char *buf, *val; + + buf = map_sysmem(0, BUF_SIZE); + + /* Single substitution */ + ut_assertok(run_command("setenv fred 'this is a test'", 0)); + ut_assertok(run_command("setexpr fred sub is us", 0)); + val = env_get("fred"); + ut_asserteq_str("thus is a test", val); + + /* Global substitution */ + ut_assertok(run_command("setenv fred 'this is a test'", 0)); + ut_assertok(run_command("setexpr fred gsub is us", 0)); + val = env_get("fred"); + ut_asserteq_str("thus us a test", val); + + /* Global substitution */ + ut_assertok(run_command("setenv fred 'this is a test'", 0)); + ut_assertok(run_command("setenv mary 'this is a test'", 0)); + ut_assertok(run_command("setexpr fred gsub is us \"${mary}\"", 0)); + val = env_get("fred"); + ut_asserteq_str("thus us a test", val); + val = env_get("mary"); + ut_asserteq_str("this is a test", val); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_regex, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with regex replacement that expands the string */ +static int setexpr_test_regex_inc(struct unit_test_state *uts) +{ + char *buf, *val; + + buf = map_sysmem(0, BUF_SIZE); + + ut_assertok(run_command("setenv fred 'this is a test'", 0)); + ut_assertok(run_command("setexpr fred gsub is much_longer_string", 0)); + val = env_get("fred"); + ut_asserteq_str("thmuch_longer_string much_longer_string a test", val); + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_regex_inc, UT_TESTF_CONSOLE_REC); + +/* Test setexpr_regex_sub() directly to check buffer usage */ +static int setexpr_test_sub(struct unit_test_state *uts) +{ + char *buf, *nbuf; + int i; + + buf = map_sysmem(0, BUF_SIZE); + nbuf = map_sysmem(0x1000, BUF_SIZE); + + /* Add a pattern so we can check the buffer limits */ + memset(buf, '\xff', BUF_SIZE); + memset(nbuf, '\xff', BUF_SIZE); + for (i = BUF_SIZE; i < 0x1000; i++) { + buf[i] = i & 0xff; + nbuf[i] = i & 0xff; + } + strcpy(buf, "this is a test"); + + /* + * This is a regression test, since a bug was found in the use of + * memmove() in setexpr + */ + ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE, "is", + "us it is longer", true)); + ut_asserteq_str("thus it is longer us it is longer a test", buf); + for (i = BUF_SIZE; i < 0x1000; i++) { + ut_assertf(buf[i] == (char)i, + "buf byte at %x should be %02x, got %02x)\n", + i, i & 0xff, (u8)buf[i]); + ut_assertf(nbuf[i] == (char)i, + "nbuf byte at %x should be %02x, got %02x)\n", + i, i & 0xff, (u8)nbuf[i]); + } + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_sub, UT_TESTF_CONSOLE_REC); + +/* Test setexpr_regex_sub() with back references */ +static int setexpr_test_backref(struct unit_test_state *uts) +{ + char *buf, *nbuf; + int i; + + buf = map_sysmem(0, BUF_SIZE); + nbuf = map_sysmem(0x1000, BUF_SIZE); + + /* Add a pattern so we can check the buffer limits */ + memset(buf, '\xff', BUF_SIZE); + memset(nbuf, '\xff', BUF_SIZE); + for (i = BUF_SIZE; i < 0x1000; i++) { + buf[i] = i & 0xff; + nbuf[i] = i & 0xff; + } + strcpy(buf, "this is surely a test is it? yes this is indeed a test"); + + /* + * This is a regression test, since a bug was found in the use of + * memmove() in setexpr + */ + ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE, + "(this) (is) (surely|indeed)", + "us \\1 \\2 \\3!", true)); + ut_asserteq_str("us this is surely! a test is it? yes us this is indeed! a test", + buf); + + /* The following checks fail at present due to a bug in setexpr */ + return 0; + for (i = BUF_SIZE; i < 0x1000; i++) { + ut_assertf(buf[i] == (char)i, + "buf byte at %x should be %02x, got %02x)\n", + i, i & 0xff, (u8)buf[i]); + ut_assertf(nbuf[i] == (char)i, + "nbuf byte at %x should be %02x, got %02x)\n", + i, i & 0xff, (u8)nbuf[i]); + } + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_backref, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with setting strings */ +static int setexpr_test_str(struct unit_test_state *uts) +{ + ulong start_mem; + char *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\xff', BUF_SIZE); + + /* + * Set 'fred' to the same length as we expect to get below, to avoid a + * new allocation in 'setexpr'. That way we can check for memory leaks. + */ + ut_assertok(env_set("fred", "x")); + start_mem = ut_check_free(); + strcpy(buf, "hello"); + ut_asserteq(1, run_command("setexpr.s fred 0", 0)); + ut_assertok(ut_check_delta(start_mem)); + + ut_assertok(env_set("fred", "12345")); + start_mem = ut_check_free(); + ut_assertok(run_command("setexpr.s fred *0", 0)); + ut_asserteq_str("hello", env_get("fred")); + ut_assertok(ut_check_delta(start_mem)); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_str, UT_TESTF_CONSOLE_REC); + + +/* Test 'setexpr' command with concatenating strings */ +static int setexpr_test_str_oper(struct unit_test_state *uts) +{ + ulong start_mem; + char *buf; + + buf = map_sysmem(0, BUF_SIZE); + memset(buf, '\xff', BUF_SIZE); + strcpy(buf, "hello"); + strcpy(buf + 0x10, " there"); + + ut_assertok(console_record_reset_enable()); + start_mem = ut_check_free(); + ut_asserteq(1, run_command("setexpr.s fred *0 * *10", 0)); + ut_assertok(ut_check_delta(start_mem)); + ut_assert_nextline("invalid op"); + ut_assert_console_end(); + + /* + * Set 'fred' to the same length as we expect to get below, to avoid a + * new allocation in 'setexpr'. That way we can check for memory leaks. + */ + ut_assertok(env_set("fred", "12345012345")); + start_mem = ut_check_free(); + ut_assertok(run_command("setexpr.s fred *0 + *10", 0)); + ut_asserteq_str("hello there", env_get("fred")); + + /* + * This check does not work with sandbox_flattree, apparently due to + * memory allocations in env_set(). + * + * The truetype console produces lots of memory allocations even though + * the LCD display is not visible. But even without these, it does not + * work. + * + * A better test would be for dlmalloc to record the allocs and frees + * for a particular caller, but that is not supported. + * + * For now, drop this test. + * + * ut_assertok(ut_check_delta(start_mem)); + */ + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_str_oper, UT_TESTF_CONSOLE_REC); + +/* Test 'setexpr' command with a string that is too long */ +static int setexpr_test_str_long(struct unit_test_state *uts) +{ + const int size = 128 << 10; /* setexpr strings are a max of 64KB */ + char *buf, *val; + + buf = map_sysmem(0, size); + memset(buf, 'a', size); + + /* String should be truncated to 64KB */ + ut_assertok(run_command("setexpr.s fred *0", 0)); + val = env_get("fred"); + ut_asserteq(64 << 10, strlen(val)); + + unmap_sysmem(buf); + + return 0; +} +SETEXPR_TEST(setexpr_test_str_long, UT_TESTF_CONSOLE_REC); + +int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(setexpr_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(setexpr_test); + + return cmd_ut_category("cmd_setexpr", "setexpr_test_", tests, n_ents, + argc, argv); +} diff --git a/roms/u-boot/test/cmd/test_echo.c b/roms/u-boot/test/cmd/test_echo.c new file mode 100644 index 000000000..091e4f823 --- /dev/null +++ b/roms/u-boot/test/cmd/test_echo.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for echo command + * + * Copyright 2020, Heinrich Schuchadt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> +#include <asm/global_data.h> +#include <display_options.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct test_data { + char *cmd; + char *expected; +}; + +static struct test_data echo_data[] = { + {"echo 1 2 3", + "1 2 3"}, + /* Test new line handling */ + {"echo -n 1 2 3; echo a b c", + "1 2 3a b c"}, + /* + * Test handling of environment variables. + * + * j, q, x are among the least frequent letters in English. + * Hence no collision for the variable name jQx is expected. + */ + {"setenv jQx X; echo \"a)\" ${jQx} 'b)' '${jQx}' c) ${jQx}; setenv jQx", + "a) X b) ${jQx} c) X"}, + /* Test shell variable assignments without substitutions */ + {"foo=bar echo baz", "baz"}, + /* Test handling of shell variables. */ + {"setenv jQx; for jQx in 1 2 3; do echo -n \"${jQx}, \"; done; echo;", + "1, 2, 3, "}, +}; + +static int lib_test_hush_echo(struct unit_test_state *uts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(echo_data); ++i) { + ut_silence_console(uts); + console_record_reset_enable(); + ut_assertok(run_command(echo_data[i].cmd, 0)); + ut_unsilence_console(uts); + console_record_readline(uts->actual_str, + sizeof(uts->actual_str)); + ut_asserteq_str(echo_data[i].expected, uts->actual_str); + ut_assertok(ut_check_console_end(uts)); + } + return 0; +} + +LIB_TEST(lib_test_hush_echo, 0); diff --git a/roms/u-boot/test/cmd_ut.c b/roms/u-boot/test/cmd_ut.c new file mode 100644 index 000000000..b9c166045 --- /dev/null +++ b/roms/u-boot/test/cmd_ut.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2015 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <test/suites.h> +#include <test/test.h> +#include <test/ut.h> + +static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]); + +int cmd_ut_category(const char *name, const char *prefix, + struct unit_test *tests, int n_ents, + int argc, char *const argv[]) +{ + int ret; + + ret = ut_run_list(name, prefix, tests, n_ents, + argc > 1 ? argv[1] : NULL); + + return ret ? CMD_RET_FAILURE : 0; +} + +static struct cmd_tbl cmd_ut_sub[] = { + U_BOOT_CMD_MKENT(all, CONFIG_SYS_MAXARGS, 1, do_ut_all, "", ""), +#if defined(CONFIG_UT_DM) + U_BOOT_CMD_MKENT(dm, CONFIG_SYS_MAXARGS, 1, do_ut_dm, "", ""), +#endif +#if defined(CONFIG_UT_ENV) + U_BOOT_CMD_MKENT(env, CONFIG_SYS_MAXARGS, 1, do_ut_env, "", ""), +#endif +#ifdef CONFIG_UT_OPTEE + U_BOOT_CMD_MKENT(optee, CONFIG_SYS_MAXARGS, 1, do_ut_optee, "", ""), +#endif +#ifdef CONFIG_UT_OVERLAY + U_BOOT_CMD_MKENT(overlay, CONFIG_SYS_MAXARGS, 1, do_ut_overlay, "", ""), +#endif +#ifdef CONFIG_UT_LIB + U_BOOT_CMD_MKENT(lib, CONFIG_SYS_MAXARGS, 1, do_ut_lib, "", ""), +#endif +#ifdef CONFIG_UT_LOG + U_BOOT_CMD_MKENT(log, CONFIG_SYS_MAXARGS, 1, do_ut_log, "", ""), +#endif + U_BOOT_CMD_MKENT(mem, CONFIG_SYS_MAXARGS, 1, do_ut_mem, "", ""), +#ifdef CONFIG_CMD_SETEXPR + U_BOOT_CMD_MKENT(setexpr, CONFIG_SYS_MAXARGS, 1, do_ut_setexpr, "", + ""), +#endif +#ifdef CONFIG_UT_TIME + U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""), +#endif +#if CONFIG_IS_ENABLED(UT_UNICODE) && !defined(API_BUILD) + U_BOOT_CMD_MKENT(unicode, CONFIG_SYS_MAXARGS, 1, do_ut_unicode, "", ""), +#endif +#ifdef CONFIG_SANDBOX + U_BOOT_CMD_MKENT(compression, CONFIG_SYS_MAXARGS, 1, do_ut_compression, + "", ""), + U_BOOT_CMD_MKENT(bloblist, CONFIG_SYS_MAXARGS, 1, do_ut_bloblist, + "", ""), + U_BOOT_CMD_MKENT(bootm, CONFIG_SYS_MAXARGS, 1, do_ut_bootm, "", ""), +#endif + U_BOOT_CMD_MKENT(str, CONFIG_SYS_MAXARGS, 1, do_ut_str, "", ""), +#ifdef CONFIG_CMD_ADDRMAP + U_BOOT_CMD_MKENT(addrmap, CONFIG_SYS_MAXARGS, 1, do_ut_addrmap, "", ""), +#endif +}; + +static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i; + int retval; + int any_fail = 0; + + for (i = 1; i < ARRAY_SIZE(cmd_ut_sub); i++) { + printf("----Running %s tests----\n", cmd_ut_sub[i].name); + retval = cmd_ut_sub[i].cmd(cmdtp, flag, 1, &cmd_ut_sub[i].name); + if (!any_fail) + any_fail = retval; + } + + return any_fail; +} + +static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct cmd_tbl *cp; + + if (argc < 2) + return CMD_RET_USAGE; + + /* drop initial "ut" arg */ + argc--; + argv++; + + cp = find_cmd_tbl(argv[0], cmd_ut_sub, ARRAY_SIZE(cmd_ut_sub)); + + if (cp) + return cp->cmd(cmdtp, flag, argc, argv); + + return CMD_RET_USAGE; +} + +#ifdef CONFIG_SYS_LONGHELP +static char ut_help_text[] = + "all - execute all enabled tests\n" +#ifdef CONFIG_SANDBOX + "ut bloblist - Test bloblist implementation\n" + "ut compression - Test compressors and bootm decompression\n" +#endif +#ifdef CONFIG_UT_DM + "ut dm [test-name]\n" +#endif +#ifdef CONFIG_UT_ENV + "ut env [test-name]\n" +#endif +#ifdef CONFIG_UT_LIB + "ut lib [test-name] - test library functions\n" +#endif +#ifdef CONFIG_UT_LOG + "ut log [test-name] - test logging functions\n" +#endif + "ut mem [test-name] - test memory-related commands\n" +#ifdef CONFIG_UT_OPTEE + "ut optee [test-name]\n" +#endif +#ifdef CONFIG_UT_OVERLAY + "ut overlay [test-name]\n" +#endif + "ut setexpr [test-name] - test setexpr command\n" +#ifdef CONFIG_SANDBOX + "ut str - Basic test of string functions\n" +#endif +#ifdef CONFIG_UT_TIME + "ut time - Very basic test of time functions\n" +#endif +#if defined(CONFIG_UT_UNICODE) && \ + !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD) + "ut unicode [test-name] - test Unicode functions\n" +#endif +#ifdef CONFIG_CMD_ADDRMAP + "ut addrmap - Very basic test of addrmap command\n" +#endif + ; +#endif /* CONFIG_SYS_LONGHELP */ + +U_BOOT_CMD( + ut, CONFIG_SYS_MAXARGS, 1, do_ut, + "unit tests", ut_help_text +); diff --git a/roms/u-boot/test/command_ut.c b/roms/u-boot/test/command_ut.c new file mode 100644 index 000000000..9837d10eb --- /dev/null +++ b/roms/u-boot/test/command_ut.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012, The Chromium Authors + */ + +#define DEBUG + +#include <common.h> +#include <command.h> +#include <env.h> +#include <log.h> + +static const char test_cmd[] = "setenv list 1\n setenv list ${list}2; " + "setenv list ${list}3\0" + "setenv list ${list}4"; + +static int do_ut_cmd(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + printf("%s: Testing commands\n", __func__); + run_command("env default -f -a", 0); + + /* commands separated by \n */ + run_command_list("setenv list 1\n setenv list ${list}1", -1, 0); + assert(!strcmp("11", env_get("list"))); + + /* command followed by \n and nothing else */ + run_command_list("setenv list 1${list}\n", -1, 0); + assert(!strcmp("111", env_get("list"))); + + /* a command string with \0 in it. Stuff after \0 should be ignored */ + run_command("setenv list", 0); + run_command_list(test_cmd, sizeof(test_cmd), 0); + assert(!strcmp("123", env_get("list"))); + + /* + * a command list where we limit execution to only the first command + * using the length parameter. + */ + run_command_list("setenv list 1\n setenv list ${list}2; " + "setenv list ${list}3", strlen("setenv list 1"), 0); + assert(!strcmp("1", env_get("list"))); + + assert(run_command("false", 0) == 1); + assert(run_command("echo", 0) == 0); + assert(run_command_list("false", -1, 0) == 1); + assert(run_command_list("echo", -1, 0) == 0); + +#ifdef CONFIG_HUSH_PARSER + run_command("setenv foo 'setenv black 1\nsetenv adder 2'", 0); + run_command("run foo", 0); + assert(env_get("black") != NULL); + assert(!strcmp("1", env_get("black"))); + assert(env_get("adder") != NULL); + assert(!strcmp("2", env_get("adder"))); +#endif + + assert(run_command("", 0) == 0); + assert(run_command(" ", 0) == 0); + + assert(run_command("'", 0) == 1); + + printf("%s: Everything went swimmingly\n", __func__); + return 0; +} + +U_BOOT_CMD( + ut_cmd, 5, 1, do_ut_cmd, + "Very basic test of command parsers", + "" +); diff --git a/roms/u-boot/test/common.sh b/roms/u-boot/test/common.sh new file mode 100644 index 000000000..904d579b7 --- /dev/null +++ b/roms/u-boot/test/common.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +OUTPUT_DIR=sandbox + +fail() { + echo "Test failed: $1" + if [ -n ${tmp} ]; then + rm ${tmp} + fi + exit 1 +} + +build_uboot() { + echo "Build sandbox" + OPTS="O=${OUTPUT_DIR} $1" + NUM_CPUS=$(nproc) + echo ${OPTS} + make ${OPTS} sandbox_config + make ${OPTS} -s -j${NUM_CPUS} +} diff --git a/roms/u-boot/test/compression.c b/roms/u-boot/test/compression.c new file mode 100644 index 000000000..4cd1be564 --- /dev/null +++ b/roms/u-boot/test/compression.c @@ -0,0 +1,547 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013, The Chromium Authors + */ + +#include <common.h> +#include <bootm.h> +#include <command.h> +#include <gzip.h> +#include <image.h> +#include <log.h> +#include <lz4.h> +#include <malloc.h> +#include <mapmem.h> +#include <asm/io.h> + +#include <u-boot/zlib.h> +#include <bzlib.h> + +#include <lzma/LzmaTypes.h> +#include <lzma/LzmaDec.h> +#include <lzma/LzmaTools.h> + +#include <linux/lzo.h> +#include <test/compression.h> +#include <test/suites.h> +#include <test/ut.h> + +static const char plain[] = + "I am a highly compressable bit of text.\n" + "I am a highly compressable bit of text.\n" + "I am a highly compressable bit of text.\n" + "There are many like me, but this one is mine.\n" + "If I were any shorter, there wouldn't be much sense in\n" + "compressing me in the first place. At least with lzo, anyway,\n" + "which appears to behave poorly in the face of short text\n" + "messages.\n"; + +/* bzip2 -c /tmp/plain.txt > /tmp/plain.bz2 */ +static const char bzip2_compressed[] = + "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xe5\x63\xdd\x09\x00\x00" + "\x28\x57\x80\x00\x10\x40\x85\x20\x20\x04\x00\x3f\xef\xdf\xf0\x30" + "\x00\xd6\xd0\x34\x91\x89\xa6\xf5\x4d\x19\x1a\x19\x0d\x02\x34\xd4" + "\xc9\x00\x34\x34\x00\x02\x48\x41\x35\x4f\xd4\xc6\x88\xd3\x50\x3d" + "\x4f\x51\x82\x4f\x88\xc3\x0d\x05\x62\x4f\x91\xa3\x52\x1b\xd0\x52" + "\x41\x4a\xa3\x98\xc2\x6b\xca\xa3\x82\xa5\xac\x8b\x15\x99\x68\xad" + "\xdf\x29\xd6\xf1\xf7\x5a\x10\xcd\x8c\x26\x61\x94\x95\xfe\x9e\x16" + "\x18\x28\x69\xd4\x23\x64\xcc\x2b\xe5\xe8\x5f\x00\xa4\x70\x26\x2c" + "\xee\xbd\x59\x6d\x6a\xec\xfc\x31\xda\x59\x0a\x14\x2a\x60\x1c\xf0" + "\x04\x86\x73\x9a\xc5\x5b\x87\x3f\x5b\x4c\x93\xe6\xb5\x35\x0d\xa6" + "\xb1\x2e\x62\x7b\xab\x67\xe7\x99\x2a\x14\x5e\x9f\x64\xcb\x96\xf4" + "\x0d\x65\xd4\x39\xe6\x8b\x7e\xea\x1c\x03\x69\x97\x83\x58\x91\x96" + "\xe1\xf0\x9d\xa4\x15\x8b\xb8\xc6\x93\xdc\x3d\xd9\x3c\x22\x55\xef" + "\xfb\xbb\x2a\xd3\x87\xa2\x8b\x04\xd9\x19\xf8\xe2\xfd\x4f\xdb\x1a" + "\x07\xc8\x60\xa3\x3f\xf8\xbb\x92\x29\xc2\x84\x87\x2b\x1e\xe8\x48"; +static const unsigned long bzip2_compressed_size = 240; + +/* lzma -z -c /tmp/plain.txt > /tmp/plain.lzma */ +static const char lzma_compressed[] = + "\x5d\x00\x00\x80\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x24\x88" + "\x08\x26\xd8\x41\xff\x99\xc8\xcf\x66\x3d\x80\xac\xba\x17\xf1\xc8" + "\xb9\xdf\x49\x37\xb1\x68\xa0\x2a\xdd\x63\xd1\xa7\xa3\x66\xf8\x15" + "\xef\xa6\x67\x8a\x14\x18\x80\xcb\xc7\xb1\xcb\x84\x6a\xb2\x51\x16" + "\xa1\x45\xa0\xd6\x3e\x55\x44\x8a\x5c\xa0\x7c\xe5\xa8\xbd\x04\x57" + "\x8f\x24\xfd\xb9\x34\x50\x83\x2f\xf3\x46\x3e\xb9\xb0\x00\x1a\xf5" + "\xd3\x86\x7e\x8f\x77\xd1\x5d\x0e\x7c\xe1\xac\xde\xf8\x65\x1f\x4d" + "\xce\x7f\xa7\x3d\xaa\xcf\x26\xa7\x58\x69\x1e\x4c\xea\x68\x8a\xe5" + "\x89\xd1\xdc\x4d\xc7\xe0\x07\x42\xbf\x0c\x9d\x06\xd7\x51\xa2\x0b" + "\x7c\x83\x35\xe1\x85\xdf\xee\xfb\xa3\xee\x2f\x47\x5f\x8b\x70\x2b" + "\xe1\x37\xf3\x16\xf6\x27\x54\x8a\x33\x72\x49\xea\x53\x7d\x60\x0b" + "\x21\x90\x66\xe7\x9e\x56\x61\x5d\xd8\xdc\x59\xf0\xac\x2f\xd6\x49" + "\x6b\x85\x40\x08\x1f\xdf\x26\x25\x3b\x72\x44\xb0\xb8\x21\x2f\xb3" + "\xd7\x9b\x24\x30\x78\x26\x44\x07\xc3\x33\xd1\x4d\x03\x1b\xe1\xff" + "\xfd\xf5\x50\x8d\xca"; +static const unsigned long lzma_compressed_size = 229; + +/* lzop -c /tmp/plain.txt > /tmp/plain.lzo */ +static const char lzo_compressed[] = + "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a\x10\x30\x20\x60\x09\x40\x01" + "\x05\x03\x00\x00\x09\x00\x00\x81\xb4\x52\x09\x54\xf1\x00\x00\x00" + "\x00\x09\x70\x6c\x61\x69\x6e\x2e\x74\x78\x74\x65\xb1\x07\x9c\x00" + "\x00\x01\x5e\x00\x00\x01\x0f\xc3\xc7\x7a\xe0\x00\x16\x49\x20\x61" + "\x6d\x20\x61\x20\x68\x69\x67\x68\x6c\x79\x20\x63\x6f\x6d\x70\x72" + "\x65\x73\x73\x61\x62\x6c\x65\x20\x62\x69\x74\x20\x6f\x66\x20\x74" + "\x65\x78\x74\x2e\x0a\x20\x2f\x9c\x00\x00\x22\x54\x68\x65\x72\x65" + "\x20\x61\x72\x65\x20\x6d\x61\x6e\x79\x20\x6c\x69\x6b\x65\x20\x6d" + "\x65\x2c\x20\x62\x75\x74\x20\x74\x68\x69\x73\x20\x6f\x6e\x65\x20" + "\x69\x73\x20\x6d\x69\x6e\x65\x2e\x0a\x49\x66\x20\x49\x20\x77\x84" + "\x06\x0a\x6e\x79\x20\x73\x68\x6f\x72\x74\x65\x72\x2c\x20\x74\x90" + "\x08\x00\x08\x77\x6f\x75\x6c\x64\x6e\x27\x74\x20\x62\x65\x20\x6d" + "\x75\x63\x68\x20\x73\x65\x6e\x73\x65\x20\x69\x6e\x0a\xf8\x19\x02" + "\x69\x6e\x67\x20\x6d\x64\x02\x64\x06\x00\x5a\x20\x66\x69\x72\x73" + "\x74\x20\x70\x6c\x61\x63\x65\x2e\x20\x41\x74\x20\x6c\x65\x61\x73" + "\x74\x20\x77\x69\x74\x68\x20\x6c\x7a\x6f\x2c\x20\x61\x6e\x79\x77" + "\x61\x79\x2c\x0a\x77\x68\x69\x63\x68\x20\x61\x70\x70\x65\x61\x72" + "\x73\x20\x74\x6f\x20\x62\x65\x68\x61\x76\x65\x20\x70\x6f\x6f\x72" + "\x6c\x79\x20\x69\x6e\x20\x74\x68\x65\x20\x66\x61\x63\x65\x20\x6f" + "\x66\x20\x73\x68\x6f\x72\x74\x20\x74\x65\x78\x74\x0a\x6d\x65\x73" + "\x73\x61\x67\x65\x73\x2e\x0a\x11\x00\x00\x00\x00\x00\x00"; +static const unsigned long lzo_compressed_size = 334; + +/* lz4 -z /tmp/plain.txt > /tmp/plain.lz4 */ +static const char lz4_compressed[] = + "\x04\x22\x4d\x18\x64\x70\xb9\x01\x01\x00\x00\xff\x19\x49\x20\x61" + "\x6d\x20\x61\x20\x68\x69\x67\x68\x6c\x79\x20\x63\x6f\x6d\x70\x72" + "\x65\x73\x73\x61\x62\x6c\x65\x20\x62\x69\x74\x20\x6f\x66\x20\x74" + "\x65\x78\x74\x2e\x0a\x28\x00\x3d\xf1\x25\x54\x68\x65\x72\x65\x20" + "\x61\x72\x65\x20\x6d\x61\x6e\x79\x20\x6c\x69\x6b\x65\x20\x6d\x65" + "\x2c\x20\x62\x75\x74\x20\x74\x68\x69\x73\x20\x6f\x6e\x65\x20\x69" + "\x73\x20\x6d\x69\x6e\x65\x2e\x0a\x49\x66\x20\x49\x20\x77\x32\x00" + "\xd1\x6e\x79\x20\x73\x68\x6f\x72\x74\x65\x72\x2c\x20\x74\x45\x00" + "\xf4\x0b\x77\x6f\x75\x6c\x64\x6e\x27\x74\x20\x62\x65\x20\x6d\x75" + "\x63\x68\x20\x73\x65\x6e\x73\x65\x20\x69\x6e\x0a\xcf\x00\x50\x69" + "\x6e\x67\x20\x6d\x12\x00\x00\x32\x00\xf0\x11\x20\x66\x69\x72\x73" + "\x74\x20\x70\x6c\x61\x63\x65\x2e\x20\x41\x74\x20\x6c\x65\x61\x73" + "\x74\x20\x77\x69\x74\x68\x20\x6c\x7a\x6f\x2c\x63\x00\xf5\x14\x77" + "\x61\x79\x2c\x0a\x77\x68\x69\x63\x68\x20\x61\x70\x70\x65\x61\x72" + "\x73\x20\x74\x6f\x20\x62\x65\x68\x61\x76\x65\x20\x70\x6f\x6f\x72" + "\x6c\x79\x4e\x00\x30\x61\x63\x65\x27\x01\x01\x95\x00\x01\x2d\x01" + "\xb0\x0a\x6d\x65\x73\x73\x61\x67\x65\x73\x2e\x0a\x00\x00\x00\x00" + "\x9d\x12\x8c\x9d"; +static const unsigned long lz4_compressed_size = 276; + + +#define TEST_BUFFER_SIZE 512 + +typedef int (*mutate_func)(struct unit_test_state *uts, void *, unsigned long, + void *, unsigned long, unsigned long *); + +static int compress_using_gzip(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + unsigned long inout_size = out_max; + + ret = gzip(out, &inout_size, in, in_size); + if (out_size) + *out_size = inout_size; + + return ret; +} + +static int uncompress_using_gzip(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + unsigned long inout_size = in_size; + + ret = gunzip(out, out_max, in, &inout_size); + if (out_size) + *out_size = inout_size; + + return ret; +} + +static int compress_using_bzip2(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + /* There is no bzip2 compression in u-boot, so fake it. */ + ut_asserteq(in_size, strlen(plain)); + ut_asserteq_mem(plain, in, in_size); + + if (bzip2_compressed_size > out_max) + return -1; + + memcpy(out, bzip2_compressed, bzip2_compressed_size); + if (out_size) + *out_size = bzip2_compressed_size; + + return 0; +} + +static int uncompress_using_bzip2(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + unsigned int inout_size = out_max; + + ret = BZ2_bzBuffToBuffDecompress(out, &inout_size, in, in_size, + CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0); + if (out_size) + *out_size = inout_size; + + return (ret != BZ_OK); +} + +static int compress_using_lzma(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + /* There is no lzma compression in u-boot, so fake it. */ + ut_asserteq(in_size, strlen(plain)); + ut_asserteq_mem(plain, in, in_size); + + if (lzma_compressed_size > out_max) + return -1; + + memcpy(out, lzma_compressed, lzma_compressed_size); + if (out_size) + *out_size = lzma_compressed_size; + + return 0; +} + +static int uncompress_using_lzma(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + SizeT inout_size = out_max; + + ret = lzmaBuffToBuffDecompress(out, &inout_size, in, in_size); + if (out_size) + *out_size = inout_size; + + return (ret != SZ_OK); +} + +static int compress_using_lzo(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + /* There is no lzo compression in u-boot, so fake it. */ + ut_asserteq(in_size, strlen(plain)); + ut_asserteq_mem(plain, in, in_size); + + if (lzo_compressed_size > out_max) + return -1; + + memcpy(out, lzo_compressed, lzo_compressed_size); + if (out_size) + *out_size = lzo_compressed_size; + + return 0; +} + +static int uncompress_using_lzo(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + size_t input_size = in_size; + size_t output_size = out_max; + + ret = lzop_decompress(in, input_size, out, &output_size); + if (out_size) + *out_size = output_size; + + return (ret != LZO_E_OK); +} + +static int compress_using_lz4(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + /* There is no lz4 compression in u-boot, so fake it. */ + ut_asserteq(in_size, strlen(plain)); + ut_asserteq_mem(plain, in, in_size); + + if (lz4_compressed_size > out_max) + return -1; + + memcpy(out, lz4_compressed, lz4_compressed_size); + if (out_size) + *out_size = lz4_compressed_size; + + return 0; +} + +static int uncompress_using_lz4(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + int ret; + size_t input_size = in_size; + size_t output_size = out_max; + + ret = ulz4fn(in, input_size, out, &output_size); + if (out_size) + *out_size = output_size; + + return (ret != 0); +} + +#define errcheck(statement) if (!(statement)) { \ + fprintf(stderr, "\tFailed: %s\n", #statement); \ + ret = 1; \ + goto out; \ +} + +struct buf_state { + ulong orig_size; + ulong compressed_size; + ulong uncompressed_size; + void *orig_buf; + void *compressed_buf; + void *uncompressed_buf; + void *compare_buf; +}; + +static int run_test_internal(struct unit_test_state *uts, char *name, + mutate_func compress, mutate_func uncompress, + struct buf_state *buf) +{ + int ret; + + /* Compress works as expected. */ + printf("\torig_size:%lu\n", buf->orig_size); + memset(buf->compressed_buf, 'A', TEST_BUFFER_SIZE); + errcheck(compress(uts, buf->orig_buf, buf->orig_size, + buf->compressed_buf, buf->compressed_size, + &buf->compressed_size) == 0); + printf("\tcompressed_size:%lu\n", buf->compressed_size); + errcheck(buf->compressed_size > 0); + errcheck(buf->compressed_size < buf->orig_size); + errcheck(((char *)buf->compressed_buf)[buf->compressed_size - 1] != + 'A'); + errcheck(((char *)buf->compressed_buf)[buf->compressed_size] == 'A'); + + /* Uncompresses with space remaining. */ + errcheck(uncompress(uts, buf->compressed_buf, buf->compressed_size, + buf->uncompressed_buf, buf->uncompressed_size, + &buf->uncompressed_size) == 0); + printf("\tuncompressed_size:%lu\n", buf->uncompressed_size); + errcheck(buf->uncompressed_size == buf->orig_size); + errcheck(memcmp(buf->orig_buf, buf->uncompressed_buf, + buf->orig_size) == 0); + + /* Uncompresses with exactly the right size output buffer. */ + memset(buf->uncompressed_buf, 'A', TEST_BUFFER_SIZE); + errcheck(uncompress(uts, buf->compressed_buf, buf->compressed_size, + buf->uncompressed_buf, buf->orig_size, + &buf->uncompressed_size) == 0); + errcheck(buf->uncompressed_size == buf->orig_size); + errcheck(memcmp(buf->orig_buf, buf->uncompressed_buf, + buf->orig_size) == 0); + errcheck(((char *)buf->uncompressed_buf)[buf->orig_size] == 'A'); + + /* Make sure compression does not over-run. */ + memset(buf->compare_buf, 'A', TEST_BUFFER_SIZE); + ret = compress(uts, buf->orig_buf, buf->orig_size, + buf->compare_buf, buf->compressed_size - 1, + NULL); + errcheck(((char *)buf->compare_buf)[buf->compressed_size] == 'A'); + errcheck(ret != 0); + printf("\tcompress does not overrun\n"); + + /* Make sure decompression does not over-run. */ + memset(buf->compare_buf, 'A', TEST_BUFFER_SIZE); + ret = uncompress(uts, buf->compressed_buf, buf->compressed_size, + buf->compare_buf, buf->uncompressed_size - 1, + NULL); + errcheck(((char *)buf->compare_buf)[buf->uncompressed_size - 1] == 'A'); + errcheck(ret != 0); + printf("\tuncompress does not overrun\n"); + + /* Got here, everything is fine. */ + ret = 0; + +out: + return ret; +} + +static int run_test(struct unit_test_state *uts, char *name, + mutate_func compress, mutate_func uncompress) +{ + struct buf_state sbuf, *buf = &sbuf; + int ret; + + printf(" testing %s ...\n", name); + + buf->orig_buf = (void *)plain; + buf->orig_size = strlen(buf->orig_buf); /* Trailing NUL not included */ + errcheck(buf->orig_size > 0); + + buf->compressed_size = TEST_BUFFER_SIZE; + buf->uncompressed_size = TEST_BUFFER_SIZE; + buf->compressed_buf = malloc(buf->compressed_size); + errcheck(buf->compressed_buf); + buf->uncompressed_buf = malloc(buf->uncompressed_size); + errcheck(buf->uncompressed_buf); + buf->compare_buf = malloc(buf->uncompressed_size); + errcheck(buf->compare_buf); + + ret = run_test_internal(uts, name, compress, uncompress, buf); +out: + printf(" %s: %s\n", name, ret == 0 ? "ok" : "FAILED"); + + free(buf->compare_buf); + free(buf->uncompressed_buf); + free(buf->compressed_buf); + + return ret; +} + +static int compression_test_gzip(struct unit_test_state *uts) +{ + return run_test(uts, "gzip", compress_using_gzip, + uncompress_using_gzip); +} +COMPRESSION_TEST(compression_test_gzip, 0); + +static int compression_test_bzip2(struct unit_test_state *uts) +{ + return run_test(uts, "bzip2", compress_using_bzip2, + uncompress_using_bzip2); +} +COMPRESSION_TEST(compression_test_bzip2, 0); + +static int compression_test_lzma(struct unit_test_state *uts) +{ + return run_test(uts, "lzma", compress_using_lzma, + uncompress_using_lzma); +} +COMPRESSION_TEST(compression_test_lzma, 0); + +static int compression_test_lzo(struct unit_test_state *uts) +{ + return run_test(uts, "lzo", compress_using_lzo, uncompress_using_lzo); +} +COMPRESSION_TEST(compression_test_lzo, 0); + +static int compression_test_lz4(struct unit_test_state *uts) +{ + return run_test(uts, "lz4", compress_using_lz4, uncompress_using_lz4); +} +COMPRESSION_TEST(compression_test_lz4, 0); + +static int compress_using_none(struct unit_test_state *uts, + void *in, unsigned long in_size, + void *out, unsigned long out_max, + unsigned long *out_size) +{ + /* Here we just copy */ + memcpy(out, in, in_size); + *out_size = in_size; + + return 0; +} + +/** + * run_bootm_test() - Run tests on the bootm decompression function + * + * @comp_type: Compression type to test + * @compress: Our function to compress data + * @return 0 if OK, non-zero on failure + */ +static int run_bootm_test(struct unit_test_state *uts, int comp_type, + mutate_func compress) +{ + ulong compress_size = 1024; + void *compress_buff; + int unc_len; + int err = 0; + const ulong image_start = 0; + const ulong load_addr = 0x1000; + ulong load_end; + + printf("Testing: %s\n", genimg_get_comp_name(comp_type)); + compress_buff = map_sysmem(image_start, 0); + unc_len = strlen(plain); + compress(uts, (void *)plain, unc_len, compress_buff, compress_size, + &compress_size); + err = image_decomp(comp_type, load_addr, image_start, + IH_TYPE_KERNEL, map_sysmem(load_addr, 0), + compress_buff, compress_size, unc_len, + &load_end); + ut_assertok(err); + err = image_decomp(comp_type, load_addr, image_start, + IH_TYPE_KERNEL, map_sysmem(load_addr, 0), + compress_buff, compress_size, unc_len - 1, + &load_end); + ut_assert(err); + + /* We can't detect corruption when not decompressing */ + if (comp_type == IH_COMP_NONE) + return 0; + memset(compress_buff + compress_size / 2, '\x49', + compress_size / 2); + err = image_decomp(comp_type, load_addr, image_start, + IH_TYPE_KERNEL, map_sysmem(load_addr, 0), + compress_buff, compress_size, 0x10000, + &load_end); + ut_assert(err); + + return 0; +} + +static int compression_test_bootm_gzip(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_GZIP, compress_using_gzip); +} +COMPRESSION_TEST(compression_test_bootm_gzip, 0); + +static int compression_test_bootm_bzip2(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_BZIP2, compress_using_bzip2); +} +COMPRESSION_TEST(compression_test_bootm_bzip2, 0); + +static int compression_test_bootm_lzma(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_LZMA, compress_using_lzma); +} +COMPRESSION_TEST(compression_test_bootm_lzma, 0); + +static int compression_test_bootm_lzo(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_LZO, compress_using_lzo); +} +COMPRESSION_TEST(compression_test_bootm_lzo, 0); + +static int compression_test_bootm_lz4(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_LZ4, compress_using_lz4); +} +COMPRESSION_TEST(compression_test_bootm_lz4, 0); + +static int compression_test_bootm_none(struct unit_test_state *uts) +{ + return run_bootm_test(uts, IH_COMP_NONE, compress_using_none); +} +COMPRESSION_TEST(compression_test_bootm_none, 0); + +int do_ut_compression(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(compression_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(compression_test); + + return cmd_ut_category("compression", "compression_test_", + tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/dm/Kconfig b/roms/u-boot/test/dm/Kconfig new file mode 100644 index 000000000..e5b341e52 --- /dev/null +++ b/roms/u-boot/test/dm/Kconfig @@ -0,0 +1,8 @@ +config UT_DM + bool "Enable driver model unit test command" + depends on SANDBOX && UNIT_TEST + help + This enables the 'ut dm' command which runs a series of unit + tests on the driver model code. Each subsystem (uclass) is tested. + If all is well then all tests pass although there will be a few + messages printed along the way. diff --git a/roms/u-boot/test/dm/Makefile b/roms/u-boot/test/dm/Makefile new file mode 100644 index 000000000..c9644617a --- /dev/null +++ b/roms/u-boot/test/dm/Makefile @@ -0,0 +1,107 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2013 Google, Inc + +obj-$(CONFIG_UT_DM) += test-dm.o + +# Tests for particular subsystems - when enabling driver model for a new +# subsystem you must add sandbox tests here. +ifeq ($(CONFIG_SPL_BUILD),y) +obj-$(CONFIG_SPL_OF_PLATDATA) += of_platdata.o +else +obj-$(CONFIG_UT_DM) += bus.o +obj-$(CONFIG_UT_DM) += test-driver.o +obj-$(CONFIG_UT_DM) += test-fdt.o +obj-$(CONFIG_UT_DM) += test-uclass.o + +obj-$(CONFIG_UT_DM) += core.o +obj-$(CONFIG_UT_DM) += read.o +obj-$(CONFIG_UT_DM) += phys2bus.o +ifneq ($(CONFIG_SANDBOX),) +obj-$(CONFIG_ACPIGEN) += acpi.o +obj-$(CONFIG_ACPIGEN) += acpigen.o +obj-$(CONFIG_ACPIGEN) += acpi_dp.o +obj-$(CONFIG_ADC) += adc.o +obj-$(CONFIG_SOUND) += audio.o +obj-$(CONFIG_AXI) += axi.o +obj-$(CONFIG_BLK) += blk.o +obj-$(CONFIG_BUTTON) += button.o +obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o +obj-$(CONFIG_CLK) += clk.o clk_ccf.o +obj-$(CONFIG_CPU) += cpu.o +obj-$(CONFIG_CROS_EC) += cros_ec.o +obj-$(CONFIG_DEVRES) += devres.o +obj-$(CONFIG_DMA) += dma.o +obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o +obj-$(CONFIG_DM_DSA) += dsa.o +obj-$(CONFIG_DM_ETH) += eth.o +ifneq ($(CONFIG_EFI_PARTITION),) +obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o +endif +obj-$(CONFIG_FIRMWARE) += firmware.o +obj-$(CONFIG_DM_GPIO) += gpio.o +obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o +obj-$(CONFIG_DM_I2C) += i2c.o +obj-$(CONFIG_SOUND) += i2s.o +obj-y += irq.o +obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o +obj-$(CONFIG_LED) += led.o +obj-$(CONFIG_DM_MAILBOX) += mailbox.o +obj-$(CONFIG_DM_MDIO) += mdio.o +obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o +obj-$(CONFIG_MISC) += misc.o +obj-$(CONFIG_DM_MMC) += mmc.o +obj-$(CONFIG_CMD_MUX) += mux-cmd.o +obj-$(CONFIG_MULTIPLEXER) += mux-emul.o +obj-$(CONFIG_MUX_MMIO) += mux-mmio.o +obj-y += fdtdec.o +obj-$(CONFIG_UT_DM) += nop.o +obj-y += ofnode.o +obj-y += ofread.o +obj-y += of_extra.o +obj-$(CONFIG_OSD) += osd.o +obj-$(CONFIG_DM_VIDEO) += panel.o +obj-$(CONFIG_EFI_PARTITION) += part.o +obj-$(CONFIG_DM_PCI) += pci.o +obj-$(CONFIG_P2SB) += p2sb.o +obj-$(CONFIG_PCI_ENDPOINT) += pci_ep.o +obj-$(CONFIG_PCH) += pch.o +obj-$(CONFIG_PHY) += phy.o +ifneq ($(CONFIG_PINMUX),) +obj-$(CONFIG_PINCONF) += pinmux.o +endif +obj-$(CONFIG_POWER_DOMAIN) += power-domain.o +obj-$(CONFIG_ACPI_PMC) += pmc.o +obj-$(CONFIG_DM_PMIC) += pmic.o +obj-$(CONFIG_DM_PWM) += pwm.o +obj-$(CONFIG_QFW) += qfw.o +obj-$(CONFIG_RAM) += ram.o +obj-y += regmap.o +obj-$(CONFIG_REMOTEPROC) += remoteproc.o +obj-$(CONFIG_DM_RESET) += reset.o +obj-$(CONFIG_SYSRESET) += sysreset.o +obj-$(CONFIG_DM_REGULATOR) += regulator.o +obj-$(CONFIG_DM_RNG) += rng.o +obj-$(CONFIG_DM_RTC) += rtc.o +obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o +obj-$(CONFIG_DM_SERIAL) += serial.o +obj-$(CONFIG_DM_SPI_FLASH) += sf.o +obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o +obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o +obj-$(CONFIG_SMEM) += smem.o +obj-$(CONFIG_SOC_DEVICE) += soc.o +obj-$(CONFIG_SOUND) += sound.o +obj-$(CONFIG_DM_SPI) += spi.o +obj-$(CONFIG_SPMI) += spmi.o +obj-y += syscon.o +obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o +obj-$(CONFIG_SYSINFO) += sysinfo.o +obj-$(CONFIG_SYSINFO_GPIO) += sysinfo-gpio.o +obj-$(CONFIG_TEE) += tee.o +obj-$(CONFIG_TIMER) += timer.o +obj-$(CONFIG_DM_USB) += usb.o +obj-$(CONFIG_DM_VIDEO) += video.o +obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o +obj-$(CONFIG_WDT) += wdt.o +endif +endif # !SPL diff --git a/roms/u-boot/test/dm/acpi.c b/roms/u-boot/test/dm/acpi.c new file mode 100644 index 000000000..2edab7be5 --- /dev/null +++ b/roms/u-boot/test/dm/acpi.c @@ -0,0 +1,569 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI table generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <malloc.h> +#include <mapmem.h> +#include <version.h> +#include <tables_csum.h> +#include <version.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include <acpi/acpi_table.h> +#include <asm/global_data.h> +#include <dm/acpi.h> +#include <dm/test.h> +#include <test/ut.h> +#include "acpi.h" + +#define BUF_SIZE 4096 + +/** + * struct testacpi_plat - Platform data for the test ACPI device + * + * @no_name: true to emit an empty ACPI name from testacpi_get_name() + * @return_error: true to return an error instead of a name + */ +struct testacpi_plat { + bool return_error; + bool no_name; +}; + +static int testacpi_write_tables(const struct udevice *dev, + struct acpi_ctx *ctx) +{ + struct acpi_dmar *dmar; + int ret; + + dmar = (struct acpi_dmar *)ctx->current; + acpi_create_dmar(dmar, DMAR_INTR_REMAP); + ctx->current += sizeof(struct acpi_dmar); + ret = acpi_add_table(ctx, dmar); + if (ret) + return log_msg_ret("add", ret); + + return 0; +} + +static int testacpi_get_name(const struct udevice *dev, char *out_name) +{ + struct testacpi_plat *plat = dev_get_plat(dev); + + if (plat->return_error) + return -EINVAL; + if (plat->no_name) { + *out_name = '\0'; + return 0; + } + if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI) + return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME); + else + return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME); +} + +static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-ssdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; +} + +static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx) +{ + const char *data; + + data = dev_read_string(dev, "acpi-dsdt-test-data"); + if (data) { + while (*data) + acpigen_emit_byte(ctx, *data++); + } + + return 0; +} + +struct acpi_ops testacpi_ops = { + .get_name = testacpi_get_name, + .write_tables = testacpi_write_tables, + .fill_ssdt = testacpi_fill_ssdt, + .inject_dsdt = testacpi_inject_dsdt, +}; + +static const struct udevice_id testacpi_ids[] = { + { .compatible = "denx,u-boot-acpi-test" }, + { } +}; + +U_BOOT_DRIVER(testacpi_drv) = { + .name = "testacpi_drv", + .of_match = testacpi_ids, + .id = UCLASS_TEST_ACPI, + .bind = dm_scan_fdt_dev, + .plat_auto = sizeof(struct testacpi_plat), + ACPI_OPS_PTR(&testacpi_ops) +}; + +UCLASS_DRIVER(testacpi) = { + .name = "testacpi", + .id = UCLASS_TEST_ACPI, +}; + +/* Test ACPI get_name() */ +static int dm_test_acpi_get_name(struct unit_test_state *uts) +{ + char name[ACPI_NAME_MAX]; + struct udevice *dev, *dev2, *i2c, *spi, *timer, *sound; + struct udevice *pci, *root; + + /* Test getting the name from the driver */ + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_assertok(acpi_get_name(dev, name)); + ut_asserteq_str(ACPI_TEST_DEV_NAME, name); + + /* Test getting the name from the device tree */ + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev2)); + ut_assertok(acpi_get_name(dev2, name)); + ut_asserteq_str("GHIJ", name); + + /* Test getting the name from acpi_device_get_name() */ + ut_assertok(uclass_first_device(UCLASS_I2C, &i2c)); + ut_assertok(acpi_get_name(i2c, name)); + ut_asserteq_str("I2C0", name); + + ut_assertok(uclass_first_device(UCLASS_SPI, &spi)); + ut_assertok(acpi_get_name(spi, name)); + ut_asserteq_str("SPI0", name); + + /* ACPI doesn't know about the timer */ + ut_assertok(uclass_first_device(UCLASS_TIMER, &timer)); + ut_asserteq(-ENOENT, acpi_get_name(timer, name)); + + /* May as well test the rest of the cases */ + ut_assertok(uclass_first_device(UCLASS_SOUND, &sound)); + ut_assertok(acpi_get_name(sound, name)); + ut_asserteq_str("HDAS", name); + + ut_assertok(uclass_first_device(UCLASS_PCI, &pci)); + ut_assertok(acpi_get_name(pci, name)); + ut_asserteq_str("PCI0", name); + + ut_assertok(uclass_first_device(UCLASS_ROOT, &root)); + ut_assertok(acpi_get_name(root, name)); + ut_asserteq_str("\\_SB", name); + + /* Note that we don't have tests for acpi_name_from_id() */ + + return 0; +} +DM_TEST(dm_test_acpi_get_name, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_get_table_revision() */ +static int dm_test_acpi_get_table_revision(struct unit_test_state *uts) +{ + ut_asserteq(1, acpi_get_table_revision(ACPITAB_MCFG)); + ut_asserteq(2, acpi_get_table_revision(ACPITAB_RSDP)); + ut_asserteq(4, acpi_get_table_revision(ACPITAB_TPM2)); + ut_asserteq(-EINVAL, acpi_get_table_revision(ACPITAB_COUNT)); + + return 0; +} +DM_TEST(dm_test_acpi_get_table_revision, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_create_dmar() */ +static int dm_test_acpi_create_dmar(struct unit_test_state *uts) +{ + struct acpi_dmar dmar; + struct udevice *cpu; + + ut_assertok(uclass_first_device(UCLASS_CPU, &cpu)); + ut_assertnonnull(cpu); + ut_assertok(acpi_create_dmar(&dmar, DMAR_INTR_REMAP)); + ut_asserteq(DMAR_INTR_REMAP, dmar.flags); + ut_asserteq(32 - 1, dmar.host_address_width); + + return 0; +} +DM_TEST(dm_test_acpi_create_dmar, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_fill_header() */ +static int dm_test_acpi_fill_header(struct unit_test_state *uts) +{ + struct acpi_table_header hdr; + + /* Make sure these 5 fields are not changed */ + hdr.length = 0x11; + hdr.revision = 0x22; + hdr.checksum = 0x33; + hdr.aslc_revision = 0x44; + acpi_fill_header(&hdr, "ABCD"); + + ut_asserteq_mem("ABCD", hdr.signature, sizeof(hdr.signature)); + ut_asserteq(0x11, hdr.length); + ut_asserteq(0x22, hdr.revision); + ut_asserteq(0x33, hdr.checksum); + ut_asserteq_mem(OEM_ID, hdr.oem_id, sizeof(hdr.oem_id)); + ut_asserteq_mem(OEM_TABLE_ID, hdr.oem_table_id, + sizeof(hdr.oem_table_id)); + ut_asserteq(U_BOOT_BUILD_DATE, hdr.oem_revision); + ut_asserteq_mem(ASLC_ID, hdr.aslc_id, sizeof(hdr.aslc_id)); + ut_asserteq(0x44, hdr.aslc_revision); + + return 0; +} +DM_TEST(dm_test_acpi_fill_header, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test ACPI write_tables() */ +static int dm_test_acpi_write_tables(struct unit_test_state *uts) +{ + struct acpi_dmar *dmar; + struct acpi_ctx ctx; + void *buf; + int i; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + acpi_setup_base_tables(&ctx, buf); + dmar = ctx.current; + ut_assertok(acpi_write_dev_tables(&ctx)); + + /* + * We should have three dmar tables, one for each + * "denx,u-boot-acpi-test" device + */ + ut_asserteq_ptr(dmar + 3, ctx.current); + ut_asserteq(DMAR_INTR_REMAP, dmar->flags); + ut_asserteq(32 - 1, dmar->host_address_width); + + ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags); + ut_asserteq(32 - 1, dmar[1].host_address_width); + + ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags); + ut_asserteq(32 - 1, dmar[2].host_address_width); + + /* Check that the pointers were added correctly */ + for (i = 0; i < 3; i++) { + ut_asserteq(map_to_sysmem(dmar + i), ctx.rsdt->entry[i]); + ut_asserteq(map_to_sysmem(dmar + i), ctx.xsdt->entry[i]); + } + ut_asserteq(0, ctx.rsdt->entry[3]); + ut_asserteq(0, ctx.xsdt->entry[3]); + + return 0; +} +DM_TEST(dm_test_acpi_write_tables, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test basic ACPI functions */ +static int dm_test_acpi_basic(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + + /* Check align works */ + ctx.current = (void *)5; + acpi_align(&ctx); + ut_asserteq_ptr((void *)16, ctx.current); + + /* Check that align does nothing if already aligned */ + acpi_align(&ctx); + ut_asserteq_ptr((void *)16, ctx.current); + acpi_align64(&ctx); + ut_asserteq_ptr((void *)64, ctx.current); + acpi_align64(&ctx); + ut_asserteq_ptr((void *)64, ctx.current); + + /* Check incrementing */ + acpi_inc(&ctx, 3); + ut_asserteq_ptr((void *)67, ctx.current); + acpi_inc_align(&ctx, 3); + ut_asserteq_ptr((void *)80, ctx.current); + + return 0; +} +DM_TEST(dm_test_acpi_basic, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_setup_base_tables */ +static int dm_test_acpi_setup_base_tables(struct unit_test_state *uts) +{ + struct acpi_rsdp *rsdp; + struct acpi_rsdt *rsdt; + struct acpi_xsdt *xsdt; + struct acpi_ctx ctx; + void *buf, *end; + + /* + * Use an unaligned address deliberately, by allocating an aligned + * address and then adding 4 to it + */ + buf = memalign(64, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf + 4); + ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd->arch.acpi_start); + + rsdp = buf + 16; + ut_asserteq_ptr(rsdp, ctx.rsdp); + ut_asserteq_mem(RSDP_SIG, rsdp->signature, sizeof(rsdp->signature)); + ut_asserteq(sizeof(*rsdp), rsdp->length); + ut_assertok(table_compute_checksum(rsdp, 20)); + ut_assertok(table_compute_checksum(rsdp, sizeof(*rsdp))); + + rsdt = PTR_ALIGN((void *)rsdp + sizeof(*rsdp), 16); + ut_asserteq_ptr(rsdt, ctx.rsdt); + ut_asserteq_mem("RSDT", rsdt->header.signature, ACPI_NAME_LEN); + ut_asserteq(sizeof(*rsdt), rsdt->header.length); + ut_assertok(table_compute_checksum(rsdt, sizeof(*rsdt))); + + xsdt = PTR_ALIGN((void *)rsdt + sizeof(*rsdt), 16); + ut_asserteq_ptr(xsdt, ctx.xsdt); + ut_asserteq_mem("XSDT", xsdt->header.signature, ACPI_NAME_LEN); + ut_asserteq(sizeof(*xsdt), xsdt->header.length); + ut_assertok(table_compute_checksum(xsdt, sizeof(*xsdt))); + + end = PTR_ALIGN((void *)xsdt + sizeof(*xsdt), 64); + ut_asserteq_ptr(end, ctx.current); + + ut_asserteq(map_to_sysmem(rsdt), rsdp->rsdt_address); + ut_asserteq(map_to_sysmem(xsdt), rsdp->xsdt_address); + + return 0; +} +DM_TEST(dm_test_acpi_setup_base_tables, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'acpi list' command */ +static int dm_test_acpi_cmd_list(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + console_record_reset(); + run_command("acpi list", 0); + addr = (ulong)map_to_sysmem(buf); + ut_assert_nextline("ACPI tables start at %lx", addr); + ut_assert_nextline("RSDP %08lx %06zx (v02 U-BOOT)", addr, + sizeof(struct acpi_rsdp)); + addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16); + ut_assert_nextline("RSDT %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + addr, sizeof(struct acpi_table_header) + + 3 * sizeof(u32), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16); + ut_assert_nextline("XSDT %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + addr, sizeof(struct acpi_table_header) + + 3 * sizeof(u64), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64); + ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); + addr = ALIGN(addr + sizeof(struct acpi_dmar), 16); + ut_assert_nextline("DMAR %08lx %06zx (v01 U-BOOT U-BOOTBL %x INTL 0)", + addr, sizeof(struct acpi_dmar), U_BOOT_BUILD_DATE); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'acpi dump' command */ +static int dm_test_acpi_cmd_dump(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + acpi_setup_base_tables(&ctx, buf); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + /* First search for a non-existent table */ + console_record_reset(); + run_command("acpi dump rdst", 0); + ut_assert_nextline("Table 'RDST' not found"); + ut_assert_console_end(); + + /* Now a real table */ + console_record_reset(); + run_command("acpi dump dmar", 0); + addr = ALIGN(map_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64); + ut_assert_nextline("DMAR @ %08lx", addr); + ut_assert_nextlines_are_dump(0x30); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_dump, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_device_path() */ +static int dm_test_acpi_device_path(struct unit_test_state *uts) +{ + struct testacpi_plat *plat; + char buf[ACPI_PATH_MAX]; + struct udevice *dev, *child; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_assertok(acpi_device_path(dev, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf); + + /* Test running out of space */ + buf[5] = '\0'; + ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5)); + ut_asserteq('\0', buf[5]); + + /* Test a three-component name */ + ut_assertok(device_first_child_err(dev, &child)); + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME, + buf); + + /* Test handling of a device which doesn't produce a name */ + plat = dev_get_plat(dev); + plat->no_name = true; + ut_assertok(acpi_device_path(child, buf, sizeof(buf))); + ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf); + + /* Test handling of a device which returns an error */ + plat = dev_get_plat(dev); + plat->return_error = true; + ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf))); + + return 0; +} +DM_TEST(dm_test_acpi_device_path, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_device_status() */ +static int dm_test_acpi_device_status(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev)); + + return 0; +} +DM_TEST(dm_test_acpi_device_status, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_fill_ssdt() */ +static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + acpi_reset_items(); + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_fill_ssdt(&ctx)); + + /* + * These values come from acpi-test2's acpi-ssdt-test-data property. + * This device comes first because of u-boot,acpi-ssdt-order + */ + ut_asserteq('c', buf[0]); + ut_asserteq('d', buf[1]); + + /* These values come from acpi-test's acpi-ssdt-test-data property */ + ut_asserteq('a', buf[2]); + ut_asserteq('b', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_fill_ssdt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test acpi_inject_dsdt() */ +static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + u8 *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + acpi_reset_items(); + ctx.current = buf; + buf[4] = 'z'; /* sentinel */ + ut_assertok(acpi_inject_dsdt(&ctx)); + + /* + * These values come from acpi-test's acpi-dsdt-test-data property. + * There is no u-boot,acpi-dsdt-order so device-tree order is used. + */ + ut_asserteq('h', buf[0]); + ut_asserteq('i', buf[1]); + + /* These values come from acpi-test's acpi-dsdt-test-data property */ + ut_asserteq('j', buf[2]); + ut_asserteq('k', buf[3]); + + ut_asserteq('z', buf[4]); + + return 0; +} +DM_TEST(dm_test_acpi_inject_dsdt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'acpi items' command */ +static int dm_test_acpi_cmd_items(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + void *buf; + + buf = malloc(BUF_SIZE); + ut_assertnonnull(buf); + + acpi_reset_items(); + ctx.current = buf; + ut_assertok(acpi_fill_ssdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 1, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 1, size 2"); + ut_assert_console_end(); + + acpi_reset_items(); + ctx.current = buf; + ut_assertok(acpi_inject_dsdt(&ctx)); + console_record_reset(); + run_command("acpi items", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_console_end(); + + console_record_reset(); + run_command("acpi items -d", 0); + ut_assert_nextline("dev 'acpi-test', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_nextline("dev 'acpi-test2', type 2, size 2"); + ut_assert_nextlines_are_dump(2); + ut_assert_nextline("%s", ""); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_items, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/acpi.h b/roms/u-boot/test/dm/acpi.h new file mode 100644 index 000000000..535db56b5 --- /dev/null +++ b/roms/u-boot/test/dm/acpi.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Common functions for ACPI tests + * + * Copyright 2020 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#ifndef __TEST_DM_ACPI_H +#define __TEST_DM_ACPI_H + +#define ACPI_TEST_DEV_NAME "ABCD" +#define ACPI_TEST_CHILD_NAME "EFGH" + +/** + * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size + * + * @ctxp: Returns allocated context + * @size: Size to allocate in bytes + * @return 0 if OK, -ENOMEM if out of memory + */ +int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size); + +/** + * acpi_test_get_length() - decode a three-byte length field + * + * @ptr: Length encoded as per ACPI + * @return decoded length, or -EINVAL on error + */ +int acpi_test_get_length(u8 *ptr); + +#endif /*__TEST_DM_ACPI_H */ diff --git a/roms/u-boot/test/dm/acpi_dp.c b/roms/u-boot/test/dm/acpi_dp.c new file mode 100644 index 000000000..44bcabda6 --- /dev/null +++ b/roms/u-boot/test/dm/acpi_dp.c @@ -0,0 +1,492 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI code generation via a device-property table + * + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <uuid.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_dp.h> +#include <asm/unaligned.h> +#include <dm/acpi.h> +#include <dm/test.h> +#include <test/ut.h> +#include "acpi.h" + +/* Maximum size of the ACPI context needed for most tests */ +#define ACPI_CONTEXT_SIZE 500 + +#define TEST_INT8 0x7d +#define TEST_INT16 0x2345 +#define TEST_INT32 0x12345678 +#define TEST_INT64 0x4567890123456 +#define TEST_STR "testing acpi strings" +#define TEST_REF "\\SB.I2C0.TPM2" +#define EXPECT_REF "SB__I2C0TPM2" + +static int alloc_context(struct acpi_ctx **ctxp) +{ + return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE); + + return 0; +} + +static void free_context(struct acpi_ctx **ctxp) +{ + free(*ctxp); + *ctxp = NULL; +} + +/* Test emitting an empty table */ +static int dm_test_acpi_dp_new_table(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(10, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, *(u8 *)ptr); + ut_asserteq_strn("FRED", (char *)ptr + 1); + ut_asserteq(PACKAGE_OP, ptr[5]); + ut_asserteq(4, acpi_test_get_length(ptr + 6)); + ut_asserteq(0, ptr[9]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_new_table, 0); + +/* Test emitting an integer */ +static int dm_test_acpi_dp_int(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + char uuid[UUID_STR_LEN + 1]; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(54, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, *(u8 *)ptr); + ut_asserteq_strn("FRED", (char *)ptr + 1); + ut_asserteq(PACKAGE_OP, ptr[5]); + ut_asserteq(48, acpi_test_get_length(ptr + 6)); + ut_asserteq(2, ptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, ptr[10]); + ut_asserteq(22, acpi_test_get_length(ptr + 11)); + ut_asserteq(WORD_PREFIX, ptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(ptr + 15))); + uuid_bin_to_str(ptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_UUID, uuid); + + /* Container package */ + ut_asserteq(PACKAGE_OP, ptr[33]); + ut_asserteq(20, acpi_test_get_length(ptr + 34)); + ut_asserteq(1, ptr[37]); + + /* Package with name and (integer) value */ + ut_asserteq(PACKAGE_OP, ptr[38]); + ut_asserteq(15, acpi_test_get_length(ptr + 39)); + ut_asserteq(2, ptr[42]); + ut_asserteq(STRING_PREFIX, ptr[43]); + ut_asserteq_str("MARY", (char *)ptr + 44); + + ut_asserteq(DWORD_PREFIX, ptr[49]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int, 0); + +/* Test emitting a 64-bit integer */ +static int dm_test_acpi_dp_int64(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(58, acpigen_get_current(ctx) - ptr); + + ut_asserteq(QWORD_PREFIX, ptr[49]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int64, 0); + +/* Test emitting a 16-bit integer */ +static int dm_test_acpi_dp_int16(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(52, acpigen_get_current(ctx) - ptr); + + ut_asserteq(WORD_PREFIX, ptr[49]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int16, 0); + +/* Test emitting a 8-bit integer */ +static int dm_test_acpi_dp_int8(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(51, acpigen_get_current(ctx) - ptr); + + ut_asserteq(BYTE_PREFIX, ptr[49]); + ut_asserteq(TEST_INT8, ptr[50]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_int8, 0); + +/* Test emitting multiple values */ +static int dm_test_acpi_dp_multiple(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16)); + ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR)); + ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(110, acpigen_get_current(ctx) - ptr); + + ut_asserteq(WORD_PREFIX, ptr[0x32]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33))); + ut_asserteq(STRING_PREFIX, ptr[0x3f]); + ut_asserteq_str(TEST_STR, (char *)ptr + 0x40); + ut_asserteq(ROOT_PREFIX, ptr[0x5f]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]); + ut_asserteq(3, ptr[0x61]); + ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_multiple, 0); + +/* Test emitting an array */ +static int dm_test_acpi_dp_array(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u64 speed[4]; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + speed[0] = TEST_INT8; + speed[1] = TEST_INT16; + speed[2] = TEST_INT32; + speed[3] = TEST_INT64; + ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed, + ARRAY_SIZE(speed))); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(75, acpigen_get_current(ctx) - ptr); + + ut_asserteq(BYTE_PREFIX, ptr[0x38]); + ut_asserteq(TEST_INT8, ptr[0x39]); + + ut_asserteq(WORD_PREFIX, ptr[0x3a]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b))); + + ut_asserteq(DWORD_PREFIX, ptr[0x3d]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e))); + + ut_asserteq(QWORD_PREFIX, ptr[0x42]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_array, 0); + +/* Test emitting a child */ +static int dm_test_acpi_dp_child(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp, *child1, *child2; + char uuid[UUID_STR_LEN + 1]; + u8 *ptr, *pptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + child1 = acpi_dp_new_table("child"); + ut_assertnonnull(child1); + ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16)); + + child2 = acpi_dp_new_table("child"); + ut_assertnonnull(child2); + ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1)); + ut_assertnonnull(acpi_dp_add_child(dp, "john", child2)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(178, acpigen_get_current(ctx) - ptr); + + /* UUID for child extension using Hierarchical Data Extension UUID */ + ut_asserteq(BUFFER_OP, ptr[10]); + ut_asserteq(22, acpi_test_get_length(ptr + 11)); + ut_asserteq(WORD_PREFIX, ptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(ptr + 15))); + uuid_bin_to_str(ptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid); + + /* Package with two children */ + ut_asserteq(PACKAGE_OP, ptr[0x21]); + ut_asserteq(0x28, acpi_test_get_length(ptr + 0x22)); + ut_asserteq(2, ptr[0x25]); + + /* First we expect the two children as string/value */ + pptr = ptr + 0x26; + for (i = 0; i < 2; i++) { + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(0x11, acpi_test_get_length(pptr + 1)); + ut_asserteq(2, pptr[4]); + ut_asserteq(STRING_PREFIX, pptr[5]); + ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6); + ut_asserteq(STRING_PREFIX, pptr[11]); + ut_asserteq_str("child", (char *)pptr + 12); + pptr += 0x12; + } + + /* Write the two children */ + ut_asserteq(0x4a, pptr - ptr); + for (i = 0; i < 2; i++) { + const char *prop = i ? "age" : "height"; + const int datalen = i ? 1 : 2; + int len = strlen(prop) + 1; + + ut_asserteq(NAME_OP, pptr[0]); + ut_asserteq_strn("chil", (char *)pptr + 1); + ut_asserteq(PACKAGE_OP, pptr[5]); + ut_asserteq(0x27 + len + datalen, acpi_test_get_length(pptr + 6)); + ut_asserteq(2, pptr[9]); + + /* UUID */ + ut_asserteq(BUFFER_OP, pptr[10]); + ut_asserteq(22, acpi_test_get_length(pptr + 11)); + ut_asserteq(WORD_PREFIX, pptr[14]); + ut_asserteq(16, get_unaligned((u16 *)(pptr + 15))); + uuid_bin_to_str(pptr + 17, uuid, 1); + ut_asserteq_str(ACPI_DP_UUID, uuid); + pptr += 33; + + /* Containing package */ + ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr); + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(0xb + len + datalen, acpi_test_get_length(pptr + 1)); + ut_asserteq(1, pptr[4]); + + /* Package containing the property-name string and the value */ + pptr += 5; + ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr); + ut_asserteq(PACKAGE_OP, pptr[0]); + ut_asserteq(6 + len + datalen, acpi_test_get_length(pptr + 1)); + ut_asserteq(2, pptr[4]); + + ut_asserteq(STRING_PREFIX, pptr[5]); + ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6); + pptr += 6 + len; + if (i) { + ut_asserteq(BYTE_PREFIX, pptr[0]); + ut_asserteq(TEST_INT8, pptr[1]); + } else { + ut_asserteq(WORD_PREFIX, pptr[0]); + ut_asserteq(TEST_INT16, + get_unaligned((u16 *)(pptr + 1))); + } + pptr += 1 + datalen; + } + ut_asserteq(178, pptr - ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_dp_child, 0); + +/* Test emitting a GPIO */ +static int dm_test_acpi_dp_gpio(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct acpi_dp *dp; + u8 *ptr, *pptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + /* Try a few different parameters */ + ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24, + ACPI_GPIO_ACTIVE_HIGH)); + ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0, + ACPI_GPIO_ACTIVE_LOW)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr); + + pptr = ptr + 0x2c; //0x3a; + ut_asserteq_str("reset", (char *)pptr); + ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe); + ut_asserteq(0x23, pptr[0x1b]); + ut_asserteq(0x24, pptr[0x1d]); + ut_asserteq(ZERO_OP, pptr[0x1e]); + + pptr = ptr + 0x51; + ut_asserteq_str("allow", (char *)pptr); + ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe); + ut_asserteq(ZERO_OP, pptr[0x1a]); + ut_asserteq(ZERO_OP, pptr[0x1b]); + ut_asserteq(ONE_OP, pptr[0x1c]); + + return 0; +} +DM_TEST(dm_test_acpi_dp_gpio, 0); + +/* Test copying info from the device tree to ACPI tables */ +static int dm_test_acpi_dp_copy(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + struct acpi_dp *dp; + ofnode node; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + dp = acpi_dp_new_table("FRED"); + ut_assertnonnull(dp); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value")); + ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value")); + ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value")); + + ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value")); + ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value")); + + node = ofnode_path("/chosen"); + ut_assert(ofnode_valid(node)); + ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values")); + ut_asserteq(-EINVAL, + acpi_dp_ofnode_copy_int(node, dp, "missing-value")); + + ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting")); + ut_asserteq(-EINVAL, + acpi_dp_ofnode_copy_str(node, dp, "missing-value")); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_dp_write(ctx, dp)); + ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr); + + ut_asserteq(STRING_PREFIX, ptr[0x2b]); + ut_asserteq_str("int-value", (char *)ptr + 0x2c); + ut_asserteq(WORD_PREFIX, ptr[0x36]); + ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37))); + + ut_asserteq(STRING_PREFIX, ptr[0x3e]); + ut_asserteq_str("uint-value", (char *)ptr + 0x3f); + ut_asserteq(DWORD_PREFIX, ptr[0x4a]); + ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b))); + + ut_asserteq(STRING_PREFIX, ptr[0x54]); + ut_asserteq_str("str-value", (char *)ptr + 0x55); + ut_asserteq(STRING_PREFIX, ptr[0x5f]); + ut_asserteq_str("test string", (char *)ptr + 0x60); + + ut_asserteq(STRING_PREFIX, ptr[0x71]); + ut_asserteq_str("int-values", (char *)ptr + 0x72); + ut_asserteq(WORD_PREFIX, ptr[0x7d]); + ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e))); + + ut_asserteq(STRING_PREFIX, ptr[0x85]); + ut_asserteq_str("setting", (char *)ptr + 0x86); + ut_asserteq(STRING_PREFIX, ptr[0x8e]); + ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f)); + + return 0; +} +DM_TEST(dm_test_acpi_dp_copy, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/acpigen.c b/roms/u-boot/test/dm/acpigen.c new file mode 100644 index 000000000..3ec2743af --- /dev/null +++ b/roms/u-boot/test/dm/acpigen.c @@ -0,0 +1,1746 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for ACPI code generation + * + * Copyright 2019 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <irq.h> +#include <malloc.h> +#include <uuid.h> +#include <acpi/acpigen.h> +#include <acpi/acpi_device.h> +#include <acpi/acpi_table.h> +#include <asm/gpio.h> +#include <asm/unaligned.h> +#include <dm/acpi.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> +#include "acpi.h" + +/* Maximum size of the ACPI context needed for most tests */ +#define ACPI_CONTEXT_SIZE 150 + +#define TEST_STRING "frogmore" +#define TEST_STRING2 "ranch" +#define TEST_STREAM2 "\xfa\xde" + +#define TEST_INT8 0x7d +#define TEST_INT16 0x2345 +#define TEST_INT32 0x12345678 +#define TEST_INT64 0x4567890123456 + +int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size) +{ + struct acpi_ctx *ctx; + + *ctxp = NULL; + ctx = malloc(sizeof(*ctx)); + if (!ctx) + return -ENOMEM; + ctx->base = malloc(size); + if (!ctx->base) { + free(ctx); + return -ENOMEM; + } + ctx->ltop = 0; + ctx->current = ctx->base; + *ctxp = ctx; + + return 0; +} + +int acpi_test_get_length(u8 *ptr) +{ + if (!(*ptr & 0x80)) + return -EINVAL; + + return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12; +} + +static int alloc_context(struct acpi_ctx **ctxp) +{ + return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE); +} + +static void free_context(struct acpi_ctx **ctxp) +{ + free((*ctxp)->base); + free(*ctxp); + *ctxp = NULL; +} + +/* Test emitting simple types and acpigen_get_current() */ +static int dm_test_acpi_emit_simple(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_byte(ctx, 0x23); + ut_asserteq(1, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x23, *(u8 *)ptr); + + acpigen_emit_word(ctx, 0x1234); + ut_asserteq(3, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x1234, get_unaligned((u16 *)(ptr + 1))); + + acpigen_emit_dword(ctx, 0x87654321); + ut_asserteq(7, acpigen_get_current(ctx) - ptr); + ut_asserteq(0x87654321, get_unaligned((u32 *)(ptr + 3))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_simple, 0); + +/* Test emitting a stream */ +static int dm_test_acpi_emit_stream(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_stream(ctx, TEST_STREAM2, 2); + ut_asserteq(2, acpigen_get_current(ctx) - ptr); + ut_asserteq((u8)TEST_STREAM2[0], ptr[0]); + ut_asserteq((u8)TEST_STREAM2[1], ptr[1]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_stream, 0); + +/* Test emitting a string */ +static int dm_test_acpi_emit_string(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_emit_string(ctx, TEST_STRING); + ut_asserteq(sizeof(TEST_STRING), acpigen_get_current(ctx) - ptr); + ut_asserteq_str(TEST_STRING, (char *)ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_emit_string, 0); + +/* Test emitting an interrupt descriptor */ +static int dm_test_acpi_interrupt(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + struct irq irq; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + + /* See a-test, property interrupts-extended in the device tree */ + ut_asserteq(3, acpi_device_write_interrupt_irq(ctx, &irq)); + ut_asserteq(9, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]); + ut_asserteq(6, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(0x19, ptr[3]); + ut_asserteq(1, ptr[4]); + ut_asserteq(3, get_unaligned((u32 *)(ptr + 5))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_interrupt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting a GPIO descriptor */ +static int dm_test_acpi_gpio(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + + /* This should write GPIO pin 4 (see device tree test.dts ) */ + ut_asserteq(4, acpi_device_write_gpio_desc(ctx, &desc)); + ut_asserteq(35, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + ut_asserteq(32, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]); + ut_asserteq(ACPI_GPIO_TYPE_IO, ptr[4]); + ut_asserteq(1, get_unaligned((u16 *)(ptr + 5))); + ut_asserteq(9, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_GPIO_PULL_UP, ptr[9]); + ut_asserteq(1234, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 12))); + ut_asserteq(23, get_unaligned((u16 *)(ptr + 14))); + ut_asserteq(0, ptr[16]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 17))); + ut_asserteq(35, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 21))); + + /* pin0 */ + ut_asserteq(4, get_unaligned((u16 *)(ptr + 23))); + + ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting a GPIO descriptor with an interrupt */ +static int dm_test_acpi_gpio_irq(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + + /* This should write GPIO pin 6 (see device tree test.dts ) */ + ut_asserteq(6, acpi_device_write_gpio_desc(ctx, &desc)); + ut_asserteq(35, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + ut_asserteq(32, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]); + ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, ptr[4]); + ut_asserteq(1, get_unaligned((u16 *)(ptr + 5))); + ut_asserteq(29, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_GPIO_PULL_DOWN, ptr[9]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(4321, get_unaligned((u16 *)(ptr + 12))); + ut_asserteq(23, get_unaligned((u16 *)(ptr + 14))); + ut_asserteq(0, ptr[16]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 17))); + ut_asserteq(35, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 21))); + + /* pin0 */ + ut_asserteq(6, get_unaligned((u16 *)(ptr + 23))); + + ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting either a GPIO or interrupt descriptor */ +static int dm_test_acpi_interrupt_or_gpio(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* This should produce an interrupt, even though it also has a GPIO */ + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_asserteq(3, acpi_device_write_interrupt_or_gpio(ctx, dev, + "test2-gpios")); + ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]); + + /* This has no interrupt so should produce a GPIO */ + ptr = ctx->current; + ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &dev)); + ut_asserteq(1, acpi_device_write_interrupt_or_gpio(ctx, dev, + "enable-gpios")); + ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]); + + /* This one has neither */ + ptr = acpigen_get_current(ctx); + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev)); + ut_asserteq_str("b-test", dev->name); + ut_asserteq(-ENOENT, + acpi_device_write_interrupt_or_gpio(ctx, dev, + "enable-gpios")); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_interrupt_or_gpio, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting an I2C descriptor */ +static int dm_test_acpi_i2c(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_asserteq(0x43, acpi_device_write_i2c_dev(ctx, dev)); + ut_asserteq(28, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]); + ut_asserteq(25, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_I2C_SERIAL_BUS_REVISION_ID, ptr[3]); + ut_asserteq(0, ptr[4]); + ut_asserteq(ACPI_SERIAL_BUS_TYPE_I2C, ptr[5]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_I2C_TYPE_SPECIFIC_REVISION_ID, ptr[9]); + ut_asserteq(6, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12))); + ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16))); + ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_i2c, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting a SPI descriptor */ +static int dm_test_acpi_spi(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev)); + ut_assertok(acpi_device_write_spi_dev(ctx, dev)); + ut_asserteq(31, acpigen_get_current(ctx) - ptr); + ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]); + ut_asserteq(28, get_unaligned((u16 *)(ptr + 1))); + ut_asserteq(ACPI_SPI_SERIAL_BUS_REVISION_ID, ptr[3]); + ut_asserteq(0, ptr[4]); + ut_asserteq(ACPI_SERIAL_BUS_TYPE_SPI, ptr[5]); + ut_asserteq(2, ptr[6]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 7))); + ut_asserteq(ACPI_SPI_TYPE_SPECIFIC_REVISION_ID, ptr[9]); + ut_asserteq(9, get_unaligned((u16 *)(ptr + 10))); + ut_asserteq(40000000, get_unaligned((u32 *)(ptr + 12))); + ut_asserteq(8, ptr[16]); + ut_asserteq(0, ptr[17]); + ut_asserteq(0, ptr[18]); + ut_asserteq(0, get_unaligned((u16 *)(ptr + 19))); + ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_spi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test emitting a length */ +static int dm_test_acpi_len(struct unit_test_state *uts) +{ + const int size = 0xc0000; + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(acpi_test_alloc_context_size(&ctx, size)); + + ptr = acpigen_get_current(ctx); + + /* Write a byte and a 3-byte length */ + acpigen_write_len_f(ctx); + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(1 + 3, acpi_test_get_length(ptr)); + + /* Write 200 bytes so we need two length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 200; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(200 + 3, acpi_test_get_length(ptr)); + + /* Write 40KB so we need three length bytes */ + ptr = ctx->current; + acpigen_write_len_f(ctx); + for (i = 0; i < 40000; i++) + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(40000 + 3, acpi_test_get_length(ptr)); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_len, 0); + +/* Test writing a package */ +static int dm_test_acpi_package(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + char *num_elements; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + num_elements = acpigen_write_package(ctx, 3); + ut_asserteq_ptr(num_elements, ptr + 4); + + /* For ease of testing, just emit a byte, not valid package contents */ + acpigen_emit_byte(ctx, 0x23); + acpigen_pop_len(ctx); + ut_asserteq(PACKAGE_OP, ptr[0]); + ut_asserteq(5, acpi_test_get_length(ptr + 1)); + ut_asserteq(3, ptr[4]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_package, 0); + +/* Test writing an integer */ +static int dm_test_acpi_integer(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + acpigen_write_integer(ctx, 0); + acpigen_write_integer(ctx, 1); + acpigen_write_integer(ctx, TEST_INT8); + acpigen_write_integer(ctx, TEST_INT16); + acpigen_write_integer(ctx, TEST_INT32); + acpigen_write_integer(ctx, TEST_INT64); + + ut_asserteq(6 + 1 + 2 + 4 + 8, acpigen_get_current(ctx) - ptr); + + ut_asserteq(ZERO_OP, ptr[0]); + + ut_asserteq(ONE_OP, ptr[1]); + + ut_asserteq(BYTE_PREFIX, ptr[2]); + ut_asserteq(TEST_INT8, ptr[3]); + + ut_asserteq(WORD_PREFIX, ptr[4]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 5))); + + ut_asserteq(DWORD_PREFIX, ptr[7]); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 8))); + + ut_asserteq(QWORD_PREFIX, ptr[12]); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 13))); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_integer, 0); + +/* Test writing a string */ +static int dm_test_acpi_string(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + acpigen_write_string(ctx, TEST_STRING); + acpigen_write_string(ctx, TEST_STRING2); + + ut_asserteq(2 + sizeof(TEST_STRING) + sizeof(TEST_STRING2), + acpigen_get_current(ctx) - ptr); + ut_asserteq(STRING_PREFIX, ptr[0]); + ut_asserteq_str(TEST_STRING, (char *)ptr + 1); + ptr += 1 + sizeof(TEST_STRING); + ut_asserteq(STRING_PREFIX, ptr[0]); + ut_asserteq_str(TEST_STRING2, (char *)ptr + 1); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_string, 0); + +/* Test writing a name */ +static int dm_test_acpi_name(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* + * The names here are made up for testing the various cases. The + * grammar is in the ACPI spec 6.3 section 19.2.2 + */ + acpigen_write_name(ctx, "\\_SB"); + acpigen_write_name(ctx, "\\_SB.I2C0"); + acpigen_write_name(ctx, "\\_SB.I2C0.TPM2"); + acpigen_write_name(ctx, "\\_SB.I2C0.TPM2.LONG"); + acpigen_write_name(ctx, "^^^^SPI0.FLAS"); + acpigen_write_name(ctx, "NN"); + acpigen_write_name(ctx, "^AB.CD.D.EFG"); + acpigen_write_name(ctx, "^^^^"); + acpigen_write_name(ctx, "\\"); + acpigen_write_name(ctx, "\\ABCD"); + + ut_asserteq(107, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("\\_SB_", (char *)ptr + 1); + ptr += 6; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(DUAL_NAME_PREFIX, ptr[2]); + ut_asserteq_strn("_SB_I2C0", (char *)ptr + 3); + ptr += 11; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(3, ptr[3]); + ut_asserteq_strn("_SB_I2C0TPM2", (char *)ptr + 4); + ptr += 16; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(4, ptr[3]); + ut_asserteq_strn("_SB_I2C0TPM2LONG", (char *)ptr + 4); + ptr += 20; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq('^', ptr[2]); + ut_asserteq('^', ptr[3]); + ut_asserteq('^', ptr[4]); + ut_asserteq(DUAL_NAME_PREFIX, ptr[5]); + ut_asserteq_strn("SPI0FLAS", (char *)ptr + 6); + ptr += 14; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("NN__", (char *)ptr + 1); + ptr += 5; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq(MULTI_NAME_PREFIX, ptr[2]); + ut_asserteq(4, ptr[3]); + ut_asserteq_strn("AB__CD__D___EFG_", (char *)ptr + 4); + ptr += 20; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('^', ptr[1]); + ut_asserteq('^', ptr[2]); + ut_asserteq('^', ptr[3]); + ut_asserteq('^', ptr[4]); + ut_asserteq(ZERO_OP, ptr[5]); + ptr += 6; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq('\\', ptr[1]); + ut_asserteq(ZERO_OP, ptr[2]); + ptr += 3; + + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn("\\ABCD", (char *)ptr + 1); + ptr += 5; + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_name, 0); + +/* Test writing a UUID */ +static int dm_test_acpi_uuid(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + ut_assertok(acpigen_write_uuid(ctx, + "dbb8e3e6-5886-4ba6-8795-1319f52a966b")); + ut_asserteq(23, acpigen_get_current(ctx) - ptr); + ut_asserteq(BUFFER_OP, ptr[0]); + ut_asserteq(22, acpi_test_get_length(ptr + 1)); + ut_asserteq(0xdbb8e3e6, get_unaligned((u32 *)(ptr + 7))); + ut_asserteq(0x5886, get_unaligned((u16 *)(ptr + 11))); + ut_asserteq(0x4ba6, get_unaligned((u16 *)(ptr + 13))); + ut_asserteq(0x9587, get_unaligned((u16 *)(ptr + 15))); + ut_asserteq(0x2af51913, get_unaligned((u32 *)(ptr + 17))); + ut_asserteq(0x6b96, get_unaligned((u16 *)(ptr + 21))); + + /* Try a bad UUID */ + ut_asserteq(-EINVAL, + acpigen_write_uuid(ctx, + "dbb8e3e6-5886-4ba6x8795-1319f52a966b")); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_uuid, 0); + +/* Test writing misc ACPI codes */ +static int dm_test_acpi_misc(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + const int flags = 3; + const int nargs = 4; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_sleep(ctx, TEST_INT64); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 3))); + ptr += 11; + + acpigen_write_store(ctx); + ut_asserteq(STORE_OP, *ptr); + ptr++; + + acpigen_write_debug_string(ctx, TEST_STRING); + ut_asserteq_str(TEST_STRING, (char *)ptr + 2); + ptr += 2 + sizeof(TEST_STRING); + ut_asserteq(EXT_OP_PREFIX, ptr[0]); + ut_asserteq(DEBUG_OP, ptr[1]); + ptr += 2; + + acpigen_write_sta(ctx, flags); + ut_asserteq(METHOD_OP, ptr[0]); + ut_asserteq(11, acpi_test_get_length(ptr + 1)); + ut_asserteq_strn("_STA", (char *)ptr + 4); + ut_asserteq(0, ptr[8]); + ut_asserteq(RETURN_OP, ptr[9]); + ut_asserteq(BYTE_PREFIX, ptr[10]); + ut_asserteq(flags, ptr[11]); + ptr += 12; + + acpigen_write_sleep(ctx, TEST_INT16); + ut_asserteq(SLEEP_OP, ptr[1]); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 3))); + ptr += 5; + + acpigen_write_method_serialized(ctx, "FRED", nargs); + ut_asserteq(METHOD_OP, ptr[0]); + ut_asserteq_strn("FRED", (char *)ptr + 4); + ut_asserteq(1 << 3 | nargs, ptr[8]); + ut_asserteq(1, ctx->ltop); /* method is unfinished */ + + ptr += 9; + acpigen_write_or(ctx, LOCAL0_OP, LOCAL1_OP, LOCAL2_OP); + acpigen_write_and(ctx, LOCAL3_OP, LOCAL4_OP, LOCAL5_OP); + acpigen_write_not(ctx, LOCAL6_OP, LOCAL7_OP); + ut_asserteq(OR_OP, ptr[0]); + ut_asserteq(LOCAL0_OP, ptr[1]); + ut_asserteq(LOCAL1_OP, ptr[2]); + ut_asserteq(LOCAL2_OP, ptr[3]); + + ptr += 4; + ut_asserteq(AND_OP, ptr[0]); + ut_asserteq(LOCAL3_OP, ptr[1]); + ut_asserteq(LOCAL4_OP, ptr[2]); + ut_asserteq(LOCAL5_OP, ptr[3]); + + ptr += 4; + ut_asserteq(NOT_OP, ptr[0]); + ut_asserteq(LOCAL6_OP, ptr[1]); + ut_asserteq(LOCAL7_OP, ptr[2]); + ptr += 3; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_misc, 0); + +/* Test writing an ACPI power resource */ +static int dm_test_acpi_power_res(struct unit_test_state *uts) +{ + const char *const states[] = { "_PR0", "_PR3" }; + const char *name = "PRIC"; + const int level = 3; + const int order = 2; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + + /* PowerResource (PRIC, 0, 0) */ + acpigen_write_power_res(ctx, name, level, order, states, + ARRAY_SIZE(states)); + ut_asserteq(0x28, acpigen_get_current(ctx) - ptr); + ut_asserteq(NAME_OP, ptr[0]); + ut_asserteq_strn(states[0], (char *)ptr + 1); + ut_asserteq(8, acpi_test_get_length(ptr + 6)); + ut_asserteq_strn(name, (char *)ptr + 0xa); + + ut_asserteq_strn(states[1], (char *)ptr + 0xf); + ut_asserteq(8, acpi_test_get_length(ptr + 0x14)); + ut_asserteq_strn(name, (char *)ptr + 0x18); + + ut_asserteq(POWER_RES_OP, ptr[0x1d]); + ut_asserteq_strn(name, (char *)ptr + 0x21); + ut_asserteq(level, ptr[0x25]); + ut_asserteq(order, get_unaligned((u16 *)(ptr + 0x26))); + + /* The length is not set - caller must use acpigen_pop_len() */ + ut_asserteq(1, ctx->ltop); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_power_res, 0); + +/* Test writing ACPI code to toggle a GPIO */ +static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts) +{ + const uint addr = 0x80012; + const int txbit = BIT(2); + struct gpio_desc desc; + struct acpi_gpio gpio; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + ut_assertok(gpio_get_acpi(&desc, &gpio)); + + /* Spot-check the results - see sb_gpio_get_acpi() */ + ptr = acpigen_get_current(ctx); + acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0", + &gpio, true); + acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0", + &gpio, false); + + /* Since this GPIO is active low, we expect it to be cleared here */ + ut_asserteq(STORE_OP, *ptr); + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 3); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0xc))); + ut_asserteq(LOCAL5_OP, ptr[0x10]); + + ut_asserteq(STORE_OP, ptr[0x11]); + ut_asserteq(BYTE_PREFIX, ptr[0x12]); + ut_asserteq(txbit, ptr[0x13]); + ut_asserteq(LOCAL0_OP, ptr[0x14]); + + ut_asserteq(NOT_OP, ptr[0x15]); + ut_asserteq(LOCAL0_OP, ptr[0x16]); + ut_asserteq(LOCAL6_OP, ptr[0x17]); + ut_asserteq(AND_OP, ptr[0x18]); + ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x1e); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x27))); + ut_asserteq(LOCAL5_OP, ptr[0x2b]); + + /* Now the second one, which should be set */ + ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x2f); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x38))); + ut_asserteq(LOCAL5_OP, ptr[0x3c]); + + ut_asserteq(STORE_OP, ptr[0x3d]); + + ut_asserteq(OR_OP, ptr[0x41]); + ut_asserteq(LOCAL0_OP, ptr[0x43]); + ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x47); + ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x50))); + ut_asserteq(LOCAL5_OP, ptr[0x54]); + ut_asserteq(0x55, acpigen_get_current(ctx) - ptr); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_gpio_toggle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test writing ACPI code to output power-sequence info */ +static int dm_test_acpi_power_seq(struct unit_test_state *uts) +{ + struct gpio_desc reset, enable, stop; + const uint addr = 0xc00dc, addr_act_low = 0x80012; + const int txbit = BIT(2); + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(acpi_test_alloc_context_size(&ctx, 400)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 0, &reset, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 1, &enable, 0)); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &stop, 0)); + ptr = acpigen_get_current(ctx); + + ut_assertok(acpi_device_add_power_res(ctx, txbit, "\\_SB.GPC0", + "\\_SB.SPC0", &reset, 2, 3, + &enable, 4, 5, &stop, 6, 7)); + ut_asserteq(0x186, acpigen_get_current(ctx) - ptr); + ut_asserteq_strn("PRIC", (char *)ptr + 0x18); + + /* First the 'ON' sequence - spot check */ + ut_asserteq_strn("_ON_", (char *)ptr + 0x38); + + /* reset set */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x49))); + ut_asserteq(OR_OP, ptr[0x52]); + + /* enable set */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x72))); + ut_asserteq(OR_OP, ptr[0x7b]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x9f))); + ut_asserteq(NOT_OP, ptr[0xa8]); + + /* stop set (disable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0xcf))); + ut_asserteq(OR_OP, ptr[0xd8]); + + /* Now the 'OFF' sequence */ + ut_asserteq_strn("_OFF", (char *)ptr + 0xf4); + + /* stop clear (enable, active low) */ + ut_asserteq(addr_act_low + stop.offset, + get_unaligned((u32 *)(ptr + 0x105))); + ut_asserteq(NOT_OP, ptr[0x10e]); + + /* reset clear */ + ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x135))); + ut_asserteq(OR_OP, ptr[0x13e]); + + /* enable clear */ + ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x162))); + ut_asserteq(NOT_OP, ptr[0x16b]); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_power_seq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test writing values */ +static int dm_test_acpi_write_values(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_zero(ctx); + acpigen_write_one(ctx); + acpigen_write_byte(ctx, TEST_INT8); + acpigen_write_word(ctx, TEST_INT16); + acpigen_write_dword(ctx, TEST_INT32); + acpigen_write_qword(ctx, TEST_INT64); + + ut_asserteq(ZERO_OP, *ptr++); + + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(TEST_INT8, *ptr++); + + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr)); + ptr += 2; + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_values, 0); + +/* Test writing a scope */ +static int dm_test_acpi_scope(struct unit_test_state *uts) +{ + char buf[ACPI_PATH_MAX]; + struct acpi_ctx *ctx; + struct udevice *dev; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev)); + ut_assertok(acpi_device_path(dev, buf, sizeof(buf))); + acpigen_write_scope(ctx, buf); + acpigen_pop_len(ctx); + + ut_asserteq(SCOPE_OP, *ptr++); + ut_asserteq(13, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ROOT_PREFIX, *ptr++); + ut_asserteq(DUAL_NAME_PREFIX, *ptr++); + ut_asserteq_strn("_SB_" ACPI_TEST_DEV_NAME, (char *)ptr); + ptr += 8; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_scope, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test writing a resource template */ +static int dm_test_acpi_resource_template(struct unit_test_state *uts) +{ + struct acpi_gen_regaddr addr; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + addr.space_id = ACPI_ADDRESS_SPACE_EC; + addr.bit_width = 32; + addr.bit_offset = 8; + addr.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS; + addr.addrl = TEST_INT64 & 0xffffffff; + addr.addrh = TEST_INT64 >> 32; + acpigen_write_register_resource(ctx, &addr); + + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(0x17, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(0x11, get_unaligned((u16 *)ptr)); + ptr += 2; + ut_asserteq(ACPI_DESCRIPTOR_REGISTER, *ptr++); + ut_asserteq(0xc, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(ACPI_ADDRESS_SPACE_EC, *ptr++); + ut_asserteq(32, *ptr++); + ut_asserteq(8, *ptr++); + ut_asserteq(ACPI_ACCESS_SIZE_DWORD_ACCESS, *ptr++); + ut_asserteq(TEST_INT64 & 0xffffffff, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(TEST_INT64 >> 32, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(ACPI_END_TAG, *ptr++); + ut_asserteq(0x00, *ptr++); + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_resource_template, 0); + +/* Test writing a device */ +static int dm_test_acpi_device(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_device(ctx, "\\_SB." ACPI_TEST_DEV_NAME); + acpigen_pop_len(ctx); + + ut_asserteq(EXT_OP_PREFIX, *ptr++); + ut_asserteq(DEVICE_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ROOT_PREFIX, *ptr++); + ut_asserteq(DUAL_NAME_PREFIX, *ptr++); + ptr += 8; + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_device, 0); + +/* Test writing named values */ +static int dm_test_acpi_write_name(struct unit_test_state *uts) +{ + const char *name = "\\_SB." ACPI_TEST_DEV_NAME; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + ptr = acpigen_get_current(ctx); + + acpigen_write_name_zero(ctx, name); + acpigen_write_name_one(ctx, name); + acpigen_write_name_byte(ctx, name, TEST_INT8); + acpigen_write_name_word(ctx, name, TEST_INT16); + acpigen_write_name_dword(ctx, name, TEST_INT32); + acpigen_write_name_qword(ctx, name, TEST_INT64); + acpigen_write_name_integer(ctx, name, TEST_INT64 + 1); + acpigen_write_name_string(ctx, name, "baldrick"); + acpigen_write_name_string(ctx, name, NULL); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("\\._SB_ABCD", (char *)ptr); + ptr += 10; + ut_asserteq(ZERO_OP, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(TEST_INT8, *ptr++); + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr)); + ptr += 2; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(QWORD_PREFIX, *ptr++); + ut_asserteq_64(TEST_INT64 + 1, get_unaligned((u64 *)ptr)); + ptr += 8; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(STRING_PREFIX, *ptr++); + ut_asserteq_str("baldrick", (char *)ptr) + ptr += 9; + + ut_asserteq(NAME_OP, *ptr++); + ptr += 10; + ut_asserteq(STRING_PREFIX, *ptr++); + ut_asserteq('\0', *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_name, 0); + +/* Test emitting a _PRW component */ +static int dm_test_acpi_write_prw(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_prw(ctx, 5, 3); + ut_asserteq(NAME_OP, *ptr++); + + ut_asserteq_strn("_PRW", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ut_asserteq(8, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(2, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(3, *ptr++); + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_prw, 0); + +/* Test emitting writing conditionals */ +static int dm_test_acpi_write_cond(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_if(ctx); + acpigen_pop_len(ctx); + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(3, acpi_test_get_length(ptr)); + ptr += 3; + + acpigen_write_else(ctx); + acpigen_pop_len(ctx); + ut_asserteq(ELSE_OP, *ptr++); + ut_asserteq(3, acpi_test_get_length(ptr)); + ptr += 3; + + acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, 5); + acpigen_pop_len(ctx); + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(7, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_cond, 0); + +/* Test emitting writing return values and ToBuffer/ToInteger */ +static int dm_test_acpi_write_return(struct unit_test_state *uts) +{ + int len = sizeof(TEST_STRING); + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP); + ut_asserteq(TO_BUFFER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + acpigen_write_to_integer(ctx, ARG0_OP, LOCAL0_OP); + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + acpigen_write_return_byte_buffer(ctx, (u8 *)TEST_STRING, len); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(5 + len, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(len, *ptr++); + ut_asserteq_mem(TEST_STRING, ptr, len); + ptr += len; + + acpigen_write_return_singleton_buffer(ctx, 123); + len = 1; + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(4 + len, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(ONE_OP, *ptr++); + ut_asserteq(123, *ptr++); + + acpigen_write_return_byte(ctx, 43); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(43, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_return, 0); + +/* Test emitting a DSM for an I2C HID */ +static int dm_test_acpi_write_i2c_dsm(struct unit_test_state *uts) +{ + char uuid_str[UUID_STR_LEN + 1]; + const int reg_offset = 0x20; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + ut_assertok(acpi_device_write_dsm_i2c_hid(ctx, reg_offset)); + + /* acpigen_write_dsm_start() */ + ut_asserteq(METHOD_OP, *ptr++); + ut_asserteq(0x78, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq_strn("_DSM", (char *)ptr); + ptr += 4; + ut_asserteq(ACPI_METHOD_SERIALIZED_MASK | 4, *ptr++); + + ut_asserteq(TO_BUFFER_OP, *ptr++); + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + /* acpigen_write_dsm_uuid_start() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0x65, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL0_OP, *ptr++); + + ut_asserteq(BUFFER_OP, *ptr++); + ut_asserteq(UUID_BIN_LEN + 6, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(WORD_PREFIX, *ptr++); + ut_asserteq(UUID_BIN_LEN, get_unaligned((u16 *)ptr)); + ptr += 2; + uuid_bin_to_str(ptr, uuid_str, UUID_STR_FORMAT_GUID); + ut_asserteq_str(ACPI_DSM_I2C_HID_UUID, uuid_str); + ptr += UUID_BIN_LEN; + + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG2_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + + /* acpigen_write_dsm_uuid_start_cond() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0x34, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(ZERO_OP, *ptr++); + + /* + * See code from acpi_device_write_dsm_i2c_hid(). We don't check every + * piece + */ + ut_asserteq(TO_INTEGER_OP, *ptr++); + ut_asserteq(ARG1_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + ut_asserteq(ZERO_OP, *ptr++); /* function 0 */ + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq(ELSE_OP, *ptr++); + ptr += 3; + + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(0xd, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL2_OP, *ptr++); + ut_asserteq(ONE_OP, *ptr++); + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq(ELSE_OP, *ptr++); + ptr += 3; + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + /* acpigen_write_dsm_uuid_start_cond() */ + ut_asserteq(IF_OP, *ptr++); + ut_asserteq(9, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq(LEQUAL_OP, *ptr++); + ut_asserteq(LOCAL1_OP, *ptr++); + ut_asserteq(ONE_OP, *ptr++); /* function 1 */ + + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(reg_offset, *ptr++); + + /* acpigen_write_dsm_uuid_end() */ + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + /* acpigen_write_dsm_end */ + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 5; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_i2c_dsm, 0); + +/* Test emitting a processor */ +static int dm_test_acpi_write_processor(struct unit_test_state *uts) +{ + const int cpuindex = 6; + const u32 pblock_addr = 0x12345600; + const u32 pblock_len = 0x60; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor(ctx, cpuindex, pblock_addr, pblock_len); + acpigen_pop_len(ctx); + + ut_asserteq(EXT_OP_PREFIX, *ptr++); + ut_asserteq(PROCESSOR_OP, *ptr++); + ut_asserteq(0x13, acpi_test_get_length(ptr)); + ptr += 3; + ut_asserteq_strn("\\._PR_CP06", (char *)ptr); + ptr += 10; + ut_asserteq(cpuindex, *ptr++); + ut_asserteq(pblock_addr, get_unaligned((u32 *)ptr)); + ptr += 4; + ut_asserteq(pblock_len, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor, 0); + +/* Test emitting a processor package */ +static int dm_test_acpi_write_processor_package(struct unit_test_state *uts) +{ + const int core_count = 3; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor_package(ctx, "XCPU", 0, core_count); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("XCPU", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(core_count, *ptr++); + + ut_asserteq_strn("\\._PR_CP00", (char *)ptr); + ptr += 10; + ut_asserteq_strn("\\._PR_CP01", (char *)ptr); + ptr += 10; + ut_asserteq_strn("\\._PR_CP02", (char *)ptr); + ptr += 10; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor_package, 0); + +/* Test emitting a processor notification package */ +static int dm_test_acpi_write_processor_cnot(struct unit_test_state *uts) +{ + const int core_count = 3; + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_processor_cnot(ctx, core_count); + + ut_asserteq(METHOD_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq_strn("\\._PR_CNOT", (char *)ptr); + ptr += 10; + ut_asserteq(1, *ptr++); + + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP00", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP01", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + ut_asserteq(NOTIFY_OP, *ptr++); + ut_asserteq_strn("\\._PR_CP02", (char *)ptr); + ptr += 10; + ut_asserteq(ARG0_OP, *ptr++); + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_processor_cnot, 0); + +/* Test acpigen_write_tpc */ +static int dm_test_acpi_write_tpc(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tpc(ctx, "\\TLVL"); + + ut_asserteq(METHOD_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq_strn("_TPC", (char *)ptr); + ptr += 4; + ut_asserteq(0, *ptr++); + ut_asserteq(RETURN_OP, *ptr++); + ut_asserteq_strn("\\TLVL", (char *)ptr); + ptr += 5; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tpc, 0); + +/* Test acpigen_write_pss_package(), etc. */ +static int dm_test_acpi_write_pss_psd(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_pss_package(ctx, 1, 2, 3, 4, 5, 6); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(6, *ptr++); + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(1, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(2, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(3, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(4, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(5, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(6, get_unaligned((u32 *)ptr)); + ptr += 4; + + acpigen_write_psd_package(ctx, 6, 7, HW_ALL); + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_PSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(6, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + + ut_asserteq(7, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_pss_psd, 0); + +/* Test acpi_write_cst_package() */ +static int dm_test_acpi_write_cst(struct unit_test_state *uts) +{ + static struct acpi_cstate cstate_map[] = { + { + /* C1 */ + .ctype = 1, /* ACPI C1 */ + .latency = 1, + .power = 1000, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_FIXED, + }, + }, { + .ctype = 2, /* ACPI C2 */ + .latency = 50, + .power = 10, + .resource = { + .space_id = ACPI_ADDRESS_SPACE_IO, + .bit_width = 8, + .addrl = 0x415, + }, + }, + }; + int nentries = ARRAY_SIZE(cstate_map); + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_cst_package(ctx, cstate_map, nentries); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_CST", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(nentries + 1, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(nentries, get_unaligned((u32 *)ptr)); + ptr += 4; + + for (i = 0; i < nentries; i++) { + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(4, *ptr++); + ut_asserteq(BUFFER_OP, *ptr++); + ptr += 0x17; + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(cstate_map[i].ctype, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(cstate_map[i].latency, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(cstate_map[i].power, get_unaligned((u32 *)ptr)); + ptr += 4; + } + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_cst, 0); + +/* Test acpi_write_cst_package() */ +static int dm_test_acpi_write_csd(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_csd_package(ctx, 12, 34, CSD_HW_ALL, 56); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_CSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(6, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(6, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(12, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(34, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(56, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_csd, 0); + +/* Test acpigen_write_tss_package() */ +static int dm_test_acpi_write_tss(struct unit_test_state *uts) +{ + static struct acpi_tstate tstate_list[] = { + { 1, 2, 3, 4, 5, }, + { 6, 7, 8, 9, 10, }, + }; + int nentries = ARRAY_SIZE(tstate_list); + struct acpi_ctx *ctx; + u8 *ptr; + int i; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tss_package(ctx, tstate_list, nentries); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_TSS", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(nentries, *ptr++); + + for (i = 0; i < nentries; i++) { + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(tstate_list[i].percent, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].power, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].latency, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].control, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(tstate_list[i].status, get_unaligned((u32 *)ptr)); + ptr += 4; + } + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tss, 0); + +/* Test acpigen_write_tsd_package() */ +static int dm_test_acpi_write_tsd_package(struct unit_test_state *uts) +{ + struct acpi_ctx *ctx; + u8 *ptr; + + ut_assertok(alloc_context(&ctx)); + + ptr = acpigen_get_current(ctx); + acpigen_write_tsd_package(ctx, 12, 34, HW_ALL); + + ut_asserteq(NAME_OP, *ptr++); + ut_asserteq_strn("_TSD", (char *)ptr); + ptr += 4; + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(1, *ptr++); + ut_asserteq(PACKAGE_OP, *ptr++); + ptr += 3; /* skip length */ + ut_asserteq(5, *ptr++); + + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(5, *ptr++); + ut_asserteq(BYTE_PREFIX, *ptr++); + ut_asserteq(0, *ptr++); + ut_asserteq(DWORD_PREFIX, *ptr++); + ut_asserteq(12, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr)); + ptr += 5; + ut_asserteq(34, get_unaligned((u32 *)ptr)); + ptr += 4; + + ut_asserteq_ptr(ptr, ctx->current); + + free_context(&ctx); + + return 0; +} +DM_TEST(dm_test_acpi_write_tsd_package, 0); diff --git a/roms/u-boot/test/dm/adc.c b/roms/u-boot/test/dm/adc.c new file mode 100644 index 000000000..740167e16 --- /dev/null +++ b/roms/u-boot/test/dm/adc.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the driver model ADC API + * + * Copyright (c) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ + +#include <common.h> +#include <adc.h> +#include <dm.h> +#include <dm/root.h> +#include <dm/util.h> +#include <dm/test.h> +#include <errno.h> +#include <fdtdec.h> +#include <power/regulator.h> +#include <power/sandbox_pmic.h> +#include <sandbox-adc.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_adc_bind(struct unit_test_state *uts) +{ + struct udevice *dev; + unsigned int channel_mask; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + ut_asserteq_str(SANDBOX_ADC_DEVNAME, dev->name); + + ut_assertok(adc_channel_mask(dev, &channel_mask)); + ut_asserteq((1 << SANDBOX_ADC_CHANNELS) - 1, channel_mask); + + return 0; +} +DM_TEST(dm_test_adc_bind, UT_TESTF_SCAN_FDT); + +static int dm_test_adc_wrong_channel_selection(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + ut_asserteq(-EINVAL, adc_start_channel(dev, SANDBOX_ADC_CHANNELS)); + + return 0; +} +DM_TEST(dm_test_adc_wrong_channel_selection, UT_TESTF_SCAN_FDT); + +static int dm_test_adc_supply(struct unit_test_state *uts) +{ + struct udevice *supply; + struct udevice *dev; + int uV; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + + /* Test Vss value - predefined 0 uV */ + ut_assertok(adc_vss_value(dev, &uV)); + ut_asserteq(SANDBOX_ADC_VSS_VALUE, uV); + + /* Test Vdd initial value - buck2 */ + ut_assertok(adc_vdd_value(dev, &uV)); + ut_asserteq(SANDBOX_BUCK2_INITIAL_EXPECTED_UV, uV); + + /* Change Vdd value - buck2 manual preset */ + ut_assertok(regulator_get_by_devname(SANDBOX_BUCK2_DEVNAME, &supply)); + ut_assertok(regulator_set_value(supply, SANDBOX_BUCK2_SET_UV)); + ut_asserteq(SANDBOX_BUCK2_SET_UV, regulator_get_value(supply)); + + /* Update ADC plat and get new Vdd value */ + ut_assertok(adc_vdd_value(dev, &uV)); + ut_asserteq(SANDBOX_BUCK2_SET_UV, uV); + + /* Disable buck2 and test ADC supply enable function */ + ut_assertok(regulator_set_enable(supply, false)); + ut_asserteq(false, regulator_get_enable(supply)); + /* adc_start_channel() should enable the supply regulator */ + ut_assertok(adc_start_channel(dev, 0)); + ut_asserteq(true, regulator_get_enable(supply)); + + return 0; +} +DM_TEST(dm_test_adc_supply, UT_TESTF_SCAN_FDT); + +struct adc_channel adc_channel_test_data[] = { + { 0, SANDBOX_ADC_CHANNEL0_DATA }, + { 1, SANDBOX_ADC_CHANNEL1_DATA }, + { 2, SANDBOX_ADC_CHANNEL2_DATA }, + { 3, SANDBOX_ADC_CHANNEL3_DATA }, +}; + +static int dm_test_adc_single_channel_conversion(struct unit_test_state *uts) +{ + struct adc_channel *tdata = adc_channel_test_data; + unsigned int i, data; + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + /* Test each ADC channel's value */ + for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) { + ut_assertok(adc_start_channel(dev, tdata->id)); + ut_assertok(adc_channel_data(dev, tdata->id, &data)); + ut_asserteq(tdata->data, data); + } + + return 0; +} +DM_TEST(dm_test_adc_single_channel_conversion, UT_TESTF_SCAN_FDT); + +static int dm_test_adc_multi_channel_conversion(struct unit_test_state *uts) +{ + struct adc_channel channels[SANDBOX_ADC_CHANNELS]; + struct udevice *dev; + struct adc_channel *tdata = adc_channel_test_data; + unsigned int i, channel_mask; + + channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) | + ADC_CHANNEL(2) | ADC_CHANNEL(3); + + /* Start multi channel conversion */ + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + ut_assertok(adc_start_channels(dev, channel_mask)); + ut_assertok(adc_channels_data(dev, channel_mask, channels)); + + /* Compare the expected and returned conversion data. */ + for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) + ut_asserteq(tdata->data, channels[i].data); + + return 0; +} +DM_TEST(dm_test_adc_multi_channel_conversion, UT_TESTF_SCAN_FDT); + +static int dm_test_adc_single_channel_shot(struct unit_test_state *uts) +{ + struct adc_channel *tdata = adc_channel_test_data; + unsigned int i, data; + + for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) { + /* Start single channel conversion */ + ut_assertok(adc_channel_single_shot("adc@0", tdata->id, &data)); + /* Compare the expected and returned conversion data. */ + ut_asserteq(tdata->data, data); + } + + return 0; +} +DM_TEST(dm_test_adc_single_channel_shot, UT_TESTF_SCAN_FDT); + +static int dm_test_adc_multi_channel_shot(struct unit_test_state *uts) +{ + struct adc_channel channels[SANDBOX_ADC_CHANNELS]; + struct adc_channel *tdata = adc_channel_test_data; + unsigned int i, channel_mask; + + channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) | + ADC_CHANNEL(2) | ADC_CHANNEL(3); + + /* Start single call and multi channel conversion */ + ut_assertok(adc_channels_single_shot("adc@0", channel_mask, channels)); + + /* Compare the expected and returned conversion data. */ + for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) + ut_asserteq(tdata->data, channels[i].data); + + return 0; +} +DM_TEST(dm_test_adc_multi_channel_shot, UT_TESTF_SCAN_FDT); + +static const int dm_test_adc_uV_data[SANDBOX_ADC_CHANNELS] = { + ((u64)SANDBOX_ADC_CHANNEL0_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) / + SANDBOX_ADC_DATA_MASK, + ((u64)SANDBOX_ADC_CHANNEL1_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) / + SANDBOX_ADC_DATA_MASK, + ((u64)SANDBOX_ADC_CHANNEL2_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) / + SANDBOX_ADC_DATA_MASK, + ((u64)SANDBOX_ADC_CHANNEL3_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) / + SANDBOX_ADC_DATA_MASK, +}; + +static int dm_test_adc_raw_to_uV(struct unit_test_state *uts) +{ + struct adc_channel *tdata = adc_channel_test_data; + unsigned int i, data; + struct udevice *dev; + int uV; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + /* Test each ADC channel's value in microvolts */ + for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) { + ut_assertok(adc_start_channel(dev, tdata->id)); + ut_assertok(adc_channel_data(dev, tdata->id, &data)); + ut_assertok(adc_raw_to_uV(dev, data, &uV)); + ut_asserteq(dm_test_adc_uV_data[i], uV); + } + + return 0; +} +DM_TEST(dm_test_adc_raw_to_uV, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/audio.c b/roms/u-boot/test/dm/audio.c new file mode 100644 index 000000000..add15ae20 --- /dev/null +++ b/roms/u-boot/test/dm/audio.c @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <audio_codec.h> +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> +#include <asm/test.h> + +/* Basic test of the audio codec uclass */ +static int dm_test_audio(struct unit_test_state *uts) +{ + int interface, rate, mclk_freq, bits_per_sample; + struct udevice *dev; + uint channels; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_AUDIO_CODEC, &dev)); + ut_assertok(audio_codec_set_params(dev, 1, 2, 3, 4, 5)); + sandbox_get_codec_params(dev, &interface, &rate, &mclk_freq, + &bits_per_sample, &channels); + ut_asserteq(1, interface); + ut_asserteq(2, rate); + ut_asserteq(3, mclk_freq); + ut_asserteq(4, bits_per_sample); + ut_asserteq(5, channels); + + return 0; +} +DM_TEST(dm_test_audio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/axi.c b/roms/u-boot/test/dm/axi.c new file mode 100644 index 000000000..dc029df5e --- /dev/null +++ b/roms/u-boot/test/dm/axi.c @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <axi.h> +#include <dm.h> +#include <log.h> +#include <asm/axi.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that sandbox AXI works correctly */ +static int dm_test_axi_base(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_assertok(uclass_get_device(UCLASS_AXI, 0, &bus)); + + return 0; +} + +DM_TEST(dm_test_axi_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that sandbox PCI bus numbering works correctly */ +static int dm_test_axi_busnum(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_assertok(uclass_get_device_by_seq(UCLASS_AXI, 0, &bus)); + + return 0; +} + +DM_TEST(dm_test_axi_busnum, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can use the store device correctly */ +static int dm_test_axi_store(struct unit_test_state *uts) +{ + struct udevice *store; + u8 tdata1[] = {0x55, 0x66, 0x77, 0x88}; + u8 tdata2[] = {0xaa, 0xbb, 0xcc, 0xdd}; + u32 val; + u8 *data; + + /* Check that asking for the device automatically fires up AXI */ + ut_assertok(uclass_get_device(UCLASS_AXI_EMUL, 0, &store)); + ut_assert(device_active(store)); + + axi_get_store(store, &data); + + /* Test reading */ + memcpy(data, tdata1, ARRAY_SIZE(tdata1)); + axi_read(store, 0, &val, AXI_SIZE_32); + ut_asserteq(0x55667788, val); + + memcpy(data + 3, tdata2, ARRAY_SIZE(tdata2)); + axi_read(store, 3, &val, AXI_SIZE_32); + ut_asserteq(0xaabbccdd, val); + + /* Reset data store */ + memset(data, 0, 16); + + /* Test writing */ + val = 0x55667788; + axi_write(store, 0, &val, AXI_SIZE_32); + ut_asserteq_mem(data, tdata1, ARRAY_SIZE(tdata1)); + + val = 0xaabbccdd; + axi_write(store, 3, &val, AXI_SIZE_32); + ut_asserteq_mem(data + 3, tdata2, ARRAY_SIZE(tdata1)); + + return 0; +} + +DM_TEST(dm_test_axi_store, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/blk.c b/roms/u-boot/test/dm/blk.c new file mode 100644 index 000000000..b7f4304e9 --- /dev/null +++ b/roms/u-boot/test/dm/blk.c @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <part.h> +#include <usb.h> +#include <asm/global_data.h> +#include <asm/state.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Test that block devices can be created */ +static int dm_test_blk_base(struct unit_test_state *uts) +{ + struct udevice *blk1, *blk3, *dev; + + /* Create two, one the parent of the other */ + ut_assertok(blk_create_device(gd->dm_root, "sandbox_host_blk", "test", + IF_TYPE_HOST, 1, 512, 2, &blk1)); + ut_assertok(blk_create_device(blk1, "sandbox_host_blk", "test", + IF_TYPE_HOST, 3, 512, 2, &blk3)); + + /* Check we can find them */ + ut_asserteq(-ENODEV, blk_get_device(IF_TYPE_HOST, 0, &dev)); + ut_assertok(blk_get_device(IF_TYPE_HOST, 1, &dev)); + ut_asserteq_ptr(blk1, dev); + ut_assertok(blk_get_device(IF_TYPE_HOST, 3, &dev)); + ut_asserteq_ptr(blk3, dev); + + /* Check we can iterate */ + ut_assertok(blk_first_device(IF_TYPE_HOST, &dev)); + ut_asserteq_ptr(blk1, dev); + ut_assertok(blk_next_device(&dev)); + ut_asserteq_ptr(blk3, dev); + + return 0; +} +DM_TEST(dm_test_blk_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int count_blk_devices(void) +{ + struct udevice *blk; + struct uclass *uc; + int count = 0; + int ret; + + ret = uclass_get(UCLASS_BLK, &uc); + if (ret) + return ret; + + uclass_foreach_dev(blk, uc) + count++; + + return count; +} + +/* Test that block devices work correctly with USB */ +static int dm_test_blk_usb(struct unit_test_state *uts) +{ + struct udevice *usb_dev, *dev; + struct blk_desc *dev_desc; + + /* Get a flash device */ + state_set_skip_delays(true); + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &usb_dev)); + ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc)); + + /* The parent should be a block device */ + ut_assertok(blk_get_device(IF_TYPE_USB, 0, &dev)); + ut_asserteq_ptr(usb_dev, dev_get_parent(dev)); + + /* Check we have one block device for each mass storage device */ + ut_asserteq(6, count_blk_devices()); + + /* Now go around again, making sure the old devices were unbound */ + ut_assertok(usb_stop()); + ut_assertok(usb_init()); + ut_asserteq(6, count_blk_devices()); + ut_assertok(usb_stop()); + + return 0; +} +DM_TEST(dm_test_blk_usb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can find block devices without probing them */ +static int dm_test_blk_find(struct unit_test_state *uts) +{ + struct udevice *blk, *dev; + + ut_assertok(blk_create_device(gd->dm_root, "sandbox_host_blk", "test", + IF_TYPE_HOST, 1, 512, 2, &blk)); + ut_asserteq(-ENODEV, blk_find_device(IF_TYPE_HOST, 0, &dev)); + ut_assertok(blk_find_device(IF_TYPE_HOST, 1, &dev)); + ut_asserteq_ptr(blk, dev); + ut_asserteq(false, device_active(dev)); + + /* Now activate it */ + ut_assertok(blk_get_device(IF_TYPE_HOST, 1, &dev)); + ut_asserteq_ptr(blk, dev); + ut_asserteq(true, device_active(dev)); + + return 0; +} +DM_TEST(dm_test_blk_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that block device numbering works as expected */ +static int dm_test_blk_devnum(struct unit_test_state *uts) +{ + struct udevice *dev, *mmc_dev, *parent; + int i; + + /* + * Probe the devices, with the first one being probed last. This is the + * one with no alias / sequence numnber. + */ + ut_assertok(uclass_get_device(UCLASS_MMC, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_MMC, 2, &dev)); + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev)); + for (i = 0; i < 3; i++) { + struct blk_desc *desc; + + /* Check that the bblock device is attached */ + ut_assertok(uclass_get_device_by_seq(UCLASS_MMC, i, &mmc_dev)); + ut_assertok(blk_find_device(IF_TYPE_MMC, i, &dev)); + parent = dev_get_parent(dev); + ut_asserteq_ptr(parent, mmc_dev); + ut_asserteq(trailing_strtol(mmc_dev->name), i); + + /* + * Check that the block device devnum matches its parent's + * sequence number + */ + desc = dev_get_uclass_plat(dev); + ut_asserteq(desc->devnum, i); + } + + return 0; +} +DM_TEST(dm_test_blk_devnum, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can get a block from its parent */ +static int dm_test_blk_get_from_parent(struct unit_test_state *uts) +{ + struct udevice *dev, *blk; + + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev)); + ut_assertok(blk_get_from_parent(dev, &blk)); + + ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev)); + ut_asserteq(-ENOTBLK, blk_get_from_parent(dev, &blk)); + + ut_assertok(uclass_get_device(UCLASS_GPIO, 0, &dev)); + ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk)); + + return 0; +} +DM_TEST(dm_test_blk_get_from_parent, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/bootcount.c b/roms/u-boot/test/dm/bootcount.c new file mode 100644 index 000000000..e0c47b5d7 --- /dev/null +++ b/roms/u-boot/test/dm/bootcount.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) 2018 Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#include <dm.h> +#include <bootcount.h> +#include <log.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_bootcount(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val; + + ut_assertok(uclass_get_device(UCLASS_BOOTCOUNT, 0, &dev)); + ut_assertok(dm_bootcount_set(dev, 0)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0); + ut_assertok(dm_bootcount_set(dev, 0xab)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0xab); + + ut_assertok(uclass_get_device(UCLASS_BOOTCOUNT, 1, &dev)); + ut_assertok(dm_bootcount_set(dev, 0)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0); + ut_assertok(dm_bootcount_set(dev, 0xab)); + ut_assertok(dm_bootcount_get(dev, &val)); + ut_assert(val == 0xab); + + return 0; +} + +DM_TEST(dm_test_bootcount, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + diff --git a/roms/u-boot/test/dm/bus.c b/roms/u-boot/test/dm/bus.c new file mode 100644 index 000000000..89a6aa655 --- /dev/null +++ b/roms/u-boot/test/dm/bus.c @@ -0,0 +1,487 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc + */ + +#include <common.h> +#ifdef CONFIG_SANDBOX +#include <log.h> +#include <os.h> +#endif +#include <dm.h> +#include <asm/global_data.h> +#include <dm/device.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Test that we can probe for children */ +static int dm_test_bus_children(struct unit_test_state *uts) +{ + int num_devices = 9; + struct udevice *bus; + struct uclass *uc; + + ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc)); + ut_asserteq(num_devices, list_count_items(&uc->dev_head)); + + /* Probe the bus, which should yield 3 more devices */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + num_devices += 3; + + ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc)); + ut_asserteq(num_devices, list_count_items(&uc->dev_head)); + + ut_assert(!dm_check_devices(uts, num_devices)); + + return 0; +} +DM_TEST(dm_test_bus_children, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test our functions for accessing children */ +static int dm_test_bus_children_funcs(struct unit_test_state *uts) +{ + const void *blob = gd->fdt_blob; + struct udevice *bus, *dev; + int node; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + + /* device_get_child() */ + ut_assertok(device_get_child(bus, 0, &dev)); + ut_asserteq(-ENODEV, device_get_child(bus, 4, &dev)); + ut_assertok(device_get_child_by_seq(bus, 5, &dev)); + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + ut_asserteq_str("c-test@5", dev->name); + + /* Device with sequence number 0 should be accessible */ + ut_asserteq(-ENODEV, device_find_child_by_seq(bus, -1, &dev)); + ut_assertok(device_find_child_by_seq(bus, 0, &dev)); + ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)); + ut_asserteq(0, device_find_child_by_seq(bus, 0, &dev)); + ut_assertok(device_get_child_by_seq(bus, 0, &dev)); + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + ut_asserteq(0, device_find_child_by_seq(bus, 0, &dev)); + + /* There is no device with sequence number 2 */ + ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, &dev)); + ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, &dev)); + ut_asserteq(-ENODEV, device_get_child_by_seq(bus, 2, &dev)); + + /* Looking for something that is not a child */ + node = fdt_path_offset(blob, "/junk"); + ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev)); + node = fdt_path_offset(blob, "/d-test"); + ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev)); + + return 0; +} +DM_TEST(dm_test_bus_children_funcs, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_bus_children_of_offset(struct unit_test_state *uts) +{ + const void *blob = gd->fdt_blob; + struct udevice *bus, *dev; + int node; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + ut_assertnonnull(bus); + + /* Find a valid child */ + node = fdt_path_offset(blob, "/some-bus/c-test@1"); + ut_assert(node > 0); + ut_assertok(device_find_child_by_of_offset(bus, node, &dev)); + ut_assertnonnull(dev); + ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)); + ut_assertok(device_get_child_by_of_offset(bus, node, &dev)); + ut_assertnonnull(dev); + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + + return 0; +} +DM_TEST(dm_test_bus_children_of_offset, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +/* Test that we can iterate through children */ +static int dm_test_bus_children_iterators(struct unit_test_state *uts) +{ + struct udevice *bus, *dev, *child; + + /* Walk through the children one by one */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + ut_assertok(device_find_first_child(bus, &dev)); + ut_asserteq_str("c-test@5", dev->name); + ut_assertok(device_find_next_child(&dev)); + ut_asserteq_str("c-test@0", dev->name); + ut_assertok(device_find_next_child(&dev)); + ut_asserteq_str("c-test@1", dev->name); + ut_assertok(device_find_next_child(&dev)); + ut_asserteq_ptr(dev, NULL); + + /* Move to the next child without using device_find_first_child() */ + ut_assertok(device_find_child_by_seq(bus, 5, &dev)); + ut_asserteq_str("c-test@5", dev->name); + ut_assertok(device_find_next_child(&dev)); + ut_asserteq_str("c-test@0", dev->name); + + /* Try a device with no children */ + ut_assertok(device_find_first_child(dev, &child)); + ut_asserteq_ptr(child, NULL); + + return 0; +} +DM_TEST(dm_test_bus_children_iterators, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that the bus can store data about each child */ +static int test_bus_parent_data(struct unit_test_state *uts) +{ + struct dm_test_parent_data *parent_data; + struct udevice *bus, *dev; + struct uclass *uc; + int value; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + + /* Check that parent data is allocated */ + ut_assertok(device_find_child_by_seq(bus, 0, &dev)); + ut_asserteq_ptr(NULL, dev_get_parent_priv(dev)); + ut_assertok(device_get_child_by_seq(bus, 0, &dev)); + parent_data = dev_get_parent_priv(dev); + ut_assert(NULL != parent_data); + + /* Check that it starts at 0 and goes away when device is removed */ + parent_data->sum += 5; + ut_asserteq(5, parent_data->sum); + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq_ptr(NULL, dev_get_parent_priv(dev)); + + /* Check that we can do this twice */ + ut_assertok(device_get_child_by_seq(bus, 0, &dev)); + parent_data = dev_get_parent_priv(dev); + ut_assert(NULL != parent_data); + parent_data->sum += 5; + ut_asserteq(5, parent_data->sum); + + /* Add parent data to all children */ + ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc)); + value = 5; + uclass_foreach_dev(dev, uc) { + /* Ignore these if they are not on this bus */ + if (dev->parent != bus) { + ut_asserteq_ptr(NULL, dev_get_parent_priv(dev)); + continue; + } + ut_assertok(device_probe(dev)); + parent_data = dev_get_parent_priv(dev); + + parent_data->sum = value; + value += 5; + } + + /* Check it is still there */ + value = 5; + uclass_foreach_dev(dev, uc) { + /* Ignore these if they are not on this bus */ + if (dev->parent != bus) + continue; + parent_data = dev_get_parent_priv(dev); + + ut_asserteq(value, parent_data->sum); + value += 5; + } + + return 0; +} +/* Test that the bus can store data about each child */ +static int dm_test_bus_parent_data(struct unit_test_state *uts) +{ + return test_bus_parent_data(uts); +} +DM_TEST(dm_test_bus_parent_data, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_data_uclass(struct unit_test_state *uts) +{ + struct driver *drv; + struct udevice *bus; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + drv = (struct driver *)bus->driver; + size = drv->per_child_auto; + +#ifdef CONFIG_SANDBOX + os_mprotect_allow(bus->uclass->uc_drv, sizeof(*bus->uclass->uc_drv)); + os_mprotect_allow(drv, sizeof(*drv)); +#endif + bus->uclass->uc_drv->per_child_auto = size; + drv->per_child_auto = 0; + ret = test_bus_parent_data(uts); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_auto = 0; + drv->per_child_auto = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_data_uclass, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that the bus ops are called when a child is probed/removed */ +static int dm_test_bus_parent_ops(struct unit_test_state *uts) +{ + struct dm_test_parent_data *parent_data; + struct udevice *bus, *dev; + struct uclass *uc; + + testbus_get_clear_removed(); + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc)); + + uclass_foreach_dev(dev, uc) { + /* Ignore these if they are not on this bus */ + if (dev->parent != bus) + continue; + ut_asserteq_ptr(NULL, dev_get_parent_priv(dev)); + + ut_assertok(device_probe(dev)); + parent_data = dev_get_parent_priv(dev); + ut_asserteq(TEST_FLAG_CHILD_PROBED, parent_data->flag); + } + + uclass_foreach_dev(dev, uc) { + /* Ignore these if they are not on this bus */ + if (dev->parent != bus) + continue; + parent_data = dev_get_parent_priv(dev); + ut_asserteq(TEST_FLAG_CHILD_PROBED, parent_data->flag); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_asserteq_ptr(NULL, dev_get_parent_priv(dev)); + ut_asserteq_ptr(testbus_get_clear_removed(), dev); + } + + return 0; +} +DM_TEST(dm_test_bus_parent_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int test_bus_parent_plat(struct unit_test_state *uts) +{ + struct dm_test_parent_plat *plat; + struct udevice *bus, *dev; + + /* Check that the bus has no children */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + device_find_first_child(bus, &dev); + ut_asserteq_ptr(NULL, dev); + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_plat(dev); + ut_assert(plat != NULL); + + /* + * Check that it is not affected by the device being + * probed/removed + */ + plat->count++; + ut_asserteq(1, plat->count); + device_probe(dev); + device_remove(dev, DM_REMOVE_NORMAL); + + ut_asserteq_ptr(plat, dev_get_parent_plat(dev)); + ut_asserteq(1, plat->count); + ut_assertok(device_probe(dev)); + } + ut_asserteq(3, device_get_child_count(bus)); + + /* Removing the bus should also have no effect (it is still bound) */ + device_remove(bus, DM_REMOVE_NORMAL); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_plat(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->count); + } + ut_asserteq(3, device_get_child_count(bus)); + + /* Unbind all the children */ + do { + device_find_first_child(bus, &dev); + if (dev) + device_unbind(dev); + } while (dev); + + /* Now the child plat should be removed and re-added */ + device_probe(bus); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_plat(dev); + ut_assert(plat != NULL); + ut_asserteq(0, plat->count); + } + ut_asserteq(3, device_get_child_count(bus)); + + return 0; +} + +/* Test that the bus can store platform data about each child */ +static int dm_test_bus_parent_plat(struct unit_test_state *uts) +{ + return test_bus_parent_plat(uts); +} +DM_TEST(dm_test_bus_parent_plat, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* As above but the size is controlled by the uclass */ +static int dm_test_bus_parent_plat_uclass(struct unit_test_state *uts) +{ + struct udevice *bus; + struct driver *drv; + int size; + int ret; + + /* Set the driver size to 0 so that the uclass size is used */ + ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus)); + drv = (struct driver *)bus->driver; + size = drv->per_child_plat_auto; +#ifdef CONFIG_SANDBOX + os_mprotect_allow(bus->uclass->uc_drv, sizeof(*bus->uclass->uc_drv)); + os_mprotect_allow(drv, sizeof(*drv)); +#endif + bus->uclass->uc_drv->per_child_plat_auto = size; + drv->per_child_plat_auto = 0; + ret = test_bus_parent_plat(uts); + if (ret) + return ret; + bus->uclass->uc_drv->per_child_plat_auto = 0; + drv->per_child_plat_auto = size; + + return 0; +} +DM_TEST(dm_test_bus_parent_plat_uclass, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind(struct unit_test_state *uts) +{ + struct dm_test_parent_plat *plat; + struct udevice *bus, *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_plat(dev); + ut_assert(plat != NULL); + ut_asserteq(1, plat->bind_flag); + } + ut_asserteq(3, device_get_child_count(bus)); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that the child post_bind method is called */ +static int dm_test_bus_child_post_bind_uclass(struct unit_test_state *uts) +{ + struct dm_test_parent_plat *plat; + struct udevice *bus, *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + /* Check that platform data is allocated */ + plat = dev_get_parent_plat(dev); + ut_assert(plat != NULL); + ut_asserteq(2, plat->uclass_bind_flag); + } + ut_asserteq(3, device_get_child_count(bus)); + + return 0; +} +DM_TEST(dm_test_bus_child_post_bind_uclass, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test that the bus' uclass' child_pre_probe() is called before the + * device's probe() method + */ +static int dm_test_bus_child_pre_probe_uclass(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + /* + * See testfdt_drv_probe() which effectively checks that the uclass + * flag is set before that method is called + */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Check that things happened in the right order */ + ut_asserteq_ptr(NULL, priv); + ut_assertok(device_probe(dev)); + + priv = dev_get_priv(dev); + ut_assert(priv != NULL); + ut_asserteq(1, priv->uclass_flag); + ut_asserteq(1, priv->uclass_total); + } + ut_asserteq(3, device_get_child_count(bus)); + + return 0; +} +DM_TEST(dm_test_bus_child_pre_probe_uclass, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test that the bus' uclass' child_post_probe() is called after the + * device's probe() method + */ +static int dm_test_bus_child_post_probe_uclass(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + /* + * See testfdt_drv_probe() which effectively initializes that + * the uclass postp flag is set to a value + */ + ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus)); + for (device_find_first_child(bus, &dev); + dev; + device_find_next_child(&dev)) { + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Check that things happened in the right order */ + ut_asserteq_ptr(NULL, priv); + ut_assertok(device_probe(dev)); + + priv = dev_get_priv(dev); + ut_assert(priv != NULL); + ut_asserteq(0, priv->uclass_postp); + } + ut_asserteq(3, device_get_child_count(bus)); + + return 0; +} +DM_TEST(dm_test_bus_child_post_probe_uclass, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/button.c b/roms/u-boot/test/dm/button.c new file mode 100644 index 000000000..f8a7fab61 --- /dev/null +++ b/roms/u-boot/test/dm/button.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com> + * + * Based on led.c + */ + +#include <common.h> +#include <dm.h> +#include <adc.h> +#include <button.h> +#include <power/regulator.h> +#include <power/sandbox_pmic.h> +#include <asm/gpio.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Base test of the button uclass */ +static int dm_test_button_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Get the top-level gpio buttons device */ + ut_assertok(uclass_get_device(UCLASS_BUTTON, 0, &dev)); + /* Get the 2 gpio buttons */ + ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_BUTTON, 2, &dev)); + + /* Get the top-level adc buttons device */ + ut_assertok(uclass_get_device(UCLASS_BUTTON, 3, &dev)); + /* Get the 3 adc buttons */ + ut_assertok(uclass_get_device(UCLASS_BUTTON, 4, &dev)); + ut_assertok(uclass_get_device(UCLASS_BUTTON, 5, &dev)); + ut_assertok(uclass_get_device(UCLASS_BUTTON, 6, &dev)); + + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_BUTTON, 7, &dev)); + + return 0; +} +DM_TEST(dm_test_button_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the button uclass using the button_gpio driver */ +static int dm_test_button_gpio(struct unit_test_state *uts) +{ + const int offset = 3; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate an BUTTON. BUTTON 1 is connected to GPIO + * bank gpio_a, offset 3. + */ + ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + + ut_asserteq(0, sandbox_gpio_set_value(gpio, offset, 0)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(BUTTON_OFF, button_get_state(dev)); + + ut_asserteq(0, sandbox_gpio_set_value(gpio, offset, 1)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(BUTTON_ON, button_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_button_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test obtaining an BUTTON by label */ +static int dm_test_button_label(struct unit_test_state *uts) +{ + struct udevice *dev, *cmp; + + ut_assertok(button_get_by_label("button1", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_assertok(button_get_by_label("button2", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_BUTTON, 2, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_asserteq(-ENODEV, button_get_by_label("nobutton", &dev)); + + return 0; +} +DM_TEST(dm_test_button_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test adc-keys driver */ +static int dm_test_button_keys_adc(struct unit_test_state *uts) +{ + struct udevice *supply; + struct udevice *dev; + int uV; + + ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev)); + + ut_assertok(regulator_get_by_devname(SANDBOX_BUCK2_DEVNAME, &supply)); + ut_assertok(regulator_set_value(supply, SANDBOX_BUCK2_SET_UV)); + ut_asserteq(SANDBOX_BUCK2_SET_UV, regulator_get_value(supply)); + /* Update ADC plat and get new Vdd value */ + ut_assertok(adc_vdd_value(dev, &uV)); + ut_asserteq(SANDBOX_BUCK2_SET_UV, uV); + + /* + * sandbox-adc returns constant value on channel 3, is used by adc-keys: + * SANDBOX_ADC_CHANNEL3_DATA * SANDBOX_BUCK2_SET_UV / SANDBOX_ADC_DATA_MASK = + * 0x3000 * 3300000 / 0xffff = 618759uV + * This means that button3 and button4 are released and button5 + * is pressed. + */ + ut_assertok(button_get_by_label("button3", &dev)); + ut_asserteq(BUTTON_OFF, button_get_state(dev)); + ut_assertok(button_get_by_label("button4", &dev)); + ut_asserteq(BUTTON_OFF, button_get_state(dev)); + ut_assertok(button_get_by_label("button5", &dev)); + ut_asserteq(BUTTON_ON, button_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_button_keys_adc, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/cache.c b/roms/u-boot/test/dm/cache.c new file mode 100644 index 000000000..bbd8f98d0 --- /dev/null +++ b/roms/u-boot/test/dm/cache.c @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Intel Corporation <www.intel.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> + +static int dm_test_reset(struct unit_test_state *uts) +{ + struct udevice *dev_cache; + struct cache_info; + + ut_assertok(uclass_get_device(UCLASS_CACHE, 0, &dev_cache)); + ut_assertok(cache_get_info(dev, &info)); + ut_assertok(cache_enable(dev)); + ut_assertok(cache_disable(dev)); + + return 0; +} +DM_TEST(dm_test_reset, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/clk.c b/roms/u-boot/test/dm/clk.c new file mode 100644 index 000000000..21997ed89 --- /dev/null +++ b/roms/u-boot/test/dm/clk.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <asm/clk.h> +#include <dm/test.h> +#include <dm/device-internal.h> +#include <linux/err.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of the clk uclass */ +static int dm_test_clk_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct clk clk_method1; + struct clk clk_method2; + + /* Get the device using the clk device */ + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test", &dev)); + + /* Get the same clk port in 2 different ways and compare */ + ut_assertok(clk_get_by_index(dev, 1, &clk_method1)); + ut_assertok(clk_get_by_index_nodev(dev_ofnode(dev), 1, &clk_method2)); + ut_asserteq(clk_is_match(&clk_method1, &clk_method2), true); + ut_asserteq(clk_method1.id, clk_method2.id); + + return 0; +} + +DM_TEST(dm_test_clk_base, UT_TESTF_SCAN_FDT); + +static int dm_test_clk(struct unit_test_state *uts) +{ + struct udevice *dev_fixed, *dev_fixed_factor, *dev_clk, *dev_test; + ulong rate; + + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-fixed", + &dev_fixed)); + + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-fixed-factor", + &dev_fixed_factor)); + + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox", + &dev_clk)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test", + &dev_test)); + ut_assertok(sandbox_clk_test_get(dev_test)); + ut_assertok(sandbox_clk_test_devm_get(dev_test)); + ut_assertok(sandbox_clk_test_valid(dev_test)); + + ut_asserteq(0, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_DEVM_NULL)); + ut_asserteq(0, sandbox_clk_test_set_rate(dev_test, + SANDBOX_CLK_TEST_ID_DEVM_NULL, + 0)); + ut_asserteq(0, sandbox_clk_test_enable(dev_test, + SANDBOX_CLK_TEST_ID_DEVM_NULL)); + ut_asserteq(0, sandbox_clk_test_disable(dev_test, + SANDBOX_CLK_TEST_ID_DEVM_NULL)); + + ut_asserteq(1234, + sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_FIXED)); + ut_asserteq(0, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(0, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + ut_asserteq(321, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_DEVM1)); + ut_asserteq(0, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_DEVM2)); + + rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED, + 12345); + ut_assert(IS_ERR_VALUE(rate)); + rate = sandbox_clk_test_get_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED); + ut_asserteq(1234, rate); + + ut_asserteq(0, sandbox_clk_test_set_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI, + 1000)); + ut_asserteq(0, sandbox_clk_test_set_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C, + 2000)); + + ut_asserteq(1000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(2000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + ut_asserteq(1000, sandbox_clk_test_set_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI, + 10000)); + ut_asserteq(2000, sandbox_clk_test_set_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C, + 20000)); + + rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0); + ut_assert(IS_ERR_VALUE(rate)); + rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0); + ut_assert(IS_ERR_VALUE(rate)); + + ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + ut_asserteq(5000, sandbox_clk_test_round_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI, + 5000)); + ut_asserteq(7000, sandbox_clk_test_round_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C, + 7000)); + + ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0); + ut_assert(IS_ERR_VALUE(rate)); + rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0); + ut_assert(IS_ERR_VALUE(rate)); + + ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + ut_asserteq(10000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(20000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_I2C)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_assertok(sandbox_clk_test_disable(dev_test, + SANDBOX_CLK_TEST_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_assertok(sandbox_clk_test_disable(dev_test, + SANDBOX_CLK_TEST_ID_I2C)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + ut_asserteq(1, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_I2C)); + ut_asserteq(1, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_UART2)); + ut_assertok(sandbox_clk_test_free(dev_test)); + ut_asserteq(0, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_I2C)); + ut_asserteq(0, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_UART2)); + + ut_asserteq(1, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_UART1)); + ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL)); + ut_asserteq(0, sandbox_clk_query_requested(dev_clk, + SANDBOX_CLK_ID_UART1)); + return 0; +} +DM_TEST(dm_test_clk, UT_TESTF_SCAN_FDT); + +static int dm_test_clk_bulk(struct unit_test_state *uts) +{ + struct udevice *dev_clk, *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox", + &dev_clk)); + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test", + &dev_test)); + ut_assertok(sandbox_clk_test_get_bulk(dev_test)); + + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + /* Fixed clock does not support enable, thus should not fail */ + ut_assertok(sandbox_clk_test_enable_bulk(dev_test)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + /* Fixed clock does not support disable, thus should not fail */ + ut_assertok(sandbox_clk_test_disable_bulk(dev_test)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + /* Fixed clock does not support enable, thus should not fail */ + ut_assertok(sandbox_clk_test_enable_bulk(dev_test)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + + /* Fixed clock does not support disable, thus should not fail */ + ut_assertok(sandbox_clk_test_release_bulk(dev_test)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI)); + ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C)); + ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL)); + + return 0; +} +DM_TEST(dm_test_clk_bulk, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/clk_ccf.c b/roms/u-boot/test/dm/clk_ccf.c new file mode 100644 index 000000000..e4ebb93cd --- /dev/null +++ b/roms/u-boot/test/dm/clk_ccf.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 + * Lukasz Majewski, DENX Software Engineering, lukma@denx.de + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <asm/clk.h> +#include <dm/test.h> +#include <dm/uclass.h> +#include <linux/err.h> +#include <test/test.h> +#include <test/ut.h> +#include <sandbox-clk.h> + +/* Tests for Common Clock Framework driver */ +static int dm_test_clk_ccf(struct unit_test_state *uts) +{ + struct clk *clk, *pclk; + struct udevice *dev; + long long rate; + int ret; +#if CONFIG_IS_ENABLED(CLK_CCF) + const char *clkname; + int clkid, i; +#endif + + /* Get the device using the clk device */ + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev)); + + /* Test for clk_get_by_id() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi_root", clk->dev->name); + ut_asserteq(CLK_SET_RATE_PARENT, clk->flags); + + /* Test for clk_get_parent_rate() */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI1, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi1", clk->dev->name); + ut_asserteq(CLK_SET_RATE_PARENT, clk->flags); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 20000000); + + /* test the gate of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_ECSPI0, &clk); + ut_assertok(ret); + ut_asserteq_str("ecspi0", clk->dev->name); + ut_asserteq(CLK_SET_RATE_PARENT, clk->flags); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 20000000); + + /* Test the mux of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc1_sel", clk->dev->name); + ut_asserteq(CLK_SET_RATE_NO_REPARENT, clk->flags); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 60000000); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 60000000); + + ret = clk_get_by_id(SANDBOX_CLK_PLL3_80M, &pclk); + ut_assertok(ret); + + ret = clk_set_parent(clk, pclk); + ut_assertok(ret); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 80000000); + + ret = clk_get_by_id(SANDBOX_CLK_USDHC2_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc2_sel", clk->dev->name); + ut_asserteq(CLK_SET_RATE_NO_REPARENT, clk->flags); + + rate = clk_get_parent_rate(clk); + ut_asserteq(rate, 80000000); + + pclk = clk_get_parent(clk); + ut_asserteq_str("pll3_80m", pclk->dev->name); + ut_asserteq(CLK_SET_RATE_PARENT, pclk->flags); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 80000000); + + ret = clk_get_by_id(SANDBOX_CLK_PLL3_60M, &pclk); + ut_assertok(ret); + + ret = clk_set_parent(clk, pclk); + ut_assertok(ret); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 60000000); + + /* Test the composite of CCF */ + ret = clk_get_by_id(SANDBOX_CLK_I2C, &clk); + ut_assertok(ret); + ut_asserteq_str("i2c", clk->dev->name); + ut_asserteq(CLK_SET_RATE_UNGATE, clk->flags); + + rate = clk_get_rate(clk); + ut_asserteq(rate, 60000000); + +#if CONFIG_IS_ENABLED(CLK_CCF) + /* Test clk tree enable/disable */ + ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("i2c_root", clk->dev->name); + + ret = clk_enable(clk); + ut_assertok(ret); + + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 1); + + ret = clk_get_by_id(SANDBOX_CLK_I2C, &pclk); + ut_assertok(ret); + + ret = sandbox_clk_enable_count(pclk); + ut_asserteq(ret, 1); + + ret = clk_disable(clk); + ut_assertok(ret); + + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 0); + + ret = sandbox_clk_enable_count(pclk); + ut_asserteq(ret, 0); + + /* Test clock re-parenting. */ + ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk); + ut_assertok(ret); + ut_asserteq_str("usdhc1_sel", clk->dev->name); + + pclk = clk_get_parent(clk); + ut_assertok_ptr(pclk); + if (!strcmp(pclk->dev->name, "pll3_60m")) { + clkname = "pll3_80m"; + clkid = SANDBOX_CLK_PLL3_80M; + } else { + clkname = "pll3_60m"; + clkid = SANDBOX_CLK_PLL3_60M; + } + + ret = clk_get_by_id(clkid, &pclk); + ut_assertok(ret); + ret = clk_set_parent(clk, pclk); + ut_assertok(ret); + pclk = clk_get_parent(clk); + ut_assertok_ptr(pclk); + ut_asserteq_str(clkname, pclk->dev->name); + + /* Test disabling critical clock. */ + ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk); + ut_assertok(ret); + ut_asserteq_str("i2c_root", clk->dev->name); + + /* Disable it, if any. */ + ret = sandbox_clk_enable_count(clk); + for (i = 0; i < ret; i++) { + ret = clk_disable(clk); + ut_assertok(ret); + } + + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 0); + + clk->flags = CLK_IS_CRITICAL; + ret = clk_enable(clk); + ut_assertok(ret); + + ret = clk_disable(clk); + ut_assertok(ret); + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 1); + clk->flags &= ~CLK_IS_CRITICAL; + + ret = clk_disable(clk); + ut_assertok(ret); + ret = sandbox_clk_enable_count(clk); + ut_asserteq(ret, 0); +#endif + + return 1; +} + +DM_TEST(dm_test_clk_ccf, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/core.c b/roms/u-boot/test/dm/core.c new file mode 100644 index 000000000..2210345dd --- /dev/null +++ b/roms/u-boot/test/dm/core.c @@ -0,0 +1,1202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the core driver model code + * + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/util.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +enum { + TEST_INTVAL1 = 0, + TEST_INTVAL2 = 3, + TEST_INTVAL3 = 6, + TEST_INTVAL_MANUAL = 101112, + TEST_INTVAL_PRE_RELOC = 7, +}; + +static const struct dm_test_pdata test_pdata[] = { + { .ping_add = TEST_INTVAL1, }, + { .ping_add = TEST_INTVAL2, }, + { .ping_add = TEST_INTVAL3, }, +}; + +static const struct dm_test_pdata test_pdata_manual = { + .ping_add = TEST_INTVAL_MANUAL, +}; + +static const struct dm_test_pdata test_pdata_pre_reloc = { + .ping_add = TEST_INTVAL_PRE_RELOC, +}; + +U_BOOT_DRVINFO(dm_test_info1) = { + .name = "test_drv", + .plat = &test_pdata[0], +}; + +U_BOOT_DRVINFO(dm_test_info2) = { + .name = "test_drv", + .plat = &test_pdata[1], +}; + +U_BOOT_DRVINFO(dm_test_info3) = { + .name = "test_drv", + .plat = &test_pdata[2], +}; + +static struct driver_info driver_info_manual = { + .name = "test_manual_drv", + .plat = &test_pdata_manual, +}; + +static struct driver_info driver_info_pre_reloc = { + .name = "test_pre_reloc_drv", + .plat = &test_pdata_pre_reloc, +}; + +static struct driver_info driver_info_act_dma = { + .name = "test_act_dma_drv", +}; + +static struct driver_info driver_info_vital_clk = { + .name = "test_vital_clk_drv", +}; + +static struct driver_info driver_info_act_dma_vital_clk = { + .name = "test_act_dma_vital_clk_drv", +}; + +void dm_leak_check_start(struct unit_test_state *uts) +{ + uts->start = mallinfo(); + if (!uts->start.uordblks) + puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n"); +} + +int dm_leak_check_end(struct unit_test_state *uts) +{ + struct mallinfo end; + int id, diff; + + /* Don't delete the root class, since we started with that */ + for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) { + struct uclass *uc; + + uc = uclass_find(id); + if (!uc) + continue; + ut_assertok(uclass_destroy(uc)); + } + + end = mallinfo(); + diff = end.uordblks - uts->start.uordblks; + if (diff > 0) + printf("Leak: lost %#xd bytes\n", diff); + else if (diff < 0) + printf("Leak: gained %#xd bytes\n", -diff); + ut_asserteq(uts->start.uordblks, end.uordblks); + + return 0; +} + +/* Test that binding with plat occurs correctly */ +static int dm_test_autobind(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* + * We should have a single class (UCLASS_ROOT) and a single root + * device with no children. + */ + ut_assert(uts->root); + ut_asserteq(1, list_count_items(gd->uclass_root)); + ut_asserteq(0, list_count_items(&gd->dm_root->child_head)); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + + ut_assertok(dm_scan_plat(false)); + + /* We should have our test class now at least, plus more children */ + ut_assert(1 < list_count_items(gd->uclass_root)); + ut_assert(0 < list_count_items(&gd->dm_root->child_head)); + + /* Our 3 dm_test_infox children should be bound to the test uclass */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]); + + /* No devices should be probed */ + list_for_each_entry(dev, &gd->dm_root->child_head, sibling_node) + ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)); + + /* Our test driver should have been bound 3 times */ + ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] == 3); + + return 0; +} +DM_TEST(dm_test_autobind, 0); + +/* Test that binding with uclass plat allocation occurs correctly */ +static int dm_test_autobind_uclass_pdata_alloc(struct unit_test_state *uts) +{ + struct dm_test_perdev_uc_pdata *uc_pdata; + struct udevice *dev; + struct uclass *uc; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + ut_assert(uc); + + /** + * Test if test uclass driver requires allocation for the uclass + * platform data and then check the dev->uclass_plat pointer. + */ + ut_assert(uc->uc_drv->per_device_plat_auto); + + for (uclass_find_first_device(UCLASS_TEST, &dev); + dev; + uclass_find_next_device(&dev)) { + ut_assertnonnull(dev); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + } + + return 0; +} +DM_TEST(dm_test_autobind_uclass_pdata_alloc, UT_TESTF_SCAN_PDATA); + +/* Test that binding with uclass plat setting occurs correctly */ +static int dm_test_autobind_uclass_pdata_valid(struct unit_test_state *uts) +{ + struct dm_test_perdev_uc_pdata *uc_pdata; + struct udevice *dev; + + /** + * In the test_postbind() method of test uclass driver, the uclass + * platform data should be set to three test int values - test it. + */ + for (uclass_find_first_device(UCLASS_TEST, &dev); + dev; + uclass_find_next_device(&dev)) { + ut_assertnonnull(dev); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + ut_assert(uc_pdata->intval1 == TEST_UC_PDATA_INTVAL1); + ut_assert(uc_pdata->intval2 == TEST_UC_PDATA_INTVAL2); + ut_assert(uc_pdata->intval3 == TEST_UC_PDATA_INTVAL3); + } + + return 0; +} +DM_TEST(dm_test_autobind_uclass_pdata_valid, UT_TESTF_SCAN_PDATA); + +/* Test that autoprobe finds all the expected devices */ +static int dm_test_autoprobe(struct unit_test_state *uts) +{ + int expected_base_add; + struct udevice *dev; + struct uclass *uc; + int i; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + ut_assert(uc); + + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + + /* The root device should not be activated until needed */ + ut_assert(dev_get_flags(uts->root) & DM_FLAG_ACTIVATED); + + /* + * We should be able to find the three test devices, and they should + * all be activated as they are used (lazy activation, required by + * U-Boot) + */ + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assertf(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED), + "Driver %d/%s already activated", i, dev->name); + + /* This should activate it */ + ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + + /* Activating a device should activate the root device */ + if (!i) + ut_assert(dev_get_flags(uts->root) & DM_FLAG_ACTIVATED); + } + + /* + * Our 3 dm_test_info children should be passed to pre_probe and + * post_probe + */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]); + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]); + + /* Also we can check the per-device data */ + expected_base_add = 0; + for (i = 0; i < 3; i++) { + struct dm_test_uclass_perdev_priv *priv; + struct dm_test_pdata *pdata; + + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + + priv = dev_get_uclass_priv(dev); + ut_assert(priv); + ut_asserteq(expected_base_add, priv->base_add); + + pdata = dev_get_plat(dev); + expected_base_add += pdata->ping_add; + } + + return 0; +} +DM_TEST(dm_test_autoprobe, UT_TESTF_SCAN_PDATA); + +/* Check that we see the correct plat in each device */ +static int dm_test_plat(struct unit_test_state *uts) +{ + const struct dm_test_pdata *pdata; + struct udevice *dev; + int i; + + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + pdata = dev_get_plat(dev); + ut_assert(pdata->ping_add == test_pdata[i].ping_add); + } + + return 0; +} +DM_TEST(dm_test_plat, UT_TESTF_SCAN_PDATA); + +/* Test that we can bind, probe, remove, unbind a driver */ +static int dm_test_lifecycle(struct unit_test_state *uts) +{ + int op_count[DM_TEST_OP_COUNT]; + struct udevice *dev, *test_dev; + int pingret; + int ret; + + memcpy(op_count, dm_testdrv_op_count, sizeof(op_count)); + + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev)); + ut_assert(dev); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] + == op_count[DM_TEST_OP_BIND] + 1); + ut_assert(!dev_get_priv(dev)); + + /* Probe the device - it should fail allocating private data */ + uts->force_fail_alloc = 1; + ret = device_probe(dev); + ut_assert(ret == -ENOMEM); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] + == op_count[DM_TEST_OP_PROBE] + 1); + ut_assert(!dev_get_priv(dev)); + + /* Try again without the alloc failure */ + uts->force_fail_alloc = 0; + ut_assertok(device_probe(dev)); + ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE] + == op_count[DM_TEST_OP_PROBE] + 2); + ut_assert(dev_get_priv(dev)); + + /* This should be device 3 in the uclass */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev == test_dev); + + /* Try ping */ + ut_assertok(test_ping(dev, 100, &pingret)); + ut_assert(pingret == 102); + + /* Now remove device 3 */ + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]); + + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + ut_assertok(device_unbind(dev)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]); + + return 0; +} +DM_TEST(dm_test_lifecycle, UT_TESTF_SCAN_PDATA | UT_TESTF_PROBE_TEST); + +/* Test that we can bind/unbind and the lists update correctly */ +static int dm_test_ordering(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_penultimate, *dev_last, *test_dev; + int pingret; + + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev)); + ut_assert(dev); + + /* Bind two new devices (numbers 4 and 5) */ + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev_penultimate)); + ut_assert(dev_penultimate); + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev_last)); + ut_assert(dev_last); + + /* Now remove device 3 */ + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_assertok(device_unbind(dev)); + + /* The device numbering should have shifted down one */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev_penultimate == test_dev); + ut_assertok(uclass_find_device(UCLASS_TEST, 4, &test_dev)); + ut_assert(dev_last == test_dev); + + /* Add back the original device 3, now in position 5 */ + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev)); + ut_assert(dev); + + /* Try ping */ + ut_assertok(test_ping(dev, 100, &pingret)); + ut_assert(pingret == 102); + + /* Remove 3 and 4 */ + ut_assertok(device_remove(dev_penultimate, DM_REMOVE_NORMAL)); + ut_assertok(device_unbind(dev_penultimate)); + ut_assertok(device_remove(dev_last, DM_REMOVE_NORMAL)); + ut_assertok(device_unbind(dev_last)); + + /* Our device should now be in position 3 */ + ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev)); + ut_assert(dev == test_dev); + + /* Now remove device 3 */ + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_assertok(device_unbind(dev)); + + return 0; +} +DM_TEST(dm_test_ordering, UT_TESTF_SCAN_PDATA); + +/* Check that we can perform operations on a device (do a ping) */ +int dm_check_operations(struct unit_test_state *uts, struct udevice *dev, + uint32_t base, struct dm_test_priv *priv) +{ + int expected; + int pingret; + + /* Getting the child device should allocate plat / priv */ + ut_assertok(testfdt_ping(dev, 10, &pingret)); + ut_assert(dev_get_priv(dev)); + ut_assert(dev_get_plat(dev)); + + expected = 10 + base; + ut_asserteq(expected, pingret); + + /* Do another ping */ + ut_assertok(testfdt_ping(dev, 20, &pingret)); + expected = 20 + base; + ut_asserteq(expected, pingret); + + /* Now check the ping_total */ + priv = dev_get_priv(dev); + ut_asserteq(DM_TEST_START_TOTAL + 10 + 20 + base * 2, + priv->ping_total); + + return 0; +} + +/* Check that we can perform operations on devices */ +static int dm_test_operations(struct unit_test_state *uts) +{ + struct udevice *dev; + int i; + + /* + * Now check that the ping adds are what we expect. This is using the + * ping-add property in each node. + */ + for (i = 0; i < ARRAY_SIZE(test_pdata); i++) { + uint32_t base; + + ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + + /* + * Get the 'reg' property, which tells us what the ping add + * should be. We don't use the plat because we want + * to test the code that sets that up (testfdt_drv_probe()). + */ + base = test_pdata[i].ping_add; + debug("dev=%d, base=%d\n", i, base); + + ut_assert(!dm_check_operations(uts, dev, base, dev_get_priv(dev))); + } + + return 0; +} +DM_TEST(dm_test_operations, UT_TESTF_SCAN_PDATA); + +/* Remove all drivers and check that things work */ +static int dm_test_remove(struct unit_test_state *uts) +{ + struct udevice *dev; + int i; + + for (i = 0; i < 3; i++) { + ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev)); + ut_assert(dev); + ut_assertf(dev_get_flags(dev) & DM_FLAG_ACTIVATED, + "Driver %d/%s not activated", i, dev->name); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_assertf(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED), + "Driver %d/%s should have deactivated", i, + dev->name); + ut_assert(!dev_get_priv(dev)); + } + + return 0; +} +DM_TEST(dm_test_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_PROBE_TEST); + +/* Remove and recreate everything, check for memory leaks */ +static int dm_test_leak(struct unit_test_state *uts) +{ + int i; + + for (i = 0; i < 2; i++) { + struct udevice *dev; + int ret; + int id; + + dm_leak_check_start(uts); + + ut_assertok(dm_scan_plat(false)); + ut_assertok(dm_scan_fdt(false)); + + /* Scanning the uclass is enough to probe all the devices */ + for (id = UCLASS_ROOT; id < UCLASS_COUNT; id++) { + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + ut_assertok(ret); + } + + ut_assertok(dm_leak_check_end(uts)); + } + + return 0; +} +DM_TEST(dm_test_leak, 0); + +/* Test uclass init/destroy methods */ +static int dm_test_uclass(struct unit_test_state *uts) +{ + struct uclass *uc; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + ut_assert(uclass_get_priv(uc)); + + ut_assertok(uclass_destroy(uc)); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]); + ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]); + + return 0; +} +DM_TEST(dm_test_uclass, 0); + +/** + * create_children() - Create children of a parent node + * + * @dms: Test system state + * @parent: Parent device + * @count: Number of children to create + * @key: Key value to put in first child. Subsequence children + * receive an incrementing value + * @child: If not NULL, then the child device pointers are written into + * this array. + * @return 0 if OK, -ve on error + */ +static int create_children(struct unit_test_state *uts, struct udevice *parent, + int count, int key, struct udevice *child[]) +{ + struct udevice *dev; + int i; + + for (i = 0; i < count; i++) { + struct dm_test_pdata *pdata; + + ut_assertok(device_bind_by_name(parent, false, + &driver_info_manual, &dev)); + pdata = calloc(1, sizeof(*pdata)); + pdata->ping_add = key + i; + dev_set_plat(dev, pdata); + if (child) + child[i] = dev; + } + + return 0; +} + +#define NODE_COUNT 10 + +static int dm_test_children(struct unit_test_state *uts) +{ + struct udevice *top[NODE_COUNT]; + struct udevice *child[NODE_COUNT]; + struct udevice *grandchild[NODE_COUNT]; + struct udevice *dev; + int total; + int ret; + int i; + + /* We don't care about the numbering for this test */ + uts->skip_post_probe = 1; + + ut_assert(NODE_COUNT > 5); + + /* First create 10 top-level children */ + ut_assertok(create_children(uts, uts->root, NODE_COUNT, 0, top)); + + /* Now a few have their own children */ + ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL)); + ut_assertok(create_children(uts, top[5], NODE_COUNT, 5, child)); + + /* And grandchildren */ + for (i = 0; i < NODE_COUNT; i++) + ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i, + i == 2 ? grandchild : NULL)); + + /* Check total number of devices */ + total = NODE_COUNT * (3 + NODE_COUNT); + ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]); + + /* Try probing one of the grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, + NODE_COUNT * 3 + 2 * NODE_COUNT, &dev)); + ut_asserteq_ptr(grandchild[0], dev); + + /* + * This should have probed the child and top node also, for a total + * of 3 nodes. + */ + ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Probe the other grandchildren */ + for (i = 1; i < NODE_COUNT; i++) + ut_assertok(device_probe(grandchild[i])); + + ut_asserteq(2 + NODE_COUNT, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Probe everything */ + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + ut_assertok(ret); + + ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_PROBE]); + + /* Remove a top-level child and check that the children are removed */ + ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL)); + ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_REMOVE]); + dm_testdrv_op_count[DM_TEST_OP_REMOVE] = 0; + + /* Try one with grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); + ut_asserteq_ptr(dev, top[5]); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), + dm_testdrv_op_count[DM_TEST_OP_REMOVE]); + + /* Try the same with unbind */ + ut_assertok(device_unbind(top[2])); + ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + dm_testdrv_op_count[DM_TEST_OP_UNBIND] = 0; + + /* Try one with grandchildren */ + ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev)); + ut_asserteq_ptr(dev, top[6]); + ut_assertok(device_unbind(top[5])); + ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT), + dm_testdrv_op_count[DM_TEST_OP_UNBIND]); + + return 0; +} +DM_TEST(dm_test_children, 0); + +static int dm_test_device_reparent(struct unit_test_state *uts) +{ + struct udevice *top[NODE_COUNT]; + struct udevice *child[NODE_COUNT]; + struct udevice *grandchild[NODE_COUNT]; + struct udevice *dev; + int total; + int ret; + int i; + + /* We don't care about the numbering for this test */ + uts->skip_post_probe = 1; + + ut_assert(NODE_COUNT > 5); + + /* First create 10 top-level children */ + ut_assertok(create_children(uts, uts->root, NODE_COUNT, 0, top)); + + /* Now a few have their own children */ + ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL)); + ut_assertok(create_children(uts, top[5], NODE_COUNT, 5, child)); + + /* And grandchildren */ + for (i = 0; i < NODE_COUNT; i++) + ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i, + i == 2 ? grandchild : NULL)); + + /* Check total number of devices */ + total = NODE_COUNT * (3 + NODE_COUNT); + ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]); + + /* Probe everything */ + for (i = 0; i < total; i++) + ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev)); + + /* Re-parent top-level children with no grandchildren. */ + ut_assertok(device_reparent(top[3], top[0])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_reparent(top[4], top[0])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + /* Re-parent top-level children with grandchildren. */ + ut_assertok(device_reparent(top[2], top[0])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_reparent(top[5], top[2])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + /* Re-parent grandchildren. */ + ut_assertok(device_reparent(grandchild[0], top[1])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_reparent(grandchild[1], top[1])); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + /* Remove re-pareneted devices. */ + ut_assertok(device_remove(top[3], DM_REMOVE_NORMAL)); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_remove(top[4], DM_REMOVE_NORMAL)); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_remove(top[5], DM_REMOVE_NORMAL)); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL)); + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_remove(grandchild[0], DM_REMOVE_NORMAL)); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(device_remove(grandchild[1], DM_REMOVE_NORMAL)); + /* try to get devices */ + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + /* Try the same with unbind */ + ut_assertok(device_unbind(top[3])); + ut_assertok(device_unbind(top[4])); + ut_assertok(device_unbind(top[5])); + ut_assertok(device_unbind(top[2])); + + ut_assertok(device_unbind(grandchild[0])); + ut_assertok(device_unbind(grandchild[1])); + + return 0; +} +DM_TEST(dm_test_device_reparent, 0); + +/* Test that pre-relocation devices work as expected */ +static int dm_test_pre_reloc(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* The normal driver should refuse to bind before relocation */ + ut_asserteq(-EPERM, device_bind_by_name(uts->root, true, + &driver_info_manual, &dev)); + + /* But this one is marked pre-reloc */ + ut_assertok(device_bind_by_name(uts->root, true, + &driver_info_pre_reloc, &dev)); + + return 0; +} +DM_TEST(dm_test_pre_reloc, 0); + +/* + * Test that removal of devices, either via the "normal" device_remove() + * API or via the device driver selective flag works as expected + */ +static int dm_test_remove_active_dma(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_act_dma, + &dev)); + ut_assert(dev); + + /* Probe the device */ + ut_assertok(device_probe(dev)); + + /* Test if device is active right now */ + ut_asserteq(true, device_active(dev)); + + /* Remove the device via selective remove flag */ + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); + + /* Test if device is inactive right now */ + ut_asserteq(false, device_active(dev)); + + /* Probe the device again */ + ut_assertok(device_probe(dev)); + + /* Test if device is active right now */ + ut_asserteq(true, device_active(dev)); + + /* Remove the device via "normal" remove API */ + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + + /* Test if device is inactive right now */ + ut_asserteq(false, device_active(dev)); + + /* + * Test if a device without the active DMA flags is not removed upon + * the active DMA remove call + */ + ut_assertok(device_unbind(dev)); + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &dev)); + ut_assert(dev); + + /* Probe the device */ + ut_assertok(device_probe(dev)); + + /* Test if device is active right now */ + ut_asserteq(true, device_active(dev)); + + /* Remove the device via selective remove flag */ + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); + + /* Test if device is still active right now */ + ut_asserteq(true, device_active(dev)); + + return 0; +} +DM_TEST(dm_test_remove_active_dma, 0); + +/* Test removal of 'vital' devices */ +static int dm_test_remove_vital(struct unit_test_state *uts) +{ + struct udevice *normal, *dma, *vital, *dma_vital; + + /* Skip the behaviour in test_post_probe() */ + uts->skip_post_probe = 1; + + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual, + &normal)); + ut_assertnonnull(normal); + + ut_assertok(device_bind_by_name(uts->root, false, &driver_info_act_dma, + &dma)); + ut_assertnonnull(dma); + + ut_assertok(device_bind_by_name(uts->root, false, + &driver_info_vital_clk, &vital)); + ut_assertnonnull(vital); + + ut_assertok(device_bind_by_name(uts->root, false, + &driver_info_act_dma_vital_clk, + &dma_vital)); + ut_assertnonnull(dma_vital); + + /* Probe the devices */ + ut_assertok(device_probe(normal)); + ut_assertok(device_probe(dma)); + ut_assertok(device_probe(vital)); + ut_assertok(device_probe(dma_vital)); + + /* Check that devices are active right now */ + ut_asserteq(true, device_active(normal)); + ut_asserteq(true, device_active(dma)); + ut_asserteq(true, device_active(vital)); + ut_asserteq(true, device_active(dma_vital)); + + /* Remove active devices via selective remove flag */ + dm_remove_devices_flags(DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_ALL); + + /* + * Check that this only has an effect on the dma device, since two + * devices are vital and the third does not have active DMA + */ + ut_asserteq(true, device_active(normal)); + ut_asserteq(false, device_active(dma)); + ut_asserteq(true, device_active(vital)); + ut_asserteq(true, device_active(dma_vital)); + + /* Remove active devices via selective remove flag */ + ut_assertok(device_probe(dma)); + dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL); + + /* This should have affected both active-dma devices */ + ut_asserteq(true, device_active(normal)); + ut_asserteq(false, device_active(dma)); + ut_asserteq(true, device_active(vital)); + ut_asserteq(false, device_active(dma_vital)); + + /* Remove non-vital devices */ + ut_assertok(device_probe(dma)); + ut_assertok(device_probe(dma_vital)); + dm_remove_devices_flags(DM_REMOVE_NON_VITAL); + + /* This should have affected only non-vital devices */ + ut_asserteq(false, device_active(normal)); + ut_asserteq(false, device_active(dma)); + ut_asserteq(true, device_active(vital)); + ut_asserteq(true, device_active(dma_vital)); + + /* Remove vital devices via normal remove flag */ + ut_assertok(device_probe(normal)); + ut_assertok(device_probe(dma)); + dm_remove_devices_flags(DM_REMOVE_NORMAL); + + /* Check that all devices are inactive right now */ + ut_asserteq(false, device_active(normal)); + ut_asserteq(false, device_active(dma)); + ut_asserteq(false, device_active(vital)); + ut_asserteq(false, device_active(dma_vital)); + + return 0; +} +DM_TEST(dm_test_remove_vital, 0); + +static int dm_test_uclass_before_ready(struct unit_test_state *uts) +{ + struct uclass *uc; + + ut_assertok(uclass_get(UCLASS_TEST, &uc)); + + gd->dm_root = NULL; + gd->dm_root_f = NULL; + memset(&gd->uclass_root, '\0', sizeof(gd->uclass_root)); + + ut_asserteq_ptr(NULL, uclass_find(UCLASS_TEST)); + + return 0; +} +DM_TEST(dm_test_uclass_before_ready, 0); + +static int dm_test_uclass_devices_find(struct unit_test_state *uts) +{ + struct udevice *dev; + int ret; + + for (ret = uclass_find_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_find_next_device(&dev)) { + ut_assert(!ret); + ut_assertnonnull(dev); + } + + ut_assertok(uclass_find_first_device(UCLASS_TEST_DUMMY, &dev)); + ut_assertnull(dev); + + return 0; +} +DM_TEST(dm_test_uclass_devices_find, UT_TESTF_SCAN_PDATA); + +static int dm_test_uclass_devices_find_by_name(struct unit_test_state *uts) +{ + struct udevice *finddev; + struct udevice *testdev; + int findret, ret; + + /* + * For each test device found in fdt like: "a-test", "b-test", etc., + * use its name and try to find it by uclass_find_device_by_name(). + * Then, on success check if: + * - current 'testdev' name is equal to the returned 'finddev' name + * - current 'testdev' pointer is equal to the returned 'finddev' + * + * We assume that, each uclass's device name is unique, so if not, then + * this will fail on checking condition: testdev == finddev, since the + * uclass_find_device_by_name(), returns the first device by given name. + */ + for (ret = uclass_find_first_device(UCLASS_TEST_FDT, &testdev); + testdev; + ret = uclass_find_next_device(&testdev)) { + ut_assertok(ret); + ut_assertnonnull(testdev); + + findret = uclass_find_device_by_name(UCLASS_TEST_FDT, + testdev->name, + &finddev); + + ut_assertok(findret); + ut_assert(testdev); + ut_asserteq_str(testdev->name, finddev->name); + ut_asserteq_ptr(testdev, finddev); + } + + return 0; +} +DM_TEST(dm_test_uclass_devices_find_by_name, UT_TESTF_SCAN_FDT); + +static int dm_test_uclass_devices_get(struct unit_test_state *uts) +{ + struct udevice *dev; + int ret; + + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) { + ut_assert(!ret); + ut_assert(dev); + ut_assert(device_active(dev)); + } + + return 0; +} +DM_TEST(dm_test_uclass_devices_get, UT_TESTF_SCAN_PDATA); + +static int dm_test_uclass_devices_get_by_name(struct unit_test_state *uts) +{ + struct udevice *finddev; + struct udevice *testdev; + int ret, findret; + + /* + * For each test device found in fdt like: "a-test", "b-test", etc., + * use its name and try to get it by uclass_get_device_by_name(). + * On success check if: + * - returned finddev' is active + * - current 'testdev' name is equal to the returned 'finddev' name + * - current 'testdev' pointer is equal to the returned 'finddev' + * + * We asserts that the 'testdev' is active on each loop entry, so we + * could be sure that the 'finddev' is activated too, but for sure + * we check it again. + * + * We assume that, each uclass's device name is unique, so if not, then + * this will fail on checking condition: testdev == finddev, since the + * uclass_get_device_by_name(), returns the first device by given name. + */ + for (ret = uclass_first_device(UCLASS_TEST_FDT, &testdev); + testdev; + ret = uclass_next_device(&testdev)) { + ut_assertok(ret); + ut_assert(testdev); + ut_assert(device_active(testdev)); + + findret = uclass_get_device_by_name(UCLASS_TEST_FDT, + testdev->name, + &finddev); + + ut_assertok(findret); + ut_assert(finddev); + ut_assert(device_active(finddev)); + ut_asserteq_str(testdev->name, finddev->name); + ut_asserteq_ptr(testdev, finddev); + } + + return 0; +} +DM_TEST(dm_test_uclass_devices_get_by_name, UT_TESTF_SCAN_FDT); + +static int dm_test_device_get_uclass_id(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST, 0, &dev)); + ut_asserteq(UCLASS_TEST, device_get_uclass_id(dev)); + + return 0; +} +DM_TEST(dm_test_device_get_uclass_id, UT_TESTF_SCAN_PDATA); + +static int dm_test_uclass_names(struct unit_test_state *uts) +{ + ut_asserteq_str("test", uclass_get_name(UCLASS_TEST)); + ut_asserteq(UCLASS_TEST, uclass_get_by_name("test")); + + return 0; +} +DM_TEST(dm_test_uclass_names, UT_TESTF_SCAN_PDATA); + +static int dm_test_inactive_child(struct unit_test_state *uts) +{ + struct udevice *parent, *dev1, *dev2; + + /* Skip the behaviour in test_post_probe() */ + uts->skip_post_probe = 1; + + ut_assertok(uclass_first_device_err(UCLASS_TEST, &parent)); + + /* + * Create a child but do not activate it. Calling the function again + * should return the same child. + */ + ut_asserteq(-ENODEV, device_find_first_inactive_child(parent, + UCLASS_TEST, &dev1)); + ut_assertok(device_bind(parent, DM_DRIVER_GET(test_drv), + "test_child", 0, ofnode_null(), &dev1)); + + ut_assertok(device_find_first_inactive_child(parent, UCLASS_TEST, + &dev2)); + ut_asserteq_ptr(dev1, dev2); + + ut_assertok(device_probe(dev1)); + ut_asserteq(-ENODEV, device_find_first_inactive_child(parent, + UCLASS_TEST, &dev2)); + + return 0; +} +DM_TEST(dm_test_inactive_child, UT_TESTF_SCAN_PDATA); + +/* Make sure all bound devices have a sequence number */ +static int dm_test_all_have_seq(struct unit_test_state *uts) +{ + struct udevice *dev; + struct uclass *uc; + + list_for_each_entry(uc, gd->uclass_root, sibling_node) { + list_for_each_entry(dev, &uc->dev_head, uclass_node) { + if (dev->seq_ == -1) + printf("Device '%s' has no seq (%d)\n", + dev->name, dev->seq_); + ut_assert(dev->seq_ != -1); + } + } + + return 0; +} +DM_TEST(dm_test_all_have_seq, UT_TESTF_SCAN_PDATA); + +static int dm_test_dma_offset(struct unit_test_state *uts) +{ + struct udevice *dev; + ofnode node; + + /* Make sure the bus's dma-ranges aren't taken into account here */ + node = ofnode_path("/mmio-bus@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev)); + ut_asserteq_64(0, dev->dma_offset); + + /* Device behind a bus with dma-ranges */ + node = ofnode_path("/mmio-bus@0/subnode@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev)); + ut_asserteq_64(-0x10000000ULL, dev->dma_offset); + + /* This one has no dma-ranges */ + node = ofnode_path("/mmio-bus@1"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev)); + node = ofnode_path("/mmio-bus@1/subnode@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev)); + ut_asserteq_64(0, dev->dma_offset); + + return 0; +} +DM_TEST(dm_test_dma_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/cpu.c b/roms/u-boot/test/dm/cpu.c new file mode 100644 index 000000000..ed12cafee --- /dev/null +++ b/roms/u-boot/test/dm/cpu.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <cpu.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_cpu(struct unit_test_state *uts) +{ + struct udevice *dev; + char text[128]; + struct cpu_info info; + + ut_assertok(cpu_probe_all()); + + /* Check that cpu_probe_all really activated all CPUs */ + for (uclass_find_first_device(UCLASS_CPU, &dev); + dev; + uclass_find_next_device(&dev)) + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + + ut_assertok(uclass_get_device_by_name(UCLASS_CPU, "cpu-test1", &dev)); + ut_asserteq_ptr(cpu_get_current_dev(), dev); + ut_asserteq(cpu_is_current(dev), 1); + + ut_assertok(cpu_get_desc(dev, text, sizeof(text))); + ut_assertok(strcmp(text, "LEG Inc. SuperMegaUltraTurbo CPU No. 1")); + + ut_assertok(cpu_get_info(dev, &info)); + ut_asserteq(info.cpu_freq, 42 * 42 * 42 * 42 * 42); + ut_asserteq(info.features, 0x42424242); + ut_asserteq(info.address_width, 32); + + ut_asserteq(cpu_get_count(dev), 42); + + ut_assertok(cpu_get_vendor(dev, text, sizeof(text))); + ut_assertok(strcmp(text, "Languid Example Garbage Inc.")); + + return 0; +} + +DM_TEST(dm_test_cpu, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/cros_ec.c b/roms/u-boot/test/dm/cros_ec.c new file mode 100644 index 000000000..30cb70e08 --- /dev/null +++ b/roms/u-boot/test/dm/cros_ec.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2021 Google LLC + */ + +#include <common.h> +#include <cros_ec.h> +#include <dm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +static int dm_test_cros_ec_hello(struct unit_test_state *uts) +{ + struct udevice *dev; + uint val; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + + ut_assertok(cros_ec_hello(dev, NULL)); + + val = 0xdead1357; + ut_assertok(cros_ec_hello(dev, &val)); + ut_asserteq(0xdead1357, val); + + sandbox_cros_ec_set_test_flags(dev, CROSECT_BREAK_HELLO); + ut_asserteq(-ENOTSYNC, cros_ec_hello(dev, &val)); + ut_asserteq(0x12345678, val); + + return 0; +} +DM_TEST(dm_test_cros_ec_hello, UT_TESTF_SCAN_FDT); + +static int dm_test_cros_ec_sku_id(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + ut_asserteq(1234, cros_ec_get_sku_id(dev)); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec sku", 0)); + ut_assert_nextline("1234"); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_cros_ec_sku_id, UT_TESTF_SCAN_FDT); + +static int dm_test_cros_ec_features(struct unit_test_state *uts) +{ + struct udevice *dev; + u64 feat; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + ut_assertok(cros_ec_get_features(dev, &feat)); + ut_asserteq_64(1U << EC_FEATURE_FLASH | 1U << EC_FEATURE_I2C | + 1u << EC_FEATURE_VSTORE | + 1ULL << EC_FEATURE_UNIFIED_WAKE_MASKS | 1ULL << EC_FEATURE_ISH, + feat); + + ut_asserteq(true, cros_ec_check_feature(dev, EC_FEATURE_I2C)); + ut_asserteq(false, cros_ec_check_feature(dev, EC_FEATURE_MOTION_SENSE)); + ut_asserteq(true, cros_ec_check_feature(dev, EC_FEATURE_ISH)); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec features", 0)); + ut_assert_nextline("flash"); + ut_assert_nextline("i2c"); + ut_assert_nextline("vstore"); + ut_assert_nextline("unified_wake_masks"); + ut_assert_nextline("ish"); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_cros_ec_features, UT_TESTF_SCAN_FDT); + +static int dm_test_cros_ec_switches(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + ut_asserteq(0, cros_ec_get_switches(dev)); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec switches", 0)); + ut_assert_console_end(); + + /* Open the lid and check the switch changes */ + sandbox_cros_ec_set_test_flags(dev, CROSECT_LID_OPEN); + ut_asserteq(EC_SWITCH_LID_OPEN, cros_ec_get_switches(dev)); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec switches", 0)); + ut_assert_nextline("lid open"); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_cros_ec_switches, UT_TESTF_SCAN_FDT); + +static int dm_test_cros_ec_events(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 events; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + ut_assertok(cros_ec_get_host_events(dev, &events)); + ut_asserteq(0, events); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec events", 0)); + ut_assert_nextline("00000000"); + ut_assert_console_end(); + + /* Open the lid and check the event appears */ + sandbox_cros_ec_set_test_flags(dev, CROSECT_LID_OPEN); + ut_assertok(cros_ec_get_host_events(dev, &events)); + ut_asserteq(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN), events); + + /* try the command */ + console_record_reset(); + ut_assertok(run_command("crosec events", 0)); + ut_assert_nextline("00000002"); + ut_assert_nextline("lid_open"); + ut_assert_console_end(); + + /* Clear the event */ + ut_assertok(cros_ec_clear_host_events(dev, + EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN))); + ut_assertok(cros_ec_get_host_events(dev, &events)); + ut_asserteq(0, events); + + return 0; +} +DM_TEST(dm_test_cros_ec_events, UT_TESTF_SCAN_FDT); + +static int dm_test_cros_ec_vstore(struct unit_test_state *uts) +{ + const int size = EC_VSTORE_SLOT_SIZE; + u8 test_data[size], data[size]; + struct udevice *dev; + u32 locked; + int i; + + ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev)); + ut_asserteq(true, cros_ec_vstore_supported(dev)); + + ut_asserteq(4, cros_ec_vstore_info(dev, &locked)); + ut_asserteq(0, locked); + + /* Write some data */ + for (i = 0; i < size; i++) + test_data[i] = ' ' + i; + ut_assertok(cros_ec_vstore_write(dev, 2, test_data, size)); + + /* Check it is locked */ + ut_asserteq(4, cros_ec_vstore_info(dev, &locked)); + ut_asserteq(1 << 2, locked); + + /* Read it back and compare */ + ut_assertok(cros_ec_vstore_read(dev, 2, data)); + ut_asserteq_mem(test_data, data, size); + + /* Try another slot to make sure it is empty */ + ut_assertok(cros_ec_vstore_read(dev, 0, data)); + for (i = 0; i < size; i++) + ut_asserteq(0, data[i]); + + return 0; +} +DM_TEST(dm_test_cros_ec_vstore, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/devres.c b/roms/u-boot/test/dm/devres.c new file mode 100644 index 000000000..4f959d11d --- /dev/null +++ b/roms/u-boot/test/dm/devres.c @@ -0,0 +1,189 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the devres ( + * + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> + +/* Test that devm_kmalloc() allocates memory, free when device is removed */ +static int dm_test_devres_alloc(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev, mem_kmalloc; + struct udevice *dev; + void *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + /* This should increase allocated memory */ + ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + mem_kmalloc = ut_check_delta(mem_dev); + ut_assert(mem_kmalloc > 0); + + /* Check that ptr is freed */ + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_alloc, UT_TESTF_SCAN_PDATA); + +/* Test devm_kfree() can be used to free memory too */ +static int dm_test_devres_free(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev, mem_kmalloc; + struct udevice *dev; + void *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + mem_kmalloc = ut_check_delta(mem_dev); + ut_assert(mem_kmalloc > 0); + + /* Free the ptr and check that memory usage goes down */ + devm_kfree(dev, ptr); + ut_assert(ut_check_delta(mem_kmalloc) < 0); + + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_free, UT_TESTF_SCAN_PDATA); + + +/* Test that kzalloc() returns memory that is zeroed */ +static int dm_test_devres_kzalloc(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 *ptr, val; + int i; + + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + + ptr = devm_kzalloc(dev, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + for (val = 0, i = 0; i < TEST_DEVRES_SIZE; i++) + val |= *ptr; + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_devres_kzalloc, UT_TESTF_SCAN_PDATA); + +/* Test that devm_kmalloc_array() allocates an array that can be set */ +static int dm_test_devres_kmalloc_array(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev; + struct udevice *dev; + u8 *ptr; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + + ptr = devm_kmalloc_array(dev, TEST_DEVRES_COUNT, TEST_DEVRES_SIZE, 0); + ut_assert(ptr != NULL); + memset(ptr, '\xff', TEST_DEVRES_TOTAL); + ut_assert(ut_check_delta(mem_dev) > 0); + + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_kmalloc_array, UT_TESTF_SCAN_PDATA); + +/* Test that devm_kcalloc() allocates a zeroed array */ +static int dm_test_devres_kcalloc(struct unit_test_state *uts) +{ + ulong mem_start, mem_dev; + struct udevice *dev; + u8 *ptr, val; + int i; + + mem_start = ut_check_delta(0); + ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev)); + mem_dev = ut_check_delta(mem_start); + ut_assert(mem_dev > 0); + + /* This should increase allocated memory */ + ptr = devm_kcalloc(dev, TEST_DEVRES_SIZE, TEST_DEVRES_COUNT, 0); + ut_assert(ptr != NULL); + ut_assert(ut_check_delta(mem_dev) > 0); + for (val = 0, i = 0; i < TEST_DEVRES_TOTAL; i++) + val |= *ptr; + ut_asserteq(0, val); + + /* Check that ptr is freed */ + device_remove(dev, DM_REMOVE_NORMAL); + ut_asserteq(0, ut_check_delta(mem_start)); + + return 0; +} +DM_TEST(dm_test_devres_kcalloc, UT_TESTF_SCAN_PDATA); + +/* Test devres releases resources automatically as expected */ +static int dm_test_devres_phase(struct unit_test_state *uts) +{ + struct devres_stats stats; + struct udevice *dev; + + /* + * The device is bound already, so find it and check that it has the + * allocation created in the bind() method. + */ + ut_assertok(uclass_find_first_device(UCLASS_TEST_DEVRES, &dev)); + ut_assertnonnull(dev); + devres_get_stats(dev, &stats); + ut_asserteq(1, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE, stats.total_size); + + /* Getting plat should add one allocation */ + ut_assertok(device_of_to_plat(dev)); + devres_get_stats(dev, &stats); + ut_asserteq(2, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE3, stats.total_size); + + /* Probing the device should add one allocation */ + ut_assertok(uclass_first_device(UCLASS_TEST_DEVRES, &dev)); + ut_assert(dev != NULL); + devres_get_stats(dev, &stats); + ut_asserteq(3, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE2 + TEST_DEVRES_SIZE3, + stats.total_size); + + /* Removing the device should drop both those allocations */ + device_remove(dev, DM_REMOVE_NORMAL); + devres_get_stats(dev, &stats); + ut_asserteq(1, stats.allocs); + ut_asserteq(TEST_DEVRES_SIZE, stats.total_size); + + /* Unbinding removes the other. Note this access a freed pointer */ + device_unbind(dev); + devres_get_stats(dev, &stats); + ut_asserteq(0, stats.allocs); + ut_asserteq(0, stats.total_size); + + return 0; +} +DM_TEST(dm_test_devres_phase, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/dma.c b/roms/u-boot/test/dm/dma.c new file mode 100644 index 000000000..cce47cb21 --- /dev/null +++ b/roms/u-boot/test/dm/dma.c @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Direct Memory Access U-Class tests + * + * Copyright (C) 2018 Texas Instruments Incorporated <www.ti.com> + * Grygorii Strashko <grygorii.strashko@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <dm/test.h> +#include <dma.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_dma_m2m(struct unit_test_state *uts) +{ + struct udevice *dev; + struct dma dma_m2m; + u8 src_buf[512]; + u8 dst_buf[512]; + size_t len = 512; + int i; + + ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev)); + ut_assertok(dma_get_by_name(dev, "m2m", &dma_m2m)); + + memset(dst_buf, 0, len); + for (i = 0; i < len; i++) + src_buf[i] = i; + + ut_assertok(dma_memcpy(dst_buf, src_buf, len)); + ut_asserteq_mem(src_buf, dst_buf, len); + + return 0; +} +DM_TEST(dm_test_dma_m2m, UT_TESTF_SCAN_FDT); + +static int dm_test_dma(struct unit_test_state *uts) +{ + struct udevice *dev; + struct dma dma_tx, dma_rx; + u8 src_buf[512]; + u8 dst_buf[512]; + void *dst_ptr; + size_t len = 512; + u32 meta1, meta2; + int i; + + ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev)); + + ut_assertok(dma_get_by_name(dev, "tx0", &dma_tx)); + ut_assertok(dma_get_by_name(dev, "rx0", &dma_rx)); + + ut_assertok(dma_enable(&dma_tx)); + ut_assertok(dma_enable(&dma_rx)); + + memset(dst_buf, 0, len); + for (i = 0; i < len; i++) + src_buf[i] = i; + meta1 = 0xADADDEAD; + meta2 = 0; + dst_ptr = &dst_buf; + + ut_assertok(dma_send(&dma_tx, src_buf, len, &meta1)); + + ut_asserteq(len, dma_receive(&dma_rx, &dst_ptr, &meta2)); + ut_asserteq(0xADADDEAD, meta2); + + ut_assertok(dma_disable(&dma_tx)); + ut_assertok(dma_disable(&dma_rx)); + + ut_assertok(dma_free(&dma_tx)); + ut_assertok(dma_free(&dma_rx)); + ut_asserteq_mem(src_buf, dst_buf, len); + + return 0; +} +DM_TEST(dm_test_dma, UT_TESTF_SCAN_FDT); + +static int dm_test_dma_rx(struct unit_test_state *uts) +{ + struct udevice *dev; + struct dma dma_tx, dma_rx; + u8 src_buf[512]; + u8 dst_buf[512]; + void *dst_ptr; + size_t len = 512; + u32 meta1, meta2; + int i; + + ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev)); + + ut_assertok(dma_get_by_name(dev, "tx0", &dma_tx)); + ut_assertok(dma_get_by_name(dev, "rx0", &dma_rx)); + + ut_assertok(dma_enable(&dma_tx)); + ut_assertok(dma_enable(&dma_rx)); + + memset(dst_buf, 0, len); + for (i = 0; i < len; i++) + src_buf[i] = i; + meta1 = 0xADADDEAD; + meta2 = 0; + dst_ptr = NULL; + + ut_assertok(dma_prepare_rcv_buf(&dma_tx, dst_buf, len)); + + ut_assertok(dma_send(&dma_tx, src_buf, len, &meta1)); + + ut_asserteq(len, dma_receive(&dma_rx, &dst_ptr, &meta2)); + ut_asserteq(0xADADDEAD, meta2); + ut_asserteq_ptr(dst_buf, dst_ptr); + + ut_assertok(dma_disable(&dma_tx)); + ut_assertok(dma_disable(&dma_rx)); + + ut_assertok(dma_free(&dma_tx)); + ut_assertok(dma_free(&dma_rx)); + ut_asserteq_mem(src_buf, dst_buf, len); + + return 0; +} +DM_TEST(dm_test_dma_rx, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/dsa.c b/roms/u-boot/test/dm/dsa.c new file mode 100644 index 000000000..18c177646 --- /dev/null +++ b/roms/u-boot/test/dm/dsa.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2020-2021 NXP Semiconductors + */ + +#include <net/dsa.h> +#include <dm/test.h> +#include <test/ut.h> +#include <net.h> +#include <dm/uclass-internal.h> +#include <dm/device-internal.h> + +/* This test exercises the major dsa.h API functions, after making sure + * that the DSA ports and the master Eth are correctly probed. + */ +static int dm_test_dsa_probe(struct unit_test_state *uts) +{ + struct udevice *dev_dsa, *dev_port, *dev_master; + struct dsa_pdata *dsa_pdata; + enum uclass_id id; + + id = uclass_get_by_name("dsa"); + ut_assert(id == UCLASS_DSA); + + ut_assertok(uclass_find_device_by_name(UCLASS_DSA, "dsa-test", + &dev_dsa)); + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test-eth", + &dev_master)); + ut_assertok(device_probe(dev_master)); + + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test@0", + &dev_port)); + ut_assertok(device_probe(dev_port)); + + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test@1", + &dev_port)); + ut_assertok(device_probe(dev_port)); + + /* exercise DSA API */ + dsa_pdata = dev_get_uclass_plat(dev_dsa); + ut_assertnonnull(dsa_pdata); + /* includes CPU port */ + ut_assert(dsa_pdata->num_ports == 3); + + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "lan0", + &dev_port)); + ut_assertok(device_probe(dev_port)); + + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "lan1", + &dev_port)); + ut_assertok(device_probe(dev_port)); + + dev_master = dsa_get_master(dev_dsa); + ut_assertnonnull(dev_master); + ut_asserteq_str("dsa-test-eth", dev_master->name); + + return 0; +} + +DM_TEST(dm_test_dsa_probe, UT_TESTF_SCAN_FDT); + +/* This test sends ping requests with the local address through each DSA port + * via the sandbox DSA master Eth. + */ +static int dm_test_dsa(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.2.3.5"); + + env_set("ethact", "eth2"); + ut_assertok(net_loop(PING)); + + env_set("ethact", "lan0"); + ut_assertok(net_loop(PING)); + env_set("ethact", "lan1"); + ut_assertok(net_loop(PING)); + + env_set("ethact", ""); + + return 0; +} + +DM_TEST(dm_test_dsa, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/dsi_host.c b/roms/u-boot/test/dm/dsi_host.c new file mode 100644 index 000000000..6e0a5df70 --- /dev/null +++ b/roms/u-boot/test/dm/dsi_host.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019 STMicroelectronics - All Rights Reserved + * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics. + */ + +#include <common.h> +#include <dm.h> +#include <dsi_host.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_dsi_host_phy_init(void *priv_data) +{ + return 0; +} + +static void dm_test_dsi_host_phy_post_set_mode(void *priv_data, + unsigned long mode_flags) +{ +} + +static int dm_test_dsi_host_phy_get_lane_mbps(void *priv_data, + struct display_timing *timings, + u32 lanes, + u32 format, + unsigned int *lane_mbps) +{ + return 0; +} + +static const struct mipi_dsi_phy_ops dm_test_dsi_host_phy_ops = { + .init = dm_test_dsi_host_phy_init, + .get_lane_mbps = dm_test_dsi_host_phy_get_lane_mbps, + .post_set_mode = dm_test_dsi_host_phy_post_set_mode, +}; + +/* Test that dsi_host driver functions are called */ +static int dm_test_dsi_host(struct unit_test_state *uts) +{ + struct udevice *dev; + struct mipi_dsi_device device; + struct display_timing timings; + unsigned int max_data_lanes = 4; + + ut_assertok(uclass_first_device_err(UCLASS_DSI_HOST, &dev)); + + ut_assertok(dsi_host_init(dev, &device, &timings, max_data_lanes, + &dm_test_dsi_host_phy_ops)); + + ut_assertok(dsi_host_enable(dev)); + + return 0; +} + +DM_TEST(dm_test_dsi_host, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/eth.c b/roms/u-boot/test/dm/eth.c new file mode 100644 index 000000000..e4ee69561 --- /dev/null +++ b/roms/u-boot/test/dm/eth.c @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2015 National Instruments + * + * (C) Copyright 2015 + * Joe Hershberger <joe.hershberger@ni.com> + */ + +#include <common.h> +#include <dm.h> +#include <env.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <net.h> +#include <asm/eth.h> +#include <dm/test.h> +#include <dm/device-internal.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +#define DM_TEST_ETH_NUM 4 + +static int dm_test_eth(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.1.2.2"); + + env_set("ethact", "eth@10002000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + env_set("ethact", "eth@10003000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10003000", env_get("ethact")); + + env_set("ethact", "eth@10004000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10004000", env_get("ethact")); + + return 0; +} +DM_TEST(dm_test_eth, UT_TESTF_SCAN_FDT); + +static int dm_test_eth_alias(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.1.2.2"); + env_set("ethact", "eth0"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + env_set("ethact", "eth6"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10004000", env_get("ethact")); + + /* Expected to fail since eth1 is not defined in the device tree */ + env_set("ethact", "eth1"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + env_set("ethact", "eth5"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10003000", env_get("ethact")); + + return 0; +} +DM_TEST(dm_test_eth_alias, UT_TESTF_SCAN_FDT); + +static int dm_test_eth_prime(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.1.2.2"); + + /* Expected to be "eth@10003000" because of ethprime variable */ + env_set("ethact", NULL); + env_set("ethprime", "eth5"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10003000", env_get("ethact")); + + /* Expected to be "eth@10002000" because it is first */ + env_set("ethact", NULL); + env_set("ethprime", NULL); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + return 0; +} +DM_TEST(dm_test_eth_prime, UT_TESTF_SCAN_FDT); + +/** + * This test case is trying to test the following scenario: + * - All ethernet devices are not probed + * - "ethaddr" for all ethernet devices are not set + * - "ethact" is set to a valid ethernet device name + * + * With Sandbox default test configuration, all ethernet devices are + * probed after power-up, so we have to manually create such scenario: + * - Remove all ethernet devices + * - Remove all "ethaddr" environment variables + * - Set "ethact" to the first ethernet device + * + * Do a ping test to see if anything goes wrong. + */ +static int dm_test_eth_act(struct unit_test_state *uts) +{ + struct udevice *dev[DM_TEST_ETH_NUM]; + const char *ethname[DM_TEST_ETH_NUM] = {"eth@10002000", "eth@10003000", + "sbe5", "eth@10004000"}; + const char *addrname[DM_TEST_ETH_NUM] = {"ethaddr", "eth5addr", + "eth3addr", "eth6addr"}; + char ethaddr[DM_TEST_ETH_NUM][18]; + int i; + + memset(ethaddr, '\0', sizeof(ethaddr)); + net_ping_ip = string_to_ip("1.1.2.2"); + + /* Prepare the test scenario */ + for (i = 0; i < DM_TEST_ETH_NUM; i++) { + ut_assertok(uclass_find_device_by_name(UCLASS_ETH, + ethname[i], &dev[i])); + ut_assertok(device_remove(dev[i], DM_REMOVE_NORMAL)); + + /* Invalidate MAC address */ + strncpy(ethaddr[i], env_get(addrname[i]), 17); + /* Must disable access protection for ethaddr before clearing */ + env_set(".flags", addrname[i]); + env_set(addrname[i], NULL); + } + + /* Set ethact to "eth@10002000" */ + env_set("ethact", ethname[0]); + + /* Segment fault might happen if something is wrong */ + ut_asserteq(-ENODEV, net_loop(PING)); + + for (i = 0; i < DM_TEST_ETH_NUM; i++) { + /* Restore the env */ + env_set(".flags", addrname[i]); + env_set(addrname[i], ethaddr[i]); + + /* Probe the device again */ + ut_assertok(device_probe(dev[i])); + } + env_set(".flags", NULL); + env_set("ethact", NULL); + + return 0; +} +DM_TEST(dm_test_eth_act, UT_TESTF_SCAN_FDT); + +/* The asserts include a return on fail; cleanup in the caller */ +static int _dm_test_eth_rotate1(struct unit_test_state *uts) +{ + /* Make sure that the default is to rotate to the next interface */ + env_set("ethact", "eth@10004000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + /* If ethrotate is no, then we should fail on a bad MAC */ + env_set("ethact", "eth@10004000"); + env_set("ethrotate", "no"); + ut_asserteq(-EINVAL, net_loop(PING)); + ut_asserteq_str("eth@10004000", env_get("ethact")); + + return 0; +} + +static int _dm_test_eth_rotate2(struct unit_test_state *uts) +{ + /* Make sure we can skip invalid devices */ + env_set("ethact", "eth@10004000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10004000", env_get("ethact")); + + /* Make sure we can handle device name which is not eth# */ + env_set("ethact", "sbe5"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("sbe5", env_get("ethact")); + + return 0; +} + +static int dm_test_eth_rotate(struct unit_test_state *uts) +{ + char ethaddr[18]; + int retval; + + /* Set target IP to mock ping */ + net_ping_ip = string_to_ip("1.1.2.2"); + + /* Invalidate eth1's MAC address */ + memset(ethaddr, '\0', sizeof(ethaddr)); + strncpy(ethaddr, env_get("eth6addr"), 17); + /* Must disable access protection for eth6addr before clearing */ + env_set(".flags", "eth6addr"); + env_set("eth6addr", NULL); + + retval = _dm_test_eth_rotate1(uts); + + /* Restore the env */ + env_set("eth6addr", ethaddr); + env_set("ethrotate", NULL); + + if (!retval) { + /* Invalidate eth0's MAC address */ + strncpy(ethaddr, env_get("ethaddr"), 17); + /* Must disable access protection for ethaddr before clearing */ + env_set(".flags", "ethaddr"); + env_set("ethaddr", NULL); + + retval = _dm_test_eth_rotate2(uts); + + /* Restore the env */ + env_set("ethaddr", ethaddr); + } + /* Restore the env */ + env_set(".flags", NULL); + + return retval; +} +DM_TEST(dm_test_eth_rotate, UT_TESTF_SCAN_FDT); + +/* The asserts include a return on fail; cleanup in the caller */ +static int _dm_test_net_retry(struct unit_test_state *uts) +{ + /* + * eth1 is disabled and netretry is yes, so the ping should succeed and + * the active device should be eth0 + */ + sandbox_eth_disable_response(1, true); + env_set("ethact", "lan1"); + env_set("netretry", "yes"); + sandbox_eth_skip_timeout(); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + /* + * eth1 is disabled and netretry is no, so the ping should fail and the + * active device should be eth1 + */ + env_set("ethact", "lan1"); + env_set("netretry", "no"); + sandbox_eth_skip_timeout(); + ut_asserteq(-ENONET, net_loop(PING)); + ut_asserteq_str("lan1", env_get("ethact")); + + return 0; +} + +static int dm_test_net_retry(struct unit_test_state *uts) +{ + int retval; + + net_ping_ip = string_to_ip("1.1.2.2"); + + retval = _dm_test_net_retry(uts); + + /* Restore the env */ + env_set("netretry", NULL); + sandbox_eth_disable_response(1, false); + + return retval; +} +DM_TEST(dm_test_net_retry, UT_TESTF_SCAN_FDT); + +static int sb_check_arp_reply(struct udevice *dev, void *packet, + unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct ethernet_hdr *eth = packet; + struct arp_hdr *arp; + /* Used by all of the ut_assert macros */ + struct unit_test_state *uts = priv->priv; + + if (ntohs(eth->et_protlen) != PROT_ARP) + return 0; + + arp = packet + ETHER_HDR_SIZE; + + if (ntohs(arp->ar_op) != ARPOP_REPLY) + return 0; + + /* This test would be worthless if we are not waiting */ + ut_assert(arp_is_waiting()); + + /* Validate response */ + ut_asserteq_mem(eth->et_src, net_ethaddr, ARP_HLEN); + ut_asserteq_mem(eth->et_dest, priv->fake_host_hwaddr, ARP_HLEN); + ut_assert(eth->et_protlen == htons(PROT_ARP)); + + ut_assert(arp->ar_hrd == htons(ARP_ETHER)); + ut_assert(arp->ar_pro == htons(PROT_IP)); + ut_assert(arp->ar_hln == ARP_HLEN); + ut_assert(arp->ar_pln == ARP_PLEN); + ut_asserteq_mem(&arp->ar_sha, net_ethaddr, ARP_HLEN); + ut_assert(net_read_ip(&arp->ar_spa).s_addr == net_ip.s_addr); + ut_asserteq_mem(&arp->ar_tha, priv->fake_host_hwaddr, ARP_HLEN); + ut_assert(net_read_ip(&arp->ar_tpa).s_addr == + string_to_ip("1.1.2.4").s_addr); + + return 0; +} + +static int sb_with_async_arp_handler(struct udevice *dev, void *packet, + unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct ethernet_hdr *eth = packet; + struct arp_hdr *arp = packet + ETHER_HDR_SIZE; + int ret; + + /* + * If we are about to generate a reply to ARP, first inject a request + * from another host + */ + if (ntohs(eth->et_protlen) == PROT_ARP && + ntohs(arp->ar_op) == ARPOP_REQUEST) { + /* Make sure sandbox_eth_recv_arp_req() knows who is asking */ + priv->fake_host_ipaddr = string_to_ip("1.1.2.4"); + + ret = sandbox_eth_recv_arp_req(dev); + if (ret) + return ret; + } + + sandbox_eth_arp_req_to_reply(dev, packet, len); + sandbox_eth_ping_req_to_reply(dev, packet, len); + + return sb_check_arp_reply(dev, packet, len); +} + +static int dm_test_eth_async_arp_reply(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.1.2.2"); + + sandbox_eth_set_tx_handler(0, sb_with_async_arp_handler); + /* Used by all of the ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, uts); + + env_set("ethact", "eth@10002000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + sandbox_eth_set_tx_handler(0, NULL); + + return 0; +} + +DM_TEST(dm_test_eth_async_arp_reply, UT_TESTF_SCAN_FDT); + +static int sb_check_ping_reply(struct udevice *dev, void *packet, + unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct ethernet_hdr *eth = packet; + struct ip_udp_hdr *ip; + struct icmp_hdr *icmp; + /* Used by all of the ut_assert macros */ + struct unit_test_state *uts = priv->priv; + + if (ntohs(eth->et_protlen) != PROT_IP) + return 0; + + ip = packet + ETHER_HDR_SIZE; + + if (ip->ip_p != IPPROTO_ICMP) + return 0; + + icmp = (struct icmp_hdr *)&ip->udp_src; + + if (icmp->type != ICMP_ECHO_REPLY) + return 0; + + /* This test would be worthless if we are not waiting */ + ut_assert(arp_is_waiting()); + + /* Validate response */ + ut_asserteq_mem(eth->et_src, net_ethaddr, ARP_HLEN); + ut_asserteq_mem(eth->et_dest, priv->fake_host_hwaddr, ARP_HLEN); + ut_assert(eth->et_protlen == htons(PROT_IP)); + + ut_assert(net_read_ip(&ip->ip_src).s_addr == net_ip.s_addr); + ut_assert(net_read_ip(&ip->ip_dst).s_addr == + string_to_ip("1.1.2.4").s_addr); + + return 0; +} + +static int sb_with_async_ping_handler(struct udevice *dev, void *packet, + unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct ethernet_hdr *eth = packet; + struct arp_hdr *arp = packet + ETHER_HDR_SIZE; + int ret; + + /* + * If we are about to generate a reply to ARP, first inject a request + * from another host + */ + if (ntohs(eth->et_protlen) == PROT_ARP && + ntohs(arp->ar_op) == ARPOP_REQUEST) { + /* Make sure sandbox_eth_recv_arp_req() knows who is asking */ + priv->fake_host_ipaddr = string_to_ip("1.1.2.4"); + + ret = sandbox_eth_recv_ping_req(dev); + if (ret) + return ret; + } + + sandbox_eth_arp_req_to_reply(dev, packet, len); + sandbox_eth_ping_req_to_reply(dev, packet, len); + + return sb_check_ping_reply(dev, packet, len); +} + +static int dm_test_eth_async_ping_reply(struct unit_test_state *uts) +{ + net_ping_ip = string_to_ip("1.1.2.2"); + + sandbox_eth_set_tx_handler(0, sb_with_async_ping_handler); + /* Used by all of the ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, uts); + + env_set("ethact", "eth@10002000"); + ut_assertok(net_loop(PING)); + ut_asserteq_str("eth@10002000", env_get("ethact")); + + sandbox_eth_set_tx_handler(0, NULL); + + return 0; +} + +DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/fastboot.c b/roms/u-boot/test/dm/fastboot.c new file mode 100644 index 000000000..e7f8c362b --- /dev/null +++ b/roms/u-boot/test/dm/fastboot.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <fastboot.h> +#include <fb_mmc.h> +#include <mmc.h> +#include <part.h> +#include <part_efi.h> +#include <dm/test.h> +#include <test/ut.h> +#include <linux/stringify.h> + +#define FB_ALIAS_PREFIX "fastboot_partition_alias_" + +static int dm_test_fastboot_mmc_part(struct unit_test_state *uts) +{ + char response[FASTBOOT_RESPONSE_LEN] = {0}; + char str_disk_guid[UUID_STR_LEN + 1]; + struct blk_desc *mmc_dev_desc, *fb_dev_desc; + struct disk_partition part_info; + struct disk_partition parts[2] = { + { + .start = 48, /* GPT data takes up the first 34 blocks or so */ + .size = 1, + .name = "test1", + }, + { + .start = 49, + .size = 1, + .name = "test2", + }, + }; + + /* + * There are a lot of literal 0s I don't want to have to construct from + * MMC_DEV. + */ + ut_asserteq(0, CONFIG_FASTBOOT_FLASH_MMC_DEV); + ut_assertok(blk_get_device_by_str("mmc", "0", &mmc_dev_desc)); + if (CONFIG_IS_ENABLED(RANDOM_UUID)) { + gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD); + gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD); + gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD); + } + ut_assertok(gpt_restore(mmc_dev_desc, str_disk_guid, parts, + ARRAY_SIZE(parts))); + + /* "Classic" partition labels */ + ut_asserteq(1, fastboot_mmc_get_part_info("test1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(2, fastboot_mmc_get_part_info("test2", &fb_dev_desc, + &part_info, response)); + + /* Test aliases */ + ut_assertnull(env_get(FB_ALIAS_PREFIX "test3")); + ut_assertok(env_set(FB_ALIAS_PREFIX "test3", "test1")); + ut_asserteq(1, fastboot_mmc_get_part_info("test3", &fb_dev_desc, + &part_info, response)); + ut_assertok(env_set(FB_ALIAS_PREFIX "test3", NULL)); + + /* "New" partition labels */ + ut_asserteq(1, fastboot_mmc_get_part_info("#test1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0#test1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0.0#test1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0:1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0.0:1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info("0.0", &fb_dev_desc, + &part_info, response)); + ut_asserteq(0, fastboot_mmc_get_part_info("0:0", &fb_dev_desc, + &part_info, response)); + ut_asserteq(0, fastboot_mmc_get_part_info("0.0:0", &fb_dev_desc, + &part_info, response)); + ut_asserteq(0, fastboot_mmc_get_part_info("1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(0, fastboot_mmc_get_part_info("1.0", &fb_dev_desc, + &part_info, response)); + ut_asserteq(1, fastboot_mmc_get_part_info(":1", &fb_dev_desc, + &part_info, response)); + ut_asserteq(0, fastboot_mmc_get_part_info(":0", &fb_dev_desc, + &part_info, response)); + + return 0; +} +DM_TEST(dm_test_fastboot_mmc_part, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/fdtdec.c b/roms/u-boot/test/dm/fdtdec.c new file mode 100644 index 000000000..1f630ea3e --- /dev/null +++ b/roms/u-boot/test/dm/fdtdec.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 NXP + */ + +#include <common.h> +#include <dm.h> +#include <asm/global_data.h> +#include <dm/of_extra.h> +#include <dm/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int dm_test_fdtdec_set_carveout(struct unit_test_state *uts) +{ + struct fdt_memory resv; + void *blob; + const fdt32_t *prop; + int blob_sz, len, offset; + + blob_sz = fdt_totalsize(gd->fdt_blob) + 4096; + blob = malloc(blob_sz); + ut_assertnonnull(blob); + + /* Make a writable copy of the fdt blob */ + ut_assertok(fdt_open_into(gd->fdt_blob, blob, blob_sz)); + + resv.start = 0x1000; + resv.end = 0x2000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 2, "test_resv1", + &resv)); + + resv.start = 0x10000; + resv.end = 0x20000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 1, "test_resv2", + &resv)); + + resv.start = 0x100000; + resv.end = 0x200000; + ut_assertok(fdtdec_set_carveout(blob, "/a-test", + "memory-region", 0, "test_resv3", + &resv)); + + offset = fdt_path_offset(blob, "/a-test"); + ut_assert(offset > 0); + prop = fdt_getprop(blob, offset, "memory-region", &len); + ut_assertnonnull(prop); + + ut_asserteq(len, 12); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[0])) > 0); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[1])) > 0); + ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[2])) > 0); + + free(blob); + + return 0; +} +DM_TEST(dm_test_fdtdec_set_carveout, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +static int dm_test_fdtdec_add_reserved_memory(struct unit_test_state *uts) +{ + struct fdt_memory resv; + fdt_addr_t addr; + fdt_size_t size; + void *blob; + int blob_sz, parent, subnode; + uint32_t phandle, phandle1; + + blob_sz = fdt_totalsize(gd->fdt_blob) + 128; + blob = malloc(blob_sz); + ut_assertnonnull(blob); + + /* Make a writable copy of the fdt blob */ + ut_assertok(fdt_open_into(gd->fdt_blob, blob, blob_sz)); + + /* Insert a memory region in /reserved-memory node */ + resv.start = 0x1000; + resv.end = 0x1fff; + ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region", + &resv, &phandle, false)); + + /* Test /reserve-memory and its subnode should exist */ + parent = fdt_path_offset(blob, "/reserved-memory"); + ut_assert(parent > 0); + subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region"); + ut_assert(subnode > 0); + + /* Test reg property of /reserved-memory/rsvd_region node */ + addr = fdtdec_get_addr_size_auto_parent(blob, parent, subnode, + "reg", 0, &size, false); + ut_assert(addr == resv.start); + ut_assert(size == resv.end - resv.start + 1); + + /* Insert another memory region in /reserved-memory node */ + subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region1"); + ut_assert(subnode < 0); + + resv.start = 0x2000; + resv.end = 0x2fff; + ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region1", + &resv, &phandle1, true)); + subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region1"); + ut_assert(subnode > 0); + + /* check that no-map property is present */ + ut_assert(fdt_getprop(blob, subnode, "no-map", NULL) > 0); + + /* phandles must be different */ + ut_assert(phandle != phandle1); + + /* + * Insert a 3rd memory region with the same addr/size as the 1st one, + * but a new node should not be inserted due to the same addr/size. + */ + resv.start = 0x1000; + resv.end = 0x1fff; + ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region2", + &resv, &phandle1, false)); + subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region2"); + ut_assert(subnode < 0); + + /* phandle must be same as the 1st one */ + ut_assert(phandle == phandle1); + + free(blob); + + return 0; +} +DM_TEST(dm_test_fdtdec_add_reserved_memory, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); diff --git a/roms/u-boot/test/dm/firmware.c b/roms/u-boot/test/dm/firmware.c new file mode 100644 index 000000000..f37bccfe4 --- /dev/null +++ b/roms/u-boot/test/dm/firmware.c @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Xilinx, Inc. + */ + +#include <common.h> +#include <dm.h> +#include <syscon.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of firmware probe */ +static int dm_test_firmware_probe(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_FIRMWARE, + "sandbox-firmware", &dev)); + return 0; +} +DM_TEST(dm_test_firmware_probe, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/gpio.c b/roms/u-boot/test/dm/gpio.c new file mode 100644 index 000000000..33ae98701 --- /dev/null +++ b/roms/u-boot/test/dm/gpio.c @@ -0,0 +1,780 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Google, Inc + */ + +#include <common.h> +#include <fdtdec.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <acpi/acpi_device.h> +#include <asm/gpio.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/util.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that sandbox GPIOs work correctly */ +static int dm_test_gpio(struct unit_test_state *uts) +{ + unsigned int offset, gpio; + struct dm_gpio_ops *ops; + struct udevice *dev; + struct gpio_desc *desc; + const char *name; + int offset_count; + char buf[80]; + + /* + * We expect to get 4 banks. One is anonymous (just numbered) and + * comes from plat. The other are named a (20 gpios), + * b (10 gpios) and c (10 gpios) and come from the device tree. See + * test/dm/test.dts. + */ + ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "extra-gpios"); + ut_asserteq(4, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("b", name); + ut_asserteq(10, offset_count); + + /* Get the operations for this device */ + ops = gpio_get_ops(dev); + ut_assert(ops->get_function); + + /* Cannot get a value until it is reserved */ + ut_asserteq(-EBUSY, gpio_get_value(gpio + 1)); + /* + * Now some tests that use the 'sandbox' back door. All GPIOs + * should default to input, include b4 that we are using here. + */ + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: input: 0 [ ]", buf); + + /* Change it to an output */ + sandbox_gpio_set_direction(dev, offset, 1); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: output: 0 [ ]", buf); + + sandbox_gpio_set_value(dev, offset, 1); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: output: 1 [ ]", buf); + + ut_assertok(gpio_request(gpio, "testing")); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: output: 1 [x] testing", buf); + + /* Change the value a bit */ + ut_asserteq(1, ops->get_value(dev, offset)); + ut_assertok(ops->set_value(dev, offset, 0)); + ut_asserteq(0, ops->get_value(dev, offset)); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: output: 0 [x] testing", buf); + ut_assertok(ops->set_value(dev, offset, 1)); + ut_asserteq(1, ops->get_value(dev, offset)); + + /* Make it an open drain output, and reset it */ + ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(dev, offset)); + ut_assertok(ops->set_flags(dev, offset, + GPIOD_IS_OUT | GPIOD_OPEN_DRAIN)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, + sandbox_gpio_get_flags(dev, offset)); + ut_assertok(ops->set_flags(dev, offset, + GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(dev, offset)); + + /* Make it an input */ + ut_assertok(ops->direction_input(dev, offset)); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: input: 1 [x] testing", buf); + sandbox_gpio_set_value(dev, offset, 0); + ut_asserteq(0, sandbox_gpio_get_value(dev, offset)); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: input: 0 [x] testing", buf); + + ut_assertok(gpio_free(gpio)); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b4: input: 0 [ ]", buf); + + /* Check the 'a' bank also */ + ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "base-gpios"); + ut_asserteq(15, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_str("a", name); + ut_asserteq(20, offset_count); + + /* add gpio hog tests */ + ut_assertok(gpio_hog_lookup_name("hog_input_active_low", &desc)); + ut_asserteq(GPIOD_IS_IN | GPIOD_ACTIVE_LOW, desc->flags); + ut_asserteq(10, desc->offset); + ut_asserteq(1, dm_gpio_get_value(desc)); + ut_assertok(gpio_hog_lookup_name("hog_input_active_high", &desc)); + ut_asserteq(GPIOD_IS_IN, desc->flags); + ut_asserteq(11, desc->offset); + ut_asserteq(0, dm_gpio_get_value(desc)); + ut_assertok(gpio_hog_lookup_name("hog_output_low", &desc)); + ut_asserteq(GPIOD_IS_OUT, desc->flags); + ut_asserteq(12, desc->offset); + ut_asserteq(0, dm_gpio_get_value(desc)); + ut_assertok(dm_gpio_set_value(desc, 1)); + ut_asserteq(1, dm_gpio_get_value(desc)); + ut_assertok(gpio_hog_lookup_name("hog_output_high", &desc)); + ut_asserteq(GPIOD_IS_OUT, desc->flags); + ut_asserteq(13, desc->offset); + ut_asserteq(1, dm_gpio_get_value(desc)); + ut_assertok(dm_gpio_set_value(desc, 0)); + ut_asserteq(0, dm_gpio_get_value(desc)); + + /* Check if lookup for labels work */ + ut_assertok(gpio_lookup_name("hog_input_active_low", &dev, &offset, + &gpio)); + ut_asserteq_str(dev->name, "base-gpios"); + ut_asserteq(10, offset); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 10, gpio); + ut_assert(gpio_lookup_name("hog_not_exist", &dev, &offset, + &gpio)); + + return 0; +} +DM_TEST(dm_test_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that GPIO open-drain/open-source emulation works correctly */ +static int dm_test_gpio_opendrain_opensource(struct unit_test_state *uts) +{ + struct gpio_desc desc_list[8]; + struct udevice *dev, *gpio_c; + char buf[80]; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(uclass_get_device(UCLASS_GPIO, 3, &gpio_c)); + ut_asserteq_str("pinmux-gpios", gpio_c->name); + + ut_asserteq(8, gpio_request_list_by_name(dev, "test3-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)) + + ut_asserteq(true, !!device_active(gpio_c)); + ut_asserteq_ptr(gpio_c, desc_list[0].dev); + ut_asserteq_ptr(gpio_c, desc_list[1].dev); + ut_asserteq_ptr(gpio_c, desc_list[2].dev); + ut_asserteq_ptr(gpio_c, desc_list[3].dev); + ut_asserteq_ptr(gpio_c, desc_list[4].dev); + ut_asserteq_ptr(gpio_c, desc_list[5].dev); + ut_asserteq_ptr(gpio_c, desc_list[6].dev); + ut_asserteq_ptr(gpio_c, desc_list[7].dev); + + /* GPIO 0 is (GPIO_OUT|GPIO_OPEN_DRAIN) */ + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, + sandbox_gpio_get_flags(gpio_c, 0)); + + /* Set it as output high */ + ut_assertok(dm_gpio_set_value(&desc_list[0], 1)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(gpio_c, 0)); + + /* Set it as output low */ + ut_assertok(dm_gpio_set_value(&desc_list[0], 0)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, + sandbox_gpio_get_flags(gpio_c, 0)); + + /* GPIO 1 is (GPIO_OUT|GPIO_OPEN_SOURCE) */ + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, + sandbox_gpio_get_flags(gpio_c, 1)); + + /* Set it as output high, should become output high */ + ut_assertok(dm_gpio_set_value(&desc_list[1], 1)); + ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf))); + ut_asserteq_str("c1: output: 1 [x] a-test.test3-gpios1", buf); + + /* Set it as output low */ + ut_assertok(dm_gpio_set_value(&desc_list[1], 0)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, + sandbox_gpio_get_flags(gpio_c, 1)); + + ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf))); + ut_asserteq_str("c1: output: 0 [x] a-test.test3-gpios1", buf); + + /* + * GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN). Looking at it + * directlt from the driver, we get GPIOD_IS_OUT_ACTIVE also, since it + * is active low + */ + ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | + GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(gpio_c, 6)); + + /* Set it as output high, should become output low */ + ut_assertok(dm_gpio_set_value(&desc_list[6], 1)); + ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf))); + ut_asserteq_str("c6: output: 0 [x] a-test.test3-gpios6", buf); + + /* Set it as output low */ + ut_assertok(dm_gpio_set_value(&desc_list[6], 0)); + ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | + GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(gpio_c, 6)); + + /* GPIO 7 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE) */ + ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE | + GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(gpio_c, 7)); + + /* Set it as output high */ + ut_assertok(dm_gpio_set_value(&desc_list[7], 1)); + ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, + sandbox_gpio_get_flags(gpio_c, 7)); + + /* Set it as output low, should become output high */ + ut_assertok(dm_gpio_set_value(&desc_list[7], 0)); + ut_assertok(gpio_get_status(gpio_c, 7, buf, sizeof(buf))); + ut_asserteq_str("c7: output: 1 [x] a-test.test3-gpios7", buf); + + ut_assertok(gpio_free_list(dev, desc_list, 8)); + + return 0; +} +DM_TEST(dm_test_gpio_opendrain_opensource, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that sandbox anonymous GPIOs work correctly */ +static int dm_test_gpio_anon(struct unit_test_state *uts) +{ + unsigned int offset, gpio; + struct udevice *dev; + const char *name; + int offset_count; + + /* And the anonymous bank */ + ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio)); + ut_asserteq_str(dev->name, "sandbox_gpio"); + ut_asserteq(14, offset); + ut_asserteq(14, gpio); + + name = gpio_get_bank_info(dev, &offset_count); + ut_asserteq_ptr(NULL, name); + ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count); + + return 0; +} +DM_TEST(dm_test_gpio_anon, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that gpio_requestf() works as expected */ +static int dm_test_gpio_requestf(struct unit_test_state *uts) +{ + unsigned int offset, gpio; + struct udevice *dev; + char buf[80]; + + ut_assertok(gpio_lookup_name("b5", &dev, &offset, &gpio)); + ut_assertok(gpio_requestf(gpio, "testing %d %s", 1, "hi")); + sandbox_gpio_set_direction(dev, offset, 1); + sandbox_gpio_set_value(dev, offset, 1); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b5: output: 1 [x] testing 1 hi", buf); + + return 0; +} +DM_TEST(dm_test_gpio_requestf, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that gpio_request() copies its string */ +static int dm_test_gpio_copy(struct unit_test_state *uts) +{ + unsigned int offset, gpio; + struct udevice *dev; + char buf[80], name[10]; + + ut_assertok(gpio_lookup_name("b6", &dev, &offset, &gpio)); + strcpy(name, "odd_name"); + ut_assertok(gpio_request(gpio, name)); + sandbox_gpio_set_direction(dev, offset, 1); + sandbox_gpio_set_value(dev, offset, 1); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b6: output: 1 [x] odd_name", buf); + strcpy(name, "nothing"); + ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf))); + ut_asserteq_str("b6: output: 1 [x] odd_name", buf); + + return 0; +} +DM_TEST(dm_test_gpio_copy, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we don't leak memory with GPIOs */ +static int dm_test_gpio_leak(struct unit_test_state *uts) +{ + ut_assertok(dm_test_gpio(uts)); + ut_assertok(dm_test_gpio_anon(uts)); + ut_assertok(dm_test_gpio_requestf(uts)); + ut_assertok(dm_leak_check_end(uts)); + + return 0; +} +DM_TEST(dm_test_gpio_leak, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can find GPIOs using phandles */ +static int dm_test_gpio_phandles(struct unit_test_state *uts) +{ + struct gpio_desc desc, desc_list[8], desc_list2[8]; + struct udevice *dev, *gpio_a, *gpio_b; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio_a)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 2, &gpio_b)); + ut_asserteq_str("base-gpios", gpio_a->name); + ut_asserteq(true, !!device_active(gpio_a)); + ut_asserteq_ptr(gpio_a, desc.dev); + ut_asserteq(4, desc.offset); + /* GPIOF_INPUT is the sandbox GPIO driver default */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL)); + ut_assertok(dm_gpio_free(dev, &desc)); + + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 3, &desc, + 0)); + ut_asserteq_ptr(NULL, desc.dev); + ut_asserteq(desc.offset, 0); + ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 5, &desc, + 0)); + + /* Last GPIO is ignord as it comes after <0> */ + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + ut_asserteq(-EBUSY, gpio_request_list_by_name(dev, "test-gpios", + desc_list2, + ARRAY_SIZE(desc_list2), + 0)); + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + ut_asserteq(GPIOF_UNUSED, gpio_get_function(gpio_a, 4, NULL)); + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list, + ARRAY_SIZE(desc_list), + GPIOD_IS_OUT | + GPIOD_IS_OUT_ACTIVE)); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_a, 4, NULL)); + ut_asserteq_ptr(gpio_a, desc_list[0].dev); + ut_asserteq(1, desc_list[0].offset); + ut_asserteq_ptr(gpio_a, desc_list[1].dev); + ut_asserteq(4, desc_list[1].offset); + ut_asserteq_ptr(gpio_b, desc_list[2].dev); + ut_asserteq(5, desc_list[2].offset); + ut_asserteq(1, dm_gpio_get_value(desc_list)); + ut_assertok(gpio_free_list(dev, desc_list, 3)); + + ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, + sandbox_gpio_get_flags(gpio_a, 1)); + ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + + /* This was set to output previously but flags resetted to 0 = INPUT */ + ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 1)); + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 1, NULL)); + + /* Active low should invert the input value */ + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 6, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[2])); + + ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 7, NULL)); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 8, NULL)); + ut_asserteq(0, dm_gpio_get_value(&desc_list[4])); + ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 9, NULL)); + ut_asserteq(1, dm_gpio_get_value(&desc_list[5])); + + return 0; +} +DM_TEST(dm_test_gpio_phandles, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Check the gpio pin configuration get from device tree information */ +static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts) +{ + struct gpio_desc desc_list[6]; + struct udevice *dev; + ulong flags; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + + ut_asserteq(6, gpio_request_list_by_name(dev, "test3-gpios", desc_list, + ARRAY_SIZE(desc_list), 0)); + + ut_assertok(dm_gpio_get_flags(&desc_list[0], &flags)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, flags); + + ut_assertok(dm_gpio_get_flags(&desc_list[1], &flags)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, flags); + + ut_assertok(dm_gpio_get_flags(&desc_list[2], &flags)); + ut_asserteq(GPIOD_IS_OUT, flags); + + ut_assertok(dm_gpio_get_flags(&desc_list[3], &flags)); + ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags); + + ut_assertok(dm_gpio_get_flags(&desc_list[4], &flags)); + ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_DOWN, flags); + + ut_assertok(dm_gpio_get_flags(&desc_list[5], &flags)); + ut_asserteq(GPIOD_IS_IN, flags); + + ut_assertok(gpio_free_list(dev, desc_list, 6)); + + return 0; +} +DM_TEST(dm_test_gpio_get_dir_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of gpio_get_acpi() */ +static int dm_test_gpio_get_acpi(struct unit_test_state *uts) +{ + struct acpi_gpio agpio; + struct udevice *dev; + struct gpio_desc desc; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + + /* See sb_gpio_get_acpi() */ + ut_assertok(gpio_get_acpi(&desc, &agpio)); + ut_asserteq(1, agpio.pin_count); + ut_asserteq(4, agpio.pins[0]); + ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type); + ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull); + ut_asserteq_str("\\_SB.PINC", agpio.resource); + ut_asserteq(0, agpio.interrupt_debounce_timeout); + ut_asserteq(0, agpio.irq.pin); + ut_asserteq(1234, agpio.output_drive_strength); + ut_asserteq(true, agpio.io_shared); + ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict); + ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity); + + return 0; +} +DM_TEST(dm_test_gpio_get_acpi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of gpio_get_acpi() with an interrupt GPIO */ +static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts) +{ + struct acpi_gpio agpio; + struct udevice *dev; + struct gpio_desc desc; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0)); + + /* See sb_gpio_get_acpi() */ + ut_assertok(gpio_get_acpi(&desc, &agpio)); + ut_asserteq(1, agpio.pin_count); + ut_asserteq(6, agpio.pins[0]); + ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type); + ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull); + ut_asserteq_str("\\_SB.PINC", agpio.resource); + ut_asserteq(4321, agpio.interrupt_debounce_timeout); + ut_asserteq(6, agpio.irq.pin); + ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity); + ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared); + ut_asserteq(true, agpio.irq.wake); + ut_asserteq(0, agpio.output_drive_strength); + ut_asserteq(false, agpio.io_shared); + ut_asserteq(0, agpio.io_restrict); + ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity); + + return 0; +} +DM_TEST(dm_test_gpio_get_acpi_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can get/release GPIOs using managed API */ +static int dm_test_gpio_devm(struct unit_test_state *uts) +{ + static const u32 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE; + struct gpio_desc *desc1, *desc2, *desc3, *desc_err; + struct udevice *dev; + struct udevice *dev2; + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "another-test", + &dev2)); + + /* Get 3 GPIOs from 'a-test' dev */ + desc1 = devm_gpiod_get_index(dev, "test4", 0, flags); + ut_assert(!IS_ERR(desc1)); + desc2 = devm_gpiod_get_index(dev, "test4", 1, flags); + ut_assert(!IS_ERR(desc2)); + desc3 = devm_gpiod_get_index_optional(dev, "test5", 0, flags); + ut_assert(!IS_ERR(desc3)); + ut_assert(desc3); + + /* + * Try get the same 3 GPIOs from 'a-test' and 'another-test' devices. + * check that it fails + */ + desc_err = devm_gpiod_get_index(dev, "test4", 0, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index(dev, "test4", 1, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index_optional(dev, "test5", 0, flags); + ut_asserteq_ptr(NULL, desc_err); + desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags); + ut_asserteq_ptr(NULL, desc_err); + + /* Try get GPIOs outside of the list */ + desc_err = devm_gpiod_get_index(dev, "test4", 2, flags); + ut_assert(IS_ERR(desc_err)); + desc_err = devm_gpiod_get_index_optional(dev, "test5", 1, flags); + ut_asserteq_ptr(NULL, desc_err); + + /* Manipulate the GPIOs */ + ut_assertok(dm_gpio_set_value(desc1, 1)); + ut_asserteq(1, dm_gpio_get_value(desc1)); + ut_assertok(dm_gpio_set_value(desc1, 0)); + ut_asserteq(0, dm_gpio_get_value(desc1)); + + ut_assertok(dm_gpio_set_value(desc2, 1)); + ut_asserteq(1, dm_gpio_get_value(desc2)); + ut_assertok(dm_gpio_set_value(desc2, 0)); + ut_asserteq(0, dm_gpio_get_value(desc2)); + + ut_assertok(dm_gpio_set_value(desc3, 1)); + ut_asserteq(1, dm_gpio_get_value(desc3)); + ut_assertok(dm_gpio_set_value(desc3, 0)); + ut_asserteq(0, dm_gpio_get_value(desc3)); + + /* Check that the GPIO cannot be owned by more than one device */ + desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags); + ut_asserteq(-EBUSY, PTR_ERR(desc_err)); + desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags); + ut_asserteq_ptr(NULL, desc_err); + + /* + * Release one GPIO and check that we can get it back using + * 'another-test' and then 'a-test' + */ + devm_gpiod_put(dev, desc2); + desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags); + ut_assert(!IS_ERR(desc2)); + + devm_gpiod_put(dev2, desc2); + desc2 = devm_gpiod_get_index(dev, "test4", 1, flags); + ut_assert(!IS_ERR(desc2)); + + /* Release one GPIO before removing the 'a-test' dev. */ + devm_gpiod_put(dev, desc2); + device_remove(dev, DM_REMOVE_NORMAL); + + /* All the GPIOs must have been freed. We should be able to claim + * them with the 'another-test' device. + */ + desc1 = devm_gpiod_get_index(dev2, "test4", 0, flags); + ut_assert(!IS_ERR(desc1)); + desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags); + ut_assert(!IS_ERR(desc2)); + desc3 = devm_gpiod_get_index_optional(dev2, "test5", 0, flags); + ut_assert(!IS_ERR(desc3)); + ut_assert(desc3); + + device_remove(dev2, DM_REMOVE_NORMAL); + return 0; +} +DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_clrset_flags(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct udevice *dev; + ulong flags; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0)); + + ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_OUT)); + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_OUT, flags); + ut_asserteq(GPIOD_IS_OUT, desc.flags); + ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset)); + + ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE)); + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, flags); + ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, desc.flags); + ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset)); + ut_asserteq(1, dm_gpio_get_value(&desc)); + + ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN)); + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_IN, flags & GPIOD_MASK_DIR); + ut_asserteq(GPIOD_IS_IN, desc.flags & GPIOD_MASK_DIR); + + ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_PULL, + GPIOD_PULL_UP)); + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags); + ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, desc.flags); + + /* Check we cannot set both PULL_UP and PULL_DOWN */ + ut_asserteq(-EINVAL, dm_gpio_clrset_flags(&desc, 0, GPIOD_PULL_DOWN)); + + return 0; +} +DM_TEST(dm_test_clrset_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Check that an active-low GPIO works as expected */ +static int dm_test_clrset_flags_invert(struct unit_test_state *uts) +{ + struct gpio_desc desc; + struct udevice *dev; + ulong flags; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, + GPIOD_IS_OUT | GPIOD_ACTIVE_LOW)); + + /* + * From this size we see it as 0 (active low), but the sandbox driver + * sees the pin value high + */ + ut_asserteq(0, dm_gpio_get_value(&desc)); + ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset)); + + ut_assertok(dm_gpio_set_value(&desc, 1)); + ut_asserteq(1, dm_gpio_get_value(&desc)); + ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset)); + + /* Do the same with dm_gpio_clrset_flags() */ + ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0)); + ut_asserteq(0, dm_gpio_get_value(&desc)); + ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset)); + + ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE)); + ut_asserteq(1, dm_gpio_get_value(&desc)); + ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset)); + + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE, + flags); + ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE, + desc.flags); + + ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0)); + ut_assertok(dm_gpio_get_flags(&desc, &flags)); + ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, flags); + ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, desc.flags); + + return 0; +} +DM_TEST(dm_test_clrset_flags_invert, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int set_gpios(struct unit_test_state *uts, struct gpio_desc *desc, + int count, uint value) +{ + int i; + + for (i = 0; i < count; i++) { + const uint mask = 1 << i; + + ut_assertok(sandbox_gpio_set_value(desc[i].dev, desc[i].offset, + value & mask)); + } + + return 0; +} + +/* Check that an active-low GPIO works as expected */ +static int dm_test_gpio_get_values_as_int(struct unit_test_state *uts) +{ + const int gpio_count = 3; + struct gpio_desc desc[gpio_count]; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc, + gpio_count, GPIOD_IS_IN)); + ut_assertok(set_gpios(uts, desc, gpio_count, 0)); + ut_asserteq(0, dm_gpio_get_values_as_int(desc, gpio_count)); + + ut_assertok(set_gpios(uts, desc, gpio_count, 5)); + ut_asserteq(5, dm_gpio_get_values_as_int(desc, gpio_count)); + + ut_assertok(set_gpios(uts, desc, gpio_count, 7)); + ut_asserteq(7, dm_gpio_get_values_as_int(desc, gpio_count)); + + return 0; +} +DM_TEST(dm_test_gpio_get_values_as_int, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Check that an active-low GPIO works as expected */ +static int dm_test_gpio_get_values_as_int_base3(struct unit_test_state *uts) +{ + const int gpio_count = 3; + struct gpio_desc desc[gpio_count]; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc, + gpio_count, GPIOD_IS_IN)); + + /* + * First test the sandbox GPIO driver works as expected. The external + * pull resistor should be stronger than the internal one. + */ + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, + GPIOD_IS_IN | GPIOD_EXT_PULL_UP | GPIOD_PULL_UP); + ut_asserteq(1, dm_gpio_get_value(desc)); + + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_IS_IN | + GPIOD_EXT_PULL_DOWN | GPIOD_PULL_UP); + ut_asserteq(0, dm_gpio_get_value(desc)); + + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, + GPIOD_IS_IN | GPIOD_PULL_UP); + ut_asserteq(1, dm_gpio_get_value(desc)); + + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_PULL_DOWN); + ut_asserteq(0, dm_gpio_get_value(desc)); + + /* + * Set up pins: pull-up (1), pull-down (0) and floating (2). This should + * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19 + */ + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(desc[1].dev, desc[1].offset, + GPIOD_EXT_PULL_DOWN); + sandbox_gpio_set_flags(desc[2].dev, desc[2].offset, 0); + ut_asserteq(19, dm_gpio_get_values_as_int_base3(desc, gpio_count)); + + /* + * Set up pins: floating (2), pull-up (1) and pull-down (0). This should + * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5 + */ + sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, 0); + sandbox_gpio_set_flags(desc[1].dev, desc[1].offset, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(desc[2].dev, desc[2].offset, + GPIOD_EXT_PULL_DOWN); + ut_asserteq(5, dm_gpio_get_values_as_int_base3(desc, gpio_count)); + + return 0; +} +DM_TEST(dm_test_gpio_get_values_as_int_base3, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/hwspinlock.c b/roms/u-boot/test/dm/hwspinlock.c new file mode 100644 index 000000000..995759d4d --- /dev/null +++ b/roms/u-boot/test/dm/hwspinlock.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2018, STMicroelectronics - All Rights Reserved + */ + +#include <common.h> +#include <dm.h> +#include <hwspinlock.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that hwspinlock driver functions are called */ +static int dm_test_hwspinlock_base(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + struct hwspinlock hws; + + ut_assertok(uclass_get_device(UCLASS_HWSPINLOCK, 0, &hws.dev)); + ut_assertnonnull(hws.dev); + ut_asserteq(false, state->hwspinlock); + + hws.id = 0; + ut_assertok(hwspinlock_lock_timeout(&hws, 1)); + ut_asserteq(true, state->hwspinlock); + + ut_assertok(hwspinlock_unlock(&hws)); + ut_asserteq(false, state->hwspinlock); + + ut_assertok(hwspinlock_lock_timeout(&hws, 1)); + ut_assertok(!hwspinlock_lock_timeout(&hws, 1)); + + ut_assertok(hwspinlock_unlock(&hws)); + ut_assertok(!hwspinlock_unlock(&hws)); + + return 0; +} + +DM_TEST(dm_test_hwspinlock_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/i2c.c b/roms/u-boot/test/dm/i2c.c new file mode 100644 index 000000000..d74f5f9fb --- /dev/null +++ b/roms/u-boot/test/dm/i2c.c @@ -0,0 +1,306 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Google, Inc + * + * Note: Test coverage does not include 10-bit addressing + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <i2c.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <hexdump.h> +#include <test/test.h> +#include <test/ut.h> + +static const int busnum; +static const int chip = 0x2c; + +/* Test that we can find buses and chips */ +static int dm_test_i2c_find(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + const int no_chip = 0x10; + + /* + * The post_bind() method will bind devices to chip selects. Check + * this then remove the emulation and the slave device. + */ + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + ut_assertok(dm_i2c_probe(bus, chip, 0, &dev)); + ut_asserteq(-ENOENT, dm_i2c_probe(bus, no_chip, 0, &dev)); + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_I2C, 1, &bus)); + + return 0; +} +DM_TEST(dm_test_i2c_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_read_write(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + uint8_t buf[5]; + + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0AB\0", sizeof(buf)); + + return 0; +} +DM_TEST(dm_test_i2c_read_write, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_speed(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + uint8_t buf[5]; + + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + + /* Use test mode so we create the required errors for invalid speeds */ + sandbox_i2c_set_test_mode(bus, true); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); + ut_assertok(dm_i2c_set_bus_speed(bus, 100000)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(dm_i2c_set_bus_speed(bus, 400000)); + ut_asserteq(400000, dm_i2c_get_bus_speed(bus)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq(-EINVAL, dm_i2c_write(dev, 0, buf, 5)); + sandbox_i2c_set_test_mode(bus, false); + + return 0; +} +DM_TEST(dm_test_i2c_speed, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_offset_len(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + uint8_t buf[5]; + + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); + ut_assertok(i2c_set_chip_offset_len(dev, 1)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + + /* This is not supported by the uclass */ + ut_asserteq(-EINVAL, i2c_set_chip_offset_len(dev, 5)); + + return 0; +} +DM_TEST(dm_test_i2c_offset_len, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_probe_empty(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + + /* Use test mode so that this chip address will always probe */ + sandbox_i2c_set_test_mode(bus, true); + ut_assertok(dm_i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev)); + sandbox_i2c_set_test_mode(bus, false); + + return 0; +} +DM_TEST(dm_test_i2c_probe_empty, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_bytewise(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + struct udevice *eeprom; + uint8_t buf[5]; + + ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus)); + ut_assertok(i2c_get_chip(bus, chip, 1, &dev)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf)); + + /* Tell the EEPROM to only read/write one register at a time */ + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + ut_assertnonnull(eeprom); + sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_SINGLE_BYTE); + + /* Now we only get the first byte - the rest will be 0xff */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf)); + + /* If we do a separate transaction for each byte, it works */ + ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf)); + + /* This will only write A */ + ut_assertok(i2c_set_chip_flags(dev, 0)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf)); + + /* Check that the B was ignored */ + ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0A\0\0\0", sizeof(buf)); + + /* Now write it again with the new flags, it should work */ + ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf)); + + ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS | + DM_I2C_CHIP_RD_ADDRESS)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem(buf, "\0\0AB\0\0", sizeof(buf)); + + /* Restore defaults */ + sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_NONE); + ut_assertok(i2c_set_chip_flags(dev, 0)); + + return 0; +} +DM_TEST(dm_test_i2c_bytewise, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_offset(struct unit_test_state *uts) +{ + struct udevice *eeprom; + struct udevice *dev; + uint8_t buf[5]; + + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); + + /* Do a transfer so we can find the emulator */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + + /* Offset length 0 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 0); + ut_assertok(i2c_set_chip_offset_len(dev, 0)); + ut_assertok(dm_i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 1 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + ut_assertok(i2c_set_chip_offset_len(dev, 1)); + ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2)); + ut_asserteq(2, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_asserteq_mem("ABAB\0", buf, sizeof(buf)); + ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 2 boundary - check model wrapping */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 2); + ut_assertok(i2c_set_chip_offset_len(dev, 2)); + ut_assertok(dm_i2c_write(dev, 0xFF, (uint8_t *)"A", 1)); + ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_write(dev, 0x100, (uint8_t *)"B", 1)); + ut_asserteq(0x100, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_write(dev, 0x101, (uint8_t *)"C", 1)); + ut_asserteq(0x101, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_assertok(dm_i2c_read(dev, 0xFF, buf, 5)); + ut_asserteq_mem("ABCAB", buf, sizeof(buf)); + ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 2 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 2); + ut_assertok(i2c_set_chip_offset_len(dev, 2)); + ut_assertok(dm_i2c_write(dev, 0x2020, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x2020, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x2020, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 3 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 3); + ut_assertok(i2c_set_chip_offset_len(dev, 3)); + ut_assertok(dm_i2c_write(dev, 0x303030, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x303030, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x303030, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Offset length 4 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 4); + ut_assertok(i2c_set_chip_offset_len(dev, 4)); + ut_assertok(dm_i2c_write(dev, 0x40404040, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x40404040, buf, 5)); + ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x40404040, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + + /* Restore defaults */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + + return 0; +} +DM_TEST(dm_test_i2c_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_i2c_addr_offset(struct unit_test_state *uts) +{ + struct udevice *eeprom; + struct udevice *dev; + u8 buf[5]; + + ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev)); + + /* Do a transfer so we can find the emulator */ + ut_assertok(dm_i2c_read(dev, 0, buf, 5)); + ut_assertok(uclass_first_device(UCLASS_I2C_EMUL, &eeprom)); + + /* Offset length 0 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 0); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 0)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x3, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x3, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x3, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 1 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 1)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x310, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x310, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x310, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 2 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 2); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 2)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x32020, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x32020, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x32020, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Offset length 3 */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 3); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3); + ut_assertok(i2c_set_chip_offset_len(dev, 3)); + ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3)); + ut_assertok(dm_i2c_write(dev, 0x3303030, (uint8_t *)"AB", 2)); + ut_assertok(dm_i2c_read(dev, 0x3303030, buf, 5)); + ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf)); + ut_asserteq(0x3303030, sanbox_i2c_eeprom_get_prev_offset(eeprom)); + ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom)); + + /* Restore defaults */ + sandbox_i2c_eeprom_set_offset_len(eeprom, 1); + sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0); + + return 0; +} + +DM_TEST(dm_test_i2c_addr_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/i2s.c b/roms/u-boot/test/dm/i2s.c new file mode 100644 index 000000000..c2bf4d560 --- /dev/null +++ b/roms/u-boot/test/dm/i2s.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <i2s.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the i2s codec uclass */ +static int dm_test_i2s(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 data[3]; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_I2S, &dev)); + data[0] = 1; + data[1] = 4; + data[2] = 6; + ut_assertok(i2s_tx_data(dev, data, ARRAY_SIZE(data))); + ut_asserteq(11, sandbox_get_i2s_sum(dev)); + ut_assertok(i2s_tx_data(dev, data, 1)); + ut_asserteq(12, sandbox_get_i2s_sum(dev)); + + return 0; +} +DM_TEST(dm_test_i2s, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/irq.c b/roms/u-boot/test/dm/irq.c new file mode 100644 index 000000000..51dd5e4ab --- /dev/null +++ b/roms/u-boot/test/dm/irq.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for irq uclass + * + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <irq.h> +#include <acpi/acpi_device.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Base test of the irq uclass */ +static int dm_test_irq_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_IRQ, &dev)); + + ut_asserteq(5, irq_route_pmc_gpio_gpe(dev, 4)); + ut_asserteq(-ENOENT, irq_route_pmc_gpio_gpe(dev, 14)); + + ut_assertok(irq_set_polarity(dev, 4, true)); + ut_asserteq(-EINVAL, irq_set_polarity(dev, 14, true)); + + ut_assertok(irq_snapshot_polarities(dev)); + ut_assertok(irq_restore_polarities(dev)); + + return 0; +} +DM_TEST(dm_test_irq_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of irq_first_device_type() */ +static int dm_test_irq_type(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &dev)); + ut_asserteq(-ENODEV, irq_first_device_type(X86_IRQT_BASE, &dev)); + + return 0; +} +DM_TEST(dm_test_irq_type, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of irq_read_and_clear() */ +static int dm_test_read_and_clear(struct unit_test_state *uts) +{ + struct irq irq; + + ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &irq.dev)); + irq.id = SANDBOX_IRQN_PEND; + ut_asserteq(0, irq_read_and_clear(&irq)); + ut_asserteq(0, irq_read_and_clear(&irq)); + ut_asserteq(0, irq_read_and_clear(&irq)); + ut_asserteq(1, irq_read_and_clear(&irq)); + ut_asserteq(0, irq_read_and_clear(&irq)); + + return 0; +} +DM_TEST(dm_test_read_and_clear, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of irq_request() */ +static int dm_test_request(struct unit_test_state *uts) +{ + struct udevice *dev; + struct irq irq; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + ut_asserteq(3, irq.id); + + return 0; +} +DM_TEST(dm_test_request, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of irq_get_acpi() */ +static int dm_test_irq_get_acpi(struct unit_test_state *uts) +{ + struct acpi_irq airq; + struct udevice *dev; + struct irq irq; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_assertok(irq_get_by_index(dev, 0, &irq)); + + /* see sandbox_get_acpi() */ + ut_assertok(irq_get_acpi(&irq, &airq)); + ut_asserteq(3, airq.pin); + ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode); + ut_asserteq(ACPI_IRQ_ACTIVE_HIGH, airq.polarity); + ut_asserteq(ACPI_IRQ_SHARED, airq.shared); + ut_asserteq(ACPI_IRQ_WAKE, airq.wake); + + return 0; +} +DM_TEST(dm_test_irq_get_acpi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/k210_pll.c b/roms/u-boot/test/dm/k210_pll.c new file mode 100644 index 000000000..54764f269 --- /dev/null +++ b/roms/u-boot/test/dm/k210_pll.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */ +#include <div64.h> +#include <dm/test.h> +#include <kendryte/pll.h> +#include <test/ut.h> + +static int dm_test_k210_pll_calc_config(u32 rate, u32 rate_in, + struct k210_pll_config *best) +{ + u64 f, r, od, max_r, inv_ratio; + s64 error, best_error; + + best_error = S64_MAX; + error = best_error; + max_r = min(16ULL, DIV_ROUND_DOWN_ULL(rate_in, 13300000)); + inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate); + + /* Brute force it */ + for (r = 1; r <= max_r; r++) { + for (f = 1; f <= 64; f++) { + for (od = 1; od <= 16; od++) { + u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r); + + if (vco > 1750000000 || vco < 340000000) + continue; + + error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio, + r * od); + /* The lower 16 bits are spurious */ + error = abs((error - BIT(32))) >> 16; + if (error < best_error) { + best->r = r; + best->f = f; + best->od = od; + best_error = error; + } + } + } + } + + if (best_error == S64_MAX) + return -EINVAL; + return 0; +} + +static int dm_test_k210_pll_compare(struct k210_pll_config *ours, + struct k210_pll_config *theirs) +{ + return (u32)ours->f * theirs->r * theirs->od != + (u32)theirs->f * ours->r * ours->od; +} + +static int dm_test_k210_pll(struct unit_test_state *uts) +{ + struct k210_pll_config ours, theirs; + + /* General range checks */ + ut_asserteq(-EINVAL, k210_pll_calc_config(0, 26000000, &theirs)); + ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 0, &theirs)); + ut_asserteq(-EINVAL, k210_pll_calc_config(2000000000, 26000000, + &theirs)); + ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 2000000000, + &theirs)); + ut_asserteq(-EINVAL, k210_pll_calc_config(1500000000, 20000000, + &theirs)); + + /* Verify we get the same output with brute-force */ + ut_assertok(dm_test_k210_pll_calc_config(390000000, 26000000, &ours)); + ut_assertok(k210_pll_calc_config(390000000, 26000000, &theirs)); + ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); + + ut_assertok(dm_test_k210_pll_calc_config(26000000, 390000000, &ours)); + ut_assertok(k210_pll_calc_config(26000000, 390000000, &theirs)); + ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); + + ut_assertok(dm_test_k210_pll_calc_config(400000000, 26000000, &ours)); + ut_assertok(k210_pll_calc_config(400000000, 26000000, &theirs)); + ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); + + ut_assertok(dm_test_k210_pll_calc_config(27000000, 26000000, &ours)); + ut_assertok(k210_pll_calc_config(27000000, 26000000, &theirs)); + ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); + + ut_assertok(dm_test_k210_pll_calc_config(26000000, 27000000, &ours)); + ut_assertok(k210_pll_calc_config(26000000, 27000000, &theirs)); + ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); + + return 0; +} +DM_TEST(dm_test_k210_pll, 0); diff --git a/roms/u-boot/test/dm/led.c b/roms/u-boot/test/dm/led.c new file mode 100644 index 000000000..ac6ee3639 --- /dev/null +++ b/roms/u-boot/test/dm/led.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <led.h> +#include <asm/gpio.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of the led uclass */ +static int dm_test_led_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Get the top-level device */ + ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 2, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 3, &dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 4, &dev)); + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_LED, 5, &dev)); + + return 0; +} +DM_TEST(dm_test_led_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the LED 'default-state' device tree property */ +static int dm_test_led_default_state(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* configure the default state (auto-probe) */ + led_default_state(); + + /* Check that we handle the default-state property correctly. */ + ut_assertok(led_get_by_label("sandbox:default_on", &dev)); + ut_asserteq(LEDST_ON, led_get_state(dev)); + + /* Also tests default label behaviour */ + ut_assertok(led_get_by_label("default_off", &dev)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_led_default_state, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the led uclass using the led_gpio driver */ +static int dm_test_led_gpio(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate an LED. LED 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(led_set_state(dev, LEDST_ON)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_ON, led_get_state(dev)); + + ut_assertok(led_set_state(dev, LEDST_OFF)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_led_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can toggle LEDs */ +static int dm_test_led_toggle(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we can manipulate an LED. LED 1 is connected to GPIO + * bank gpio_a, offset 1. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_assertok(led_set_state(dev, LEDST_TOGGLE)); + ut_asserteq(1, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_ON, led_get_state(dev)); + + ut_assertok(led_set_state(dev, LEDST_TOGGLE)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + + return 0; +} +DM_TEST(dm_test_led_toggle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test obtaining an LED by label */ +static int dm_test_led_label(struct unit_test_state *uts) +{ + struct udevice *dev, *cmp; + + ut_assertok(led_get_by_label("sandbox:red", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 1, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_assertok(led_get_by_label("sandbox:green", &dev)); + ut_asserteq(1, device_active(dev)); + ut_assertok(uclass_get_device(UCLASS_LED, 2, &cmp)); + ut_asserteq_ptr(dev, cmp); + + ut_asserteq(-ENODEV, led_get_by_label("sandbox:blue", &dev)); + + return 0; +} +DM_TEST(dm_test_led_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test LED blinking */ +#ifdef CONFIG_LED_BLINK +static int dm_test_led_blink(struct unit_test_state *uts) +{ + const int offset = 1; + struct udevice *dev, *gpio; + + /* + * Check that we get an error when trying to blink an LED, since it is + * not supported by the GPIO LED driver. + */ + ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(-ENOSYS, led_set_state(dev, LEDST_BLINK)); + ut_asserteq(0, sandbox_gpio_get_value(gpio, offset)); + ut_asserteq(LEDST_OFF, led_get_state(dev)); + ut_asserteq(-ENOSYS, led_set_period(dev, 100)); + + return 0; +} +DM_TEST(dm_test_led_blink, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +#endif diff --git a/roms/u-boot/test/dm/mailbox.c b/roms/u-boot/test/dm/mailbox.c new file mode 100644 index 000000000..7ad8a1cbb --- /dev/null +++ b/roms/u-boot/test/dm/mailbox.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <dm/test.h> +#include <asm/mbox.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_mailbox(struct unit_test_state *uts) +{ + struct udevice *dev; + uint32_t msg; + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "mbox-test", &dev)); + ut_assertok(sandbox_mbox_test_get(dev)); + + ut_asserteq(-ETIMEDOUT, sandbox_mbox_test_recv(dev, &msg)); + ut_assertok(sandbox_mbox_test_send(dev, 0xaaff9955UL)); + ut_assertok(sandbox_mbox_test_recv(dev, &msg)); + ut_asserteq(msg, 0xaaff9955UL ^ SANDBOX_MBOX_PING_XOR); + ut_asserteq(-ETIMEDOUT, sandbox_mbox_test_recv(dev, &msg)); + + ut_assertok(sandbox_mbox_test_free(dev)); + + return 0; +} +DM_TEST(dm_test_mailbox, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mdio.c b/roms/u-boot/test/dm/mdio.c new file mode 100644 index 000000000..64347e127 --- /dev/null +++ b/roms/u-boot/test/dm/mdio.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <miiphy.h> +#include <misc.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* macros copied over from mdio_sandbox.c */ +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG_CNT 2 + +/* test using 1st register, 0 */ +#define SANDBOX_PHY_REG 0 + +#define TEST_REG_VALUE 0xabcd + +static int dm_test_mdio(struct unit_test_state *uts) +{ + struct uclass *uc; + struct udevice *dev; + struct mdio_ops *ops; + u16 reg; + + ut_assertok(uclass_get(UCLASS_MDIO, &uc)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &dev)); + + ops = mdio_get_ops(dev); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + ut_assertnonnull(ops->write); + + ut_assertok(ops->write(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG, TEST_REG_VALUE)); + reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG); + ut_asserteq(reg, TEST_REG_VALUE); + + ut_assert(ops->read(dev, SANDBOX_PHY_ADDR + 1, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG) != 0); + + ut_assertok(ops->reset(dev)); + reg = ops->read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG); + ut_asserteq(reg, 0); + + return 0; +} + +DM_TEST(dm_test_mdio, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mdio_mux.c b/roms/u-boot/test/dm/mdio_mux.c new file mode 100644 index 000000000..950f385d1 --- /dev/null +++ b/roms/u-boot/test/dm/mdio_mux.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 + * Alex Marginean, NXP + */ + +#include <common.h> +#include <dm.h> +#include <miiphy.h> +#include <misc.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* macros copied over from mdio_sandbox.c */ +#define SANDBOX_PHY_ADDR 5 +#define SANDBOX_PHY_REG_CNT 2 + +#define TEST_REG_VALUE 0xabcd + +static int dm_test_mdio_mux(struct unit_test_state *uts) +{ + struct uclass *uc; + struct udevice *mux; + struct udevice *mdio_ch0, *mdio_ch1, *mdio; + struct mdio_ops *ops, *ops_parent; + struct mdio_mux_ops *mmops; + u16 reg; + + ut_assertok(uclass_get(UCLASS_MDIO_MUX, &uc)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO_MUX, "mdio-mux-test", + &mux)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@0", + &mdio_ch0)); + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@1", + &mdio_ch1)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio)); + + ops = mdio_get_ops(mdio_ch0); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + ut_assertnonnull(ops->write); + + mmops = mdio_mux_get_ops(mux); + ut_assertnonnull(mmops); + ut_assertnonnull(mmops->select); + + ops_parent = mdio_get_ops(mdio); + ut_assertnonnull(ops); + ut_assertnonnull(ops->read); + + /* + * mux driver sets last register on the emulated PHY whenever a group + * is selected to the selection #. Just reading that register from + * either of the child buses should return the id of the child bus + */ + reg = ops->read(mdio_ch0, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 0); + + reg = ops->read(mdio_ch1, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 1); + + mmops->select(mux, MDIO_MUX_SELECT_NONE, 5); + reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, 5); + + mmops->deselect(mux, 5); + reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE, + SANDBOX_PHY_REG_CNT - 1); + ut_asserteq(reg, (u16)MDIO_MUX_SELECT_NONE); + + return 0; +} + +DM_TEST(dm_test_mdio_mux, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/misc.c b/roms/u-boot/test/dm/misc.c new file mode 100644 index 000000000..1506fdefe --- /dev/null +++ b/roms/u-boot/test/dm/misc.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <misc.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_misc(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 buf[16]; + int id; + ulong last_ioctl; + bool enabled; + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "misc-test", &dev)); + + /* Read / write tests */ + ut_asserteq(4, misc_write(dev, 0, "TEST", 4)); + ut_asserteq(5, misc_write(dev, 4, "WRITE", 5)); + ut_asserteq(9, misc_read(dev, 0, buf, 9)); + + ut_asserteq_mem(buf, "TESTWRITE", 9); + + /* Call tests */ + + id = 0; + ut_assertok(misc_call(dev, 0, &id, 4, buf, 16)); + ut_asserteq_mem(buf, "Zero", 4); + + id = 2; + ut_assertok(misc_call(dev, 0, &id, 4, buf, 16)); + ut_asserteq_mem(buf, "Two", 3); + + ut_assertok(misc_call(dev, 1, &id, 4, buf, 16)); + ut_asserteq_mem(buf, "Forty-two", 9); + + id = 1; + ut_assertok(misc_call(dev, 1, &id, 4, buf, 16)); + ut_asserteq_mem(buf, "Forty-one", 9); + + /* IOCTL tests */ + + ut_assertok(misc_ioctl(dev, 6, NULL)); + /* Read back last issued ioctl */ + ut_assertok(misc_call(dev, 2, NULL, 0, &last_ioctl, + sizeof(last_ioctl))); + ut_asserteq(6, last_ioctl) + + ut_assertok(misc_ioctl(dev, 23, NULL)); + /* Read back last issued ioctl */ + ut_assertok(misc_call(dev, 2, NULL, 0, &last_ioctl, + sizeof(last_ioctl))); + ut_asserteq(23, last_ioctl) + + /* Enable / disable tests */ + + /* Read back enable/disable status */ + ut_assertok(misc_call(dev, 3, NULL, 0, &enabled, + sizeof(enabled))); + ut_asserteq(true, enabled); + + ut_assertok(misc_set_enabled(dev, false)); + /* Read back enable/disable status */ + ut_assertok(misc_call(dev, 3, NULL, 0, &enabled, + sizeof(enabled))); + ut_asserteq(false, enabled); + + ut_assertok(misc_set_enabled(dev, true)); + /* Read back enable/disable status */ + ut_assertok(misc_call(dev, 3, NULL, 0, &enabled, + sizeof(enabled))); + ut_asserteq(true, enabled); + + return 0; +} + +DM_TEST(dm_test_misc, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mmc.c b/roms/u-boot/test/dm/mmc.c new file mode 100644 index 000000000..f744452ff --- /dev/null +++ b/roms/u-boot/test/dm/mmc.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <mmc.h> +#include <part.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* + * Basic test of the mmc uclass. We could expand this by implementing an MMC + * stack for sandbox, or at least implementing the basic operation. + */ +static int dm_test_mmc_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev)); + + return 0; +} +DM_TEST(dm_test_mmc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_mmc_blk(struct unit_test_state *uts) +{ + struct udevice *dev; + struct blk_desc *dev_desc; + int i; + char write[1024], read[1024]; + + ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev)); + ut_assertok(blk_get_device_by_str("mmc", "0", &dev_desc)); + + /* Write a few blocks and verify that we get the same data back */ + ut_asserteq(512, dev_desc->blksz); + for (i = 0; i < sizeof(write); i++) + write[i] = i; + ut_asserteq(2, blk_dwrite(dev_desc, 0, 2, write)); + ut_asserteq(2, blk_dread(dev_desc, 0, 2, read)); + ut_asserteq_mem(write, read, sizeof(write)); + + /* Now erase them */ + memset(write, '\0', sizeof(write)); + ut_asserteq(2, blk_derase(dev_desc, 0, 2)); + ut_asserteq(2, blk_dread(dev_desc, 0, 2, read)); + ut_asserteq_mem(write, read, sizeof(write)); + + return 0; +} +DM_TEST(dm_test_mmc_blk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mux-cmd.c b/roms/u-boot/test/dm/mux-cmd.c new file mode 100644 index 000000000..11c237b5d --- /dev/null +++ b/roms/u-boot/test/dm/mux-cmd.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Texas Instruments Inc. + * Pratyush Yadav <p.yadav@ti.com> + */ +#include <common.h> +#include <dm.h> +#include <mux.h> +#include <mux-internal.h> +#include <dt-bindings/mux/mux.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> +#include <console.h> +#include <rand.h> + +#define BUF_SIZE 256 + +/* Test 'mux list' */ +static int dm_test_cmd_mux_list(struct unit_test_state *uts) +{ + char str[BUF_SIZE], *tok; + struct udevice *dev; + struct mux_chip *chip; + struct mux_control *mux; + int i; + unsigned long val; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller", + &dev)); + chip = dev_get_uclass_priv(dev); + ut_assertnonnull(chip); + + run_command("mux list", 0); + ut_assert_nextline("a-mux-controller:"); + + /* + * Check the table header to make sure we are not out of sync with the + * code in the command. If we are, catch it early. + */ + console_record_readline(str, BUF_SIZE); + tok = strtok(str, " "); + ut_asserteq_str("ID", tok); + + tok = strtok(NULL, " "); + ut_asserteq_str("Selected", tok); + + tok = strtok(NULL, " "); + ut_asserteq_str("Current", tok); + tok = strtok(NULL, " "); + ut_asserteq_str("State", tok); + + tok = strtok(NULL, " "); + ut_asserteq_str("Idle", tok); + tok = strtok(NULL, " "); + ut_asserteq_str("State", tok); + + tok = strtok(NULL, " "); + ut_asserteq_str("Num", tok); + tok = strtok(NULL, " "); + ut_asserteq_str("States", tok); + + for (i = 0; i < chip->controllers; i++) { + mux = &chip->mux[i]; + + console_record_readline(str, BUF_SIZE); + + /* + * Check if the ID printed matches with the ID of the chip we + * have. + */ + tok = strtok(str, " "); + ut_assertok(strict_strtoul(tok, 10, &val)); + ut_asserteq(i, val); + + /* Check if mux selection state matches. */ + tok = strtok(NULL, " "); + if (mux->in_use) { + ut_asserteq_str("yes", tok); + } else { + ut_asserteq_str("no", tok); + } + + /* Check if the current state matches. */ + tok = strtok(NULL, " "); + if (mux->cached_state == MUX_IDLE_AS_IS) { + ut_asserteq_str("unknown", tok); + } else { + ut_assertok(strict_strtoul(tok, 16, &val)); + ut_asserteq(mux->cached_state, val); + } + + /* Check if the idle state matches */ + tok = strtok(NULL, " "); + if (mux->idle_state == MUX_IDLE_AS_IS) { + ut_asserteq_str("as-is", tok); + } else { + ut_assertok(strict_strtoul(tok, 16, &val)); + ut_asserteq(mux->idle_state, val); + } + + /* Check if the number of states matches */ + tok = strtok(NULL, " "); + ut_assertok(strict_strtoul(tok, 16, &val)); + ut_asserteq(mux->states, val); + } + + return 0; +} +DM_TEST(dm_test_cmd_mux_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_cmd_mux_select(struct unit_test_state *uts) +{ + struct udevice *dev; + struct mux_chip *chip; + struct mux_control *mux; + char cmd[BUF_SIZE]; + unsigned int i, state; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller", + &dev)); + chip = dev_get_uclass_priv(dev); + ut_assertnonnull(chip); + + srand(get_ticks() + rand()); + for (i = 0; i < chip->controllers; i++) { + mux = &chip->mux[i]; + + state = rand() % mux->states; + + snprintf(cmd, BUF_SIZE, "mux select a-mux-controller %x %x", i, + state); + run_command(cmd, 0); + ut_asserteq(!!mux->in_use, true); + ut_asserteq(state, mux->cached_state); + + ut_assertok(mux_control_deselect(mux)); + } + + return 0; +} +DM_TEST(dm_test_cmd_mux_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_cmd_mux_deselect(struct unit_test_state *uts) +{ + struct udevice *dev; + struct mux_chip *chip; + struct mux_control *mux; + char cmd[BUF_SIZE]; + unsigned int i, state; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller", + &dev)); + chip = dev_get_uclass_priv(dev); + ut_assertnonnull(chip); + + srand(get_ticks() + rand()); + for (i = 0; i < chip->controllers; i++) { + mux = &chip->mux[i]; + + state = rand() % mux->states; + ut_assertok(mux_control_select(mux, state)); + + snprintf(cmd, BUF_SIZE, "mux deselect a-mux-controller %d", i); + run_command(cmd, 0); + ut_asserteq(!!mux->in_use, false); + } + + return 0; +} +DM_TEST(dm_test_cmd_mux_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mux-emul.c b/roms/u-boot/test/dm/mux-emul.c new file mode 100644 index 000000000..58233edc9 --- /dev/null +++ b/roms/u-boot/test/dm/mux-emul.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Texas Instruments Incorporated - http://www.ti.com/ + * Pratyush Yadav <p.yadav@ti.com> + */ +#include <common.h> +#include <dm.h> +#include <mux.h> +#include <mux-internal.h> +#include <dm/test.h> +#include <test/ut.h> +#include <asm/global_data.h> + +struct mux_emul_priv { + u32 state; +}; + +static int mux_emul_set(struct mux_control *mux, int state) +{ + struct mux_emul_priv *priv = dev_get_priv(mux->dev); + + priv->state = state; + return 0; +} + +static int mux_emul_probe(struct udevice *dev) +{ + struct mux_chip *mux_chip = dev_get_uclass_priv(dev); + struct mux_control *mux; + u32 idle_state; + int ret; + + ret = mux_alloc_controllers(dev, 1); + if (ret < 0) + return ret; + + mux = &mux_chip->mux[0]; + + ret = dev_read_u32(dev, "idle-state", &idle_state); + if (ret) + return ret; + + mux->idle_state = idle_state; + mux->states = 0x100000; + + return 0; +} + +static const struct mux_control_ops mux_emul_ops = { + .set = mux_emul_set, +}; + +static const struct udevice_id mux_emul_of_match[] = { + { .compatible = "mux-emul" }, + { /* sentinel */ }, +}; + +U_BOOT_DRIVER(emul_mux) = { + .name = "mux-emul", + .id = UCLASS_MUX, + .of_match = mux_emul_of_match, + .ops = &mux_emul_ops, + .probe = mux_emul_probe, + .priv_auto = sizeof(struct mux_emul_priv), +}; + +static int dm_test_mux_emul_default_state(struct unit_test_state *uts) +{ + struct udevice *dev; + struct mux_control *mux; + struct mux_emul_priv *priv; + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(mux_control_get(dev, "mux4", &mux)); + + priv = dev_get_priv(mux->dev); + + ut_asserteq(0xabcd, priv->state); + + return 0; +} +DM_TEST(dm_test_mux_emul_default_state, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_mux_emul_select_deselect(struct unit_test_state *uts) +{ + struct udevice *dev; + struct mux_control *mux; + struct mux_emul_priv *priv; + + gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(mux_control_get(dev, "mux4", &mux)); + + priv = dev_get_priv(mux->dev); + + ut_assertok(mux_control_select(mux, 0x1234)); + ut_asserteq(priv->state, 0x1234); + + ut_assertok(mux_control_deselect(mux)); + ut_asserteq(priv->state, 0xabcd); + + return 0; +} +DM_TEST(dm_test_mux_emul_select_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/mux-mmio.c b/roms/u-boot/test/dm/mux-mmio.c new file mode 100644 index 000000000..fd353d8b1 --- /dev/null +++ b/roms/u-boot/test/dm/mux-mmio.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot <jjhiblot@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <mux.h> +#include <regmap.h> +#include <syscon.h> +#include <asm/test.h> +#include <dm/test.h> +#include <dm/device-internal.h> +#include <test/ut.h> + +static int dm_test_mux_mmio_select(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_b; + struct regmap *map; + struct mux_control *ctl0_a, *ctl0_b; + struct mux_control *ctl1; + struct mux_control *ctl_err; + u32 val; + int i; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test", + &dev_b)); + map = syscon_regmap_lookup_by_phandle(dev, "mux-syscon"); + ut_assertok_ptr(map); + ut_assert(map); + + ut_assertok(mux_control_get(dev, "mux0", &ctl0_a)); + ut_assertok(mux_control_get(dev, "mux1", &ctl1)); + ut_asserteq(-ERANGE, mux_control_get(dev, "mux3", &ctl_err)); + ut_asserteq(-ENODATA, mux_control_get(dev, "dummy", &ctl_err)); + ut_assertok(mux_control_get(dev_b, "mux0", &ctl0_b)); + + for (i = 0; i < mux_control_states(ctl0_a); i++) { + /* Select a new state and verify the value in the regmap. */ + ut_assertok(mux_control_select(ctl0_a, i)); + ut_assertok(regmap_read(map, 0, &val)); + ut_asserteq(i, (val & 0x30) >> 4); + /* + * Deselect the mux and verify that the value in the regmap + * reflects the idle state (fixed to MUX_IDLE_AS_IS). + */ + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(regmap_read(map, 0, &val)); + ut_asserteq(i, (val & 0x30) >> 4); + } + + for (i = 0; i < mux_control_states(ctl1); i++) { + /* Select a new state and verify the value in the regmap. */ + ut_assertok(mux_control_select(ctl1, i)); + ut_assertok(regmap_read(map, 0xc, &val)); + ut_asserteq(i, (val & 0x1E) >> 1); + /* + * Deselect the mux and verify that the value in the regmap + * reflects the idle state (fixed to 2). + */ + ut_assertok(mux_control_deselect(ctl1)); + ut_assertok(regmap_read(map, 0xc, &val)); + ut_asserteq(2, (val & 0x1E) >> 1); + } + + /* Try unbalanced selection/deselection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 1)); + ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + + /* Try concurrent selection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(mux_control_select(ctl0_b, 0)); + ut_assert(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_b)); + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + + return 0; +} +DM_TEST(dm_test_mux_mmio_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that managed API for mux work correctly */ +static int dm_test_devm_mux_mmio(struct unit_test_state *uts) +{ + struct udevice *dev, *dev_b; + struct mux_control *ctl0_a, *ctl0_b; + struct mux_control *ctl1; + struct mux_control *ctl_err; + + sandbox_set_enable_memio(true); + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test", + &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test", + &dev_b)); + + ctl0_a = devm_mux_control_get(dev, "mux0"); + ut_assertok_ptr(ctl0_a); + ut_assert(ctl0_a); + ctl1 = devm_mux_control_get(dev, "mux1"); + ut_assertok_ptr(ctl1); + ut_assert(ctl1); + ctl_err = devm_mux_control_get(dev, "mux3"); + ut_asserteq(-ERANGE, PTR_ERR(ctl_err)); + ctl_err = devm_mux_control_get(dev, "dummy"); + ut_asserteq(-ENODATA, PTR_ERR(ctl_err)); + + ctl0_b = devm_mux_control_get(dev_b, "mux0"); + ut_assertok_ptr(ctl0_b); + ut_assert(ctl0_b); + + /* Try concurrent selection. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + ut_assertok(mux_control_deselect(ctl0_a)); + ut_assertok(mux_control_select(ctl0_b, 0)); + ut_assert(mux_control_select(ctl0_a, 0)); + ut_assertok(mux_control_deselect(ctl0_b)); + + /* Remove one device and check that the mux is released. */ + ut_assertok(mux_control_select(ctl0_a, 0)); + ut_assert(mux_control_select(ctl0_b, 0)); + device_remove(dev, DM_REMOVE_NORMAL); + ut_assertok(mux_control_select(ctl0_b, 0)); + + device_remove(dev_b, DM_REMOVE_NORMAL); + return 0; +} +DM_TEST(dm_test_devm_mux_mmio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/nop.c b/roms/u-boot/test/dm/nop.c new file mode 100644 index 000000000..2cd92c524 --- /dev/null +++ b/roms/u-boot/test/dm/nop.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for the NOP uclass + * + * (C) Copyright 2019 - Texas Instruments Incorporated - http://www.ti.com/ + * Jean-Jacques Hiblot <jjhiblot@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/ofnode.h> +#include <dm/lists.h> +#include <dm/device.h> +#include <dm/test.h> +#include <misc.h> +#include <test/test.h> +#include <test/ut.h> + +static int noptest_bind(struct udevice *parent) +{ + ofnode ofnode = dev_read_first_subnode(parent); + + while (ofnode_valid(ofnode)) { + struct udevice *dev; + const char *bind_flag = ofnode_read_string(ofnode, "bind"); + + if (bind_flag && (strcmp(bind_flag, "True") == 0)) + lists_bind_fdt(parent, ofnode, &dev, false); + ofnode = dev_read_next_subnode(ofnode); + } + + return 0; +} + +static const struct udevice_id noptest1_ids[] = { + { + .compatible = "sandbox,nop_sandbox1", + }, + { } +}; + +U_BOOT_DRIVER(noptest_drv1) = { + .name = "noptest1_drv", + .of_match = noptest1_ids, + .id = UCLASS_NOP, + .bind = noptest_bind, +}; + +static const struct udevice_id noptest2_ids[] = { + { + .compatible = "sandbox,nop_sandbox2", + }, + { } +}; + +U_BOOT_DRIVER(noptest_drv2) = { + .name = "noptest2_drv", + .of_match = noptest2_ids, + .id = UCLASS_NOP, +}; + +static int dm_test_nop(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "nop-test_0", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "nop-test_1", &dev)); + ut_asserteq(-ENODEV, + uclass_get_device_by_name(UCLASS_NOP, "nop-test_2", &dev)); + + return 0; +} + +DM_TEST(dm_test_nop, UT_TESTF_FLAT_TREE | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/of_extra.c b/roms/u-boot/test/dm/of_extra.c new file mode 100644 index 000000000..ac2d88689 --- /dev/null +++ b/roms/u-boot/test/dm/of_extra.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <dm/of_extra.h> +#include <dm/test.h> +#include <test/ut.h> +#include <u-boot/sha256.h> + +static int dm_test_ofnode_read_fmap_entry(struct unit_test_state *uts) +{ + const char hash_expect[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }; + struct fmap_entry entry; + ofnode node; + + node = ofnode_path("/cros-ec/flash/wp-ro"); + ut_assertok(ofnode_read_fmap_entry(node, &entry)); + ut_asserteq(0xf000, entry.offset); + ut_asserteq(0x1000, entry.length); + ut_asserteq(0x884, entry.used); + ut_asserteq(FMAP_COMPRESS_LZ4, entry.compress_algo); + ut_asserteq(0xcf8, entry.unc_length); + ut_asserteq(FMAP_HASH_SHA256, entry.hash_algo); + ut_asserteq(SHA256_SUM_LEN, entry.hash_size); + ut_asserteq_mem(hash_expect, entry.hash, SHA256_SUM_LEN); + + return 0; +} +DM_TEST(dm_test_ofnode_read_fmap_entry, 0); + +static int dm_test_ofnode_phy_is_fixed_link(struct unit_test_state *uts) +{ + ofnode eth_node, phy_node, node; + + eth_node = ofnode_path("/dsa-test/ports/port@0"); + ut_assert(ofnode_phy_is_fixed_link(eth_node, &phy_node)); + node = ofnode_path("/dsa-test/ports/port@0/fixed-link"); + ut_asserteq_mem(&phy_node, &node, sizeof(ofnode)); + + eth_node = ofnode_path("/dsa-test/ports/port@1"); + ut_assert(ofnode_phy_is_fixed_link(eth_node, &phy_node)); + node = eth_node; + ut_asserteq_mem(&phy_node, &node, sizeof(ofnode)); + + return 0; +} +DM_TEST(dm_test_ofnode_phy_is_fixed_link, 0); diff --git a/roms/u-boot/test/dm/of_platdata.c b/roms/u-boot/test/dm/of_platdata.c new file mode 100644 index 000000000..0f89c7a7d --- /dev/null +++ b/roms/u-boot/test/dm/of_platdata.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <dm.h> +#include <dt-structs.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> +#include <asm/global_data.h> + +/* Test that we can find a device using of-platdata */ +static int dm_test_of_plat_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_SERIAL, &dev)); + ut_asserteq_str("sandbox_serial", dev->name); + + return 0; +} +DM_TEST(dm_test_of_plat_base, UT_TESTF_SCAN_PDATA); + +/* Test that we can read properties from a device */ +static int dm_test_of_plat_props(struct unit_test_state *uts) +{ + struct dtd_sandbox_spl_test *plat; + struct udevice *dev; + int i; + + /* Skip the clock */ + ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev)); + ut_asserteq_str("sandbox_clk_test", dev->name); + + ut_assertok(uclass_next_device_err(&dev)); + plat = dev_get_plat(dev); + ut_assert(plat->boolval); + ut_asserteq(1, plat->intval); + ut_asserteq(4, ARRAY_SIZE(plat->intarray)); + ut_asserteq(2, plat->intarray[0]); + ut_asserteq(3, plat->intarray[1]); + ut_asserteq(4, plat->intarray[2]); + ut_asserteq(0, plat->intarray[3]); + ut_asserteq(5, plat->byteval); + ut_asserteq(3, ARRAY_SIZE(plat->bytearray)); + ut_asserteq(6, plat->bytearray[0]); + ut_asserteq(0, plat->bytearray[1]); + ut_asserteq(0, plat->bytearray[2]); + ut_asserteq(9, ARRAY_SIZE(plat->longbytearray)); + for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++) + ut_asserteq(9 + i, plat->longbytearray[i]); + ut_asserteq_str("message", plat->stringval); + ut_asserteq(3, ARRAY_SIZE(plat->stringarray)); + ut_asserteq_str("multi-word", plat->stringarray[0]); + ut_asserteq_str("message", plat->stringarray[1]); + ut_asserteq_str("", plat->stringarray[2]); + + ut_assertok(uclass_next_device_err(&dev)); + plat = dev_get_plat(dev); + ut_assert(!plat->boolval); + ut_asserteq(3, plat->intval); + ut_asserteq(5, plat->intarray[0]); + ut_asserteq(0, plat->intarray[1]); + ut_asserteq(0, plat->intarray[2]); + ut_asserteq(0, plat->intarray[3]); + ut_asserteq(8, plat->byteval); + ut_asserteq(3, ARRAY_SIZE(plat->bytearray)); + ut_asserteq(1, plat->bytearray[0]); + ut_asserteq(0x23, plat->bytearray[1]); + ut_asserteq(0x34, plat->bytearray[2]); + for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++) + ut_asserteq(i < 4 ? 9 + i : 0, plat->longbytearray[i]); + ut_asserteq_str("message2", plat->stringval); + ut_asserteq_str("another", plat->stringarray[0]); + ut_asserteq_str("multi-word", plat->stringarray[1]); + ut_asserteq_str("message", plat->stringarray[2]); + + ut_assertok(uclass_next_device_err(&dev)); + plat = dev_get_plat(dev); + ut_assert(!plat->boolval); + ut_asserteq_str("one", plat->stringarray[0]); + ut_asserteq_str("", plat->stringarray[1]); + ut_asserteq_str("", plat->stringarray[2]); + + ut_assertok(uclass_next_device_err(&dev)); + plat = dev_get_plat(dev); + ut_assert(!plat->boolval); + ut_asserteq_str("spl", plat->stringarray[0]); + + ut_asserteq(-ENODEV, uclass_next_device_err(&dev)); + + return 0; +} +DM_TEST(dm_test_of_plat_props, UT_TESTF_SCAN_PDATA); + +/* + * find_driver_info - recursively find the driver_info for a device + * + * This sets found[idx] to true when it finds the driver_info record for a + * device, where idx is the index in the driver_info linker list. + * + * @uts: Test state + * @parent: Parent to search + * @found: bool array to update + * @return 0 if OK, non-zero on error + */ +static int find_driver_info(struct unit_test_state *uts, struct udevice *parent, + bool found[]) +{ + struct udevice *dev; + + /* If not the root device, find the entry that caused it to be bound */ + if (parent->parent) { + const int n_ents = + ll_entry_count(struct driver_info, driver_info); + int idx = -1; + int i; + + for (i = 0; i < n_ents; i++) { + const struct driver_rt *drt = gd_dm_driver_rt() + i; + + if (drt->dev == parent) { + idx = i; + found[idx] = true; + break; + } + } + + ut_assert(idx != -1); + } + + device_foreach_child(dev, parent) { + int ret; + + ret = find_driver_info(uts, dev, found); + if (ret < 0) + return ret; + } + + return 0; +} + +/* Check that every device is recorded in its driver_info struct */ +static int dm_test_of_plat_dev(struct unit_test_state *uts) +{ + const int n_ents = ll_entry_count(struct driver_info, driver_info); + bool found[n_ents]; + uint i; + + /* Skip this test if there is no platform data */ + if (!CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT)) + return 0; + + /* Record the indexes that are found */ + memset(found, '\0', sizeof(found)); + ut_assertok(find_driver_info(uts, gd->dm_root, found)); + + /* Make sure that the driver entries without devices have no ->dev */ + for (i = 0; i < n_ents; i++) { + const struct driver_rt *drt = gd_dm_driver_rt() + i; + struct udevice *dev; + + if (found[i]) { + /* Make sure we can find it */ + ut_assertnonnull(drt->dev); + ut_assertok(device_get_by_ofplat_idx(i, &dev)); + ut_asserteq_ptr(dev, drt->dev); + } else { + ut_assertnull(drt->dev); + ut_asserteq(-ENOENT, device_get_by_ofplat_idx(i, &dev)); + } + } + + return 0; +} +DM_TEST(dm_test_of_plat_dev, UT_TESTF_SCAN_PDATA); + +/* Test handling of phandles that point to other devices */ +static int dm_test_of_plat_phandle(struct unit_test_state *uts) +{ + struct dtd_sandbox_clk_test *plat; + struct udevice *dev, *clk; + + ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev)); + ut_asserteq_str("sandbox_clk_test", dev->name); + plat = dev_get_plat(dev); + + ut_assertok(device_get_by_ofplat_idx(plat->clocks[0].idx, &clk)); + ut_asserteq_str("sandbox_fixed_clock", clk->name); + + ut_assertok(device_get_by_ofplat_idx(plat->clocks[1].idx, &clk)); + ut_asserteq_str("sandbox_clk", clk->name); + ut_asserteq(1, plat->clocks[1].arg[0]); + + ut_assertok(device_get_by_ofplat_idx(plat->clocks[2].idx, &clk)); + ut_asserteq_str("sandbox_clk", clk->name); + ut_asserteq(0, plat->clocks[2].arg[0]); + + ut_assertok(device_get_by_ofplat_idx(plat->clocks[3].idx, &clk)); + ut_asserteq_str("sandbox_clk", clk->name); + ut_asserteq(3, plat->clocks[3].arg[0]); + + ut_assertok(device_get_by_ofplat_idx(plat->clocks[4].idx, &clk)); + ut_asserteq_str("sandbox_clk", clk->name); + ut_asserteq(2, plat->clocks[4].arg[0]); + + return 0; +} +DM_TEST(dm_test_of_plat_phandle, UT_TESTF_SCAN_PDATA); + +#if CONFIG_IS_ENABLED(OF_PLATDATA_PARENT) +/* Test that device parents are correctly set up */ +static int dm_test_of_plat_parent(struct unit_test_state *uts) +{ + struct udevice *rtc, *i2c; + + ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc)); + ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c)); + ut_asserteq_ptr(i2c, dev_get_parent(rtc)); + + return 0; +} +DM_TEST(dm_test_of_plat_parent, UT_TESTF_SCAN_PDATA); +#endif diff --git a/roms/u-boot/test/dm/ofnode.c b/roms/u-boot/test/dm/ofnode.c new file mode 100644 index 000000000..de895773b --- /dev/null +++ b/roms/u-boot/test/dm/ofnode.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <dm/of_extra.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_ofnode_compatible(struct unit_test_state *uts) +{ + ofnode root_node = ofnode_path("/"); + + ut_assert(ofnode_valid(root_node)); + ut_assert(ofnode_device_is_compatible(root_node, "sandbox")); + + return 0; +} +DM_TEST(dm_test_ofnode_compatible, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_get_by_phandle(struct unit_test_state *uts) +{ + /* test invalid phandle */ + ut_assert(!ofnode_valid(ofnode_get_by_phandle(0))); + ut_assert(!ofnode_valid(ofnode_get_by_phandle(-1))); + + /* test first valid phandle */ + ut_assert(ofnode_valid(ofnode_get_by_phandle(1))); + + /* test unknown phandle */ + ut_assert(!ofnode_valid(ofnode_get_by_phandle(0x1000000))); + + return 0; +} +DM_TEST(dm_test_ofnode_get_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_by_prop_value(struct unit_test_state *uts) +{ + const char propname[] = "compatible"; + const char propval[] = "denx,u-boot-fdt-test"; + const char *str; + ofnode node = ofnode_null(); + + /* Find first matching node, there should be at least one */ + node = ofnode_by_prop_value(node, propname, propval, sizeof(propval)); + ut_assert(ofnode_valid(node)); + str = ofnode_read_string(node, propname); + ut_assert(str && !strcmp(str, propval)); + + /* Find the rest of the matching nodes */ + while (true) { + node = ofnode_by_prop_value(node, propname, propval, + sizeof(propval)); + if (!ofnode_valid(node)) + break; + str = ofnode_read_string(node, propname); + ut_assert(str && !strcmp(str, propval)); + } + + return 0; +} +DM_TEST(dm_test_ofnode_by_prop_value, UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_fmap(struct unit_test_state *uts) +{ + struct fmap_entry entry; + ofnode node; + + node = ofnode_path("/cros-ec/flash"); + ut_assert(ofnode_valid(node)); + ut_assertok(ofnode_read_fmap_entry(node, &entry)); + ut_asserteq(0x08000000, entry.offset); + ut_asserteq(0x20000, entry.length); + + return 0; +} +DM_TEST(dm_test_ofnode_fmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_read(struct unit_test_state *uts) +{ + const u32 *val; + ofnode node; + int size; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + val = ofnode_read_prop(node, "int-value", &size); + ut_assertnonnull(val); + ut_asserteq(4, size); + ut_asserteq(1234, fdt32_to_cpu(val[0])); + + val = ofnode_read_prop(node, "missing", &size); + ut_assertnull(val); + ut_asserteq(-FDT_ERR_NOTFOUND, size); + + /* Check it works without a size parameter */ + val = ofnode_read_prop(node, "missing", NULL); + ut_assertnull(val); + + return 0; +} +DM_TEST(dm_test_ofnode_read, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_phandle(struct unit_test_state *uts) +{ + struct ofnode_phandle_args args; + ofnode node; + int ret; + const char prop[] = "test-gpios"; + const char cell[] = "#gpio-cells"; + const char prop2[] = "phandle-value"; + + node = ofnode_path("/a-test"); + ut_assert(ofnode_valid(node)); + + /* Test ofnode_count_phandle_with_args with cell name */ + ret = ofnode_count_phandle_with_args(node, "missing", cell, 0); + ut_asserteq(-ENOENT, ret); + ret = ofnode_count_phandle_with_args(node, prop, "#invalid", 0); + ut_asserteq(-EINVAL, ret); + ret = ofnode_count_phandle_with_args(node, prop, cell, 0); + ut_asserteq(5, ret); + + /* Test ofnode_parse_phandle_with_args with cell name */ + ret = ofnode_parse_phandle_with_args(node, "missing", cell, 0, 0, + &args); + ut_asserteq(-ENOENT, ret); + ret = ofnode_parse_phandle_with_args(node, prop, "#invalid", 0, 0, + &args); + ut_asserteq(-EINVAL, ret); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 0, &args); + ut_assertok(ret); + ut_asserteq(1, args.args_count); + ut_asserteq(1, args.args[0]); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 1, &args); + ut_assertok(ret); + ut_asserteq(1, args.args_count); + ut_asserteq(4, args.args[0]); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 2, &args); + ut_assertok(ret); + ut_asserteq(5, args.args_count); + ut_asserteq(5, args.args[0]); + ut_asserteq(1, args.args[4]); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 3, &args); + ut_asserteq(-ENOENT, ret); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 4, &args); + ut_assertok(ret); + ut_asserteq(1, args.args_count); + ut_asserteq(12, args.args[0]); + ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 5, &args); + ut_asserteq(-ENOENT, ret); + + /* Test ofnode_count_phandle_with_args with cell count */ + ret = ofnode_count_phandle_with_args(node, "missing", NULL, 2); + ut_asserteq(-ENOENT, ret); + ret = ofnode_count_phandle_with_args(node, prop2, NULL, 1); + ut_asserteq(3, ret); + + /* Test ofnode_parse_phandle_with_args with cell count */ + ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 0, &args); + ut_assertok(ret); + ut_asserteq(1, ofnode_valid(args.node)); + ut_asserteq(1, args.args_count); + ut_asserteq(10, args.args[0]); + ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 1, &args); + ut_asserteq(-EINVAL, ret); + ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 2, &args); + ut_assertok(ret); + ut_asserteq(1, ofnode_valid(args.node)); + ut_asserteq(1, args.args_count); + ut_asserteq(30, args.args[0]); + ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 3, &args); + ut_asserteq(-ENOENT, ret); + + return 0; +} +DM_TEST(dm_test_ofnode_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_read_chosen(struct unit_test_state *uts) +{ + const char *str; + const u32 *val; + ofnode node; + int size; + + str = ofnode_read_chosen_string("setting"); + ut_assertnonnull(str); + ut_asserteq_str("sunrise ohoka", str); + ut_asserteq_ptr(NULL, ofnode_read_chosen_string("no-setting")); + + node = ofnode_get_chosen_node("other-node"); + ut_assert(ofnode_valid(node)); + ut_asserteq_str("c-test@5", ofnode_get_name(node)); + + node = ofnode_get_chosen_node("setting"); + ut_assert(!ofnode_valid(node)); + + val = ofnode_read_chosen_prop("int-values", &size); + ut_assertnonnull(val); + ut_asserteq(8, size); + ut_asserteq(0x1937, fdt32_to_cpu(val[0])); + ut_asserteq(72993, fdt32_to_cpu(val[1])); + + return 0; +} +DM_TEST(dm_test_ofnode_read_chosen, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_read_aliases(struct unit_test_state *uts) +{ + const void *val; + ofnode node; + int size; + + node = ofnode_get_aliases_node("ethernet3"); + ut_assert(ofnode_valid(node)); + ut_asserteq_str("sbe5", ofnode_get_name(node)); + + node = ofnode_get_aliases_node("unknown"); + ut_assert(!ofnode_valid(node)); + + val = ofnode_read_aliases_prop("spi0", &size); + ut_assertnonnull(val); + ut_asserteq(7, size); + ut_asserteq_str("/spi@0", (const char *)val); + + return 0; +} +DM_TEST(dm_test_ofnode_read_aliases, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_get_child_count(struct unit_test_state *uts) +{ + ofnode node, child_node; + u32 val; + + node = ofnode_path("/i-test"); + ut_assert(ofnode_valid(node)); + + val = ofnode_get_child_count(node); + ut_asserteq(3, val); + + child_node = ofnode_first_subnode(node); + ut_assert(ofnode_valid(child_node)); + val = ofnode_get_child_count(child_node); + ut_asserteq(0, val); + + return 0; +} +DM_TEST(dm_test_ofnode_get_child_count, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_is_enabled(struct unit_test_state *uts) +{ + ofnode root_node = ofnode_path("/"); + ofnode node = ofnode_path("/usb@0"); + + ut_assert(ofnode_is_enabled(root_node)); + ut_assert(!ofnode_is_enabled(node)); + + return 0; +} +DM_TEST(dm_test_ofnode_is_enabled, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_ofnode_get_reg(struct unit_test_state *uts) +{ + ofnode node; + fdt_addr_t addr; + fdt_size_t size; + + node = ofnode_path("/translation-test@8000"); + ut_assert(ofnode_valid(node)); + addr = ofnode_get_addr(node); + size = ofnode_get_size(node); + ut_asserteq(0x8000, addr); + ut_asserteq(0x4000, size); + + node = ofnode_path("/translation-test@8000/dev@1,100"); + ut_assert(ofnode_valid(node)); + addr = ofnode_get_addr(node); + size = ofnode_get_size(node); + ut_asserteq(0x9000, addr); + ut_asserteq(0x1000, size); + + node = ofnode_path("/emul-mux-controller"); + ut_assert(ofnode_valid(node)); + addr = ofnode_get_addr(node); + size = ofnode_get_size(node); + ut_asserteq(FDT_ADDR_T_NONE, addr); + ut_asserteq(FDT_SIZE_T_NONE, size); + + return 0; +} +DM_TEST(dm_test_ofnode_get_reg, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/ofread.c b/roms/u-boot/test/dm/ofread.c new file mode 100644 index 000000000..8c7dd8251 --- /dev/null +++ b/roms/u-boot/test/dm/ofread.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <test/ut.h> + +static int dm_test_ofnode_get_property_by_prop(struct unit_test_state *uts) +{ + ofnode node; + struct ofprop prop; + const void *value; + const char *propname; + int res, len, count = 0; + + node = ofnode_path("/cros-ec/flash"); + for (res = ofnode_get_first_property(node, &prop); + !res; + res = ofnode_get_next_property(&prop)) { + value = ofnode_get_property_by_prop(&prop, &propname, &len); + ut_assertnonnull(value); + switch (count) { + case 0: + ut_asserteq_str("image-pos", propname); + ut_asserteq(4, len); + break; + case 1: + ut_asserteq_str("size", propname); + ut_asserteq(4, len); + break; + case 2: + ut_asserteq_str("erase-value", propname); + ut_asserteq(4, len); + break; + case 3: + /* only for plat */ + ut_asserteq_str("name", propname); + ut_asserteq(6, len); + ut_asserteq_str("flash", value); + break; + default: + break; + } + count++; + } + + return 0; +} +DM_TEST(dm_test_ofnode_get_property_by_prop, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/osd.c b/roms/u-boot/test/dm/osd.c new file mode 100644 index 000000000..6279b391c --- /dev/null +++ b/roms/u-boot/test/dm/osd.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <display_options.h> +#include <dm.h> +#include <video_osd.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +#include "../../drivers/video/sandbox_osd.h" + +const uint memsize = 2 * 10 * 10; + +static void split(u8 *mem, uint size, u8 *text, u8 *colors) +{ + int i; + u16 *p = (u16 *)mem; + + for (i = 0; i < size; i++) { + colors[i] = p[i] % 0x100; + text[i] = p[i] / 0x100; + } +} + +static void print_mem(u8 *mem, uint width, uint height) +{ + const uint memsize = 2 * 10 * 10; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + int i; + + split(mem, memsize / 2, text, colors); + + for (i = 0; i < width * height; i++) { + printf("%c", text[i]); + if (i > 0 && ((i + 1) % width) == 0) + printf("\n"); + } + + printf("\n"); + + for (i = 0; i < width * height; i++) { + printf("%c", colors[i]); + if (i > 0 && ((i + 1) % width) == 0) + printf("\n"); + } +} + +static int dm_test_osd_basics(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 mem[memsize + 1]; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + struct video_osd_info info; + + ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev)); + + video_osd_get_info(dev, &info); + + ut_asserteq(10, info.width); + ut_asserteq(10, info.height); + ut_asserteq(1, info.major_version); + ut_asserteq(0, info.minor_version); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_asserteq_mem(text, + " " + " " + " " + " " + " " + " " + " " + " " + " " + " ", memsize / 2); + + ut_asserteq_mem(colors, + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk", memsize / 2); + + print_mem(mem, 10, 10); + + ut_assertok(video_osd_print(dev, 1, 1, COLOR_RED, "Blah")); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_asserteq_mem(text, + " " + " Blah " + " " + " " + " " + " " + " " + " " + " " + " ", memsize / 2); + + ut_asserteq_mem(colors, + "kkkkkkkkkk" + "krrrrkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk" + "kkkkkkkkkk", memsize / 2); + + print_mem(mem, 10, 10); + + return 0; +} + +DM_TEST(dm_test_osd_basics, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_osd_extended(struct unit_test_state *uts) +{ + struct udevice *dev; + u8 mem[memsize + 1]; + u8 colors[memsize / 2]; + u8 text[memsize / 2]; + struct video_osd_info info; + u16 val; + + ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev)); + + ut_assertok(video_osd_set_size(dev, 20, 5)); + + video_osd_get_info(dev, &info); + + ut_asserteq(20, info.width); + ut_asserteq(5, info.height); + ut_asserteq(1, info.major_version); + ut_asserteq(0, info.minor_version); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + ut_asserteq_mem(text, + " " + " " + " " + " " + " ", memsize / 2); + + ut_asserteq_mem(colors, + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk" + "kkkkkkkkkkkkkkkkkkkk", memsize / 2); + + print_mem(mem, 20, 5); + + /* Draw green border */ + val = '-' * 0x100 + 'g'; + ut_assertok(video_osd_set_mem(dev, 1, 0, (u8 *)&val, 2, 18)); + ut_assertok(video_osd_set_mem(dev, 1, 4, (u8 *)&val, 2, 18)); + ut_assertok(video_osd_print(dev, 0, 1, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 2, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 3, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 1, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 2, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 19, 3, COLOR_GREEN, "|")); + ut_assertok(video_osd_print(dev, 0, 0, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 19, 0, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 19, 4, COLOR_GREEN, "+")); + ut_assertok(video_osd_print(dev, 0, 4, COLOR_GREEN, "+")); + + /* Add menu caption and entries */ + ut_assertok(video_osd_print(dev, 5, 0, COLOR_GREEN, " OSD menu ")); + ut_assertok(video_osd_print(dev, 2, 1, COLOR_BLUE, " * Entry 1")); + ut_assertok(video_osd_print(dev, 2, 2, COLOR_BLUE, "(*) Entry 2")); + ut_assertok(video_osd_print(dev, 2, 3, COLOR_BLUE, " * Entry 3")); + + ut_assertok(sandbox_osd_get_mem(dev, mem, memsize)); + split(mem, memsize / 2, text, colors); + + print_mem(mem, 20, 5); + + ut_asserteq_mem(text, + "+---- OSD menu ----+" + "| * Entry 1 |" + "| (*) Entry 2 |" + "| * Entry 3 |" + "+------------------+", memsize / 2); + + ut_asserteq_mem(colors, + "gggggggggggggggggggg" + "gkbbbbbbbbbbbkkkkkkg" + "gkbbbbbbbbbbbkkkkkkg" + "gkbbbbbbbbbbbkkkkkkg" + "gggggggggggggggggggg", memsize / 2); + + return 0; +} + +DM_TEST(dm_test_osd_extended, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/p2sb.c b/roms/u-boot/test/dm/p2sb.c new file mode 100644 index 000000000..df2470914 --- /dev/null +++ b/roms/u-boot/test/dm/p2sb.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for Primary-to-Sideband bus (P2SB) + * + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <p2sb.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Base test of the PMC uclass */ +static int dm_test_p2sb_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + sandbox_set_enable_memio(true); + ut_assertok(uclass_get_device_by_name(UCLASS_AXI, "adder", &dev)); + ut_asserteq(0x03000004, pcr_read32(dev, 4)); + ut_asserteq(0x300, pcr_read16(dev, 6)); + ut_asserteq(4, pcr_read8(dev, 4)); + + return 0; +} +DM_TEST(dm_test_p2sb_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/panel.c b/roms/u-boot/test/dm/panel.c new file mode 100644 index 000000000..49f5ac716 --- /dev/null +++ b/roms/u-boot/test/dm/panel.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for panel uclass + * + * Copyright (c) 2018 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <backlight.h> +#include <dm.h> +#include <panel.h> +#include <video.h> +#include <asm/gpio.h> +#include <asm/test.h> +#include <dm/test.h> +#include <power/regulator.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the panel uclass */ +static int dm_test_panel(struct unit_test_state *uts) +{ + struct udevice *dev, *pwm, *gpio, *reg; + uint period_ns; + uint duty_ns; + bool enable; + bool polarity; + + ut_assertok(uclass_first_device_err(UCLASS_PANEL, &dev)); + ut_assertok(uclass_first_device_err(UCLASS_PWM, &pwm)); + ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio)); + ut_assertok(regulator_get_by_platname("VDD_EMMC_1.8V", ®)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(false, enable); + ut_asserteq(false, regulator_get_enable(reg)); + + ut_assertok(panel_enable_backlight(dev)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(1000, period_ns); + ut_asserteq(170 * 1000 / 255, duty_ns); + ut_asserteq(true, enable); + ut_asserteq(false, polarity); + ut_asserteq(1, sandbox_gpio_get_value(gpio, 1)); + ut_asserteq(true, regulator_get_enable(reg)); + + ut_assertok(panel_set_backlight(dev, 40)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(64 * 1000 / 255, duty_ns); + + ut_assertok(panel_set_backlight(dev, BACKLIGHT_MAX)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(255 * 1000 / 255, duty_ns); + + ut_assertok(panel_set_backlight(dev, BACKLIGHT_MIN)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(0 * 1000 / 255, duty_ns); + ut_asserteq(1, sandbox_gpio_get_value(gpio, 1)); + + ut_assertok(panel_set_backlight(dev, BACKLIGHT_DEFAULT)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(true, enable); + ut_asserteq(170 * 1000 / 255, duty_ns); + + ut_assertok(panel_set_backlight(dev, BACKLIGHT_OFF)); + ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(0 * 1000 / 255, duty_ns); + ut_asserteq(0, sandbox_gpio_get_value(gpio, 1)); + ut_asserteq(false, regulator_get_enable(reg)); + + return 0; +} +DM_TEST(dm_test_panel, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/part.c b/roms/u-boot/test/dm/part.c new file mode 100644 index 000000000..78dd8472c --- /dev/null +++ b/roms/u-boot/test/dm/part.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <sean.anderson@seco.com> + */ + +#include <common.h> +#include <dm.h> +#include <mmc.h> +#include <part.h> +#include <part_efi.h> +#include <dm/test.h> +#include <test/ut.h> + +static inline int do_test(struct unit_test_state *uts, int expected, + const char *part_str, bool whole) +{ + struct blk_desc *mmc_dev_desc; + struct disk_partition part_info; + + ut_asserteq(expected, + part_get_info_by_dev_and_name_or_num("mmc", part_str, + &mmc_dev_desc, + &part_info, whole)); + return 0; +} + +static int dm_test_part(struct unit_test_state *uts) +{ + char *oldbootdevice; + char str_disk_guid[UUID_STR_LEN + 1]; + int ret; + struct blk_desc *mmc_dev_desc; + struct disk_partition parts[2] = { + { + .start = 48, /* GPT data takes up the first 34 blocks or so */ + .size = 1, + .name = "test1", + }, + { + .start = 49, + .size = 1, + .name = "test2", + }, + }; + + ut_asserteq(1, blk_get_device_by_str("mmc", "1", &mmc_dev_desc)); + if (CONFIG_IS_ENABLED(RANDOM_UUID)) { + gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD); + gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD); + gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD); + } + ut_assertok(gpt_restore(mmc_dev_desc, str_disk_guid, parts, + ARRAY_SIZE(parts))); + + oldbootdevice = env_get("bootdevice"); + +#define test(expected, part_str, whole) do { \ + ret = do_test(uts, expected, part_str, whole); \ + if (ret) \ + goto out; \ +} while (0) + + env_set("bootdevice", NULL); + test(-ENODEV, NULL, true); + test(-ENODEV, "", true); + env_set("bootdevice", "0"); + test(0, NULL, true); + test(0, "", true); + env_set("bootdevice", "1"); + test(1, NULL, false); + test(1, "", false); + test(1, "-", false); + env_set("bootdevice", ""); + test(-EPROTONOSUPPORT, "0", false); + test(0, "0", true); + test(0, ":0", true); + test(0, ".0", true); + test(0, ".0:0", true); + test(-EINVAL, "#test1", true); + test(1, "1", false); + test(1, "1", true); + test(-ENOENT, "1:0", false); + test(0, "1:0", true); + test(1, "1:1", false); + test(2, "1:2", false); + test(1, "1.0", false); + test(0, "1.0:0", true); + test(1, "1.0:1", false); + test(2, "1.0:2", false); + test(-EINVAL, "1#bogus", false); + test(1, "1#test1", false); + test(2, "1#test2", false); + ret = 0; + +out: + env_set("bootdevice", oldbootdevice); + return ret; +} +DM_TEST(dm_test_part, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pch.c b/roms/u-boot/test/dm/pch.c new file mode 100644 index 000000000..53f7bbf18 --- /dev/null +++ b/roms/u-boot/test/dm/pch.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <pch.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that sandbox PCH works correctly */ +static int dm_test_pch_base(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 gbase, iobase; + ulong sbase; + + ut_assertok(uclass_first_device_err(UCLASS_PCH, &dev)); + ut_assertok(pch_get_spi_base(dev, &sbase)); + ut_asserteq(0x10, sbase); + + ut_asserteq(0, sandbox_get_pch_spi_protect(dev)); + ut_assertok(pch_set_spi_protect(dev, true)); + ut_asserteq(1, sandbox_get_pch_spi_protect(dev)); + + ut_assertok(pch_get_gpio_base(dev, &gbase)); + ut_asserteq(0x20, gbase); + + ut_assertok(pch_get_io_base(dev, &iobase)); + ut_asserteq(0x30, iobase); + + return 0; +} +DM_TEST(dm_test_pch_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test sandbox PCH ioctl */ +static int dm_test_pch_ioctl(struct unit_test_state *uts) +{ + struct udevice *dev; + char data; + + ut_assertok(uclass_first_device_err(UCLASS_PCH, &dev)); + + ut_asserteq(-ENOSYS, pch_ioctl(dev, PCH_REQ_TEST1, NULL, 0)); + + ut_asserteq('a', pch_ioctl(dev, PCH_REQ_TEST2, "a", 1)); + + ut_asserteq(1, pch_ioctl(dev, PCH_REQ_TEST3, &data, 1)); + ut_asserteq('x', data); + + return 0; +} +DM_TEST(dm_test_pch_ioctl, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pci.c b/roms/u-boot/test/dm/pci.c new file mode 100644 index 000000000..fa2e4a855 --- /dev/null +++ b/roms/u-boot/test/dm/pci.c @@ -0,0 +1,378 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <asm/io.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that sandbox PCI works correctly */ +static int dm_test_pci_base(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_assertok(uclass_get_device(UCLASS_PCI, 0, &bus)); + + return 0; +} +DM_TEST(dm_test_pci_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that sandbox PCI bus numbering and device works correctly */ +static int dm_test_pci_busdev(struct unit_test_state *uts) +{ + struct udevice *bus; + struct udevice *swap; + u16 vendor, device; + + /* Test bus#0 and its devices */ + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus)); + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x00, 0), &swap)); + vendor = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor)); + ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap)); + device = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device)); + ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device); + + /* Test bus#1 and its devices */ + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus)); + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap)); + vendor = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor)); + ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x0c, 0), &swap)); + device = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device)); + ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device); + + return 0; +} +DM_TEST(dm_test_pci_busdev, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can use the swapcase device correctly */ +static int dm_test_pci_swapcase(struct unit_test_state *uts) +{ + struct udevice *swap; + ulong io_addr, mem_addr; + char *ptr; + + /* Check that asking for the device 0 automatically fires up PCI */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x00, 0), &swap)); + + /* First test I/O */ + io_addr = dm_pci_read_bar32(swap, 0); + outb(2, io_addr); + ut_asserteq(2, inb(io_addr)); + + /* + * Now test memory mapping - note we must unmap and remap to cause + * the swapcase emulation to see our data and response. + */ + mem_addr = dm_pci_read_bar32(swap, 1); + ptr = map_sysmem(mem_addr, 20); + strcpy(ptr, "This is a TesT"); + unmap_sysmem(ptr); + + ptr = map_sysmem(mem_addr, 20); + ut_asserteq_str("tHIS IS A tESt", ptr); + unmap_sysmem(ptr); + + /* Check that asking for the device 1 automatically fires up PCI */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap)); + + /* First test I/O */ + io_addr = dm_pci_read_bar32(swap, 0); + outb(2, io_addr); + ut_asserteq(2, inb(io_addr)); + + /* + * Now test memory mapping - note we must unmap and remap to cause + * the swapcase emulation to see our data and response. + */ + mem_addr = dm_pci_read_bar32(swap, 1); + ptr = map_sysmem(mem_addr, 20); + strcpy(ptr, "This is a TesT"); + unmap_sysmem(ptr); + + ptr = map_sysmem(mem_addr, 20); + ut_asserteq_str("tHIS IS A tESt", ptr); + unmap_sysmem(ptr); + + return 0; +} +DM_TEST(dm_test_pci_swapcase, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can dynamically bind the device driver correctly */ +static int dm_test_pci_drvdata(struct unit_test_state *uts) +{ + struct udevice *bus, *swap; + + /* Check that asking for the device automatically fires up PCI */ + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus)); + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap)); + ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data); + ut_assertok(dev_has_ofnode(swap)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x0c, 0), &swap)); + ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data); + ut_assertok(dev_has_ofnode(swap)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x10, 0), &swap)); + ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data); + ut_assertok(!dev_has_ofnode(swap)); + + return 0; +} +DM_TEST(dm_test_pci_drvdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that devices on PCI bus#2 can be accessed correctly */ +static int dm_test_pci_mixed(struct unit_test_state *uts) +{ + /* PCI bus#2 has both statically and dynamic declared devices */ + struct udevice *bus, *swap; + u16 vendor, device; + ulong io_addr, mem_addr; + char *ptr; + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 2, &bus)); + + /* Test the dynamic device */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(2, 0x08, 0), &swap)); + vendor = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor)); + ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor); + + /* First test I/O */ + io_addr = dm_pci_read_bar32(swap, 0); + outb(2, io_addr); + ut_asserteq(2, inb(io_addr)); + + /* + * Now test memory mapping - note we must unmap and remap to cause + * the swapcase emulation to see our data and response. + */ + mem_addr = dm_pci_read_bar32(swap, 1); + ptr = map_sysmem(mem_addr, 30); + strcpy(ptr, "This is a TesT oN dYNAMIc"); + unmap_sysmem(ptr); + + ptr = map_sysmem(mem_addr, 30); + ut_asserteq_str("tHIS IS A tESt On DynamiC", ptr); + unmap_sysmem(ptr); + + /* Test the static device */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(2, 0x1f, 0), &swap)); + device = 0; + ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device)); + ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device); + + /* First test I/O */ + io_addr = dm_pci_read_bar32(swap, 0); + outb(2, io_addr); + ut_asserteq(2, inb(io_addr)); + + /* + * Now test memory mapping - note we must unmap and remap to cause + * the swapcase emulation to see our data and response. + */ + mem_addr = dm_pci_read_bar32(swap, 1); + ptr = map_sysmem(mem_addr, 30); + strcpy(ptr, "This is a TesT oN sTATIc"); + unmap_sysmem(ptr); + + ptr = map_sysmem(mem_addr, 30); + ut_asserteq_str("tHIS IS A tESt On StatiC", ptr); + unmap_sysmem(ptr); + + return 0; +} +DM_TEST(dm_test_pci_mixed, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test looking up PCI capability and extended capability */ +static int dm_test_pci_cap(struct unit_test_state *uts) +{ + struct udevice *bus, *swap; + int cap; + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap)); + + /* look up PCI_CAP_ID_EXP */ + cap = dm_pci_find_capability(swap, PCI_CAP_ID_EXP); + ut_asserteq(PCI_CAP_ID_EXP_OFFSET, cap); + + /* look up PCI_CAP_ID_PCIX */ + cap = dm_pci_find_capability(swap, PCI_CAP_ID_PCIX); + ut_asserteq(0, cap); + + /* look up PCI_CAP_ID_MSIX starting from PCI_CAP_ID_PM_OFFSET */ + cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_PM_OFFSET, + PCI_CAP_ID_MSIX); + ut_asserteq(PCI_CAP_ID_MSIX_OFFSET, cap); + + /* look up PCI_CAP_ID_VNDR starting from PCI_CAP_ID_EXP_OFFSET */ + cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_EXP_OFFSET, + PCI_CAP_ID_VNDR); + ut_asserteq(0, cap); + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap)); + + /* look up PCI_EXT_CAP_ID_DSN */ + cap = dm_pci_find_ext_capability(swap, PCI_EXT_CAP_ID_DSN); + ut_asserteq(PCI_EXT_CAP_ID_DSN_OFFSET, cap); + + /* look up PCI_EXT_CAP_ID_SRIOV */ + cap = dm_pci_find_ext_capability(swap, PCI_EXT_CAP_ID_SRIOV); + ut_asserteq(0, cap); + + /* look up PCI_EXT_CAP_ID_DSN starting from PCI_EXT_CAP_ID_ERR_OFFSET */ + cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_ERR_OFFSET, + PCI_EXT_CAP_ID_DSN); + ut_asserteq(PCI_EXT_CAP_ID_DSN_OFFSET, cap); + + /* look up PCI_EXT_CAP_ID_RCRB starting from PCI_EXT_CAP_ID_VC_OFFSET */ + cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_VC_OFFSET, + PCI_EXT_CAP_ID_RCRB); + ut_asserteq(0, cap); + + return 0; +} +DM_TEST(dm_test_pci_cap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test looking up BARs in EA capability structure */ +static int dm_test_pci_ea(struct unit_test_state *uts) +{ + struct udevice *bus, *swap; + void *bar; + int cap; + + /* + * use emulated device mapping function, we're not using real physical + * addresses in this test + */ + sandbox_set_enable_pci_map(true); + + ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus)); + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap)); + + /* look up PCI_CAP_ID_EA */ + cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA); + ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap); + + /* test swap case in BAR 1 */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0); + ut_assertnonnull(bar); + *(int *)bar = 2; /* swap upper/lower */ + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + strcpy(bar, "ea TEST"); + unmap_sysmem(bar); + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0); + ut_assertnonnull(bar); + ut_asserteq_str("EA test", bar); + + /* test magic values in BARs2, 4; BAR 3 is n/a */ + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0); + ut_assertnull(bar); + + bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0); + ut_assertnonnull(bar); + ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar); + + return 0; +} +DM_TEST(dm_test_pci_ea, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test the dev_read_addr_pci() function */ +static int dm_test_pci_addr_flat(struct unit_test_state *uts) +{ + struct udevice *swap1f, *swap1; + ulong io_addr, mem_addr; + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f)); + io_addr = dm_pci_read_bar32(swap1f, 0); + ut_asserteq(io_addr, dev_read_addr_pci(swap1f)); + + /* + * This device has both I/O and MEM spaces but the MEM space appears + * first + */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1)); + mem_addr = dm_pci_read_bar32(swap1, 1); + ut_asserteq(mem_addr, dev_read_addr_pci(swap1)); + + return 0; +} +DM_TEST(dm_test_pci_addr_flat, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | + UT_TESTF_FLAT_TREE); + +/* + * Test the dev_read_addr_pci() function with livetree. That function is + * not currently fully implemented, in that it fails to return the BAR address. + * Once that is implemented this test can be removed and dm_test_pci_addr_flat() + * can be used for both flattree and livetree by removing the UT_TESTF_FLAT_TREE + * flag above. + */ +static int dm_test_pci_addr_live(struct unit_test_state *uts) +{ + struct udevice *swap1f, *swap1; + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f)); + ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f)); + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1)); + ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1)); + + return 0; +} +DM_TEST(dm_test_pci_addr_live, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | + UT_TESTF_LIVE_TREE); + +/* Test device_is_on_pci_bus() */ +static int dm_test_pci_on_bus(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &dev)); + ut_asserteq(true, device_is_on_pci_bus(dev)); + ut_asserteq(false, device_is_on_pci_bus(dev_get_parent(dev))); + ut_asserteq(true, device_is_on_pci_bus(dev)); + + return 0; +} +DM_TEST(dm_test_pci_on_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test support for multiple memory regions enabled via + * CONFIG_PCI_REGION_MULTI_ENTRY. When this feature is not enabled, + * only the last region of one type is stored. In this test-case, + * we have 2 memory regions, the first at 0x3000.0000 and the 2nd + * at 0x3100.0000. A correct test results now in BAR1 located at + * 0x3000.0000. + */ +static int dm_test_pci_region_multi(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong mem_addr; + + /* Test memory BAR1 on bus#1 */ + ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &dev)); + mem_addr = dm_pci_read_bar32(dev, 1); + ut_asserteq(mem_addr, 0x30000000); + + return 0; +} +DM_TEST(dm_test_pci_region_multi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pci_ep.c b/roms/u-boot/test/dm/pci_ep.c new file mode 100644 index 000000000..9b03b8bae --- /dev/null +++ b/roms/u-boot/test/dm/pci_ep.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Ramon Fried + */ + +#include <common.h> +#include <dm.h> +#include <hexdump.h> +#include <pci_ep.h> +#include <asm/io.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that sandbox PCI EP works correctly */ +static int dm_test_pci_ep_base(struct unit_test_state *uts) +{ + struct udevice *bus; + struct pci_bar tmp_bar; + struct pci_ep_header tmp_header; + int i; + + struct pci_ep_header ep_header = { + .vendorid = 0x1234, + .deviceid = 0x2020, + .revid = 1, + .interrupt_pin = PCI_INTERRUPT_INTA, + }; + + struct pci_bar bar = { + .phys_addr = 0x80000000, + .size = 0x100000, + .barno = BAR_0, + .flags = PCI_BASE_ADDRESS_MEM_TYPE_64 | + PCI_BASE_ADDRESS_MEM_PREFETCH, + }; + + ut_assertok(uclass_get_device(UCLASS_PCI_EP, 0, &bus)); + ut_assertnonnull(bus); + + ut_assertok(pci_ep_write_header(bus, 0, &ep_header)); + ut_assertok(pci_ep_read_header(bus, 0, &tmp_header)); + ut_asserteq_mem(&tmp_header, &ep_header, sizeof(ep_header)); + + ut_assertok(pci_ep_set_msi(bus, 0, 4)); + ut_asserteq(pci_ep_get_msi(bus, 0), 4); + + ut_assertok(pci_ep_set_msix(bus, 0, 360)); + ut_asserteq(pci_ep_get_msix(bus, 0), 360); + + ut_assertok(pci_ep_set_bar(bus, 0, &bar)); + + ut_assertok(pci_ep_read_bar(bus, 0, &tmp_bar, BAR_0)); + ut_asserteq_mem(&tmp_bar, &bar, sizeof(bar)); + + for (i = 0; i < 10; i++) + ut_assertok(pci_ep_raise_irq(bus, 0, 1, PCI_EP_IRQ_LEGACY)); + + ut_asserteq(sandbox_get_pci_ep_irq_count(bus), 10); + return 0; +} + +DM_TEST(dm_test_pci_ep_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + diff --git a/roms/u-boot/test/dm/phy.c b/roms/u-boot/test/dm/phy.c new file mode 100644 index 000000000..ecbd47bf1 --- /dev/null +++ b/roms/u-boot/test/dm/phy.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ + * Written by Jean-Jacques Hiblot <jjhiblot@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <generic-phy.h> +#include <log.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of the phy uclass */ +static int dm_test_phy_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct phy phy1_method1; + struct phy phy1_method2; + struct phy phy2; + struct phy phy3; + struct udevice *parent; + + /* Get the device using the phy device*/ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + /* + * Get the same phy port in 2 different ways and compare. + */ + ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1)) + ut_assertok(generic_phy_get_by_index(parent, 0, &phy1_method2)) + ut_asserteq(phy1_method1.id, phy1_method2.id); + + /* + * Get the second phy port. Check that the same phy provider (device) + * provides this 2nd phy port, but that the IDs are different + */ + ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2)) + ut_asserteq_ptr(phy1_method2.dev, phy2.dev); + ut_assert(phy1_method1.id != phy2.id); + + /* + * Get the third phy port. Check that the phy provider is different + */ + ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3)) + ut_assert(phy2.dev != phy3.dev); + + /* Try to get a non-existing phy */ + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PHY, 4, &dev)); + ut_asserteq(-ENODATA, generic_phy_get_by_name(parent, + "phy_not_existing", &phy1_method1)); + + return 0; +} +DM_TEST(dm_test_phy_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the phy uclass using the sandbox phy driver operations */ +static int dm_test_phy_ops(struct unit_test_state *uts) +{ + struct phy phy1; + struct phy phy2; + struct phy phy3; + struct udevice *parent; + + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + + ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1)); + ut_asserteq(0, phy1.id); + ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2)); + ut_asserteq(1, phy2.id); + ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3)); + ut_asserteq(0, phy3.id); + + /* test normal operations */ + ut_assertok(generic_phy_init(&phy1)); + ut_assertok(generic_phy_power_on(&phy1)); + ut_assertok(generic_phy_power_off(&phy1)); + + /* + * test operations after exit(). + * The sandbox phy driver does not allow it. + */ + ut_assertok(generic_phy_exit(&phy1)); + ut_assert(generic_phy_power_on(&phy1) != 0); + ut_assert(generic_phy_power_off(&phy1) != 0); + + /* + * test normal operations again (after re-init) + */ + ut_assertok(generic_phy_init(&phy1)); + ut_assertok(generic_phy_power_on(&phy1)); + ut_assertok(generic_phy_power_off(&phy1)); + + /* + * test calling unimplemented feature. + * The call is expected to succeed + */ + ut_assertok(generic_phy_reset(&phy1)); + + /* PHY2 has a known problem with power off */ + ut_assertok(generic_phy_init(&phy2)); + ut_assertok(generic_phy_power_on(&phy2)); + ut_asserteq(-EIO, generic_phy_power_off(&phy2)); + + /* PHY3 has a known problem with power off and power on */ + ut_assertok(generic_phy_init(&phy3)); + ut_asserteq(-EIO, generic_phy_power_off(&phy3)); + ut_asserteq(-EIO, generic_phy_power_off(&phy3)); + + return 0; +} +DM_TEST(dm_test_phy_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_phy_bulk(struct unit_test_state *uts) +{ + struct phy_bulk phys; + struct udevice *parent; + + /* test normal operations */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user1", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(2, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(0, generic_phy_power_on_bulk(&phys)); + ut_asserteq(0, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + /* has a known problem phy */ + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, + "gen_phy_user", &parent)); + + ut_assertok(generic_phy_get_bulk(parent, &phys)); + ut_asserteq(3, phys.count); + + ut_asserteq(0, generic_phy_init_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_on_bulk(&phys)); + ut_asserteq(-EIO, generic_phy_power_off_bulk(&phys)); + ut_asserteq(0, generic_phy_exit_bulk(&phys)); + + return 0; +} +DM_TEST(dm_test_phy_bulk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/phys2bus.c b/roms/u-boot/test/dm/phys2bus.c new file mode 100644 index 000000000..342f2fa8e --- /dev/null +++ b/roms/u-boot/test/dm/phys2bus.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> + */ + +#include <common.h> +#include <dm.h> +#include <mapmem.h> +#include <phys2bus.h> +#include <dm/device.h> +#include <dm/ofnode.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> + +static int dm_test_phys_to_bus(struct unit_test_state *uts) +{ + struct udevice *dev; + ofnode node; + + node = ofnode_path("/mmio-bus@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev)); + /* In this case it should be transparent, no dma-ranges in parent bus */ + ut_asserteq_addr((void*)0xfffffULL, (void*)dev_phys_to_bus(dev, 0xfffff)); + ut_asserteq_addr((void*)0xfffffULL, (void*)(ulong)dev_bus_to_phys(dev, 0xfffff)); + + node = ofnode_path("/mmio-bus@0/subnode@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev)); + ut_asserteq_addr((void*)0x100fffffULL, (void*)dev_phys_to_bus(dev, 0xfffff)); + ut_asserteq_addr((void*)0xfffffULL, (void*)(ulong)dev_bus_to_phys(dev, 0x100fffff)); + + return 0; +} +DM_TEST(dm_test_phys_to_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pinmux.c b/roms/u-boot/test/dm/pinmux.c new file mode 100644 index 000000000..265df4ccb --- /dev/null +++ b/roms/u-boot/test/dm/pinmux.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/pinctrl.h> +#include <dm/test.h> +#include <test/ut.h> + +static char buf[64]; +#define test_muxing(selector, expected) do { \ + ut_assertok(pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf))); \ + ut_asserteq_str(expected, (char *)&buf); \ +} while (0) + +#define test_name(selector, expected) do { \ + ut_assertok(pinctrl_get_pin_name(dev, selector, buf, sizeof(buf))); \ + ut_asserteq_str(expected, (char *)&buf); \ +} while (0) + +static int dm_test_pinmux(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL, "pinctrl", &dev)); + test_muxing(0, "UART TX."); + test_muxing(1, "UART RX."); + test_muxing(2, "I2S SCK."); + test_muxing(3, "I2S SD."); + test_muxing(4, "I2S WS."); + test_muxing(5, "GPIO0 bias-pull-up input-disable."); + test_muxing(6, "GPIO1 drive-open-drain."); + test_muxing(7, "GPIO2 bias-pull-down input-enable."); + test_muxing(8, "GPIO3 bias-disable."); + + ut_assertok(pinctrl_select_state(dev, "alternate")); + test_muxing(0, "I2C SCL drive-open-drain."); + test_muxing(1, "I2C SDA drive-open-drain."); + test_muxing(2, "SPI SCLK."); + test_muxing(3, "SPI MOSI."); + test_muxing(4, "SPI MISO."); + test_muxing(5, "SPI CS0."); + test_muxing(6, "SPI CS1."); + test_muxing(7, "GPIO2 bias-pull-down input-enable."); + test_muxing(8, "GPIO3 bias-disable."); + + ut_assertok(pinctrl_select_state(dev, "0")); + test_muxing(0, "I2C SCL drive-open-drain."); + test_muxing(1, "I2C SDA drive-open-drain."); + test_muxing(2, "I2S SCK."); + test_muxing(3, "I2S SD."); + test_muxing(4, "I2S WS."); + test_muxing(5, "GPIO0 bias-pull-up input-disable."); + test_muxing(6, "GPIO1 drive-open-drain."); + test_muxing(7, "GPIO2 bias-pull-down input-enable."); + test_muxing(8, "GPIO3 bias-disable."); + + return 0; +} + +DM_TEST(dm_test_pinmux, UT_TESTF_SCAN_FDT); + +static int dm_test_pinctrl_single(struct unit_test_state *uts) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_name(UCLASS_PINCTRL, + "pinctrl-single-no-width", &dev); + ut_asserteq(-EINVAL, ret); + ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_SPI, "spi@0", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL, + "pinctrl-single-pins", &dev)); + ut_asserteq(142, pinctrl_get_pins_count(dev)); + test_name(0, "PIN0"); + test_name(141, "PIN141"); + test_name(142, "Error"); + test_muxing(0, "0x00000000 0x00000000 UNCLAIMED"); + test_muxing(18, "0x00000048 0x00000006 pinmux_pwm_pins"); + test_muxing(28, "0x00000070 0x00000030 pinmux_uart0_pins"); + test_muxing(29, "0x00000074 0x00000000 pinmux_uart0_pins"); + test_muxing(100, "0x00000190 0x0000000c pinmux_spi0_pins"); + test_muxing(101, "0x00000194 0x0000000c pinmux_spi0_pins"); + test_muxing(102, "0x00000198 0x00000023 pinmux_spi0_pins"); + test_muxing(103, "0x0000019c 0x0000000c pinmux_spi0_pins"); + ret = pinctrl_get_pin_muxing(dev, 142, buf, sizeof(buf)); + ut_asserteq(-EINVAL, ret); + ut_assertok(uclass_get_device_by_name(UCLASS_I2C, "i2c@0", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_VIDEO, "lcd", &dev)); + ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL, + "pinctrl-single-bits", &dev)); + ut_asserteq(160, pinctrl_get_pins_count(dev)); + test_name(0, "PIN0"); + test_name(159, "PIN159"); + test_name(160, "Error"); + test_muxing(0, "0x00000000 0x00000000 UNCLAIMED"); + test_muxing(34, "0x00000010 0x00000200 pinmux_i2c0_pins"); + test_muxing(35, "0x00000010 0x00002000 pinmux_i2c0_pins"); + test_muxing(130, "0x00000040 0x00000200 pinmux_lcd_pins"); + test_muxing(131, "0x00000040 0x00002000 pinmux_lcd_pins"); + test_muxing(132, "0x00000040 0x00020000 pinmux_lcd_pins"); + test_muxing(133, "0x00000040 0x00200000 pinmux_lcd_pins"); + test_muxing(134, "0x00000040 0x02000000 pinmux_lcd_pins"); + test_muxing(135, "0x00000040 0x20000000 pinmux_lcd_pins"); + test_muxing(136, "0x00000044 0x00000002 pinmux_lcd_pins"); + test_muxing(137, "0x00000044 0x00000020 pinmux_lcd_pins"); + test_muxing(138, "0x00000044 0x00000200 pinmux_lcd_pins"); + test_muxing(139, "0x00000044 0x00002000 pinmux_lcd_pins"); + test_muxing(140, "0x00000044 0x00020000 pinmux_lcd_pins"); + test_muxing(141, "0x00000044 0x00200000 pinmux_lcd_pins"); + test_muxing(142, "0x00000044 0x02000000 pinmux_lcd_pins"); + test_muxing(143, "0x00000044 0x20000000 pinmux_lcd_pins"); + test_muxing(144, "0x00000048 0x00000002 pinmux_lcd_pins"); + test_muxing(145, "0x00000048 0x00000020 pinmux_lcd_pins"); + test_muxing(146, "0x00000048 0x00000000 UNCLAIMED"); + test_muxing(147, "0x00000048 0x00000000 UNCLAIMED"); + test_muxing(148, "0x00000048 0x00000000 UNCLAIMED"); + test_muxing(149, "0x00000048 0x00000000 UNCLAIMED"); + test_muxing(150, "0x00000048 0x02000000 pinmux_lcd_pins"); + test_muxing(151, "0x00000048 0x00000000 UNCLAIMED"); + test_muxing(152, "0x0000004c 0x00000002 pinmux_lcd_pins"); + test_muxing(153, "0x0000004c 0x00000020 pinmux_lcd_pins"); + test_muxing(154, "0x0000004c 0x00000000 UNCLAIMED"); + test_muxing(155, "0x0000004c 0x00000000 UNCLAIMED"); + test_muxing(156, "0x0000004c 0x00000000 UNCLAIMED"); + test_muxing(157, "0x0000004c 0x00000000 UNCLAIMED"); + test_muxing(158, "0x0000004c 0x02000000 pinmux_lcd_pins"); + test_muxing(159, "0x0000004c 0x00000000 UNCLAIMED"); + ret = pinctrl_get_pin_muxing(dev, 160, buf, sizeof(buf)); + ut_asserteq(-EINVAL, ret); + return 0; +} + +DM_TEST(dm_test_pinctrl_single, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pmc.c b/roms/u-boot/test/dm/pmc.c new file mode 100644 index 000000000..e70227e78 --- /dev/null +++ b/roms/u-boot/test/dm/pmc.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for power-management controller uclass (PMC) + * + * Copyright 2019 Google LLC + */ + +#include <common.h> +#include <dm.h> +#include <power/acpi_pmc.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Base test of the PMC uclass */ +static int dm_test_pmc_base(struct unit_test_state *uts) +{ + struct acpi_pmc_upriv *upriv; + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_ACPI_PMC, &dev)); + + ut_assertok(pmc_disable_tco(dev)); + ut_assertok(pmc_init(dev)); + ut_assertok(pmc_prev_sleep_state(dev)); + + /* Check some values to see that I/O works */ + upriv = dev_get_uclass_priv(dev); + ut_asserteq(0x24, upriv->gpe0_sts[1]); + ut_asserteq(0x64, upriv->tco1_sts); + + return 0; +} +DM_TEST(dm_test_pmc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pmic.c b/roms/u-boot/test/dm/pmic.c new file mode 100644 index 000000000..ce671202f --- /dev/null +++ b/roms/u-boot/test/dm/pmic.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the driver model pmic API + * + * Copyright (c) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <fsl_pmic.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <power/pmic.h> +#include <power/sandbox_pmic.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test PMIC get method */ + +static inline int power_pmic_get(struct unit_test_state *uts, char *name) +{ + struct udevice *dev; + + ut_assertok(pmic_get(name, &dev)); + ut_assertnonnull(dev); + + /* Check PMIC's name */ + ut_asserteq_str(name, dev->name); + + return 0; +} + +/* Test PMIC get method */ +static int dm_test_power_pmic_get(struct unit_test_state *uts) +{ + power_pmic_get(uts, "sandbox_pmic"); + + return 0; +} +DM_TEST(dm_test_power_pmic_get, UT_TESTF_SCAN_FDT); + +/* PMIC get method - MC34708 - for 3 bytes transmission */ +static int dm_test_power_pmic_mc34708_get(struct unit_test_state *uts) +{ + power_pmic_get(uts, "pmic@41"); + + return 0; +} + +DM_TEST(dm_test_power_pmic_mc34708_get, UT_TESTF_SCAN_FDT); + +/* Test PMIC I/O */ +static int dm_test_power_pmic_io(struct unit_test_state *uts) +{ + const char *name = "sandbox_pmic"; + uint8_t out_buffer, in_buffer; + struct udevice *dev; + int reg_count, i; + + ut_assertok(pmic_get(name, &dev)); + + reg_count = pmic_reg_count(dev); + ut_asserteq(reg_count, SANDBOX_PMIC_REG_COUNT); + + /* + * Test PMIC I/O - write and read a loop counter. + * usually we can't write to all PMIC's registers in the real hardware, + * but we can to the sandbox pmic. + */ + for (i = 0; i < reg_count; i++) { + out_buffer = i; + ut_assertok(pmic_write(dev, i, &out_buffer, 1)); + ut_assertok(pmic_read(dev, i, &in_buffer, 1)); + ut_asserteq(out_buffer, in_buffer); + } + + return 0; +} +DM_TEST(dm_test_power_pmic_io, UT_TESTF_SCAN_FDT); + +#define MC34708_PMIC_REG_COUNT 64 +#define MC34708_PMIC_TEST_VAL 0x125534 +static int dm_test_power_pmic_mc34708_regs_check(struct unit_test_state *uts) +{ + struct udevice *dev; + int reg_count; + + ut_assertok(pmic_get("pmic@41", &dev)); + + /* Check number of PMIC registers */ + reg_count = pmic_reg_count(dev); + ut_asserteq(reg_count, MC34708_PMIC_REG_COUNT); + + return 0; +} + +DM_TEST(dm_test_power_pmic_mc34708_regs_check, UT_TESTF_SCAN_FDT); + +static int dm_test_power_pmic_mc34708_rw_val(struct unit_test_state *uts) +{ + struct udevice *dev; + int val; + + ut_assertok(pmic_get("pmic@41", &dev)); + + /* Check if single 3 byte read is successful */ + val = pmic_reg_read(dev, REG_POWER_CTL2); + ut_asserteq(val, 0x422100); + + /* Check if RW works */ + val = 0; + ut_assertok(pmic_reg_write(dev, REG_RTC_TIME, val)); + ut_assertok(pmic_reg_write(dev, REG_RTC_TIME, MC34708_PMIC_TEST_VAL)); + val = pmic_reg_read(dev, REG_RTC_TIME); + ut_asserteq(val, MC34708_PMIC_TEST_VAL); + + pmic_clrsetbits(dev, REG_POWER_CTL2, 0x3 << 8, 1 << 9); + val = pmic_reg_read(dev, REG_POWER_CTL2); + ut_asserteq(val, (0x422100 & ~(0x3 << 8)) | (1 << 9)); + + return 0; +} + +DM_TEST(dm_test_power_pmic_mc34708_rw_val, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/power-domain.c b/roms/u-boot/test/dm/power-domain.c new file mode 100644 index 000000000..8604b5d72 --- /dev/null +++ b/roms/u-boot/test/dm/power-domain.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <dm/test.h> +#include <asm/power-domain.h> +#include <test/test.h> +#include <test/ut.h> + +/* This must match the specifier for power-domains in the DT node */ +#define TEST_POWER_DOMAIN 2 + +static int dm_test_power_domain(struct unit_test_state *uts) +{ + struct udevice *dev_power_domain; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN, + "power-domain", + &dev_power_domain)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "power-domain-test", + &dev_test)); + ut_asserteq(1, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + ut_assertok(sandbox_power_domain_test_get(dev_test)); + + ut_assertok(sandbox_power_domain_test_on(dev_test)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(1, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(sandbox_power_domain_test_off(dev_test)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0)); + ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, + TEST_POWER_DOMAIN)); + + ut_assertok(sandbox_power_domain_test_free(dev_test)); + + return 0; +} +DM_TEST(dm_test_power_domain, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/pwm.c b/roms/u-boot/test/dm/pwm.c new file mode 100644 index 000000000..b624cf3d6 --- /dev/null +++ b/roms/u-boot/test/dm/pwm.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2017 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <pwm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the pwm uclass */ +static int dm_test_pwm_base(struct unit_test_state *uts) +{ + struct udevice *dev; + uint period_ns; + uint duty_ns; + bool enable; + bool polarity; + + ut_assertok(uclass_get_device(UCLASS_PWM, 0, &dev)); + ut_assertnonnull(dev); + ut_assertok(pwm_set_config(dev, 0, 100, 50)); + ut_assertok(pwm_set_enable(dev, 0, true)); + ut_assertok(pwm_set_enable(dev, 1, true)); + ut_assertok(pwm_set_enable(dev, 2, true)); + ut_asserteq(-ENOSPC, pwm_set_enable(dev, 3, true)); + ut_assertok(pwm_set_invert(dev, 0, true)); + + ut_assertok(pwm_set_config(dev, 2, 100, 50)); + ut_assertok(sandbox_pwm_get_config(dev, 2, &period_ns, &duty_ns, + &enable, &polarity)); + ut_asserteq(period_ns, 4096); + ut_asserteq(duty_ns, 50 * 4096 / 100); + + ut_assertok(uclass_get_device(UCLASS_PWM, 1, &dev)); + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PWM, 2, &dev)); + + return 0; +} +DM_TEST(dm_test_pwm_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/qfw.c b/roms/u-boot/test/dm/qfw.c new file mode 100644 index 000000000..f3f356898 --- /dev/null +++ b/roms/u-boot/test/dm/qfw.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Asherah Connor <ashe@kivikakk.ee> + */ + +#include <common.h> +#include <qfw.h> +#include <dm.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/ut.h> + +/* + * Exercise the device enough to be satisfied the initialisation and DMA + * interfaces work. + */ + +static int dm_test_qfw_cpus(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_asserteq(5, qfw_online_cpus(dev)); + + return 0; +} + +DM_TEST(dm_test_qfw_cpus, UT_TESTF_SCAN_PDATA); + +static int dm_test_qfw_firmware_list(struct unit_test_state *uts) +{ + struct udevice *dev; + struct fw_file *file; + + ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev)); + ut_assertok(qfw_read_firmware_list(dev)); + ut_assertok_ptr((file = qfw_find_file(dev, "test-one"))); + + return 0; +} + +DM_TEST(dm_test_qfw_firmware_list, UT_TESTF_SCAN_PDATA); diff --git a/roms/u-boot/test/dm/ram.c b/roms/u-boot/test/dm/ram.c new file mode 100644 index 000000000..f62434313 --- /dev/null +++ b/roms/u-boot/test/dm/ram.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <ram.h> +#include <asm/global_data.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Basic test of the ram uclass */ +static int dm_test_ram_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct ram_info info; + + ut_assertok(uclass_get_device(UCLASS_RAM, 0, &dev)); + ut_assertok(ram_get_info(dev, &info)); + ut_asserteq(0, info.base); + ut_asserteq(gd->ram_size, info.size); + + return 0; +} +DM_TEST(dm_test_ram_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/read.c b/roms/u-boot/test/dm/read.c new file mode 100644 index 000000000..7768aa296 --- /dev/null +++ b/roms/u-boot/test/dm/read.c @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de> + */ + +#include <common.h> +#include <dm.h> +#include <dm/device.h> +#include <dm/ofnode.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> + +static int dm_test_dma_ranges(struct unit_test_state *uts) +{ + struct udevice *dev; + phys_addr_t cpu; + dma_addr_t bus; + ofnode node; + u64 size; + + /* dma-ranges are on the device's node */ + node = ofnode_path("/mmio-bus@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev)); + ut_assertok(dev_get_dma_range(dev, &cpu, &bus, &size)); + ut_asserteq_64(0x40000, size); + ut_asserteq_64(0x0, cpu); + ut_asserteq_64(0x10000000, bus); + + /* dma-ranges are on the bus' node */ + node = ofnode_path("/mmio-bus@0/subnode@0"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev)); + ut_assertok(dev_get_dma_range(dev, &cpu, &bus, &size)); + ut_asserteq_64(0x40000, size); + ut_asserteq_64(0x0, cpu); + ut_asserteq_64(0x10000000, bus); + + /* No dma-ranges available */ + node = ofnode_path("/mmio-bus@1"); + ut_assert(ofnode_valid(node)); + ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev)); + ut_asserteq(-ENOENT, dev_get_dma_range(dev, &cpu, &bus, &size)); + + return 0; +} +DM_TEST(dm_test_dma_ranges, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/regmap.c b/roms/u-boot/test/dm/regmap.c new file mode 100644 index 000000000..04bb1645d --- /dev/null +++ b/roms/u-boot/test/dm/regmap.c @@ -0,0 +1,387 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <mapmem.h> +#include <regmap.h> +#include <syscon.h> +#include <rand.h> +#include <asm/test.h> +#include <dm/test.h> +#include <dm/devres.h> +#include <linux/err.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of register maps */ +static int dm_test_regmap_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + ofnode node; + int i; + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + ut_asserteq(1, map->range_count); + ut_asserteq(0x10, map->ranges[0].start); + ut_asserteq(16, map->ranges[0].size); + ut_asserteq(0x10, map_to_sysmem(regmap_get_range(map, 0))); + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + ut_asserteq(4, map->range_count); + ut_asserteq(0x20, map->ranges[0].start); + for (i = 0; i < 4; i++) { + const unsigned long addr = 0x20 + 8 * i; + + ut_asserteq(addr, map->ranges[i].start); + ut_asserteq(5 + i, map->ranges[i].size); + ut_asserteq(addr, map_to_sysmem(regmap_get_range(map, i))); + } + + /* Check that we can't pretend a different device is a syscon */ + ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev)); + map = syscon_get_regmap(dev); + ut_asserteq_ptr(ERR_PTR(-ENOEXEC), map); + + /* A different device can be a syscon by using Linux-compat API */ + node = ofnode_path("/syscon@2"); + ut_assert(ofnode_valid(node)); + + map = syscon_node_to_regmap(node); + ut_assertok_ptr(map); + ut_asserteq(4, map->range_count); + ut_asserteq(0x40, map->ranges[0].start); + for (i = 0; i < 4; i++) { + const unsigned long addr = 0x40 + 8 * i; + + ut_asserteq(addr, map->ranges[i].start); + ut_asserteq(5 + i, map->ranges[i].size); + ut_asserteq(addr, map_to_sysmem(regmap_get_range(map, i))); + } + + return 0; +} +DM_TEST(dm_test_regmap_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test we can access a regmap through syscon */ +static int dm_test_regmap_syscon(struct unit_test_state *uts) +{ + struct regmap *map; + + map = syscon_get_regmap_by_driver_data(SYSCON0); + ut_assertok_ptr(map); + ut_asserteq(1, map->range_count); + + map = syscon_get_regmap_by_driver_data(SYSCON1); + ut_assertok_ptr(map); + ut_asserteq(4, map->range_count); + + map = syscon_get_regmap_by_driver_data(SYSCON_COUNT); + ut_asserteq_ptr(ERR_PTR(-ENODEV), map); + + ut_asserteq(0x10, map_to_sysmem(syscon_get_first_range(SYSCON0))); + ut_asserteq(0x20, map_to_sysmem(syscon_get_first_range(SYSCON1))); + ut_asserteq_ptr(ERR_PTR(-ENODEV), + syscon_get_first_range(SYSCON_COUNT)); + + return 0; +} + +DM_TEST(dm_test_regmap_syscon, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Read/Write/Modify test */ +static int dm_test_regmap_rw(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + uint reg; + + sandbox_set_enable_memio(true); + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + + ut_assertok(regmap_write(map, 0, 0xcacafafa)); + ut_assertok(regmap_write(map, 5, 0x55aa2211)); + + ut_assertok(regmap_read(map, 0, ®)); + ut_asserteq(0xcacafafa, reg); + ut_assertok(regmap_read(map, 5, ®)); + ut_asserteq(0x55aa2211, reg); + + ut_assertok(regmap_read(map, 0, ®)); + ut_asserteq(0xcacafafa, reg); + ut_assertok(regmap_update_bits(map, 0, 0xff00ff00, 0x55aa2211)); + ut_assertok(regmap_read(map, 0, ®)); + ut_asserteq(0x55ca22fa, reg); + ut_assertok(regmap_update_bits(map, 5, 0x00ff00ff, 0xcacafada)); + ut_assertok(regmap_read(map, 5, ®)); + ut_asserteq(0x55ca22da, reg); + + return 0; +} + +DM_TEST(dm_test_regmap_rw, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Get/Set test */ +static int dm_test_regmap_getset(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + uint reg; + struct layout { + u32 val0; + u32 val1; + u32 val2; + u32 val3; + }; + + sandbox_set_enable_memio(true); + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + + regmap_set(map, struct layout, val0, 0xcacafafa); + regmap_set(map, struct layout, val3, 0x55aa2211); + + ut_assertok(regmap_get(map, struct layout, val0, ®)); + ut_asserteq(0xcacafafa, reg); + ut_assertok(regmap_get(map, struct layout, val3, ®)); + ut_asserteq(0x55aa2211, reg); + + return 0; +} + +DM_TEST(dm_test_regmap_getset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Read polling test */ +static int dm_test_regmap_poll(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + uint reg; + unsigned long start; + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + map = syscon_get_regmap(dev); + ut_assertok_ptr(map); + + start = get_timer(0); + + ut_assertok(regmap_write(map, 0, 0x0)); + ut_asserteq(-ETIMEDOUT, + regmap_read_poll_timeout_test(map, 0, reg, + (reg == 0xcacafafa), + 1, 5 * CONFIG_SYS_HZ, + 5 * CONFIG_SYS_HZ)); + + ut_assert(get_timer(start) > (5 * CONFIG_SYS_HZ)); + + return 0; +} + +DM_TEST(dm_test_regmap_poll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +struct regmaptest_priv { + struct regmap *cfg_regmap; /* For testing regmap_config options. */ + struct regmap *fld_regmap; /* For testing regmap fields. */ + struct regmap_field **fields; +}; + +static const struct reg_field field_cfgs[] = { + { + .reg = 0, + .lsb = 0, + .msb = 6, + }, + { + .reg = 2, + .lsb = 4, + .msb = 12, + }, + { + .reg = 2, + .lsb = 12, + .msb = 15, + } +}; + +#define REGMAP_TEST_BUF_START 0 +#define REGMAP_TEST_BUF_SZ 5 + +static int remaptest_probe(struct udevice *dev) +{ + struct regmaptest_priv *priv = dev_get_priv(dev); + struct regmap *regmap; + struct regmap_field *field; + struct regmap_config cfg; + int i; + static const int n = ARRAY_SIZE(field_cfgs); + + /* + * To exercise all the regmap config options, create a regmap that + * points to a custom memory area instead of the one defined in device + * tree. Use 2-byte elements. To allow directly indexing into the + * elements, use an offset shift of 1. So, accessing offset 1 gets the + * element at index 1 at memory location 2. + * + * REGMAP_TEST_BUF_SZ is the number of elements, so we need to multiply + * it by 2 because r_size expects number of bytes. + */ + cfg.reg_offset_shift = 1; + cfg.r_start = REGMAP_TEST_BUF_START; + cfg.r_size = REGMAP_TEST_BUF_SZ * 2; + cfg.width = REGMAP_SIZE_16; + + regmap = devm_regmap_init(dev, NULL, NULL, &cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + priv->cfg_regmap = regmap; + + memset(&cfg, 0, sizeof(struct regmap_config)); + cfg.width = REGMAP_SIZE_16; + + regmap = devm_regmap_init(dev, NULL, NULL, &cfg); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + priv->fld_regmap = regmap; + + priv->fields = devm_kzalloc(dev, sizeof(struct regmap_field *) * n, + GFP_KERNEL); + if (!priv->fields) + return -ENOMEM; + + for (i = 0 ; i < n; i++) { + field = devm_regmap_field_alloc(dev, priv->fld_regmap, + field_cfgs[i]); + if (IS_ERR(field)) + return PTR_ERR(field); + priv->fields[i] = field; + } + + return 0; +} + +static const struct udevice_id regmaptest_ids[] = { + { .compatible = "sandbox,regmap_test" }, + { } +}; + +U_BOOT_DRIVER(regmap_test) = { + .name = "regmaptest_drv", + .of_match = regmaptest_ids, + .id = UCLASS_NOP, + .probe = remaptest_probe, + .priv_auto = sizeof(struct regmaptest_priv), +}; + +static int dm_test_devm_regmap(struct unit_test_state *uts) +{ + int i = 0; + u16 val; + void *valp = &val; + u16 pattern[REGMAP_TEST_BUF_SZ]; + u16 *buffer; + struct udevice *dev; + struct regmaptest_priv *priv; + + sandbox_set_enable_memio(true); + + /* + * Map the memory area the regmap should point to so we can make sure + * the writes actually go to that location. + */ + buffer = map_physmem(REGMAP_TEST_BUF_START, + REGMAP_TEST_BUF_SZ * 2, MAP_NOCACHE); + + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0", + &dev)); + priv = dev_get_priv(dev); + + for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) { + pattern[i] = i * 0x87654321; + ut_assertok(regmap_write(priv->cfg_regmap, i, pattern[i])); + } + for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) { + ut_assertok(regmap_read(priv->cfg_regmap, i, valp)); + ut_asserteq(val, buffer[i]); + ut_asserteq(val, pattern[i]); + } + + ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, REGMAP_TEST_BUF_SZ, + val)); + ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, REGMAP_TEST_BUF_SZ, + valp)); + ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, -1, val)); + ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, -1, valp)); + + return 0; +} +DM_TEST(dm_test_devm_regmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int test_one_field(struct unit_test_state *uts, + struct regmap *regmap, + struct regmap_field *field, + struct reg_field field_cfg) +{ + int j; + unsigned int val; + int mask = (1 << (field_cfg.msb - field_cfg.lsb + 1)) - 1; + int shift = field_cfg.lsb; + + ut_assertok(regmap_write(regmap, field_cfg.reg, 0)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0, val); + + for (j = 0; j <= mask; j++) { + ut_assertok(regmap_field_write(field, j)); + ut_assertok(regmap_field_read(field, &val)); + ut_asserteq(j, val); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(j << shift, val); + } + + ut_assertok(regmap_field_write(field, mask + 1)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0, val); + + ut_assertok(regmap_field_write(field, 0xFFFF)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(mask << shift, val); + + ut_assertok(regmap_write(regmap, field_cfg.reg, 0xFFFF)); + ut_assertok(regmap_field_write(field, 0)); + ut_assertok(regmap_read(regmap, field_cfg.reg, &val)); + ut_asserteq(0xFFFF & ~(mask << shift), val); + return 0; +} + +static int dm_test_devm_regmap_field(struct unit_test_state *uts) +{ + int i, rc; + struct udevice *dev; + struct regmaptest_priv *priv; + + ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0", + &dev)); + priv = dev_get_priv(dev); + + sandbox_set_enable_memio(true); + for (i = 0 ; i < ARRAY_SIZE(field_cfgs); i++) { + rc = test_one_field(uts, priv->fld_regmap, priv->fields[i], + field_cfgs[i]); + if (rc) + break; + } + + return 0; +} +DM_TEST(dm_test_devm_regmap_field, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/regulator.c b/roms/u-boot/test/dm/regulator.c new file mode 100644 index 000000000..86f4862d9 --- /dev/null +++ b/roms/u-boot/test/dm/regulator.c @@ -0,0 +1,404 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for the driver model regulator API + * + * Copyright (c) 2015 Samsung Electronics + * Przemyslaw Marczak <p.marczak@samsung.com> + */ + +#include <common.h> +#include <errno.h> +#include <dm.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/util.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/sandbox_pmic.h> +#include <test/test.h> +#include <test/ut.h> + +enum { + BUCK1, + BUCK2, + BUCK3, + LDO1, + LDO2, + OUTPUT_COUNT, +}; + +enum { + DEVNAME = 0, + PLATNAME, + OUTPUT_NAME_COUNT, +}; + +static const char *regulator_names[OUTPUT_COUNT][OUTPUT_NAME_COUNT] = { + /* devname, platname */ + { SANDBOX_BUCK1_DEVNAME, SANDBOX_BUCK1_PLATNAME }, + { SANDBOX_BUCK2_DEVNAME, SANDBOX_BUCK2_PLATNAME }, + { SANDBOX_BUCK3_DEVNAME, SANDBOX_BUCK3_PLATNAME }, + { SANDBOX_LDO1_DEVNAME, SANDBOX_LDO1_PLATNAME}, + { SANDBOX_LDO2_DEVNAME, SANDBOX_LDO2_PLATNAME}, +}; + +/* Test regulator get method */ +static int dm_test_power_regulator_get(struct unit_test_state *uts) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev_by_devname; + struct udevice *dev_by_platname; + const char *devname; + const char *platname; + int i; + + for (i = 0; i < OUTPUT_COUNT; i++) { + /* + * Do the test for each regulator's devname and platname, + * which are related to a single device. + */ + devname = regulator_names[i][DEVNAME]; + platname = regulator_names[i][PLATNAME]; + + /* + * Check, that regulator_get_by_devname() function, returns + * a device with the name equal to the requested one. + */ + ut_assertok(regulator_get_by_devname(devname, &dev_by_devname)); + ut_asserteq_str(devname, dev_by_devname->name); + + /* + * Check, that regulator_get_by_platname() function, returns + * a device with the name equal to the requested one. + */ + ut_assertok(regulator_get_by_platname(platname, &dev_by_platname)); + uc_pdata = dev_get_uclass_plat(dev_by_platname); + ut_assert(uc_pdata); + ut_asserteq_str(platname, uc_pdata->name); + + /* + * Check, that the pointers returned by both get functions, + * points to the same regulator device. + */ + ut_asserteq_ptr(dev_by_devname, dev_by_platname); + } + + return 0; +} +DM_TEST(dm_test_power_regulator_get, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get Voltage method */ +static int dm_test_power_regulator_set_get_voltage(struct unit_test_state *uts) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + const char *platname; + int val_set, val_get; + + /* Set and get Voltage of BUCK1 - set to 'min' constraint */ + platname = regulator_names[BUCK1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + + val_set = uc_pdata->min_uV; + ut_assertok(regulator_set_value(dev, val_set)); + + val_get = regulator_get_value(dev); + ut_assert(val_get >= 0); + + ut_asserteq(val_set, val_get); + + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_voltage, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get Current method */ +static int dm_test_power_regulator_set_get_current(struct unit_test_state *uts) +{ + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + const char *platname; + int val_set, val_get; + + /* Set and get the Current of LDO1 - set to 'min' constraint */ + platname = regulator_names[LDO1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + + val_set = uc_pdata->min_uA; + ut_assertok(regulator_set_current(dev, val_set)); + + val_get = regulator_get_current(dev); + ut_assert(val_get >= 0); + + ut_asserteq(val_set, val_get); + + /* Check LDO2 current limit constraints - should be -ENODATA */ + platname = regulator_names[LDO2][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + ut_asserteq(-ENODATA, uc_pdata->min_uA); + ut_asserteq(-ENODATA, uc_pdata->max_uA); + + /* Try set the Current of LDO2 - should return -ENOSYS */ + ut_asserteq(-ENOSYS, regulator_set_current(dev, 0)); + + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_current, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get Enable method */ +static int dm_test_power_regulator_set_get_enable(struct unit_test_state *uts) +{ + const char *platname; + struct udevice *dev; + bool val_set = true; + + /* Set the Enable of LDO1 - default is disabled */ + platname = regulator_names[LDO1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + ut_assertok(regulator_set_enable(dev, val_set)); + + /* Get the Enable state of LDO1 and compare it with the requested one */ + ut_asserteq(regulator_get_enable(dev), val_set); + + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_enable, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get enable if allowed method */ +static +int dm_test_power_regulator_set_enable_if_allowed(struct unit_test_state *uts) +{ + const char *platname; + struct udevice *dev, *dev_autoset; + bool val_set = false; + + /* Get BUCK1 - always on regulator */ + platname = regulator_names[BUCK1][PLATNAME]; + ut_assertok(regulator_autoset_by_name(platname, &dev_autoset)); + ut_assertok(regulator_get_by_platname(platname, &dev)); + + /* Try disabling always-on regulator */ + ut_assertok(regulator_set_enable_if_allowed(dev, val_set)); + ut_asserteq(regulator_get_enable(dev), !val_set); + + return 0; +} +DM_TEST(dm_test_power_regulator_set_enable_if_allowed, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get mode method */ +static int dm_test_power_regulator_set_get_mode(struct unit_test_state *uts) +{ + const char *platname; + struct udevice *dev; + int val_set = LDO_OM_SLEEP; + + /* Set the mode id to LDO_OM_SLEEP of LDO1 - default is LDO_OM_OFF */ + platname = regulator_names[LDO1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + ut_assertok(regulator_set_mode(dev, val_set)); + + /* Get the mode id of LDO1 and compare it with the requested one */ + ut_asserteq(regulator_get_mode(dev), val_set); + + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_mode, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get suspend Voltage method */ +static int dm_test_power_regulator_set_get_suspend_voltage(struct unit_test_state *uts) +{ + struct dm_regulator_uclass_plat *uc_pdata; + const struct dm_regulator_ops *ops; + struct udevice *dev; + const char *platname; + int val_set, val_get; + + /* Set and get Voltage of BUCK1 - set to 'min' constraint */ + platname = regulator_names[BUCK1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + + ops = dev_get_driver_ops(dev); + + if (ops->set_suspend_value && ops->get_suspend_value) { + val_set = uc_pdata->suspend_uV; + ut_assertok(regulator_set_suspend_value(dev, val_set)); + val_get = regulator_get_suspend_value(dev); + ut_assert(val_get >= 0); + + ut_asserteq(val_set, val_get); + } + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_suspend_voltage, UT_TESTF_SCAN_FDT); + +/* Test regulator set and get suspend Enable method */ +static int dm_test_power_regulator_set_get_suspend_enable(struct unit_test_state *uts) +{ + const struct dm_regulator_ops *ops; + const char *platname; + struct udevice *dev; + bool val_set = true; + + /* Set the Enable of LDO1 - default is disabled */ + platname = regulator_names[LDO1][PLATNAME]; + ut_assertok(regulator_get_by_platname(platname, &dev)); + + ops = dev_get_driver_ops(dev); + + if (ops->set_suspend_enable && ops->get_suspend_enable) { + ut_assertok(regulator_set_suspend_enable(dev, val_set)); + + /* + * Get the Enable state of LDO1 and + * compare it with the requested one + */ + ut_asserteq(regulator_get_suspend_enable(dev), val_set); + } + return 0; +} +DM_TEST(dm_test_power_regulator_set_get_suspend_enable, UT_TESTF_SCAN_FDT); + +/* Test regulator autoset method */ +static int dm_test_power_regulator_autoset(struct unit_test_state *uts) +{ + const char *platname; + struct udevice *dev, *dev_autoset; + + /* + * Test the BUCK1 with fdt properties + * - min-microvolt = max-microvolt = 1200000 + * - min-microamp = max-microamp = 200000 + * - always-on = set + * - boot-on = not set + * Expected output state: uV=1200000; uA=200000; output enabled + */ + platname = regulator_names[BUCK1][PLATNAME]; + ut_assertok(regulator_autoset_by_name(platname, &dev_autoset)); + + /* Check, that the returned device is proper */ + ut_assertok(regulator_get_by_platname(platname, &dev)); + ut_asserteq_ptr(dev, dev_autoset); + + /* Check the setup after autoset */ + ut_asserteq(regulator_get_value(dev), + SANDBOX_BUCK1_AUTOSET_EXPECTED_UV); + ut_asserteq(regulator_get_current(dev), + SANDBOX_BUCK1_AUTOSET_EXPECTED_UA); + ut_asserteq(regulator_get_enable(dev), + SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE); + + return 0; +} +DM_TEST(dm_test_power_regulator_autoset, UT_TESTF_SCAN_FDT); + +/* + * Struct setting: to keep the expected output settings. + * @voltage: Voltage value [uV] + * @current: Current value [uA] + * @enable: output enable state: true/false + */ +struct setting { + int voltage; + int current; + bool enable; +}; + +/* + * platname_list: an array of regulator platform names. + * For testing regulator_list_autoset() for outputs: + * - LDO1 + * - LDO2 + */ +static const char *platname_list[] = { + SANDBOX_LDO1_PLATNAME, + SANDBOX_LDO2_PLATNAME, + NULL, +}; + +/* + * expected_setting_list: an array of regulator output setting, expected after + * call of the regulator_list_autoset() for the "platname_list" array. + * For testing results of regulator_list_autoset() for outputs: + * - LDO1 + * - LDO2 + * The settings are defined in: include/power/sandbox_pmic.h + */ +static const struct setting expected_setting_list[] = { + [0] = { /* LDO1 */ + .voltage = SANDBOX_LDO1_AUTOSET_EXPECTED_UV, + .current = SANDBOX_LDO1_AUTOSET_EXPECTED_UA, + .enable = SANDBOX_LDO1_AUTOSET_EXPECTED_ENABLE, + }, + [1] = { /* LDO2 */ + .voltage = SANDBOX_LDO2_AUTOSET_EXPECTED_UV, + .current = SANDBOX_LDO2_AUTOSET_EXPECTED_UA, + .enable = SANDBOX_LDO2_AUTOSET_EXPECTED_ENABLE, + }, +}; + +static int list_count = ARRAY_SIZE(expected_setting_list); + +/* Test regulator list autoset method */ +static int dm_test_power_regulator_autoset_list(struct unit_test_state *uts) +{ + struct udevice *dev_list[2], *dev; + int i; + + /* + * Test the settings of the regulator list: + * LDO1 with fdt properties: + * - min-microvolt = max-microvolt = 1800000 + * - min-microamp = max-microamp = 100000 + * - always-on = not set + * - boot-on = set + * Expected output state: uV=1800000; uA=100000; output enabled + * + * LDO2 with fdt properties: + * - min-microvolt = max-microvolt = 3300000 + * - always-on = not set + * - boot-on = not set + * Expected output state: uV=300000(default); output disabled(default) + * The expected settings are defined in: include/power/sandbox_pmic.h. + */ + ut_assertok(regulator_list_autoset(platname_list, dev_list, false)); + + for (i = 0; i < list_count; i++) { + /* Check, that the returned device is non-NULL */ + ut_assert(dev_list[i]); + + /* Check, that the returned device is proper */ + ut_assertok(regulator_get_by_platname(platname_list[i], &dev)); + ut_asserteq_ptr(dev_list[i], dev); + + /* Check, that regulator output Voltage value is as expected */ + ut_asserteq(regulator_get_value(dev_list[i]), + expected_setting_list[i].voltage); + + /* Check, that regulator output Current value is as expected */ + ut_asserteq(regulator_get_current(dev_list[i]), + expected_setting_list[i].current); + + /* Check, that regulator output Enable state is as expected */ + ut_asserteq(regulator_get_enable(dev_list[i]), + expected_setting_list[i].enable); + } + + return 0; +} +DM_TEST(dm_test_power_regulator_autoset_list, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/remoteproc.c b/roms/u-boot/test/dm/remoteproc.c new file mode 100644 index 000000000..1cc07bc80 --- /dev/null +++ b/roms/u-boot/test/dm/remoteproc.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + */ +#include <common.h> +#include <dm.h> +#include <elf.h> +#include <errno.h> +#include <remoteproc.h> +#include <asm/io.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/** + * dm_test_remoteproc_base() - test the operations after initializations + * @uts: unit test state + * + * Return: 0 if test passed, else error + */ +static int dm_test_remoteproc_base(struct unit_test_state *uts) +{ + if (!rproc_is_initialized()) + ut_assertok(rproc_init()); + + /* Ensure we are initialized */ + ut_asserteq(true, rproc_is_initialized()); + + + /* platform data device 1 */ + ut_assertok(rproc_stop(0)); + ut_assertok(rproc_reset(0)); + /* -> invalid attempt tests */ + ut_asserteq(-EINVAL, rproc_start(0)); + ut_asserteq(-EINVAL, rproc_ping(0)); + /* Valid tests */ + ut_assertok(rproc_load(0, 1, 0)); + ut_assertok(rproc_start(0)); + ut_assertok(rproc_is_running(0)); + ut_assertok(rproc_ping(0)); + ut_assertok(rproc_reset(0)); + ut_assertok(rproc_stop(0)); + + /* dt device device 1 */ + ut_assertok(rproc_stop(1)); + ut_assertok(rproc_reset(1)); + ut_assertok(rproc_load(1, 1, 0)); + ut_assertok(rproc_start(1)); + ut_assertok(rproc_is_running(1)); + ut_assertok(rproc_ping(1)); + ut_assertok(rproc_reset(1)); + ut_assertok(rproc_stop(1)); + + /* dt device device 2 */ + ut_assertok(rproc_stop(0)); + ut_assertok(rproc_reset(0)); + /* -> invalid attempt tests */ + ut_asserteq(-EINVAL, rproc_start(0)); + ut_asserteq(-EINVAL, rproc_ping(0)); + /* Valid tests */ + ut_assertok(rproc_load(2, 1, 0)); + ut_assertok(rproc_start(2)); + ut_assertok(rproc_is_running(2)); + ut_assertok(rproc_ping(2)); + ut_assertok(rproc_reset(2)); + ut_assertok(rproc_stop(2)); + + return 0; +} +DM_TEST(dm_test_remoteproc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +#define DEVICE_TO_PHYSICAL_OFFSET 0x1000 +/** + * dm_test_remoteproc_elf() - test the ELF operations + * @uts: unit test state + * + * Return: 0 if test passed, else error + */ +static int dm_test_remoteproc_elf(struct unit_test_state *uts) +{ + u8 valid_elf32[] = { + /* @0x00 - ELF HEADER - */ + /* ELF magic */ + 0x7f, 0x45, 0x4c, 0x46, + /* 32 Bits */ + 0x01, + /* Endianness */ +#ifdef __LITTLE_ENDIAN + 0x01, +#else + 0x02, +#endif + /* Version */ + 0x01, + /* Padding */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Type : executable */ + 0x02, 0x00, + /* Machine: ARM */ + 0x28, 0x00, + /* Version */ + 0x01, 0x00, 0x00, 0x00, + /* Entry */ + 0x00, 0x00, 0x00, 0x08, + /* phoff (program header offset @ 0x40)*/ + 0x40, 0x00, 0x00, 0x00, + /* shoff (section header offset @ 0x90) */ + 0x90, 0x00, 0x00, 0x00, + /* flags */ + 0x00, 0x00, 0x00, 0x00, + /* ehsize (elf header size = 0x34) */ + 0x34, 0x00, + /* phentsize (program header size = 0x20) */ + 0x20, 0x00, + /* phnum (program header number : 1) */ + 0x01, 0x00, + /* shentsize (section header size : 40 bytes) */ + 0x28, 0x00, + /* shnum (section header number: 3) */ + 0x02, 0x00, + /* shstrndx (section header name section index: 1) */ + 0x01, 0x00, + /* padding */ + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + + /* @0x40 - PROGRAM HEADER TABLE - */ + /* type : PT_LOAD */ + 0x01, 0x00, 0x00, 0x00, + /* offset */ + 0x00, 0x00, 0x00, 0x00, + /* vaddr */ + 0x00, 0x00, 0x00, 0x00, + /* paddr : physical address */ + 0x00, 0x00, 0x00, 0x00, + /* filesz : 0x20 bytes (program header size) */ + 0x20, 0x00, 0x00, 0x00, + /* memsz = filesz */ + 0x20, 0x00, 0x00, 0x00, + /* flags : readable and exectuable */ + 0x05, 0x00, 0x00, 0x00, + /* padding */ + 0x00, 0x00, 0x00, 0x00, + + /* @0x60 - RESOURCE TABLE SECTION - */ + /* version */ + 0x01, 0x00, 0x00, 0x00, + /* num (0, no entries) */ + 0x00, 0x00, 0x00, 0x00, + /* Reserved */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* @0x70 - SECTION'S NAMES SECTION - */ + /* section 0 name (".shrtrtab") */ + 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, + /* section 1 name (".resource_table") */ + 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00, + /* padding */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + + /* @0x90 - SECTION HEADER TABLE - */ + /* Section 0 : resource table header */ + /* sh_name - index into section header string table section */ + 0x0a, 0x00, 0x00, 0x00, + /* sh_type and sh_flags */ + 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + /* sh_addr = where the resource table has to be copied to */ + 0x00, 0x00, 0x00, 0x00, + /* sh_offset = 0x60 */ + 0x60, 0x00, 0x00, 0x00, + /* sh_size = 16 bytes */ + 0x10, 0x00, 0x00, 0x00, + /* sh_link, sh_info, sh_addralign, sh_entsize */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* Section 1 : section's names section header */ + /* sh_name - index into section header string table section */ + 0x00, 0x00, 0x00, 0x00, + /* sh_type and sh_flags */ + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* sh_addr */ + 0x00, 0x00, 0x00, 0x00, + /* sh_offset = 0x70 */ + 0x70, 0x00, 0x00, 0x00, + /* sh_size = 27 bytes */ + 0x1b, 0x00, 0x00, 0x00, + /* sh_link, sh_info, sh_addralign, sh_entsize */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + unsigned int size = ARRAY_SIZE(valid_elf32); + struct udevice *dev; + phys_addr_t loaded_firmware_paddr, loaded_rsc_table_paddr; + void *loaded_firmware, *loaded_rsc_table; + u32 loaded_firmware_size, rsc_table_size; + ulong rsc_addr, rsc_size; + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)valid_elf32; + Elf32_Phdr *phdr = (Elf32_Phdr *)(valid_elf32 + ehdr->e_phoff); + Elf32_Shdr *shdr = (Elf32_Shdr *)(valid_elf32 + ehdr->e_shoff); + + ut_assertok(uclass_get_device(UCLASS_REMOTEPROC, 0, &dev)); + + /* + * In its Program Header Table, let the firmware specifies to be loaded + * at SDRAM_BASE *device* address (p_paddr field). + * Its size is defined by the p_filesz field. + */ + phdr->p_paddr = CONFIG_SYS_SDRAM_BASE; + loaded_firmware_size = phdr->p_filesz; + + /* + * This *device* address is converted to a *physical* address by the + * device_to_virt() operation of sandbox_test_rproc which returns + * DeviceAddress + DEVICE_TO_PHYSICAL_OFFSET. + * This is where we expect to get the firmware loaded. + */ + loaded_firmware_paddr = phdr->p_paddr + DEVICE_TO_PHYSICAL_OFFSET; + loaded_firmware = map_physmem(loaded_firmware_paddr, + loaded_firmware_size, MAP_NOCACHE); + ut_assertnonnull(loaded_firmware); + memset(loaded_firmware, 0, loaded_firmware_size); + + /* Load firmware in loaded_firmware, and verify it */ + ut_assertok(rproc_elf32_load_image(dev, (ulong)valid_elf32, size)); + ut_asserteq_mem(loaded_firmware, valid_elf32, loaded_firmware_size); + ut_asserteq(rproc_elf_get_boot_addr(dev, (unsigned long)valid_elf32), + 0x08000000); + unmap_physmem(loaded_firmware, MAP_NOCACHE); + + /* Resource table */ + shdr->sh_addr = CONFIG_SYS_SDRAM_BASE; + rsc_table_size = shdr->sh_size; + + loaded_rsc_table_paddr = shdr->sh_addr + DEVICE_TO_PHYSICAL_OFFSET; + loaded_rsc_table = map_physmem(loaded_rsc_table_paddr, + rsc_table_size, MAP_NOCACHE); + ut_assertnonnull(loaded_rsc_table); + memset(loaded_rsc_table, 0, rsc_table_size); + + /* Load and verify */ + ut_assertok(rproc_elf32_load_rsc_table(dev, (ulong)valid_elf32, size, + &rsc_addr, &rsc_size)); + ut_asserteq(rsc_addr, CONFIG_SYS_SDRAM_BASE); + ut_asserteq(rsc_size, rsc_table_size); + ut_asserteq_mem(loaded_firmware, valid_elf32 + shdr->sh_offset, + shdr->sh_size); + unmap_physmem(loaded_firmware, MAP_NOCACHE); + + /* Invalid ELF Magic */ + valid_elf32[0] = 0; + ut_asserteq(-EPROTONOSUPPORT, + rproc_elf32_sanity_check((ulong)valid_elf32, size)); + + return 0; +} +DM_TEST(dm_test_remoteproc_elf, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/reset.c b/roms/u-boot/test/dm/reset.c new file mode 100644 index 000000000..9c0045233 --- /dev/null +++ b/roms/u-boot/test/dm/reset.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <dm/device-internal.h> +#include <log.h> +#include <malloc.h> +#include <reset.h> +#include <dm/test.h> +#include <asm/reset.h> +#include <test/test.h> +#include <test/ut.h> + +/* This must match the specifier for mbox-names="test" in the DT node */ +#define TEST_RESET_ID 2 + +/* This is the other reset phandle specifier handled by bulk */ +#define OTHER_RESET_ID 2 + +/* Base test of the reset uclass */ +static int dm_test_reset_base(struct unit_test_state *uts) +{ + struct udevice *dev; + struct reset_ctl reset_method1, reset_method1_1; + struct reset_ctl reset_method2, reset_method2_1; + struct reset_ctl reset_method3, reset_method3_1; + struct reset_ctl reset_method4, reset_method4_1; + + /* Get the device using the reset device */ + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev)); + + /* Get the same reset port in 2 different ways and compare */ + ut_assertok(reset_get_by_index(dev, 0, &reset_method1)); + ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 0, + &reset_method1_1)); + ut_assertok(reset_get_by_index(dev, 1, &reset_method2)); + ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 1, + &reset_method2_1)); + ut_assertok(reset_get_by_index(dev, 2, &reset_method3)); + ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 2, + &reset_method3_1)); + ut_assertok(reset_get_by_index(dev, 3, &reset_method4)); + ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 3, + &reset_method4_1)); + + ut_asserteq(reset_method1.id, reset_method1_1.id); + ut_asserteq(reset_method2.id, reset_method2_1.id); + ut_asserteq(reset_method3.id, reset_method3_1.id); + ut_asserteq(reset_method4.id, reset_method4_1.id); + + ut_asserteq(true, reset_method1.id != reset_method2.id); + ut_asserteq(true, reset_method1.id != reset_method3.id); + ut_asserteq(true, reset_method1.id != reset_method4.id); + ut_asserteq(true, reset_method2.id != reset_method3.id); + ut_asserteq(true, reset_method2.id != reset_method4.id); + ut_asserteq(true, reset_method3.id != reset_method4.id); + + ut_asserteq(true, reset_method1_1.id != reset_method2_1.id); + ut_asserteq(true, reset_method1_1.id != reset_method3_1.id); + ut_asserteq(true, reset_method1_1.id != reset_method4_1.id); + ut_asserteq(true, reset_method2_1.id != reset_method3_1.id); + ut_asserteq(true, reset_method2_1.id != reset_method4_1.id); + ut_asserteq(true, reset_method3_1.id != reset_method4_1.id); + + return 0; +} + +DM_TEST(dm_test_reset_base, UT_TESTF_SCAN_FDT); + +static int dm_test_reset(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get(dev_test)); + + ut_assertok(sandbox_reset_test_assert(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_assertok(sandbox_reset_test_deassert(dev_test)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + ut_assertok(sandbox_reset_test_free(dev_test)); + ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + + return 0; +} +DM_TEST(dm_test_reset, UT_TESTF_SCAN_FDT); + +static int dm_test_reset_devm(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get_devm(dev_test)); + + ut_assertok(sandbox_reset_test_assert(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_assertok(sandbox_reset_test_deassert(dev_test)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + + ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL)); + ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + + return 0; +} +DM_TEST(dm_test_reset_devm, UT_TESTF_SCAN_FDT); + +static int dm_test_reset_bulk(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get_bulk(dev_test)); + + ut_assertok(sandbox_reset_test_assert_bulk(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_deassert_bulk(dev_test)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_release_bulk(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + return 0; +} +DM_TEST(dm_test_reset_bulk, UT_TESTF_SCAN_FDT); + +static int dm_test_reset_bulk_devm(struct unit_test_state *uts) +{ + struct udevice *dev_reset; + struct udevice *dev_test; + + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl", + &dev_reset)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test", + &dev_test)); + ut_assertok(sandbox_reset_test_get_bulk_devm(dev_test)); + + ut_assertok(sandbox_reset_test_assert_bulk(dev_test)); + ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_assertok(sandbox_reset_test_deassert_bulk(dev_test)); + ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID)); + + ut_asserteq(1, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID)); + ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL)); + ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID)); + ut_asserteq(0, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID)); + + return 0; +} +DM_TEST(dm_test_reset_bulk_devm, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/rng.c b/roms/u-boot/test/dm/rng.c new file mode 100644 index 000000000..5b34c93ed --- /dev/null +++ b/roms/u-boot/test/dm/rng.c @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2019, Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <rng.h> +#include <dm/test.h> +#include <test/ut.h> + +/* Basic test of the rng uclass */ +static int dm_test_rng_read(struct unit_test_state *uts) +{ + unsigned long rand1 = 0, rand2 = 0; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_RNG, 0, &dev)); + ut_assertnonnull(dev); + ut_assertok(dm_rng_read(dev, &rand1, sizeof(rand1))); + ut_assertok(dm_rng_read(dev, &rand2, sizeof(rand2))); + ut_assert(rand1 != rand2); + + return 0; +} +DM_TEST(dm_test_rng_read, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/rtc.c b/roms/u-boot/test/dm/rtc.c new file mode 100644 index 000000000..8ab997c87 --- /dev/null +++ b/roms/u-boot/test/dm/rtc.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <rtc.h> +#include <asm/io.h> +#include <asm/rtc.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Simple RTC sanity check */ +static int dm_test_rtc_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_RTC, 2, &dev)); + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev)); + + return 0; +} +DM_TEST(dm_test_rtc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static void show_time(const char *msg, struct rtc_time *time) +{ + printf("%s: %02d/%02d/%04d %02d:%02d:%02d\n", msg, + time->tm_mday, time->tm_mon, time->tm_year, + time->tm_hour, time->tm_min, time->tm_sec); +} + +static int cmp_times(struct rtc_time *expect, struct rtc_time *time, bool show) +{ + bool same; + + same = expect->tm_sec == time->tm_sec; + same &= expect->tm_min == time->tm_min; + same &= expect->tm_hour == time->tm_hour; + same &= expect->tm_mday == time->tm_mday; + same &= expect->tm_mon == time->tm_mon; + same &= expect->tm_year == time->tm_year; + if (!same && show) { + show_time("expected", expect); + show_time("actual", time); + } + + return same ? 0 : -EINVAL; +} + +/* Set and get the time */ +static int dm_test_rtc_set_get(struct unit_test_state *uts) +{ + struct rtc_time now, time, cmp; + struct udevice *dev, *emul; + long offset, old_offset, old_base_time; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(dm_rtc_get(dev, &now)); + + ut_assertok(i2c_emul_find(dev, &emul)); + ut_assert(emul != NULL); + + /* Tell the RTC to go into manual mode */ + old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0); + old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1); + + memset(&time, '\0', sizeof(time)); + time.tm_mday = 3; + time.tm_mon = 6; + time.tm_year = 2004; + time.tm_sec = 0; + time.tm_min = 18; + time.tm_hour = 18; + ut_assertok(dm_rtc_set(dev, &time)); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&time, &cmp, true)); + + memset(&time, '\0', sizeof(time)); + time.tm_mday = 31; + time.tm_mon = 8; + time.tm_year = 2004; + time.tm_sec = 0; + time.tm_min = 18; + time.tm_hour = 18; + ut_assertok(dm_rtc_set(dev, &time)); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&time, &cmp, true)); + + /* Increment by 1 second */ + offset = sandbox_i2c_rtc_set_offset(emul, false, 0); + sandbox_i2c_rtc_set_offset(emul, false, offset + 1); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_asserteq(1, cmp.tm_sec); + + /* Check against original offset */ + sandbox_i2c_rtc_set_offset(emul, false, old_offset); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&now, &cmp, true)); + + /* Back to the original offset */ + sandbox_i2c_rtc_set_offset(emul, false, 0); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + ut_assertok(cmp_times(&now, &cmp, true)); + + /* Increment the base time by 1 emul */ + sandbox_i2c_rtc_get_set_base_time(emul, old_base_time + 1); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev, &cmp)); + if (now.tm_sec == 59) { + ut_asserteq(0, cmp.tm_sec); + } else { + ut_asserteq(now.tm_sec + 1, cmp.tm_sec); + } + + old_offset = sandbox_i2c_rtc_set_offset(emul, true, 0); + + return 0; +} +DM_TEST(dm_test_rtc_set_get, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_rtc_read_write(struct unit_test_state *uts) +{ + struct rtc_time time; + struct udevice *dev, *emul; + long old_offset; + u8 buf[4], reg; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + + memcpy(buf, "car", 4); + ut_assertok(dm_rtc_write(dev, REG_AUX0, buf, 4)); + memset(buf, '\0', sizeof(buf)); + ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4)); + ut_asserteq(memcmp(buf, "car", 4), 0); + + reg = 'b'; + ut_assertok(dm_rtc_write(dev, REG_AUX0, ®, 1)); + memset(buf, '\0', sizeof(buf)); + ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4)); + ut_asserteq(memcmp(buf, "bar", 4), 0); + + reg = 't'; + ut_assertok(dm_rtc_write(dev, REG_AUX2, ®, 1)); + memset(buf, '\0', sizeof(buf)); + ut_assertok(dm_rtc_read(dev, REG_AUX1, buf, 3)); + ut_asserteq(memcmp(buf, "at", 3), 0); + + ut_assertok(i2c_emul_find(dev, &emul)); + ut_assert(emul != NULL); + + old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0); + ut_assertok(dm_rtc_get(dev, &time)); + + ut_assertok(dm_rtc_read(dev, REG_SEC, ®, 1)); + ut_asserteq(time.tm_sec, reg); + ut_assertok(dm_rtc_read(dev, REG_MDAY, ®, 1)); + ut_asserteq(time.tm_mday, reg); + + sandbox_i2c_rtc_set_offset(emul, true, old_offset); + + return 0; +} +DM_TEST(dm_test_rtc_read_write, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'rtc list' command */ +static int dm_test_rtc_cmd_list(struct unit_test_state *uts) +{ + console_record_reset(); + + run_command("rtc list", 0); + ut_assert_nextline("RTC #0 - rtc@43"); + ut_assert_nextline("RTC #1 - rtc@61"); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_rtc_cmd_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'rtc read' and 'rtc write' commands */ +static int dm_test_rtc_cmd_rw(struct unit_test_state *uts) +{ + console_record_reset(); + + run_command("rtc dev 0", 0); + ut_assert_nextline("RTC #0 - rtc@43"); + ut_assert_console_end(); + + run_command("rtc write 0x30 aabb", 0); + ut_assert_console_end(); + + run_command("rtc read 0x30 2", 0); + ut_assert_nextline("00000030: aa bb .."); + ut_assert_console_end(); + + run_command("rtc dev 1", 0); + ut_assert_nextline("RTC #1 - rtc@61"); + ut_assert_console_end(); + + run_command("rtc write 0x30 ccdd", 0); + ut_assert_console_end(); + + run_command("rtc read 0x30 2", 0); + ut_assert_nextline("00000030: cc dd .."); + ut_assert_console_end(); + + /* + * Switch back to device #0, check that its aux registers + * still have the same values. + */ + run_command("rtc dev 0", 0); + ut_assert_nextline("RTC #0 - rtc@43"); + ut_assert_console_end(); + + run_command("rtc read 0x30 2", 0); + ut_assert_nextline("00000030: aa bb .."); + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_rtc_cmd_rw, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Reset the time */ +static int dm_test_rtc_reset(struct unit_test_state *uts) +{ + struct rtc_time now; + struct udevice *dev, *emul; + long old_base_time, base_time; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev)); + ut_assertok(dm_rtc_get(dev, &now)); + + ut_assertok(i2c_emul_find(dev, &emul)); + ut_assert(emul != NULL); + + old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, 0); + + ut_asserteq(0, sandbox_i2c_rtc_get_set_base_time(emul, -1)); + + /* Resetting the RTC should put he base time back to normal */ + ut_assertok(dm_rtc_reset(dev)); + base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1); + ut_asserteq(old_base_time, base_time); + + return 0; +} +DM_TEST(dm_test_rtc_reset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Check that two RTC devices can be used independently */ +static int dm_test_rtc_dual(struct unit_test_state *uts) +{ + struct rtc_time now1, now2, cmp; + struct udevice *dev1, *dev2; + struct udevice *emul1, *emul2; + long offset; + + ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev1)); + ut_assertok(dm_rtc_get(dev1, &now1)); + ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev2)); + ut_assertok(dm_rtc_get(dev2, &now2)); + + ut_assertok(i2c_emul_find(dev1, &emul1)); + ut_assert(emul1 != NULL); + ut_assertok(i2c_emul_find(dev2, &emul2)); + ut_assert(emul2 != NULL); + + offset = sandbox_i2c_rtc_set_offset(emul1, false, -1); + sandbox_i2c_rtc_set_offset(emul2, false, offset + 1); + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev2, &cmp)); + ut_asserteq(-EINVAL, cmp_times(&now1, &cmp, false)); + + memset(&cmp, '\0', sizeof(cmp)); + ut_assertok(dm_rtc_get(dev1, &cmp)); + ut_assertok(cmp_times(&now1, &cmp, true)); + + return 0; +} +DM_TEST(dm_test_rtc_dual, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/scmi.c b/roms/u-boot/test/dm/scmi.c new file mode 100644 index 000000000..c938e6d4f --- /dev/null +++ b/roms/u-boot/test/dm/scmi.c @@ -0,0 +1,260 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020, Linaro Limited + * + * Tests scmi_agent uclass and the SCMI drivers implemented in other + * uclass devices probe when a SCMI server exposes resources. + * + * Note in test.dts the protocol@10 node in agent 1. Protocol 0x10 is not + * implemented in U-Boot SCMI components but the implementation is exepected + * to not complain on unknown protocol IDs, as long as it is not used. Note + * in test.dts tests that SCMI drivers probing does not fail for such an + * unknown SCMI protocol ID. + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <reset.h> +#include <asm/scmi_test.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <linux/kconfig.h> +#include <power/regulator.h> +#include <test/ut.h> + +static int ut_assert_scmi_state_preprobe(struct unit_test_state *uts) +{ + struct sandbox_scmi_service *scmi_ctx = sandbox_scmi_service_ctx(); + + ut_assertnonnull(scmi_ctx); + if (scmi_ctx->agent_count) + ut_asserteq(2, scmi_ctx->agent_count); + + return 0; +} + +static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts, + struct udevice *dev) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct sandbox_scmi_agent *agent1; + + /* Device references to check context against test sequence */ + scmi_devices = sandbox_scmi_devices_ctx(dev); + + ut_assertnonnull(scmi_devices); + ut_asserteq(3, scmi_devices->clk_count); + ut_asserteq(1, scmi_devices->reset_count); + ut_asserteq(2, scmi_devices->regul_count); + + /* State of the simulated SCMI server exposed */ + scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + agent1 = scmi_ctx->agent[1]; + + ut_asserteq(2, scmi_ctx->agent_count); + + ut_assertnonnull(agent0); + ut_asserteq(2, agent0->clk_count); + ut_assertnonnull(agent0->clk); + ut_asserteq(1, agent0->reset_count); + ut_assertnonnull(agent0->reset); + ut_asserteq(2, agent0->voltd_count); + ut_assertnonnull(agent0->voltd); + + ut_assertnonnull(agent1); + ut_assertnonnull(agent1->clk); + ut_asserteq(1, agent1->clk_count); + + return 0; +} + +static int load_sandbox_scmi_test_devices(struct unit_test_state *uts, + struct udevice **dev) +{ + int ret; + + ret = ut_assert_scmi_state_preprobe(uts); + if (ret) + return ret; + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_scmi", + dev)); + ut_assertnonnull(*dev); + + return ut_assert_scmi_state_postprobe(uts, *dev); +} + +static int release_sandbox_scmi_test_devices(struct unit_test_state *uts, + struct udevice *dev) +{ + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + + /* Not sure test devices are fully removed, agent may not be visible */ + return 0; +} + +/* + * Test SCMI states when loading and releasing resources + * related to SCMI drivers. + */ +static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts) +{ + struct udevice *dev = NULL; + int ret; + + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (!ret) + ret = release_sandbox_scmi_test_devices(uts, dev); + + return ret; +} +DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_clocks(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct sandbox_scmi_agent *agent1; + struct udevice *dev = NULL; + int ret_dev; + int ret; + + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (ret) + return ret; + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + agent1 = scmi_ctx->agent[1]; + + /* Test SCMI clocks rate manipulation */ + ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); + ut_asserteq(333, clk_get_rate(&scmi_devices->clk[1])); + ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2])); + + ret_dev = clk_set_rate(&scmi_devices->clk[1], 1088); + ut_assert(!ret_dev || ret_dev == 1088); + + ut_asserteq(1000, agent0->clk[0].rate); + ut_asserteq(1088, agent0->clk[1].rate); + ut_asserteq(44, agent1->clk[0].rate); + + ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0])); + ut_asserteq(1088, clk_get_rate(&scmi_devices->clk[1])); + ut_asserteq(44, clk_get_rate(&scmi_devices->clk[2])); + + /* restore original rate for further tests */ + ret_dev = clk_set_rate(&scmi_devices->clk[1], 333); + ut_assert(!ret_dev || ret_dev == 333); + + /* Test SCMI clocks gating manipulation */ + ut_assert(!agent0->clk[0].enabled); + ut_assert(!agent0->clk[1].enabled); + ut_assert(!agent1->clk[0].enabled); + + ut_asserteq(0, clk_enable(&scmi_devices->clk[1])); + ut_asserteq(0, clk_enable(&scmi_devices->clk[2])); + + ut_assert(!agent0->clk[0].enabled); + ut_assert(agent0->clk[1].enabled); + ut_assert(agent1->clk[0].enabled); + + ut_assertok(clk_disable(&scmi_devices->clk[1])); + ut_assertok(clk_disable(&scmi_devices->clk[2])); + + ut_assert(!agent0->clk[0].enabled); + ut_assert(!agent0->clk[1].enabled); + ut_assert(!agent1->clk[0].enabled); + + return release_sandbox_scmi_test_devices(uts, dev); +} +DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_resets(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct udevice *dev = NULL; + int ret; + + ret = load_sandbox_scmi_test_devices(uts, &dev); + if (ret) + return ret; + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + + /* Test SCMI resect controller manipulation */ + ut_assert(!agent0->reset[0].asserted) + + ut_assertok(reset_assert(&scmi_devices->reset[0])); + ut_assert(agent0->reset[0].asserted) + + ut_assertok(reset_deassert(&scmi_devices->reset[0])); + ut_assert(!agent0->reset[0].asserted); + + return release_sandbox_scmi_test_devices(uts, dev); +} +DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT); + +static int dm_test_scmi_voltage_domains(struct unit_test_state *uts) +{ + struct sandbox_scmi_devices *scmi_devices; + struct sandbox_scmi_service *scmi_ctx; + struct sandbox_scmi_agent *agent0; + struct dm_regulator_uclass_plat *uc_pdata; + struct udevice *dev; + struct udevice *regul0_dev; + + ut_assertok(load_sandbox_scmi_test_devices(uts, &dev)); + + scmi_devices = sandbox_scmi_devices_ctx(dev); + scmi_ctx = sandbox_scmi_service_ctx(); + agent0 = scmi_ctx->agent[0]; + + /* Set/Get an SCMI voltage domain level */ + regul0_dev = scmi_devices->regul[0]; + ut_assert(regul0_dev); + + uc_pdata = dev_get_uclass_plat(regul0_dev); + ut_assert(uc_pdata); + + ut_assertok(regulator_set_value(regul0_dev, uc_pdata->min_uV)); + ut_asserteq(agent0->voltd[0].voltage_uv, uc_pdata->min_uV); + + ut_assert(regulator_get_value(regul0_dev) == uc_pdata->min_uV); + + ut_assertok(regulator_set_value(regul0_dev, uc_pdata->max_uV)); + ut_asserteq(agent0->voltd[0].voltage_uv, uc_pdata->max_uV); + + ut_assert(regulator_get_value(regul0_dev) == uc_pdata->max_uV); + + /* Enable/disable SCMI voltage domains */ + ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); + ut_assertok(regulator_set_enable(scmi_devices->regul[1], false)); + ut_assert(!agent0->voltd[0].enabled); + ut_assert(!agent0->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[0], true)); + ut_assert(agent0->voltd[0].enabled); + ut_assert(!agent0->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[1], true)); + ut_assert(agent0->voltd[0].enabled); + ut_assert(agent0->voltd[1].enabled); + + ut_assertok(regulator_set_enable(scmi_devices->regul[0], false)); + ut_assert(!agent0->voltd[0].enabled); + ut_assert(agent0->voltd[1].enabled); + + return release_sandbox_scmi_test_devices(uts, dev); +} +DM_TEST(dm_test_scmi_voltage_domains, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/serial.c b/roms/u-boot/test/dm/serial.c new file mode 100644 index 000000000..0662b5f09 --- /dev/null +++ b/roms/u-boot/test/dm/serial.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, STMicroelectronics + */ + +#include <common.h> +#include <log.h> +#include <serial.h> +#include <dm.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_serial(struct unit_test_state *uts) +{ + struct serial_device_info info_serial = {0}; + struct udevice *dev_serial; + uint value_serial; + + ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial", + &dev_serial)); + + ut_assertok(serial_tstc()); + /* + * test with default config which is the only one supported by + * sandbox_serial driver + */ + ut_assertok(serial_setconfig(dev_serial, SERIAL_DEFAULT_CONFIG)); + ut_assertok(serial_getconfig(dev_serial, &value_serial)); + ut_assert(value_serial == SERIAL_DEFAULT_CONFIG); + ut_assertok(serial_getinfo(dev_serial, &info_serial)); + ut_assert(info_serial.type == SERIAL_CHIP_UNKNOWN); + ut_assert(info_serial.addr == SERIAL_DEFAULT_ADDRESS); + ut_assert(info_serial.clock == SERIAL_DEFAULT_CLOCK); + /* + * test with a parameter which is NULL pointer + */ + ut_asserteq(-EINVAL, serial_getconfig(dev_serial, NULL)); + ut_asserteq(-EINVAL, serial_getinfo(dev_serial, NULL)); + /* + * test with a serial config which is not supported by + * sandbox_serial driver: test with wrong parity + */ + ut_asserteq(-ENOTSUPP, + serial_setconfig(dev_serial, + SERIAL_CONFIG(SERIAL_PAR_ODD, + SERIAL_8_BITS, + SERIAL_ONE_STOP))); + /* + * test with a serial config which is not supported by + * sandbox_serial driver: test with wrong bits number + */ + ut_asserteq(-ENOTSUPP, + serial_setconfig(dev_serial, + SERIAL_CONFIG(SERIAL_PAR_NONE, + SERIAL_6_BITS, + SERIAL_ONE_STOP))); + + /* + * test with a serial config which is not supported by + * sandbox_serial driver: test with wrong stop bits number + */ + ut_asserteq(-ENOTSUPP, + serial_setconfig(dev_serial, + SERIAL_CONFIG(SERIAL_PAR_NONE, + SERIAL_8_BITS, + SERIAL_TWO_STOP))); + + return 0; +} + +DM_TEST(dm_test_serial, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/sf.c b/roms/u-boot/test/dm/sf.c new file mode 100644 index 000000000..17d43fef3 --- /dev/null +++ b/roms/u-boot/test/dm/sf.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Google, Inc + */ + +#include <common.h> +#include <command.h> +#include <dm.h> +#include <fdtdec.h> +#include <mapmem.h> +#include <os.h> +#include <spi.h> +#include <spi_flash.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <dm/util.h> +#include <test/test.h> +#include <test/ut.h> + +/* Simple test of sandbox SPI flash */ +static int dm_test_spi_flash(struct unit_test_state *uts) +{ + struct udevice *dev, *emul; + int full_size = 0x200000; + int size = 0x10000; + u8 *src, *dst; + uint map_size; + ulong map_base; + uint offset; + int i; + + src = map_sysmem(0x20000, full_size); + ut_assertok(os_write_file("spi.bin", src, full_size)); + ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev)); + + dst = map_sysmem(0x20000 + full_size, full_size); + ut_assertok(spi_flash_read_dm(dev, 0, size, dst)); + ut_asserteq_mem(src, dst, size); + + /* Erase */ + ut_assertok(spi_flash_erase_dm(dev, 0, size)); + ut_assertok(spi_flash_read_dm(dev, 0, size, dst)); + for (i = 0; i < size; i++) + ut_asserteq(dst[i], 0xff); + + /* Write some new data */ + for (i = 0; i < size; i++) + src[i] = i; + ut_assertok(spi_flash_write_dm(dev, 0, size, src)); + ut_assertok(spi_flash_read_dm(dev, 0, size, dst)); + ut_asserteq_mem(src, dst, size); + + /* Try the write-protect stuff */ + ut_assertok(uclass_first_device_err(UCLASS_SPI_EMUL, &emul)); + ut_asserteq(0, spl_flash_get_sw_write_prot(dev)); + sandbox_sf_set_block_protect(emul, 1); + ut_asserteq(1, spl_flash_get_sw_write_prot(dev)); + sandbox_sf_set_block_protect(emul, 0); + ut_asserteq(0, spl_flash_get_sw_write_prot(dev)); + + /* Check mapping */ + ut_assertok(dm_spi_get_mmap(dev, &map_base, &map_size, &offset)); + ut_asserteq(0x1000, map_base); + ut_asserteq(0x2000, map_size); + ut_asserteq(0x100, offset); + + /* + * Since we are about to destroy all devices, we must tell sandbox + * to forget the emulation device + */ + sandbox_sf_unbind_emul(state_get_current(), 0, 0); + + return 0; +} +DM_TEST(dm_test_spi_flash, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Functional test that sandbox SPI flash works correctly */ +static int dm_test_spi_flash_func(struct unit_test_state *uts) +{ + /* + * Create an empty test file and run the SPI flash tests. This is a + * long way from being a unit test, but it does test SPI device and + * emulator binding, probing, the SPI flash emulator including + * device tree decoding, plus the file-based backing store of SPI. + * + * More targeted tests could be created to perform the above steps + * one at a time. This might not increase test coverage much, but + * it would make bugs easier to find. It's not clear whether the + * benefit is worth the extra complexity. + */ + ut_asserteq(0, run_command_list( + "host save hostfs - 0 spi.bin 200000;" + "sf probe;" + "sf test 0 10000", -1, 0)); + /* + * Since we are about to destroy all devices, we must tell sandbox + * to forget the emulation device + */ + sandbox_sf_unbind_emul(state_get_current(), 0, 0); + + return 0; +} +DM_TEST(dm_test_spi_flash_func, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/simple-bus.c b/roms/u-boot/test/dm/simple-bus.c new file mode 100644 index 000000000..3530b47fa --- /dev/null +++ b/roms/u-boot/test/dm/simple-bus.c @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <dm/simple_bus.h> +#include <dm/uclass-internal.h> +#include <test/ut.h> + +static int dm_test_simple_bus(struct unit_test_state *uts) +{ + struct udevice *dev; + struct simple_bus_plat *plat; + + /* locate the dummy device @ translation-test node */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + ut_asserteq_str("dev@0,0", dev->name); + + /* locate the parent node which is a simple-bus */ + ut_assertnonnull(dev = dev_get_parent(dev)); + ut_asserteq_str("translation-test@8000", dev->name); + + ut_assertnonnull(plat = dev_get_uclass_plat(dev)); + ut_asserteq(0, plat->base); + ut_asserteq(0x8000, plat->target); + ut_asserteq(0x1000, plat->size); + + return 0; +} +DM_TEST(dm_test_simple_bus, UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); diff --git a/roms/u-boot/test/dm/simple-pm-bus.c b/roms/u-boot/test/dm/simple-pm-bus.c new file mode 100644 index 000000000..792c74505 --- /dev/null +++ b/roms/u-boot/test/dm/simple-pm-bus.c @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <dm/device-internal.h> +#include <test/ut.h> +#include <asm/clk.h> +#include <asm/power-domain.h> + +/* These must match the ids in the device tree */ +#define TEST_CLOCK_ID 4 +#define TEST_POWER_ID 1 + +static int dm_test_simple_pm_bus(struct unit_test_state *uts) +{ + struct udevice *power; + struct udevice *clock; + struct udevice *bus; + + ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN, + "power-domain", &power)); + ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox", + &clock)); + ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID)); + ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID)); + + ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, "pm-bus-test", + &bus)); + ut_asserteq(1, sandbox_power_domain_query(power, TEST_POWER_ID)); + ut_asserteq(1, sandbox_clk_query_enable(clock, TEST_CLOCK_ID)); + + ut_assertok(device_remove(bus, DM_REMOVE_NORMAL)); + /* must re-probe since device_remove also removes the power domain */ + ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN, + "power-domain", &power)); + ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID)); + ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID)); + + return 0; +} +DM_TEST(dm_test_simple_pm_bus, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/smem.c b/roms/u-boot/test/dm/smem.c new file mode 100644 index 000000000..a797026cb --- /dev/null +++ b/roms/u-boot/test/dm/smem.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Ramon Fried <ramon.fried@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <smem.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the smem uclass */ +static int dm_test_smem_base(struct unit_test_state *uts) +{ + struct udevice *dev; + size_t size; + + ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev)); + ut_assertnonnull(dev); + ut_assertok(smem_alloc(dev, -1, 0, 16)); + ut_asserteq(0, smem_get_free_space(dev, -1)); + ut_assertnull(smem_get(dev, -1, 0, &size)); + + return 0; +} +DM_TEST(dm_test_smem_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + diff --git a/roms/u-boot/test/dm/soc.c b/roms/u-boot/test/dm/soc.c new file mode 100644 index 000000000..17e1b5ba0 --- /dev/null +++ b/roms/u-boot/test/dm/soc.c @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test for the SOC uclass + * + * (C) Copyright 2020 - Texas Instruments Incorporated - http://www.ti.com/ + * Dave Gerlach <d-gerlach@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <soc.h> +#include <test/ut.h> + +struct sb_soc_data { + unsigned long param; +}; + +static int dm_test_soc(struct unit_test_state *uts) +{ + struct udevice *dev; + char text[128]; + const struct soc_attr *soc_data; + const struct sb_soc_data *match_data; + + static const struct sb_soc_data soc_sandbox1_sr10_data = { 0x91919191 }; + static const struct sb_soc_data soc_sandbox123_data = { 0x84848484 }; + + static const struct soc_attr sb_soc_devices_full[] = { + { + .family = "SANDBOX0xx", + .machine = "SANDBOX012", + .revision = "1.0", + .data = NULL, + }, + { + .family = "SANDBOX1xx", + .machine = "SANDBOX107", + .revision = "1.0", + .data = NULL, + }, + { + .family = "SANDBOX1xx", + .machine = "SANDBOX123", + .revision = "1.0", + .data = &soc_sandbox123_data, + }, + { + .family = "SANDBOX1xx", + .machine = "SANDBOX131", + .revision = "2.0", + .data = NULL, + }, + { /* sentinel */ } + }; + + static const struct soc_attr sb_soc_devices_partial[] = { + { + .family = "SANDBOX0xx", + .revision = "1.0", + .data = NULL, + }, + { + .family = "SANDBOX1xx", + .revision = "1.0", + .data = &soc_sandbox1_sr10_data, + }, + { + .family = "SANDBOX1xx", + .revision = "2.0", + .data = NULL, + }, + { /* sentinel */ } + }; + + static const struct soc_attr sb_soc_devices_nomatch[] = { + { + .family = "SANDBOX0xx", + .revision = "1.0", + .data = NULL, + }, + { + .family = "SANDBOX1xx", + .revision = "2.0", + .data = NULL, + }, + { /* sentinel */ } + }; + + ut_assertok(soc_get(&dev)); + + ut_assertok(soc_get_machine(dev, text, sizeof(text))); + ut_assertok(strcmp(text, "SANDBOX123")); + + ut_assertok(soc_get_family(dev, text, sizeof(text))); + ut_assertok(strcmp(text, "SANDBOX1xx")); + + ut_assertok(soc_get_revision(dev, text, sizeof(text))); + ut_asserteq_str(text, "1.0"); + + soc_data = soc_device_match(sb_soc_devices_full); + ut_assert(soc_data); + + match_data = soc_data->data; + ut_asserteq(match_data->param, 0x84848484); + + soc_data = soc_device_match(sb_soc_devices_partial); + ut_assert(soc_data); + + match_data = soc_data->data; + ut_asserteq(match_data->param, 0x91919191); + + soc_data = soc_device_match(sb_soc_devices_nomatch); + ut_asserteq_ptr(soc_data, NULL); + + return 0; +} + +DM_TEST(dm_test_soc, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/sound.c b/roms/u-boot/test/dm/sound.c new file mode 100644 index 000000000..b73f6ab11 --- /dev/null +++ b/roms/u-boot/test/dm/sound.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2018 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <dm.h> +#include <sound.h> +#include <dm/test.h> +#include <test/ut.h> +#include <test/test.h> +#include <asm/test.h> + +/* Basic test of the sound codec uclass */ +static int dm_test_sound(struct unit_test_state *uts) +{ + struct sound_uc_priv *uc_priv; + struct udevice *dev; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev)); + uc_priv = dev_get_uclass_priv(dev); + ut_asserteq_str("audio-codec", uc_priv->codec->name); + ut_asserteq_str("i2s", uc_priv->i2s->name); + ut_asserteq(0, sandbox_get_setup_called(dev)); + + ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(4560, sandbox_get_sound_sum(dev)); + ut_assertok(sound_beep(dev, 1, 100)); + ut_asserteq(9120, sandbox_get_sound_sum(dev)); + ut_asserteq(false, sandbox_get_sound_active(dev)); + + return 0; +} +DM_TEST(dm_test_sound, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the 'start beep' operations */ +static int dm_test_sound_beep(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* check probe success */ + ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev)); + ut_asserteq(-ENOSYS, sound_start_beep(dev, 100)); + ut_asserteq(0, sandbox_get_beep_frequency(dev)); + + sandbox_set_allow_beep(dev, true); + ut_asserteq(0, sound_start_beep(dev, 100)); + ut_asserteq(100, sandbox_get_beep_frequency(dev)); + + ut_asserteq(0, sound_stop_beep(dev)); + ut_asserteq(0, sandbox_get_beep_frequency(dev)); + + return 0; +} +DM_TEST(dm_test_sound_beep, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/spi.c b/roms/u-boot/test/dm/spi.c new file mode 100644 index 000000000..ee4ad3aba --- /dev/null +++ b/roms/u-boot/test/dm/spi.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2013 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <spi.h> +#include <spi_flash.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that we can find buses and chip-selects */ +static int dm_test_spi_find(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + struct spi_slave *slave; + struct udevice *bus, *dev; + const int busnum = 0, cs = 0, mode = 0, speed = 1000000, cs_b = 2; + struct spi_cs_info info; + ofnode node; + + /* + * The post_bind() method will bind devices to chip selects. Check + * this then remove the emulation and the slave device. + */ + ut_asserteq(0, uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus)); + ut_assertok(spi_cs_info(bus, cs, &info)); + node = dev_ofnode(info.dev); + device_remove(info.dev, DM_REMOVE_NORMAL); + device_unbind(info.dev); + + /* + * Even though the device is gone, the sandbox SPI drivers always + * reports that CS 0 is present + */ + ut_assertok(spi_cs_info(bus, cs, &info)); + ut_asserteq_ptr(NULL, info.dev); + + /* This finds nothing because we removed the device */ + ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev)); + ut_asserteq(-ENODEV, spi_get_bus_and_cs(busnum, cs, speed, mode, + NULL, 0, &bus, &slave)); + + /* + * This forces the device to be re-added, but there is no emulation + * connected so the probe will fail. We require that bus is left + * alone on failure, and that the spi_get_bus_and_cs() does not add + * a 'partially-inited' device. + */ + ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev)); + ut_asserteq(-ENOENT, spi_get_bus_and_cs(busnum, cs, speed, mode, + "jedec_spi_nor", "name", &bus, + &slave)); + sandbox_sf_unbind_emul(state_get_current(), busnum, cs); + ut_assertok(spi_cs_info(bus, cs, &info)); + ut_asserteq_ptr(NULL, info.dev); + + /* Add the emulation and try again */ + ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, node, + "name")); + ut_assertok(spi_find_bus_and_cs(busnum, cs, &bus, &dev)); + ut_assertok(spi_get_bus_and_cs(busnum, cs, speed, mode, + "jedec_spi_nor", "name", &bus, &slave)); + + ut_assertok(spi_cs_info(bus, cs, &info)); + ut_asserteq_ptr(info.dev, slave->dev); + + /* We should be able to add something to another chip select */ + ut_assertok(sandbox_sf_bind_emul(state, busnum, cs_b, bus, node, + "name")); + ut_asserteq(-EINVAL, spi_get_bus_and_cs(busnum, cs_b, speed, mode, + "jedec_spi_nor", "name", &bus, &slave)); + ut_asserteq(-EINVAL, spi_cs_info(bus, cs_b, &info)); + ut_asserteq_ptr(NULL, info.dev); + + /* + * Since we are about to destroy all devices, we must tell sandbox + * to forget the emulation device + */ + sandbox_sf_unbind_emul(state_get_current(), busnum, cs); + sandbox_sf_unbind_emul(state_get_current(), busnum, cs_b); + + return 0; +} +DM_TEST(dm_test_spi_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* dm_test_spi_switch_slaves - Helper function to check whether spi_claim_bus + * operates correctly with two spi slaves. + * + * Check that switching back and forth between two slaves claiming the bus + * will update dm_spi_bus->speed and sandbox_spi bus speed/mode correctly. + * + * @uts - unit test state + * @slave_a - first spi slave used for testing + * @slave_b - second spi slave used for testing + */ +static int dm_test_spi_switch_slaves(struct unit_test_state *uts, + struct spi_slave *slave_a, + struct spi_slave *slave_b) +{ + struct udevice *bus; + struct dm_spi_bus *bus_data; + + /* Check that slaves are on the same bus */ + ut_asserteq_ptr(dev_get_parent(slave_a->dev), + dev_get_parent(slave_b->dev)); + + bus = dev_get_parent(slave_a->dev); + bus_data = dev_get_uclass_priv(bus); + + ut_assertok(spi_claim_bus(slave_a)); + ut_asserteq(slave_a->max_hz, bus_data->speed); + ut_asserteq(slave_a->max_hz, sandbox_spi_get_speed(bus)); + ut_asserteq(slave_a->mode, sandbox_spi_get_mode(bus)); + spi_release_bus(slave_a); + + ut_assertok(spi_claim_bus(slave_b)); + ut_asserteq(slave_b->max_hz, bus_data->speed); + ut_asserteq(slave_b->max_hz, sandbox_spi_get_speed(bus)); + ut_asserteq(slave_b->mode, sandbox_spi_get_mode(bus)); + spi_release_bus(slave_b); + + ut_assertok(spi_claim_bus(slave_a)); + ut_asserteq(slave_a->max_hz, bus_data->speed); + ut_asserteq(slave_a->max_hz, sandbox_spi_get_speed(bus)); + ut_asserteq(slave_a->mode, sandbox_spi_get_mode(bus)); + spi_release_bus(slave_a); + + return 0; +} + +static int dm_test_spi_claim_bus(struct unit_test_state *uts) +{ + struct udevice *bus; + struct spi_slave *slave_a, *slave_b; + struct dm_spi_slave_plat *slave_plat; + const int busnum = 0, cs_a = 0, cs_b = 1, mode = 0; + + /* Get spi slave on CS0 */ + ut_assertok(spi_get_bus_and_cs(busnum, cs_a, 1000000, mode, NULL, 0, + &bus, &slave_a)); + /* Get spi slave on CS1 */ + ut_assertok(spi_get_bus_and_cs(busnum, cs_b, 1000000, mode, NULL, 0, + &bus, &slave_b)); + + /* Different max_hz, different mode. */ + ut_assert(slave_a->max_hz != slave_b->max_hz); + ut_assert(slave_a->mode != slave_b->mode); + dm_test_spi_switch_slaves(uts, slave_a, slave_b); + + /* Different max_hz, same mode. */ + slave_a->mode = slave_b->mode; + dm_test_spi_switch_slaves(uts, slave_a, slave_b); + + /* + * Same max_hz, different mode. + * Restore original mode for slave_a, from platdata. + */ + slave_plat = dev_get_parent_plat(slave_a->dev); + slave_a->mode = slave_plat->mode; + slave_a->max_hz = slave_b->max_hz; + dm_test_spi_switch_slaves(uts, slave_a, slave_b); + + return 0; +} +DM_TEST(dm_test_spi_claim_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that sandbox SPI works correctly */ +static int dm_test_spi_xfer(struct unit_test_state *uts) +{ + struct spi_slave *slave; + struct udevice *bus; + const int busnum = 0, cs = 0, mode = 0; + const char dout[5] = {0x9f}; + unsigned char din[5]; + + ut_assertok(spi_get_bus_and_cs(busnum, cs, 1000000, mode, NULL, 0, + &bus, &slave)); + ut_assertok(spi_claim_bus(slave)); + ut_assertok(spi_xfer(slave, 40, dout, din, + SPI_XFER_BEGIN | SPI_XFER_END)); + ut_asserteq(0xff, din[0]); + ut_asserteq(0x20, din[1]); + ut_asserteq(0x20, din[2]); + ut_asserteq(0x15, din[3]); + spi_release_bus(slave); + + /* + * Since we are about to destroy all devices, we must tell sandbox + * to forget the emulation device + */ +#if CONFIG_IS_ENABLED(DM_SPI_FLASH) + sandbox_sf_unbind_emul(state_get_current(), busnum, cs); +#endif + + return 0; +} +DM_TEST(dm_test_spi_xfer, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/spmi.c b/roms/u-boot/test/dm/spmi.c new file mode 100644 index 000000000..114fd2d22 --- /dev/null +++ b/roms/u-boot/test/dm/spmi.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com> + */ + +#include <common.h> +#include <fdtdec.h> +#include <dm.h> +#include <malloc.h> +#include <dm/device.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/util.h> +#include <power/pmic.h> +#include <spmi/spmi.h> +#include <asm/gpio.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test if bus childs got probed propperly*/ +static int dm_test_spmi_probe(struct unit_test_state *uts) +{ + const char *name = "spmi@0"; + struct udevice *bus, *dev; + + ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus)); + + /* Check bus name */ + ut_asserteq_str(name, bus->name); + + /* Check that it has some devices */ + ut_asserteq(device_has_children(bus), true); + + ut_assertok(device_find_first_child(bus, &dev)); + + /* There should be at least one child */ + ut_assertnonnull(dev); + + /* Check that only PMICs are connected to the bus */ + while (dev) { + ut_asserteq(device_get_uclass_id(dev), UCLASS_PMIC); + device_find_next_child(&dev); + } + + return 0; +} +DM_TEST(dm_test_spmi_probe, UT_TESTF_SCAN_FDT); + +/* Test if it's possible to read bus directly and indirectly */ +static int dm_test_spmi_access(struct unit_test_state *uts) +{ + const char *pmic_name = "pm8916@0"; + struct udevice *bus, *pmic; + + ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus)); + + ut_assertok(device_get_child(bus, 0, &pmic)); + + /* Sanity check if it's proper PMIC */ + ut_asserteq_str(pmic_name, pmic->name); + + /* Read PMIC ID reg using SPMI bus - it assumes it has slaveID == 0*/ + ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x4), 0x10); + ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x5), 0x5); + + /* Read ID reg via pmic interface */ + ut_asserteq(pmic_reg_read(pmic, 0xC004), 0x10); + ut_asserteq(pmic_reg_read(pmic, 0xC005), 0x5); + + return 0; +} +DM_TEST(dm_test_spmi_access, UT_TESTF_SCAN_FDT); + + +/* Test if it's possible to access GPIO that should be in pmic */ +static int dm_test_spmi_access_peripheral(struct unit_test_state *uts) +{ + struct udevice *dev; + unsigned int offset, gpio; + const char *name; + int offset_count; + + /* Get second pin of PMIC GPIO */ + ut_assertok(gpio_lookup_name("spmi1", &dev, &offset, &gpio)); + + /* Check if PMIC is parent */ + ut_asserteq(device_get_uclass_id(dev->parent), UCLASS_PMIC); + + /* This should be second gpio */ + ut_asserteq(1, offset); + + name = gpio_get_bank_info(dev, &offset_count); + + /* Check bank name */ + ut_asserteq_str("spmi", name); + /* Check pin count */ + ut_asserteq(4, offset_count); + + ut_assertok(gpio_request(gpio, "testing")); + + /* Try to set/clear gpio */ + ut_assertok(gpio_direction_output(gpio, 0)); + ut_asserteq(gpio_get_value(gpio), 0); + ut_assertok(gpio_direction_output(gpio, 1)); + ut_asserteq(gpio_get_value(gpio), 1); + ut_assertok(gpio_direction_input(gpio)); + ut_asserteq(gpio_get_value(gpio), 1); + + ut_assertok(gpio_free(gpio)); + + return 0; +} +DM_TEST(dm_test_spmi_access_peripheral, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/syscon-reset.c b/roms/u-boot/test/dm/syscon-reset.c new file mode 100644 index 000000000..eeaddf883 --- /dev/null +++ b/roms/u-boot/test/dm/syscon-reset.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <dm/test.h> +#include <regmap.h> +#include <reset.h> +#include <syscon.h> +#include <test/ut.h> +#include <asm/test.h> +#include <linux/bitops.h> + +/* The following values must match the device tree */ +#define TEST_RESET_REG 1 +#define TEST_RESET_ASSERT_HIGH 0 +#define TEST_RESET_ASSERT (TEST_RESET_ASSERT_HIGH ? (u32)-1 : (u32)0) +#define TEST_RESET_DEASSERT (~TEST_RESET_ASSERT) + +#define TEST_RESET_VALID 15 +#define TEST_RESET_NOMASK 30 +#define TEST_RESET_OUTOFRANGE 60 + +static int dm_test_syscon_reset(struct unit_test_state *uts) +{ + struct regmap *map; + struct reset_ctl rst; + struct udevice *reset; + struct udevice *syscon; + struct udevice *syscon_reset; + uint reg; + + ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "syscon-reset-test", + &reset)); + ut_assertok(uclass_get_device_by_name(UCLASS_SYSCON, "syscon@0", + &syscon)); + ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "syscon-reset", + &syscon_reset)); + ut_assertok_ptr((map = syscon_get_regmap(syscon))); + + ut_asserteq(-EINVAL, reset_get_by_name(reset, "no_mask", &rst)); + ut_asserteq(-EINVAL, reset_get_by_name(reset, "out_of_range", &rst)); + ut_assertok(reset_get_by_name(reset, "valid", &rst)); + + sandbox_set_enable_memio(true); + ut_assertok(regmap_write(map, TEST_RESET_REG, TEST_RESET_DEASSERT)); + ut_assertok(reset_assert(&rst)); + ut_assertok(regmap_read(map, TEST_RESET_REG, ®)); + ut_asserteq(TEST_RESET_DEASSERT ^ BIT(TEST_RESET_VALID), reg); + + ut_assertok(reset_deassert(&rst)); + ut_assertok(regmap_read(map, TEST_RESET_REG, ®)); + ut_asserteq(TEST_RESET_DEASSERT, reg); + + return 0; +} +DM_TEST(dm_test_syscon_reset, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/syscon.c b/roms/u-boot/test/dm/syscon.c new file mode 100644 index 000000000..be2329723 --- /dev/null +++ b/roms/u-boot/test/dm/syscon.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <syscon.h> +#include <regmap.h> +#include <asm/test.h> +#include <dm/test.h> +#include <linux/err.h> +#include <test/test.h> +#include <test/ut.h> + +/* Base test of system controllers */ +static int dm_test_syscon_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev)); + ut_asserteq(SYSCON0, dev->driver_data); + + ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev)); + ut_asserteq(SYSCON1, dev->driver_data); + + ut_asserteq(-ENODEV, uclass_get_device(UCLASS_SYSCON, 2, &dev)); + + return 0; +} +DM_TEST(dm_test_syscon_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test system controller finding */ +static int dm_test_syscon_by_driver_data(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(syscon_get_by_driver_data(SYSCON0, &dev)); + ut_asserteq(SYSCON0, dev->driver_data); + + ut_assertok(syscon_get_by_driver_data(SYSCON1, &dev)); + ut_asserteq(SYSCON1, dev->driver_data); + + ut_asserteq(-ENODEV, syscon_get_by_driver_data(2, &dev)); + + return 0; +} +DM_TEST(dm_test_syscon_by_driver_data, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test system controller by phandle */ +static int dm_test_syscon_by_phandle(struct unit_test_state *uts) +{ + struct udevice *dev; + struct regmap *map; + + ut_assertok(uclass_get_device_by_name(UCLASS_TEST_PROBE, "test4", + &dev)); + + ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev, "first-syscon")); + map = syscon_regmap_lookup_by_phandle(dev, "first-syscon"); + ut_assert(map); + ut_assert(!IS_ERR(map)); + ut_asserteq(1, map->range_count); + + ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev, + "second-sys-ctrl")); + map = syscon_regmap_lookup_by_phandle(dev, "second-sys-ctrl"); + ut_assert(map); + ut_assert(!IS_ERR(map)); + ut_asserteq(4, map->range_count); + + ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev, + "third-syscon")); + map = syscon_regmap_lookup_by_phandle(dev, "third-syscon"); + ut_assert(map); + ut_assert(!IS_ERR(map)); + ut_asserteq(4, map->range_count); + + ut_assert(IS_ERR(syscon_regmap_lookup_by_phandle(dev, "not-present"))); + + return 0; +} +DM_TEST(dm_test_syscon_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/sysinfo-gpio.c b/roms/u-boot/test/dm/sysinfo-gpio.c new file mode 100644 index 000000000..2e494b3f3 --- /dev/null +++ b/roms/u-boot/test/dm/sysinfo-gpio.c @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <sysinfo.h> +#include <asm/gpio.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int dm_test_sysinfo_gpio(struct unit_test_state *uts) +{ + char buf[64]; + int val; + struct udevice *sysinfo, *gpio; + + ut_assertok(uclass_get_device_by_name(UCLASS_SYSINFO, "sysinfo-gpio", + &sysinfo)); + ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio)); + + /* + * Set up pins: pull-up (1), pull-down (0) and floating (2). This should + * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19 + */ + sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_DOWN); + sandbox_gpio_set_flags(gpio, 17, 0); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(19, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("rev_a", buf); + + /* + * Set up pins: floating (2), pull-up (1) and pull-down (0). This should + * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5 + */ + sandbox_gpio_set_flags(gpio, 15, 0); + sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_UP); + sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_DOWN); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(5, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("foo", buf); + + /* + * Set up pins: floating (2), pull-up (1) and pull-down (0). This should + * result in digits 1 2 0, i.e. 1 * 9 + 2 * 3 = 15 + */ + sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_DOWN); + sandbox_gpio_set_flags(gpio, 16, 0); + sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_UP); + ut_assertok(sysinfo_detect(sysinfo)); + ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val)); + ut_asserteq(15, val); + ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf), + buf)); + ut_asserteq_str("unknown", buf); + + return 0; +} +DM_TEST(dm_test_sysinfo_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/sysinfo.c b/roms/u-boot/test/dm/sysinfo.c new file mode 100644 index 000000000..96b3a8eba --- /dev/null +++ b/roms/u-boot/test/dm/sysinfo.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <dm/test.h> +#include <sysinfo.h> +#include <test/test.h> +#include <test/ut.h> + +#include "../../drivers/sysinfo/sandbox.h" + +static int dm_test_sysinfo(struct unit_test_state *uts) +{ + struct udevice *sysinfo; + bool called_detect = false; + char str[64]; + int i; + + ut_assertok(sysinfo_get(&sysinfo)); + ut_assert(sysinfo); + + ut_asserteq(-EPERM, sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, + &called_detect)); + ut_assert(!called_detect); + + sysinfo_detect(sysinfo); + + ut_assertok(sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT, + &called_detect)); + ut_assert(called_detect); + + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); + ut_assertok(strcmp(str, "R'lyeh")); + + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i)); + ut_asserteq(0, i); + + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i)); + ut_asserteq(100, i); + + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); + ut_assertok(strcmp(str, "Carcosa")); + + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i)); + ut_asserteq(1, i); + + ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i)); + ut_asserteq(99, i); + + ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str), + str)); + ut_assertok(strcmp(str, "Yuggoth")); + + return 0; +} + +DM_TEST(dm_test_sysinfo, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/sysreset.c b/roms/u-boot/test/dm/sysreset.c new file mode 100644 index 000000000..691683c56 --- /dev/null +++ b/roms/u-boot/test/dm/sysreset.c @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <sysreset.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that we can use particular sysreset devices */ +static int dm_test_sysreset_base(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + struct udevice *dev; + + /* Device 0 is the platform data device - it should never respond */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 0, &dev)); + ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_WARM)); + ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_COLD)); + ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_POWER)); + + /* Device 1 is the warm sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev)); + ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_WARM)); + ut_asserteq(-ENOSYS, sysreset_request(dev, SYSRESET_COLD)); + ut_asserteq(-ENOSYS, sysreset_request(dev, SYSRESET_POWER)); + + state->sysreset_allowed[SYSRESET_WARM] = true; + ut_asserteq(-EINPROGRESS, sysreset_request(dev, SYSRESET_WARM)); + state->sysreset_allowed[SYSRESET_WARM] = false; + + /* Device 2 is the cold sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev)); + ut_asserteq(-ENOSYS, sysreset_request(dev, SYSRESET_WARM)); + state->sysreset_allowed[SYSRESET_COLD] = false; + ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_COLD)); + state->sysreset_allowed[SYSRESET_COLD] = true; + state->sysreset_allowed[SYSRESET_POWER] = false; + ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_POWER)); + state->sysreset_allowed[SYSRESET_POWER] = true; + + return 0; +} +DM_TEST(dm_test_sysreset_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_sysreset_get_status(struct unit_test_state *uts) +{ + struct udevice *dev; + char msg[64]; + + /* Device 1 is the warm sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev)); + ut_assertok(sysreset_get_status(dev, msg, sizeof(msg))); + ut_asserteq_str("Reset Status: WARM", msg); + + /* Device 2 is the cold sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev)); + ut_assertok(sysreset_get_status(dev, msg, sizeof(msg))); + ut_asserteq_str("Reset Status: COLD", msg); + + return 0; +} +DM_TEST(dm_test_sysreset_get_status, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can walk through the sysreset devices */ +static int dm_test_sysreset_walk(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + + /* If we generate a power sysreset, we will exit sandbox! */ + state->sysreset_allowed[SYSRESET_WARM] = false; + state->sysreset_allowed[SYSRESET_COLD] = false; + state->sysreset_allowed[SYSRESET_POWER] = false; + state->sysreset_allowed[SYSRESET_POWER_OFF] = false; + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_WARM)); + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD)); + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER)); + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER_OFF)); + + /* + * Enable cold system reset - this should make cold system reset work, + * plus a warm system reset should be promoted to cold, since this is + * the next step along. + */ + state->sysreset_allowed[SYSRESET_WARM] = true; + ut_asserteq(-EINPROGRESS, sysreset_walk(SYSRESET_WARM)); + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD)); + ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER)); + state->sysreset_allowed[SYSRESET_COLD] = true; + state->sysreset_allowed[SYSRESET_POWER] = true; + + return 0; +} +DM_TEST(dm_test_sysreset_walk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_sysreset_get_last(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Device 1 is the warm sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev)); + ut_asserteq(SYSRESET_WARM, sysreset_get_last(dev)); + + /* Device 2 is the cold sysreset device */ + ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev)); + ut_asserteq(SYSRESET_POWER, sysreset_get_last(dev)); + + /* This is device 0, the non-DT one */ + ut_asserteq(SYSRESET_POWER, sysreset_get_last_walk()); + + return 0; +} +DM_TEST(dm_test_sysreset_get_last, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/tee.c b/roms/u-boot/test/dm/tee.c new file mode 100644 index 000000000..7a11bf891 --- /dev/null +++ b/roms/u-boot/test/dm/tee.c @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018 Linaro Limited + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <dm/test.h> +#include <sandboxtee.h> +#include <tee.h> +#include <test/test.h> +#include <test/ut.h> +#include <tee/optee_ta_avb.h> +#include <tee/optee_ta_rpc_test.h> + +static int open_session(struct udevice *dev, u32 *session, + struct tee_optee_ta_uuid *uuid) +{ + struct tee_open_session_arg arg; + int rc; + + memset(&arg, 0, sizeof(arg)); + tee_optee_ta_uuid_to_octets(arg.uuid, uuid); + rc = tee_open_session(dev, &arg, 0, NULL); + if (rc) + return rc; + if (arg.ret) + return -EIO; + *session = arg.session; + + return 0; +} + +static int invoke_func_avb(struct udevice *dev, u32 session) +{ + struct tee_param param = { .attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT }; + struct tee_invoke_arg arg; + + memset(&arg, 0, sizeof(arg)); + arg.session = session; + arg.func = TA_AVB_CMD_READ_LOCK_STATE; + + if (tee_invoke_func(dev, &arg, 1, ¶m) || arg.ret) + return -1; + + return 0; +} + +static int invoke_func_rpc_test(struct udevice *dev, u32 session, + u64 op, u64 busnum, u64 chip_addr, + u64 xfer_flags, u8 *buf, size_t buf_size) +{ + struct tee_param param[2]; + struct tee_invoke_arg arg; + struct tee_shm *shm_buf; + int rc; + + memset(&arg, 0, sizeof(arg)); + arg.session = session; + arg.func = op; + + rc = tee_shm_alloc(dev, buf_size, + TEE_SHM_ALLOC, &shm_buf); + if (rc) + return rc; + + if (op == TA_RPC_TEST_CMD_I2C_WRITE) + memcpy(shm_buf->addr, buf, buf_size); + + memset(param, 0, sizeof(param)); + param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT; + param[0].u.value.a = busnum; + param[0].u.value.b = chip_addr; + param[0].u.value.c = xfer_flags; + param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT; + param[1].u.memref.shm = shm_buf; + param[1].u.memref.size = buf_size; + + if (tee_invoke_func(dev, &arg, 2, param) || arg.ret) { + rc = -1; + goto out; + } + + if (op == TA_RPC_TEST_CMD_I2C_READ) + memcpy(buf, shm_buf->addr, buf_size); +out: + tee_shm_free(shm_buf); + return rc; +} + +static int match(struct tee_version_data *vers, const void *data) +{ + return vers->gen_caps & TEE_GEN_CAP_GP; +} + +struct test_tee_vars { + struct tee_shm *reg_shm; + struct tee_shm *alloc_shm; +}; + +static int test_tee(struct unit_test_state *uts, struct test_tee_vars *vars) +{ + struct tee_version_data vers; + struct udevice *dev; + struct sandbox_tee_state *state; + struct tee_optee_ta_uuid avb_uuid = TA_AVB_UUID; + u32 session = 0; + int rc; + u8 data[128]; + + dev = tee_find_device(NULL, match, NULL, &vers); + ut_assert(dev); + state = dev_get_priv(dev); + ut_assert(!state->session); + + rc = open_session(dev, &session, &avb_uuid); + ut_assert(!rc); + ut_assert(session == state->session); + + rc = invoke_func_avb(dev, session); + ut_assert(!rc); + + rc = tee_close_session(dev, session); + ut_assert(!rc); + ut_assert(!state->session); + + ut_assert(!state->num_shms); + rc = tee_shm_register(dev, data, sizeof(data), 0, &vars->reg_shm); + ut_assert(!rc); + ut_assert(state->num_shms == 1); + + rc = tee_shm_alloc(dev, 256, 0, &vars->alloc_shm); + ut_assert(!rc); + ut_assert(state->num_shms == 2); + + ut_assert(tee_shm_is_registered(vars->reg_shm, dev)); + ut_assert(tee_shm_is_registered(vars->alloc_shm, dev)); + + tee_shm_free(vars->reg_shm); + vars->reg_shm = NULL; + tee_shm_free(vars->alloc_shm); + vars->alloc_shm = NULL; + ut_assert(!state->num_shms); + + return rc; +} + +#define I2C_BUF_SIZE 64 + +static int test_tee_rpc(struct unit_test_state *uts) +{ + struct tee_version_data vers; + struct udevice *dev; + struct sandbox_tee_state *state; + struct tee_optee_ta_uuid rpc_test_uuid = TA_RPC_TEST_UUID; + u32 session = 0; + int rc; + + char *test_str = "Test string"; + u8 data[I2C_BUF_SIZE] = {0}; + u8 data_from_eeprom[I2C_BUF_SIZE] = {0}; + + /* Use sandbox I2C EEPROM emulation; bus: 0, chip: 0x2c */ + u64 bus = 0; + u64 chip = 0x2c; + u64 xfer_flags = 0; + + dev = tee_find_device(NULL, match, NULL, &vers); + ut_assert(dev); + state = dev_get_priv(dev); + ut_assert(!state->session); + + /* Test RPC call asking for I2C service */ + rc = open_session(dev, &session, &rpc_test_uuid); + ut_assert(!rc); + ut_assert(session == state->session); + + /* Write buffer */ + strncpy((char *)data, test_str, strlen(test_str)); + rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_WRITE, + bus, chip, xfer_flags, data, sizeof(data)); + ut_assert(!rc); + + /* Read buffer */ + rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_READ, + bus, chip, xfer_flags, data_from_eeprom, + sizeof(data_from_eeprom)); + ut_assert(!rc); + + /* Compare */ + ut_assert(!memcmp(data, data_from_eeprom, sizeof(data))); + + rc = tee_close_session(dev, session); + ut_assert(!rc); + ut_assert(!state->session); + + return rc; +} + +static int dm_test_tee(struct unit_test_state *uts) +{ + struct test_tee_vars vars = { NULL, NULL }; + int rc = test_tee(uts, &vars); + + if (rc) + goto out; + + if (IS_ENABLED(CONFIG_OPTEE_TA_RPC_TEST)) + rc = test_tee_rpc(uts); +out: + /* In case test_tee() asserts these may still remain allocated */ + tee_shm_free(vars.reg_shm); + tee_shm_free(vars.alloc_shm); + + return rc; +} + +DM_TEST(dm_test_tee, UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/test-dm.c b/roms/u-boot/test/dm/test-dm.c new file mode 100644 index 000000000..9ba2ba23d --- /dev/null +++ b/roms/u-boot/test/dm/test-dm.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <command.h> +#include <console.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <asm/state.h> +#include <dm/root.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * dm_test_run() - Run driver model tests + * + * Run all the available driver model tests, or a selection + * + * @test_name: Name of single test to run (e.g. "dm_test_fdt_pre_reloc" or just + * "fdt_pre_reloc"), or NULL to run all + * @return 0 if all tests passed, 1 if not + */ +static int dm_test_run(const char *test_name) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(dm_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(dm_test); + int ret; + + ret = ut_run_list("driver model", "dm_test_", tests, n_ents, test_name); + + return ret ? CMD_RET_FAILURE : 0; +} + +int do_ut_dm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + const char *test_name = NULL; + + if (argc > 1) + test_name = argv[1]; + + return dm_test_run(test_name); +} diff --git a/roms/u-boot/test/dm/test-driver.c b/roms/u-boot/test/dm/test-driver.c new file mode 100644 index 000000000..02cb974b0 --- /dev/null +++ b/roms/u-boot/test/dm/test-driver.c @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +int dm_testdrv_op_count[DM_TEST_OP_COUNT]; + +static int testdrv_ping(struct udevice *dev, int pingval, int *pingret) +{ + const struct dm_test_pdata *pdata = dev_get_plat(dev); + struct dm_test_priv *priv = dev_get_priv(dev); + + *pingret = pingval + pdata->ping_add; + priv->ping_total += *pingret; + + return 0; +} + +static const struct test_ops test_ops = { + .ping = testdrv_ping, +}; + +static int test_bind(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + + /* Private data should not be allocated */ + ut_assert(!dev_get_priv(dev)); + + dm_testdrv_op_count[DM_TEST_OP_BIND]++; + return 0; +} + +static int test_probe(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + struct dm_test_priv *priv = dev_get_priv(dev); + + /* Private data should be allocated */ + ut_assert(priv); + + dm_testdrv_op_count[DM_TEST_OP_PROBE]++; + priv->ping_total += DM_TEST_START_TOTAL; + return 0; +} + +static int test_remove(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + + /* Private data should still be allocated */ + ut_assert(dev_get_priv(dev)); + + dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; + return 0; +} + +static int test_unbind(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + + /* Private data should not be allocated */ + ut_assert(!dev_get_priv(dev)); + + dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; + return 0; +} + +U_BOOT_DRIVER(test_drv) = { + .name = "test_drv", + .id = UCLASS_TEST, + .ops = &test_ops, + .bind = test_bind, + .probe = test_probe, + .remove = test_remove, + .unbind = test_unbind, + .priv_auto = sizeof(struct dm_test_priv), +}; + +U_BOOT_DRIVER(test2_drv) = { + .name = "test2_drv", + .id = UCLASS_TEST, + .ops = &test_ops, + .bind = test_bind, + .probe = test_probe, + .remove = test_remove, + .unbind = test_unbind, + .priv_auto = sizeof(struct dm_test_priv), +}; + +static int test_manual_drv_ping(struct udevice *dev, int pingval, int *pingret) +{ + *pingret = pingval + 2; + + return 0; +} + +static const struct test_ops test_manual_ops = { + .ping = test_manual_drv_ping, +}; + +static int test_manual_bind(struct udevice *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_BIND]++; + + return 0; +} + +static int test_manual_probe(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + + dm_testdrv_op_count[DM_TEST_OP_PROBE]++; + if (!uts->force_fail_alloc) + dev_set_priv(dev, calloc(1, sizeof(struct dm_test_priv))); + if (!dev_get_priv(dev)) + return -ENOMEM; + + return 0; +} + +static int test_manual_remove(struct udevice *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_REMOVE]++; + return 0; +} + +static int test_manual_unbind(struct udevice *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_UNBIND]++; + return 0; +} + +U_BOOT_DRIVER(test_manual_drv) = { + .name = "test_manual_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, +}; + +U_BOOT_DRIVER(test_pre_reloc_drv) = { + .name = "test_pre_reloc_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, + .flags = DM_FLAG_PRE_RELOC, +}; + +U_BOOT_DRIVER(test_act_dma_drv) = { + .name = "test_act_dma_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, + .flags = DM_FLAG_ACTIVE_DMA, +}; + +U_BOOT_DRIVER(test_vital_clk_drv) = { + .name = "test_vital_clk_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, + .flags = DM_FLAG_VITAL, +}; + +U_BOOT_DRIVER(test_act_dma_vital_clk_drv) = { + .name = "test_act_dma_vital_clk_drv", + .id = UCLASS_TEST, + .ops = &test_manual_ops, + .bind = test_manual_bind, + .probe = test_manual_probe, + .remove = test_manual_remove, + .unbind = test_manual_unbind, + .flags = DM_FLAG_VITAL | DM_FLAG_ACTIVE_DMA, +}; diff --git a/roms/u-boot/test/dm/test-fdt.c b/roms/u-boot/test/dm/test-fdt.c new file mode 100644 index 000000000..8866d4d95 --- /dev/null +++ b/roms/u-boot/test/dm/test-fdt.c @@ -0,0 +1,1200 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <dm/test.h> +#include <dm/root.h> +#include <dm/device-internal.h> +#include <dm/devres.h> +#include <dm/uclass-internal.h> +#include <dm/util.h> +#include <dm/lists.h> +#include <dm/of_access.h> +#include <linux/ioport.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct dm_testprobe_pdata { + int probe_err; +}; + +static int testprobe_drv_probe(struct udevice *dev) +{ + struct dm_testprobe_pdata *pdata = dev_get_plat(dev); + + return pdata->probe_err; +} + +static const struct udevice_id testprobe_ids[] = { + { .compatible = "denx,u-boot-probe-test" }, + { } +}; + +U_BOOT_DRIVER(testprobe_drv) = { + .name = "testprobe_drv", + .of_match = testprobe_ids, + .id = UCLASS_TEST_PROBE, + .probe = testprobe_drv_probe, + .plat_auto = sizeof(struct dm_testprobe_pdata), +}; + +UCLASS_DRIVER(testprobe) = { + .name = "testprobe", + .id = UCLASS_TEST_PROBE, + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; + +struct dm_testdevres_pdata { + void *ptr; +}; + +struct dm_testdevres_priv { + void *ptr; + void *ptr_ofdata; +}; + +static int testdevres_drv_bind(struct udevice *dev) +{ + struct dm_testdevres_pdata *pdata = dev_get_plat(dev); + + pdata->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0); + + return 0; +} + +static int testdevres_drv_of_to_plat(struct udevice *dev) +{ + struct dm_testdevres_priv *priv = dev_get_priv(dev); + + priv->ptr_ofdata = devm_kmalloc(dev, TEST_DEVRES_SIZE3, 0); + + return 0; +} + +static int testdevres_drv_probe(struct udevice *dev) +{ + struct dm_testdevres_priv *priv = dev_get_priv(dev); + + priv->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE2, 0); + + return 0; +} + +static const struct udevice_id testdevres_ids[] = { + { .compatible = "denx,u-boot-devres-test" }, + { } +}; + +U_BOOT_DRIVER(testdevres_drv) = { + .name = "testdevres_drv", + .of_match = testdevres_ids, + .id = UCLASS_TEST_DEVRES, + .bind = testdevres_drv_bind, + .of_to_plat = testdevres_drv_of_to_plat, + .probe = testdevres_drv_probe, + .plat_auto = sizeof(struct dm_testdevres_pdata), + .priv_auto = sizeof(struct dm_testdevres_priv), +}; + +UCLASS_DRIVER(testdevres) = { + .name = "testdevres", + .id = UCLASS_TEST_DEVRES, + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; + +int dm_check_devices(struct unit_test_state *uts, int num_devices) +{ + struct udevice *dev; + int ret; + int i; + + /* + * Now check that the ping adds are what we expect. This is using the + * ping-add property in each node. + */ + for (i = 0; i < num_devices; i++) { + uint32_t base; + + ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev); + ut_assert(!ret); + + /* + * Get the 'ping-expect' property, which tells us what the + * ping add should be. We don't use the plat because we + * want to test the code that sets that up + * (testfdt_drv_probe()). + */ + base = fdtdec_get_addr(gd->fdt_blob, dev_of_offset(dev), + "ping-expect"); + debug("dev=%d, base=%d: %s\n", i, base, + fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL)); + + ut_assert(!dm_check_operations(uts, dev, base, + dev_get_priv(dev))); + } + + return 0; +} + +/* Test that FDT-based binding works correctly */ +static int dm_test_fdt(struct unit_test_state *uts) +{ + const int num_devices = 9; + struct udevice *dev; + struct uclass *uc; + int ret; + int i; + + ret = dm_extended_scan(false); + ut_assert(!ret); + + ret = uclass_get(UCLASS_TEST_FDT, &uc); + ut_assert(!ret); + + /* These are num_devices compatible root-level device tree nodes */ + ut_asserteq(num_devices, list_count_items(&uc->dev_head)); + + /* Each should have platform data but no private data */ + for (i = 0; i < num_devices; i++) { + ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev); + ut_assert(!ret); + ut_assert(!dev_get_priv(dev)); + ut_assert(dev_get_plat(dev)); + } + + ut_assertok(dm_check_devices(uts, num_devices)); + + return 0; +} +DM_TEST(dm_test_fdt, 0); + +static int dm_test_alias_highest_id(struct unit_test_state *uts) +{ + int ret; + + ret = dev_read_alias_highest_id("ethernet"); + ut_asserteq(5, ret); + + ret = dev_read_alias_highest_id("gpio"); + ut_asserteq(3, ret); + + ret = dev_read_alias_highest_id("pci"); + ut_asserteq(2, ret); + + ret = dev_read_alias_highest_id("i2c"); + ut_asserteq(0, ret); + + ret = dev_read_alias_highest_id("deadbeef"); + ut_asserteq(-1, ret); + + return 0; +} +DM_TEST(dm_test_alias_highest_id, 0); + +static int dm_test_fdt_pre_reloc(struct unit_test_state *uts) +{ + struct uclass *uc; + int ret; + + ret = dm_scan_fdt(true); + ut_assert(!ret); + + ret = uclass_get(UCLASS_TEST_FDT, &uc); + ut_assert(!ret); + + /* + * These are 2 pre-reloc devices: + * one with "u-boot,dm-pre-reloc" property (a-test node), and the other + * one whose driver marked with DM_FLAG_PRE_RELOC flag (h-test node). + */ + ut_asserteq(2, list_count_items(&uc->dev_head)); + + return 0; +} +DM_TEST(dm_test_fdt_pre_reloc, 0); + +/* Test that sequence numbers are allocated properly */ +static int dm_test_fdt_uclass_seq(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* A few basic santiy tests */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 3, &dev)); + ut_asserteq_str("b-test", dev->name); + ut_asserteq(3, dev_seq(dev)); + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 8, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_asserteq(8, dev_seq(dev)); + + /* + * This device has no alias so gets the next value after all available + * aliases. The last alias is testfdt12 + */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 13, &dev)); + ut_asserteq_str("d-test", dev->name); + ut_asserteq(13, dev_seq(dev)); + + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 9, + &dev)); + ut_asserteq_ptr(NULL, dev); + + /* Test aliases */ + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 6, &dev)); + ut_asserteq_str("e-test", dev->name); + ut_asserteq(6, dev_seq(dev)); + + /* + * Note that c-test nodes are not probed since it is not a top-level + * node + */ + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev)); + ut_asserteq_str("b-test", dev->name); + ut_asserteq(3, dev_seq(dev)); + + /* + * d-test wants sequence number 3 also, but it can't have it because + * b-test gets it first. + */ + ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 2, &dev)); + ut_asserteq_str("d-test", dev->name); + ut_asserteq(13, dev_seq(dev)); + + /* g-test gets the next value after f-test */ + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 15, &dev)); + ut_asserteq_str("g-test", dev->name); + ut_asserteq(15, dev_seq(dev)); + + /* And we should still have holes in our sequence numbers */ + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 0, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 1, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 2, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 4, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 7, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 9, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 10, + &dev)); + ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 11, + &dev)); + + return 0; +} +DM_TEST(dm_test_fdt_uclass_seq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* More tests for sequence numbers */ +static int dm_test_fdt_uclass_seq_manual(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* + * Since DM_UC_FLAG_NO_AUTO_SEQ is set for this uclass, only testfdtm1 + * should get a sequence number assigned + */ + ut_assertok(uclass_get_device(UCLASS_TEST_FDT_MANUAL, 0, &dev)); + ut_asserteq_str("testfdtm0", dev->name); + ut_asserteq(-1, dev_seq(dev)); + + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT_MANUAL, 1, &dev)); + ut_asserteq_str("testfdtm1", dev->name); + ut_asserteq(1, dev_seq(dev)); + + ut_assertok(uclass_get_device(UCLASS_TEST_FDT_MANUAL, 2, &dev)); + ut_asserteq_str("testfdtm2", dev->name); + ut_asserteq(-1, dev_seq(dev)); + + return 0; +} +DM_TEST(dm_test_fdt_uclass_seq_manual, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts) +{ + struct udevice *dev; + ofnode node; + + /* Check creating a device with an alias */ + node = ofnode_path("/some-bus/c-test@1"); + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), + "c-test@1", NULL, node, &dev)); + ut_asserteq(12, dev_seq(dev)); + ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 12, &dev)); + ut_asserteq_str("c-test@1", dev->name); + + /* + * Now bind a device without an alias. It should not get the next + * sequence number after all aliases, and existing bound devices. The + * last alias is 12, so we have: + * + * 13 d-test + * 14 f-test + * 15 g-test + * 16 h-test + * 17 another-test + * 18 chosen-test + * + * So next available is 19 + */ + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), + "fred", NULL, ofnode_null(), &dev)); + ut_asserteq(19, dev_seq(dev)); + + ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test), + "fred2", NULL, ofnode_null(), &dev)); + ut_asserteq(20, dev_seq(dev)); + + return 0; +} +DM_TEST(dm_test_fdt_uclass_seq_more, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that we can find a device by device tree offset */ +static int dm_test_fdt_offset(struct unit_test_state *uts) +{ + const void *blob = gd->fdt_blob; + struct udevice *dev; + int node; + + node = fdt_path_offset(blob, "/e-test"); + ut_assert(node > 0); + ut_assertok(uclass_get_device_by_of_offset(UCLASS_TEST_FDT, node, + &dev)); + ut_asserteq_str("e-test", dev->name); + + /* This node should not be bound */ + node = fdt_path_offset(blob, "/junk"); + ut_assert(node > 0); + ut_asserteq(-ENODEV, uclass_get_device_by_of_offset(UCLASS_TEST_FDT, + node, &dev)); + + /* This is not a top level node so should not be probed */ + node = fdt_path_offset(blob, "/some-bus/c-test@5"); + ut_assert(node > 0); + ut_asserteq(-ENODEV, uclass_get_device_by_of_offset(UCLASS_TEST_FDT, + node, &dev)); + + return 0; +} +DM_TEST(dm_test_fdt_offset, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +/** + * Test various error conditions with uclass_first_device() and + * uclass_next_device() + */ +static int dm_test_first_next_device(struct unit_test_state *uts) +{ + struct dm_testprobe_pdata *pdata; + struct udevice *dev, *parent = NULL; + int count; + int ret; + + /* There should be 4 devices */ + for (ret = uclass_first_device(UCLASS_TEST_PROBE, &dev), count = 0; + dev; + ret = uclass_next_device(&dev)) { + count++; + parent = dev_get_parent(dev); + } + ut_assertok(ret); + ut_asserteq(4, count); + + /* Remove them and try again, with an error on the second one */ + ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 1, &dev)); + pdata = dev_get_plat(dev); + pdata->probe_err = -ENOMEM; + device_remove(parent, DM_REMOVE_NORMAL); + ut_assertok(uclass_first_device(UCLASS_TEST_PROBE, &dev)); + ut_asserteq(-ENOMEM, uclass_next_device(&dev)); + ut_asserteq_ptr(dev, NULL); + + /* Now an error on the first one */ + ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 0, &dev)); + pdata = dev_get_plat(dev); + pdata->probe_err = -ENOENT; + device_remove(parent, DM_REMOVE_NORMAL); + ut_asserteq(-ENOENT, uclass_first_device(UCLASS_TEST_PROBE, &dev)); + + return 0; +} +DM_TEST(dm_test_first_next_device, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test iteration through devices in a uclass */ +static int dm_test_uclass_foreach(struct unit_test_state *uts) +{ + struct udevice *dev; + struct uclass *uc; + int count; + + count = 0; + uclass_id_foreach_dev(UCLASS_TEST_FDT, dev, uc) + count++; + ut_asserteq(9, count); + + count = 0; + uclass_foreach_dev(dev, uc) + count++; + ut_asserteq(9, count); + + return 0; +} +DM_TEST(dm_test_uclass_foreach, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/** + * check_devices() - Check return values and pointers + * + * This runs through a full sequence of uclass_first_device_check()... + * uclass_next_device_check() checking that the return values and devices + * are correct. + * + * @uts: Test state + * @devlist: List of expected devices + * @mask: Indicates which devices should return an error. Device n should + * return error (-NOENT - n) if bit n is set, or no error (i.e. 0) if + * bit n is clear. + */ +static int check_devices(struct unit_test_state *uts, + struct udevice *devlist[], int mask) +{ + int expected_ret; + struct udevice *dev; + int i; + + expected_ret = (mask & 1) ? -ENOENT : 0; + mask >>= 1; + ut_asserteq(expected_ret, + uclass_first_device_check(UCLASS_TEST_PROBE, &dev)); + for (i = 0; i < 4; i++) { + ut_asserteq_ptr(devlist[i], dev); + expected_ret = (mask & 1) ? -ENOENT - (i + 1) : 0; + mask >>= 1; + ut_asserteq(expected_ret, uclass_next_device_check(&dev)); + } + ut_asserteq_ptr(NULL, dev); + + return 0; +} + +/* Test uclass_first_device_check() and uclass_next_device_check() */ +static int dm_test_first_next_ok_device(struct unit_test_state *uts) +{ + struct dm_testprobe_pdata *pdata; + struct udevice *dev, *parent = NULL, *devlist[4]; + int count; + int ret; + + /* There should be 4 devices */ + count = 0; + for (ret = uclass_first_device_check(UCLASS_TEST_PROBE, &dev); + dev; + ret = uclass_next_device_check(&dev)) { + ut_assertok(ret); + devlist[count++] = dev; + parent = dev_get_parent(dev); + } + ut_asserteq(4, count); + ut_assertok(uclass_first_device_check(UCLASS_TEST_PROBE, &dev)); + ut_assertok(check_devices(uts, devlist, 0)); + + /* Remove them and try again, with an error on the second one */ + pdata = dev_get_plat(devlist[1]); + pdata->probe_err = -ENOENT - 1; + device_remove(parent, DM_REMOVE_NORMAL); + ut_assertok(check_devices(uts, devlist, 1 << 1)); + + /* Now an error on the first one */ + pdata = dev_get_plat(devlist[0]); + pdata->probe_err = -ENOENT - 0; + device_remove(parent, DM_REMOVE_NORMAL); + ut_assertok(check_devices(uts, devlist, 3 << 0)); + + /* Now errors on all */ + pdata = dev_get_plat(devlist[2]); + pdata->probe_err = -ENOENT - 2; + pdata = dev_get_plat(devlist[3]); + pdata->probe_err = -ENOENT - 3; + device_remove(parent, DM_REMOVE_NORMAL); + ut_assertok(check_devices(uts, devlist, 0xf << 0)); + + return 0; +} +DM_TEST(dm_test_first_next_ok_device, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static const struct udevice_id fdt_dummy_ids[] = { + { .compatible = "denx,u-boot-fdt-dummy", }, + { } +}; + +UCLASS_DRIVER(fdt_dummy) = { + .name = "fdt-dummy", + .id = UCLASS_TEST_DUMMY, + .flags = DM_UC_FLAG_SEQ_ALIAS, +}; + +U_BOOT_DRIVER(fdt_dummy_drv) = { + .name = "fdt_dummy_drv", + .of_match = fdt_dummy_ids, + .id = UCLASS_TEST_DUMMY, +}; + +static int dm_test_fdt_translation(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt32_t dma_addr[2]; + + /* Some simple translations */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + ut_asserteq_str("dev@0,0", dev->name); + ut_asserteq(0x8000, dev_read_addr(dev)); + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 1, &dev)); + ut_asserteq_str("dev@1,100", dev->name); + ut_asserteq(0x9000, dev_read_addr(dev)); + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 2, &dev)); + ut_asserteq_str("dev@2,200", dev->name); + ut_asserteq(0xA000, dev_read_addr(dev)); + + /* No translation for busses with #size-cells == 0 */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 3, &dev)); + ut_asserteq_str("dev@42", dev->name); + ut_asserteq(0x42, dev_read_addr(dev)); + + /* dma address translation */ + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + dma_addr[0] = cpu_to_be32(0); + dma_addr[1] = cpu_to_be32(0); + ut_asserteq(0x10000000, dev_translate_dma_address(dev, dma_addr)); + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 1, &dev)); + dma_addr[0] = cpu_to_be32(1); + dma_addr[1] = cpu_to_be32(0x100); + ut_asserteq(0x20000000, dev_translate_dma_address(dev, dma_addr)); + + return 0; +} +DM_TEST(dm_test_fdt_translation, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_get_addr_ptr_flat(struct unit_test_state *uts) +{ + struct udevice *gpio, *dev; + void *ptr; + + /* Test for missing reg property */ + ut_assertok(uclass_first_device_err(UCLASS_GPIO, &gpio)); + ut_assertnull(devfdt_get_addr_ptr(gpio)); + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + ptr = devfdt_get_addr_ptr(dev); + ut_asserteq_ptr((void *)0x8000, ptr); + + return 0; +} +DM_TEST(dm_test_fdt_get_addr_ptr_flat, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +static int dm_test_fdt_remap_addr_flat(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = devfdt_get_addr(dev); + ut_asserteq(0x8000, addr); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, devfdt_remap_addr(dev)); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_flat, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +static int dm_test_fdt_remap_addr_index_flat(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + fdt_size_t size; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = devfdt_get_addr_size_index(dev, 0, &size); + ut_asserteq(0x8000, addr); + ut_asserteq(0x1000, size); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, devfdt_remap_addr_index(dev, 0)); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_index_flat, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +static int dm_test_fdt_remap_addr_name_flat(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + fdt_size_t size; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = devfdt_get_addr_size_name(dev, "sandbox-dummy-0", &size); + ut_asserteq(0x8000, addr); + ut_asserteq(0x1000, size); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, devfdt_remap_addr_name(dev, "sandbox-dummy-0")); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_name_flat, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE); + +static int dm_test_fdt_remap_addr_live(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = dev_read_addr(dev); + ut_asserteq(0x8000, addr); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, dev_remap_addr(dev)); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_live, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_remap_addr_index_live(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + fdt_size_t size; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = dev_read_addr_size_index(dev, 0, &size); + ut_asserteq(0x8000, addr); + ut_asserteq(0x1000, size); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, dev_remap_addr_index(dev, 0)); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_index_live, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_remap_addr_name_live(struct unit_test_state *uts) +{ + struct udevice *dev; + fdt_addr_t addr; + fdt_size_t size; + void *paddr; + + ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev)); + + addr = dev_read_addr_size_name(dev, "sandbox-dummy-0", &size); + ut_asserteq(0x8000, addr); + ut_asserteq(0x1000, size); + + paddr = map_physmem(addr, 0, MAP_NOCACHE); + ut_assertnonnull(paddr); + ut_asserteq_ptr(paddr, dev_remap_addr_name(dev, "sandbox-dummy-0")); + + return 0; +} +DM_TEST(dm_test_fdt_remap_addr_name_live, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_livetree_writing(struct unit_test_state *uts) +{ + struct udevice *dev; + ofnode node; + + if (!of_live_active()) { + printf("Live tree not active; ignore test\n"); + return 0; + } + + /* Test enabling devices */ + + node = ofnode_path("/usb@2"); + + ut_assert(!of_device_is_available(ofnode_to_np(node))); + ofnode_set_enabled(node, true); + ut_assert(of_device_is_available(ofnode_to_np(node))); + + device_bind_driver_to_node(dm_root(), "usb_sandbox", "usb@2", node, + &dev); + ut_assertok(uclass_find_device_by_seq(UCLASS_USB, 2, &dev)); + + /* Test string property setting */ + + ut_assert(device_is_compatible(dev, "sandbox,usb")); + ofnode_write_string(node, "compatible", "gdsys,super-usb"); + ut_assert(device_is_compatible(dev, "gdsys,super-usb")); + ofnode_write_string(node, "compatible", "sandbox,usb"); + ut_assert(device_is_compatible(dev, "sandbox,usb")); + + /* Test setting generic properties */ + + /* Non-existent in DTB */ + ut_asserteq(FDT_ADDR_T_NONE, dev_read_addr(dev)); + /* reg = 0x42, size = 0x100 */ + ut_assertok(ofnode_write_prop(node, "reg", 8, + "\x00\x00\x00\x42\x00\x00\x01\x00")); + ut_asserteq(0x42, dev_read_addr(dev)); + + /* Test disabling devices */ + + device_remove(dev, DM_REMOVE_NORMAL); + device_unbind(dev); + + ut_assert(of_device_is_available(ofnode_to_np(node))); + ofnode_set_enabled(node, false); + ut_assert(!of_device_is_available(ofnode_to_np(node))); + + return 0; +} +DM_TEST(dm_test_fdt_livetree_writing, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_fdt_disable_enable_by_path(struct unit_test_state *uts) +{ + ofnode node; + + if (!of_live_active()) { + printf("Live tree not active; ignore test\n"); + return 0; + } + + node = ofnode_path("/usb@2"); + + /* Test enabling devices */ + + ut_assert(!of_device_is_available(ofnode_to_np(node))); + dev_enable_by_path("/usb@2"); + ut_assert(of_device_is_available(ofnode_to_np(node))); + + /* Test disabling devices */ + + ut_assert(of_device_is_available(ofnode_to_np(node))); + dev_disable_by_path("/usb@2"); + ut_assert(!of_device_is_available(ofnode_to_np(node))); + + return 0; +} +DM_TEST(dm_test_fdt_disable_enable_by_path, UT_TESTF_SCAN_PDATA | + UT_TESTF_SCAN_FDT); + +/* Test a few uclass phandle functions */ +static int dm_test_fdt_phandle(struct unit_test_state *uts) +{ + struct udevice *back, *dev, *dev2; + + ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &back)); + ut_assertnonnull(back); + ut_asserteq(-ENOENT, uclass_find_device_by_phandle(UCLASS_REGULATOR, + back, "missing", &dev)); + ut_assertok(uclass_find_device_by_phandle(UCLASS_REGULATOR, back, + "power-supply", &dev)); + ut_assertnonnull(dev); + ut_asserteq(0, device_active(dev)); + ut_asserteq_str("ldo1", dev->name); + ut_assertok(uclass_get_device_by_phandle(UCLASS_REGULATOR, back, + "power-supply", &dev2)); + ut_asserteq_ptr(dev, dev2); + + return 0; +} +DM_TEST(dm_test_fdt_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test device_find_first_child_by_uclass() */ +static int dm_test_first_child(struct unit_test_state *uts) +{ + struct udevice *i2c, *dev, *dev2; + + ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c)); + ut_assertok(device_find_first_child_by_uclass(i2c, UCLASS_RTC, &dev)); + ut_asserteq_str("rtc@43", dev->name); + ut_assertok(device_find_child_by_name(i2c, "rtc@43", &dev2)); + ut_asserteq_ptr(dev, dev2); + ut_assertok(device_find_child_by_name(i2c, "rtc@61", &dev2)); + ut_asserteq_str("rtc@61", dev2->name); + + ut_assertok(device_find_first_child_by_uclass(i2c, UCLASS_I2C_EEPROM, + &dev)); + ut_asserteq_str("eeprom@2c", dev->name); + ut_assertok(device_find_child_by_name(i2c, "eeprom@2c", &dev2)); + ut_asserteq_ptr(dev, dev2); + + ut_asserteq(-ENODEV, device_find_first_child_by_uclass(i2c, + UCLASS_VIDEO, &dev)); + ut_asserteq(-ENODEV, device_find_child_by_name(i2c, "missing", &dev)); + + return 0; +} +DM_TEST(dm_test_first_child, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test integer functions in dm_read_...() */ +static int dm_test_read_int(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val32; + s32 sval; + uint val; + u64 val64; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + ut_assertok(dev_read_u32(dev, "int-value", &val32)); + ut_asserteq(1234, val32); + + ut_asserteq(-EINVAL, dev_read_u32(dev, "missing", &val32)); + ut_asserteq(6, dev_read_u32_default(dev, "missing", 6)); + + ut_asserteq(1234, dev_read_u32_default(dev, "int-value", 6)); + ut_asserteq(1234, val32); + + ut_asserteq(-EINVAL, dev_read_s32(dev, "missing", &sval)); + ut_asserteq(6, dev_read_s32_default(dev, "missing", 6)); + + ut_asserteq(-1234, dev_read_s32_default(dev, "uint-value", 6)); + ut_assertok(dev_read_s32(dev, "uint-value", &sval)); + ut_asserteq(-1234, sval); + + val = 0; + ut_asserteq(-EINVAL, dev_read_u32u(dev, "missing", &val)); + ut_assertok(dev_read_u32u(dev, "uint-value", &val)); + ut_asserteq(-1234, val); + + ut_assertok(dev_read_u64(dev, "int64-value", &val64)); + ut_asserteq_64(0x1111222233334444, val64); + + ut_asserteq_64(-EINVAL, dev_read_u64(dev, "missing", &val64)); + ut_asserteq_64(6, dev_read_u64_default(dev, "missing", 6)); + + ut_asserteq_64(0x1111222233334444, + dev_read_u64_default(dev, "int64-value", 6)); + + return 0; +} +DM_TEST(dm_test_read_int, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_read_int_index(struct unit_test_state *uts) +{ + struct udevice *dev; + u32 val32; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_asserteq(-EINVAL, dev_read_u32_index(dev, "missing", 0, &val32)); + ut_asserteq(19, dev_read_u32_index_default(dev, "missing", 0, 19)); + + ut_assertok(dev_read_u32_index(dev, "int-array", 0, &val32)); + ut_asserteq(5678, val32); + ut_assertok(dev_read_u32_index(dev, "int-array", 1, &val32)); + ut_asserteq(9123, val32); + ut_assertok(dev_read_u32_index(dev, "int-array", 2, &val32)); + ut_asserteq(4567, val32); + ut_asserteq(-EOVERFLOW, dev_read_u32_index(dev, "int-array", 3, + &val32)); + + ut_asserteq(5678, dev_read_u32_index_default(dev, "int-array", 0, 2)); + ut_asserteq(9123, dev_read_u32_index_default(dev, "int-array", 1, 2)); + ut_asserteq(4567, dev_read_u32_index_default(dev, "int-array", 2, 2)); + ut_asserteq(2, dev_read_u32_index_default(dev, "int-array", 3, 2)); + + return 0; +} +DM_TEST(dm_test_read_int_index, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int dm_test_read_phandle(struct unit_test_state *uts) +{ + struct udevice *dev; + struct ofnode_phandle_args args; + int ret; + const char prop[] = "test-gpios"; + const char cell[] = "#gpio-cells"; + const char prop2[] = "phandle-value"; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + + /* Test dev_count_phandle_with_args with cell name */ + ret = dev_count_phandle_with_args(dev, "missing", cell, 0); + ut_asserteq(-ENOENT, ret); + ret = dev_count_phandle_with_args(dev, prop, "#invalid", 0); + ut_asserteq(-EINVAL, ret); + ut_asserteq(5, dev_count_phandle_with_args(dev, prop, cell, 0)); + + /* Test dev_read_phandle_with_args with cell name */ + ret = dev_read_phandle_with_args(dev, "missing", cell, 0, 0, &args); + ut_asserteq(-ENOENT, ret); + ret = dev_read_phandle_with_args(dev, prop, "#invalid", 0, 0, &args); + ut_asserteq(-EINVAL, ret); + ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 0, &args)); + ut_asserteq(1, args.args_count); + ut_asserteq(1, args.args[0]); + ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 1, &args)); + ut_asserteq(1, args.args_count); + ut_asserteq(4, args.args[0]); + ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 2, &args)); + ut_asserteq(5, args.args_count); + ut_asserteq(5, args.args[0]); + ut_asserteq(1, args.args[4]); + ret = dev_read_phandle_with_args(dev, prop, cell, 0, 3, &args); + ut_asserteq(-ENOENT, ret); + ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 4, &args)); + ut_asserteq(1, args.args_count); + ut_asserteq(12, args.args[0]); + ret = dev_read_phandle_with_args(dev, prop, cell, 0, 5, &args); + ut_asserteq(-ENOENT, ret); + + /* Test dev_count_phandle_with_args with cell count */ + ret = dev_count_phandle_with_args(dev, "missing", NULL, 2); + ut_asserteq(-ENOENT, ret); + ut_asserteq(3, dev_count_phandle_with_args(dev, prop2, NULL, 1)); + + /* Test dev_read_phandle_with_args with cell count */ + ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 0, &args)); + ut_asserteq(1, ofnode_valid(args.node)); + ut_asserteq(1, args.args_count); + ut_asserteq(10, args.args[0]); + ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 1, &args); + ut_asserteq(-EINVAL, ret); + ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 2, &args)); + ut_asserteq(1, ofnode_valid(args.node)); + ut_asserteq(1, args.args_count); + ut_asserteq(30, args.args[0]); + ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 3, &args); + ut_asserteq(-ENOENT, ret); + + return 0; +} +DM_TEST(dm_test_read_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test iteration through devices by drvdata */ +static int dm_test_uclass_drvdata(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT, + DM_TEST_TYPE_FIRST, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT, + DM_TEST_TYPE_SECOND, &dev)); + ut_asserteq_str("d-test", dev->name); + + ut_asserteq(-ENODEV, uclass_first_device_drvdata(UCLASS_TEST_FDT, + DM_TEST_TYPE_COUNT, + &dev)); + + return 0; +} +DM_TEST(dm_test_uclass_drvdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test device_first_child_ofdata_err(), etc. */ +static int dm_test_child_ofdata(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + int count; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &bus)); + count = 0; + device_foreach_child_of_to_plat(dev, bus) { + ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID); + ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)); + count++; + } + ut_asserteq(3, count); + + return 0; +} +DM_TEST(dm_test_child_ofdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test device_first_child_err(), etc. */ +static int dm_test_first_child_probe(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + int count; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &bus)); + count = 0; + device_foreach_child_probe(dev, bus) { + ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID); + ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED); + count++; + } + ut_asserteq(3, count); + + return 0; +} +DM_TEST(dm_test_first_child_probe, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test that ofdata is read for parents before children */ +static int dm_test_ofdata_order(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + ut_assertok(uclass_find_first_device(UCLASS_I2C, &bus)); + ut_assertnonnull(bus); + ut_assert(!(dev_get_flags(bus) & DM_FLAG_PLATDATA_VALID)); + + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + ut_assert(!(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID)); + + /* read the child's ofdata which should cause the parent's to be read */ + ut_assertok(device_of_to_plat(dev)); + ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID); + ut_assert(dev_get_flags(bus) & DM_FLAG_PLATDATA_VALID); + + ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED)); + ut_assert(!(dev_get_flags(bus) & DM_FLAG_ACTIVATED)); + + return 0; +} +DM_TEST(dm_test_ofdata_order, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test dev_decode_display_timing() */ +static int dm_test_decode_display_timing(struct unit_test_state *uts) +{ + struct udevice *dev; + struct display_timing timing; + + ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev)); + ut_asserteq_str("a-test", dev->name); + + ut_assertok(dev_decode_display_timing(dev, 0, &timing)); + ut_assert(timing.hactive.typ == 240); + ut_assert(timing.hback_porch.typ == 7); + ut_assert(timing.hfront_porch.typ == 6); + ut_assert(timing.hsync_len.typ == 1); + ut_assert(timing.vactive.typ == 320); + ut_assert(timing.vback_porch.typ == 5); + ut_assert(timing.vfront_porch.typ == 8); + ut_assert(timing.vsync_len.typ == 2); + ut_assert(timing.pixelclock.typ == 6500000); + ut_assert(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH); + ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_LOW)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH)); + ut_assert(timing.flags & DISPLAY_FLAGS_VSYNC_LOW); + ut_assert(timing.flags & DISPLAY_FLAGS_DE_HIGH); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_LOW)); + ut_assert(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE); + ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)); + ut_assert(timing.flags & DISPLAY_FLAGS_INTERLACED); + ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLESCAN); + ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLECLK); + + ut_assertok(dev_decode_display_timing(dev, 1, &timing)); + ut_assert(timing.hactive.typ == 480); + ut_assert(timing.hback_porch.typ == 59); + ut_assert(timing.hfront_porch.typ == 10); + ut_assert(timing.hsync_len.typ == 12); + ut_assert(timing.vactive.typ == 800); + ut_assert(timing.vback_porch.typ == 15); + ut_assert(timing.vfront_porch.typ == 17); + ut_assert(timing.vsync_len.typ == 16); + ut_assert(timing.pixelclock.typ == 9000000); + ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH)); + ut_assert(timing.flags & DISPLAY_FLAGS_HSYNC_LOW); + ut_assert(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH); + ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_LOW)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_HIGH)); + ut_assert(timing.flags & DISPLAY_FLAGS_DE_LOW); + ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)); + ut_assert(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE); + ut_assert(!(timing.flags & DISPLAY_FLAGS_INTERLACED)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLESCAN)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLECLK)); + + ut_assertok(dev_decode_display_timing(dev, 2, &timing)); + ut_assert(timing.hactive.typ == 800); + ut_assert(timing.hback_porch.typ == 89); + ut_assert(timing.hfront_porch.typ == 164); + ut_assert(timing.hsync_len.typ == 11); + ut_assert(timing.vactive.typ == 480); + ut_assert(timing.vback_porch.typ == 23); + ut_assert(timing.vfront_porch.typ == 10); + ut_assert(timing.vsync_len.typ == 13); + ut_assert(timing.pixelclock.typ == 33500000); + ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_LOW)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_LOW)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_HIGH)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_LOW)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_INTERLACED)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLESCAN)); + ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLECLK)); + + ut_assert(dev_decode_display_timing(dev, 3, &timing)); + return 0; +} +DM_TEST(dm_test_decode_display_timing, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test read_resourcee() */ +static int dm_test_read_resource(struct unit_test_state *uts) +{ + struct udevice *dev; + struct resource res; + + /* test resource without translation */ + ut_assertok(uclass_find_device_by_name(UCLASS_SIMPLE_BUS, "syscon@2", &dev)); + ut_assertok(dev_read_resource(dev, 0, &res)); + ut_asserteq(0x40, res.start); + ut_asserteq(0x44, res.end); + ut_assertok(dev_read_resource(dev, 1, &res)); + ut_asserteq(0x48, res.start); + ut_asserteq(0x4d, res.end); + + /* test resource with translation */ + ut_assertok(uclass_find_device_by_name(UCLASS_TEST_DUMMY, "dev@1,100", &dev)); + ut_assertok(dev_read_resource(dev, 0, &res)); + ut_asserteq(0x9000, res.start); + ut_asserteq(0x9fff, res.end); + + /* test named resource */ + ut_assertok(uclass_find_device_by_name(UCLASS_TEST_DUMMY, "dev@0,0", &dev)); + ut_assertok(dev_read_resource_byname(dev, "sandbox-dummy-0", &res)); + ut_asserteq(0x8000, res.start); + ut_asserteq(0x8fff, res.end); + + return 0; +} + +DM_TEST(dm_test_read_resource, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/test-uclass.c b/roms/u-boot/test/dm/test-uclass.c new file mode 100644 index 000000000..067701734 --- /dev/null +++ b/roms/u-boot/test/dm/test-uclass.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2013 Google, Inc + * + * (C) Copyright 2012 + * Pavel Herrmann <morpheus.ibis@gmail.com> + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <dm.h> +#include <errno.h> +#include <asm/io.h> +#include <dm/test.h> +#include <linux/list.h> +#include <test/test.h> +#include <test/ut.h> + +int test_ping(struct udevice *dev, int pingval, int *pingret) +{ + const struct test_ops *ops = device_get_ops(dev); + + if (!ops->ping) + return -ENOSYS; + + return ops->ping(dev, pingval, pingret); +} + +static int test_post_bind(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + struct dm_test_perdev_uc_pdata *uc_pdata; + + dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++; + ut_assert(!device_active(dev)); + + uc_pdata = dev_get_uclass_plat(dev); + ut_assert(uc_pdata); + + uc_pdata->intval1 = TEST_UC_PDATA_INTVAL1; + uc_pdata->intval2 = TEST_UC_PDATA_INTVAL2; + uc_pdata->intval3 = TEST_UC_PDATA_INTVAL3; + + return 0; +} + +static int test_pre_unbind(struct udevice *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]++; + + return 0; +} + +static int test_pre_probe(struct udevice *dev) +{ + struct dm_test_uclass_perdev_priv *priv = dev_get_uclass_priv(dev); + struct unit_test_state *uts = test_get_state(); + + dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]++; + ut_assert(priv); + ut_assert(device_active(dev)); + + return 0; +} + +static int test_post_probe(struct udevice *dev) +{ + struct unit_test_state *uts = test_get_state(); + struct udevice *prev = list_entry(dev->uclass_node.prev, + struct udevice, uclass_node); + + struct dm_test_uclass_perdev_priv *priv = dev_get_uclass_priv(dev); + struct uclass *uc = dev->uclass; + + dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]++; + ut_assert(priv); + ut_assert(device_active(dev)); + priv->base_add = 0; + if (uts->skip_post_probe) + return 0; + if (&prev->uclass_node != &uc->dev_head) { + struct dm_test_uclass_perdev_priv *prev_uc_priv + = dev_get_uclass_priv(prev); + struct dm_test_pdata *pdata = dev_get_plat(prev); + + ut_assert(pdata); + ut_assert(prev_uc_priv); + priv->base_add = prev_uc_priv->base_add + pdata->ping_add; + } + + return 0; +} + +static int test_pre_remove(struct udevice *dev) +{ + dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]++; + + return 0; +} + +static int test_init(struct uclass *uc) +{ + struct unit_test_state *uts = test_get_state(); + + dm_testdrv_op_count[DM_TEST_OP_INIT]++; + ut_assert(uclass_get_priv(uc)); + + return 0; +} + +static int test_destroy(struct uclass *uc) +{ + dm_testdrv_op_count[DM_TEST_OP_DESTROY]++; + + return 0; +} + +UCLASS_DRIVER(test) = { + .name = "test", + .id = UCLASS_TEST, + .post_bind = test_post_bind, + .pre_unbind = test_pre_unbind, + .pre_probe = test_pre_probe, + .post_probe = test_post_probe, + .pre_remove = test_pre_remove, + .init = test_init, + .destroy = test_destroy, + .priv_auto = sizeof(struct dm_test_uclass_priv), + .per_device_auto = sizeof(struct dm_test_uclass_perdev_priv), + .per_device_plat_auto = sizeof(struct dm_test_perdev_uc_pdata), +}; diff --git a/roms/u-boot/test/dm/timer.c b/roms/u-boot/test/dm/timer.c new file mode 100644 index 000000000..70043b9ee --- /dev/null +++ b/roms/u-boot/test/dm/timer.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> + */ + +#include <common.h> +#include <dm.h> +#include <timer.h> +#include <dm/test.h> +#include <dm/device-internal.h> +#include <test/test.h> +#include <test/ut.h> +#include <asm/cpu.h> + +/* + * Basic test of the timer uclass. + */ +static int dm_test_timer_base(struct unit_test_state *uts) +{ + struct udevice *dev; + + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@0", &dev)); + ut_asserteq(1000000, timer_get_rate(dev)); + + return 0; +} +DM_TEST(dm_test_timer_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test of timebase fallback + */ +static int dm_test_timer_timebase_fallback(struct unit_test_state *uts) +{ + struct udevice *dev; + + cpu_sandbox_set_current("cpu-test1"); + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev)); + ut_asserteq(3000000, timer_get_rate(dev)); + ut_assertok(device_remove(dev, DM_REMOVE_NORMAL)); + + cpu_sandbox_set_current("cpu-test2"); + ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev)); + ut_asserteq(2000000, timer_get_rate(dev)); + + cpu_sandbox_set_current("cpu-test1"); + + return 0; +} +DM_TEST(dm_test_timer_timebase_fallback, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/usb.c b/roms/u-boot/test/dm/usb.c new file mode 100644 index 000000000..5d6ceefce --- /dev/null +++ b/roms/u-boot/test/dm/usb.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2015 Google, Inc + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <part.h> +#include <usb.h> +#include <asm/io.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/device-internal.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +struct keyboard_test_data { + const char modifiers; + const char scancode; + const char result[6]; +}; + +/* Test that sandbox USB works correctly */ +static int dm_test_usb_base(struct unit_test_state *uts) +{ + struct udevice *bus; + + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 0, &bus)); + ut_assertok(uclass_get_device(UCLASS_USB, 0, &bus)); + ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 2, &bus)); + + return 0; +} +DM_TEST(dm_test_usb_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Test that we can use the flash stick. This is more of a functional test. It + * covers scanning the bug, setting up a hub and a flash stick and reading + * data from the flash stick. + */ +static int dm_test_usb_flash(struct unit_test_state *uts) +{ + struct udevice *dev; + struct blk_desc *dev_desc; + char cmp[1024]; + + state_set_skip_delays(true); + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); + ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc)); + + /* Read a few blocks and look for the string we expect */ + ut_asserteq(512, dev_desc->blksz); + memset(cmp, '\0', sizeof(cmp)); + ut_asserteq(2, blk_dread(dev_desc, 0, 2, cmp)); + ut_assertok(strcmp(cmp, "this is a test")); + ut_assertok(usb_stop()); + + return 0; +} +DM_TEST(dm_test_usb_flash, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* test that we can handle multiple storage devices */ +static int dm_test_usb_multi(struct unit_test_state *uts) +{ + struct udevice *dev; + + state_set_skip_delays(true); + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); + ut_assertok(usb_stop()); + + return 0; +} +DM_TEST(dm_test_usb_multi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* test that we have an associated ofnode with the usb device */ +static int dm_test_usb_fdt_node(struct unit_test_state *uts) +{ + struct udevice *dev; + ofnode node; + + state_set_skip_delays(true); + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); + node = ofnode_path("/usb@1/hub/usbstor@1"); + ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev))); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev)); + ut_asserteq(1, ofnode_equal(ofnode_null(), dev_ofnode(dev))); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); + node = ofnode_path("/usb@1/hub/usbstor@3"); + ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev))); + ut_assertok(usb_stop()); + + return 0; +} +DM_TEST(dm_test_usb_fdt_node, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int count_usb_devices(void) +{ + struct udevice *hub; + struct uclass *uc; + int count = 0; + int ret; + + ret = uclass_get(UCLASS_USB_HUB, &uc); + if (ret) + return ret; + + uclass_foreach_dev(hub, uc) { + struct udevice *dev; + + count++; + for (device_find_first_child(hub, &dev); + dev; + device_find_next_child(&dev)) { + count++; + } + } + + return count; +} + +/* test that no USB devices are found after we stop the stack */ +static int dm_test_usb_stop(struct unit_test_state *uts) +{ + struct udevice *dev; + + /* Scan and check that all devices are present */ + state_set_skip_delays(true); + ut_assertok(usb_init()); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev)); + ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev)); + ut_asserteq(6, count_usb_devices()); + ut_assertok(usb_stop()); + ut_asserteq(0, count_usb_devices()); + + return 0; +} +DM_TEST(dm_test_usb_stop, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/** + * dm_test_usb_keyb() - test USB keyboard driver + * + * This test copies USB keyboard scan codes into the key buffer of the USB + * keyboard emulation driver. These are picked up during emulated interrupts + * by the USB keyboard driver and converted to characters and escape sequences. + * The test then reads and verifies these characters and escape sequences from + * the standard input. + * + * TODO: The following features are not yet tested: + * + * * LED status + * * caps-lock + * * num-lock + * * numerical pad keys + * + * TODO: The following features are not yet implemented by the USB keyboard + * driver and therefore not tested: + * + * * modifiers for non-alpha-numeric keys, e.g. <SHIFT><TAB> and <ALT><F4> + * * some special keys, e.g. <PRINT> + * * some modifiers, e.g. <ALT> and <META> + * * alternative keyboard layouts + * + * @uts: unit test state + * Return: 0 on success + */ +static int dm_test_usb_keyb(struct unit_test_state *uts) +{ + struct udevice *dev; + const struct keyboard_test_data *pos; + const struct keyboard_test_data kbd_test_data[] = { + /* <A> */ + {0x00, 0x04, "a"}, + /* <B> */ + {0x00, 0x05, "b"}, + /* <C> */ + {0x00, 0x06, "c"}, + /* <D> */ + {0x00, 0x07, "d"}, + /* <E> */ + {0x00, 0x08, "e"}, + /* <F> */ + {0x00, 0x09, "f"}, + /* <G> */ + {0x00, 0x0a, "g"}, + /* <H> */ + {0x00, 0x0b, "h"}, + /* <I> */ + {0x00, 0x0c, "i"}, + /* <J> */ + {0x00, 0x0d, "j"}, + /* <K> */ + {0x00, 0x0e, "k"}, + /* <L> */ + {0x00, 0x0f, "l"}, + /* <M> */ + {0x00, 0x10, "m"}, + /* <N> */ + {0x00, 0x11, "n"}, + /* <O> */ + {0x00, 0x12, "o"}, + /* <P> */ + {0x00, 0x13, "p"}, + /* <Q> */ + {0x00, 0x14, "q"}, + /* <R> */ + {0x00, 0x15, "r"}, + /* <S> */ + {0x00, 0x16, "s"}, + /* <T> */ + {0x00, 0x17, "t"}, + /* <U> */ + {0x00, 0x18, "u"}, + /* <V> */ + {0x00, 0x19, "v"}, + /* <W> */ + {0x00, 0x1a, "w"}, + /* <X> */ + {0x00, 0x1b, "x"}, + /* <Y> */ + {0x00, 0x1c, "y"}, + /* <Z> */ + {0x00, 0x1d, "z"}, + + /* <LEFT-SHIFT><A> */ + {0x02, 0x04, "A"}, + /* <RIGHT-SHIFT><Z> */ + {0x20, 0x1d, "Z"}, + + /* <LEFT-CONTROL><A> */ + {0x01, 0x04, "\x01"}, + /* <RIGHT-CONTROL><Z> */ + {0x10, 0x1d, "\x1a"}, + + /* <1> */ + {0x00, 0x1e, "1"}, + /* <2> */ + {0x00, 0x1f, "2"}, + /* <3> */ + {0x00, 0x20, "3"}, + /* <4> */ + {0x00, 0x21, "4"}, + /* <5> */ + {0x00, 0x22, "5"}, + /* <6> */ + {0x00, 0x23, "6"}, + /* <7> */ + {0x00, 0x24, "7"}, + /* <8> */ + {0x00, 0x25, "8"}, + /* <9> */ + {0x00, 0x26, "9"}, + /* <0> */ + {0x00, 0x27, "0"}, + + /* <LEFT-SHIFT><1> */ + {0x02, 0x1e, "!"}, + /* <RIGHT-SHIFT><2> */ + {0x20, 0x1f, "@"}, + /* <LEFT-SHIFT><3> */ + {0x02, 0x20, "#"}, + /* <RIGHT-SHIFT><4> */ + {0x20, 0x21, "$"}, + /* <LEFT-SHIFT><5> */ + {0x02, 0x22, "%"}, + /* <RIGHT-SHIFT><6> */ + {0x20, 0x23, "^"}, + /* <LEFT-SHIFT><7> */ + {0x02, 0x24, "&"}, + /* <RIGHT-SHIFT><8> */ + {0x20, 0x25, "*"}, + /* <LEFT-SHIFT><9> */ + {0x02, 0x26, "("}, + /* <RIGHT-SHIFT><0> */ + {0x20, 0x27, ")"}, + + /* <ENTER> */ + {0x00, 0x28, "\r"}, + /* <ESCAPE> */ + {0x00, 0x29, "\x1b"}, + /* <BACKSPACE> */ + {0x00, 0x2a, "\x08"}, + /* <TAB> */ + {0x00, 0x2b, "\x09"}, + /* <SPACE> */ + {0x00, 0x2c, " "}, + /* <MINUS> */ + {0x00, 0x2d, "-"}, + /* <EQUAL> */ + {0x00, 0x2e, "="}, + /* <LEFT BRACE> */ + {0x00, 0x2f, "["}, + /* <RIGHT BRACE> */ + {0x00, 0x30, "]"}, + /* <BACKSLASH> */ + {0x00, 0x31, "\\"}, + /* <HASH-TILDE> */ + {0x00, 0x32, "#"}, + /* <SEMICOLON> */ + {0x00, 0x33, ";"}, + /* <APOSTROPHE> */ + {0x00, 0x34, "'"}, + /* <GRAVE> */ + {0x00, 0x35, "`"}, + /* <COMMA> */ + {0x00, 0x36, ","}, + /* <DOT> */ + {0x00, 0x37, "."}, + /* <SLASH> */ + {0x00, 0x38, "/"}, + + /* <LEFT-SHIFT><ENTER> */ + {0x02, 0x28, "\r"}, + /* <RIGHT-SHIFT><ESCAPE> */ + {0x20, 0x29, "\x1b"}, + /* <LEFT-SHIFT><BACKSPACE> */ + {0x02, 0x2a, "\x08"}, + /* <RIGHT-SHIFT><TAB> */ + {0x20, 0x2b, "\x09"}, + /* <LEFT-SHIFT><SPACE> */ + {0x02, 0x2c, " "}, + /* <MINUS> */ + {0x20, 0x2d, "_"}, + /* <LEFT-SHIFT><EQUAL> */ + {0x02, 0x2e, "+"}, + /* <RIGHT-SHIFT><LEFT BRACE> */ + {0x20, 0x2f, "{"}, + /* <LEFT-SHIFT><RIGHT BRACE> */ + {0x02, 0x30, "}"}, + /* <RIGHT-SHIFT><BACKSLASH> */ + {0x20, 0x31, "|"}, + /* <LEFT-SHIFT><HASH-TILDE> */ + {0x02, 0x32, "~"}, + /* <RIGHT-SHIFT><SEMICOLON> */ + {0x20, 0x33, ":"}, + /* <LEFT-SHIFT><APOSTROPHE> */ + {0x02, 0x34, "\""}, + /* <RIGHT-SHIFT><GRAVE> */ + {0x20, 0x35, "~"}, + /* <LEFT-SHIFT><COMMA> */ + {0x02, 0x36, "<"}, + /* <RIGHT-SHIFT><DOT> */ + {0x20, 0x37, ">"}, + /* <LEFT-SHIFT><SLASH> */ + {0x02, 0x38, "?"}, +#ifdef CONFIG_USB_KEYBOARD_FN_KEYS + /* <F1> */ + {0x00, 0x3a, "\x1bOP"}, + /* <F2> */ + {0x00, 0x3b, "\x1bOQ"}, + /* <F3> */ + {0x00, 0x3c, "\x1bOR"}, + /* <F4> */ + {0x00, 0x3d, "\x1bOS"}, + /* <F5> */ + {0x00, 0x3e, "\x1b[15~"}, + /* <F6> */ + {0x00, 0x3f, "\x1b[17~"}, + /* <F7> */ + {0x00, 0x40, "\x1b[18~"}, + /* <F8> */ + {0x00, 0x41, "\x1b[19~"}, + /* <F9> */ + {0x00, 0x42, "\x1b[20~"}, + /* <F10> */ + {0x00, 0x43, "\x1b[21~"}, + /* <F11> */ + {0x00, 0x44, "\x1b[23~"}, + /* <F12> */ + {0x00, 0x45, "\x1b[24~"}, + /* <INSERT> */ + {0x00, 0x49, "\x1b[2~"}, + /* <HOME> */ + {0x00, 0x4a, "\x1b[H"}, + /* <PAGE UP> */ + {0x00, 0x4b, "\x1b[5~"}, + /* <DELETE> */ + {0x00, 0x4c, "\x1b[3~"}, + /* <END> */ + {0x00, 0x4d, "\x1b[F"}, + /* <PAGE DOWN> */ + {0x00, 0x4e, "\x1b[6~"}, + /* <RIGHT> */ + {0x00, 0x4f, "\x1b[C"}, + /* <LEFT> */ + {0x00, 0x50, "\x1b[D"}, + /* <DOWN> */ + {0x00, 0x51, "\x1b[B"}, + /* <UP> */ + {0x00, 0x52, "\x1b[A"}, +#endif /* CONFIG_USB_KEYBOARD_FN_KEYS */ + + /* End of list */ + {0x00, 0x00, "\0"} + }; + + + state_set_skip_delays(true); + ut_assertok(usb_init()); + + /* Initially there should be no characters */ + ut_asserteq(0, tstc()); + + ut_assertok(uclass_get_device_by_name(UCLASS_USB_EMUL, "keyb@3", + &dev)); + + /* + * Add scan codes to the USB keyboard buffer. They should appear as + * corresponding characters and escape sequences in stdin. + */ + for (pos = kbd_test_data; pos->scancode; ++pos) { + const char *c; + char scancodes[USB_KBD_BOOT_REPORT_SIZE] = {0}; + + scancodes[0] = pos->modifiers; + scancodes[2] = pos->scancode; + + ut_assertok(sandbox_usb_keyb_add_string(dev, scancodes)); + + for (c = pos->result; *c; ++c) { + ut_asserteq(1, tstc()); + ut_asserteq(*c, getchar()); + } + ut_asserteq(0, tstc()); + } + ut_assertok(usb_stop()); + + return 0; +} +DM_TEST(dm_test_usb_keyb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/video.c b/roms/u-boot/test/dm/video.c new file mode 100644 index 000000000..da0ae3622 --- /dev/null +++ b/roms/u-boot/test/dm/video.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2014 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <bzlib.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <mapmem.h> +#include <os.h> +#include <video.h> +#include <video_console.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +/* + * These tests use the standard sandbox frame buffer, the resolution of which + * is defined in the device tree. This only supports 16bpp so the tests only + * test that code path. It would be possible to adjust this fairly easily, + * by adjusting the bpix value in struct sandbox_sdl_plat. However the code + * in sandbox_sdl_sync() would also need to change to handle the different + * surface depth. + */ +/* Basic test of the video uclass */ +static int dm_test_video_base(struct unit_test_state *uts) +{ + struct video_priv *priv; + struct udevice *dev; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_asserteq(1366, video_get_xsize(dev)); + ut_asserteq(768, video_get_ysize(dev)); + priv = dev_get_uclass_priv(dev); + ut_asserteq(priv->fb_size, 1366 * 768 * 2); + + return 0; +} +DM_TEST(dm_test_video_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/** + * compress_frame_buffer() - Compress the frame buffer and return its size + * + * We want to write tests which perform operations on the video console and + * check that the frame buffer ends up with the correct contents. But it is + * painful to store 'known good' images for comparison with the frame + * buffer. As an alternative, we can compress the frame buffer and check the + * size of the compressed data. This provides a pretty good level of + * certainty and the resulting tests need only check a single value. + * + * If the copy framebuffer is enabled, this compares it to the main framebuffer + * too. + * + * @uts: Test state + * @dev: Video device + * @return compressed size of the frame buffer, or -ve on error + */ +static int compress_frame_buffer(struct unit_test_state *uts, + struct udevice *dev) +{ + struct video_priv *priv = dev_get_uclass_priv(dev); + struct video_priv *uc_priv = dev_get_uclass_priv(dev); + uint destlen; + void *dest; + int ret; + + destlen = priv->fb_size; + dest = malloc(priv->fb_size); + if (!dest) + return -ENOMEM; + ret = BZ2_bzBuffToBuffCompress(dest, &destlen, + priv->fb, priv->fb_size, + 3, 0, 0); + free(dest); + if (ret) + return ret; + + /* Check here that the copy frame buffer is working correctly */ + if (IS_ENABLED(CONFIG_VIDEO_COPY)) { + ut_assertf(!memcmp(uc_priv->fb, uc_priv->copy_fb, + uc_priv->fb_size), + "Copy framebuffer does not match fb"); + } + + return destlen; +} + +/* + * Call this function at any point to halt and show the current display. Be + * sure to run the test with the -l flag. + */ +static void __maybe_unused see_output(void) +{ + video_sync_all(); + while (1); +} + +/* Select the video console driver to use for a video device */ +static int select_vidconsole(struct unit_test_state *uts, const char *drv_name) +{ + struct sandbox_sdl_plat *plat; + struct udevice *dev; + + ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev)); + ut_assert(!device_active(dev)); + plat = dev_get_plat(dev); + plat->vidconsole_drv_name = "vidconsole0"; + + return 0; +} + +/* Test text output works on the video console */ +static int dm_test_video_text(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + int i; + +#define WHITE 0xffff +#define SCROLL_LINES 100 + + ut_assertok(select_vidconsole(uts, "vidconsole0")); + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_putc_xy(con, 0, 0, 'a'); + ut_asserteq(79, compress_frame_buffer(uts, dev)); + + vidconsole_putc_xy(con, 0, 0, ' '); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + for (i = 0; i < 20; i++) + vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i); + ut_asserteq(273, compress_frame_buffer(uts, dev)); + + vidconsole_set_row(con, 0, WHITE); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + for (i = 0; i < 20; i++) + vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i); + ut_asserteq(273, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_text, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test handling of special characters in the console */ +static int dm_test_video_chars(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very \amodest \bman\n\t\tand Has much to\b\bto be modest about."; + + ut_assertok(select_vidconsole(uts, "vidconsole0")); + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_put_string(con, test_string); + ut_asserteq(466, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_chars, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +#ifdef CONFIG_VIDEO_ANSI +#define ANSI_ESC "\x1b" +/* Test handling of ANSI escape sequences */ +static int dm_test_video_ansi(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + + ut_assertok(select_vidconsole(uts, "vidconsole0")); + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + + /* reference clear: */ + video_clear(con->parent); + video_sync(con->parent, false); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + /* test clear escape sequence: [2J */ + vidconsole_put_string(con, "A\tB\tC"ANSI_ESC"[2J"); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + /* test set-cursor: [%d;%df */ + vidconsole_put_string(con, "abc"ANSI_ESC"[2;2fab"ANSI_ESC"[4;4fcd"); + ut_asserteq(143, compress_frame_buffer(uts, dev)); + + /* test colors (30-37 fg color, 40-47 bg color) */ + vidconsole_put_string(con, ANSI_ESC"[30;41mfoo"); /* black on red */ + vidconsole_put_string(con, ANSI_ESC"[33;44mbar"); /* yellow on blue */ + ut_asserteq(272, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_ansi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +#endif + +/** + * check_vidconsole_output() - Run a text console test + * + * @uts: Test state + * @rot: Console rotation (0=normal orientation, 1=90 degrees clockwise, + * 2=upside down, 3=90 degree counterclockwise) + * @wrap_size: Expected size of compressed frame buffer for the wrap test + * @scroll_size: Same for the scroll test + * @return 0 on success + */ +static int check_vidconsole_output(struct unit_test_state *uts, int rot, + int wrap_size, int scroll_size) +{ + struct udevice *dev, *con; + struct sandbox_sdl_plat *plat; + int i; + + ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev)); + ut_assert(!device_active(dev)); + plat = dev_get_plat(dev); + plat->rot = rot; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + /* Check display wrap */ + for (i = 0; i < 120; i++) + vidconsole_put_char(con, 'A' + i % 50); + ut_asserteq(wrap_size, compress_frame_buffer(uts, dev)); + + /* Check display scrolling */ + for (i = 0; i < SCROLL_LINES; i++) { + vidconsole_put_char(con, 'A' + i % 50); + vidconsole_put_char(con, '\n'); + } + ut_asserteq(scroll_size, compress_frame_buffer(uts, dev)); + + /* If we scroll enough, the screen becomes blank again */ + for (i = 0; i < SCROLL_LINES; i++) + vidconsole_put_char(con, '\n'); + ut_asserteq(46, compress_frame_buffer(uts, dev)); + + return 0; +} + +/* Test text output through the console uclass */ +static int dm_test_video_context(struct unit_test_state *uts) +{ + ut_assertok(select_vidconsole(uts, "vidconsole0")); + ut_assertok(check_vidconsole_output(uts, 0, 788, 453)); + + return 0; +} +DM_TEST(dm_test_video_context, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation1(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 1, 1112, 680)); + + return 0; +} +DM_TEST(dm_test_video_rotation1, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation2(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 2, 783, 445)); + + return 0; +} +DM_TEST(dm_test_video_rotation2, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test rotated text output through the console uclass */ +static int dm_test_video_rotation3(struct unit_test_state *uts) +{ + ut_assertok(check_vidconsole_output(uts, 3, 1134, 681)); + + return 0; +} +DM_TEST(dm_test_video_rotation3, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Read a file into memory and return a pointer to it */ +static int read_file(struct unit_test_state *uts, const char *fname, + ulong *addrp) +{ + int buf_size = 100000; + ulong addr = 0; + int size, fd; + char *buf; + + buf = map_sysmem(addr, 0); + ut_assert(buf != NULL); + fd = os_open(fname, OS_O_RDONLY); + ut_assert(fd >= 0); + size = os_read(fd, buf, buf_size); + os_close(fd); + ut_assert(size >= 0); + ut_assert(size < buf_size); + *addrp = addr; + + return 0; +} + +/* Test drawing a bitmap file */ +static int dm_test_video_bmp(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1368, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test drawing a compressed bitmap file */ +static int dm_test_video_bmp_comp(struct unit_test_state *uts) +{ + struct udevice *dev; + ulong addr; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr)); + + ut_assertok(video_bmp_display(dev, addr, 0, 0, false)); + ut_asserteq(1368, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_bmp_comp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test TrueType console */ +static int dm_test_video_truetype(struct unit_test_state *uts) +{ + struct udevice *dev, *con; + const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye"; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_put_string(con, test_string); + ut_asserteq(12237, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_truetype, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test scrolling TrueType console */ +static int dm_test_video_truetype_scroll(struct unit_test_state *uts) +{ + struct sandbox_sdl_plat *plat; + struct udevice *dev, *con; + const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye"; + + ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev)); + ut_assert(!device_active(dev)); + plat = dev_get_plat(dev); + plat->font_size = 100; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_put_string(con, test_string); + ut_asserteq(35030, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_truetype_scroll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test TrueType backspace, within and across lines */ +static int dm_test_video_truetype_bs(struct unit_test_state *uts) +{ + struct sandbox_sdl_plat *plat; + struct udevice *dev, *con; + const char *test_string = "...Criticism may or may\b\b\b\b\b\bnot be agreeable, but seldom it is necessary\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bit is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things."; + + ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev)); + ut_assert(!device_active(dev)); + plat = dev_get_plat(dev); + plat->font_size = 100; + + ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev)); + ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con)); + vidconsole_put_string(con, test_string); + ut_asserteq(29018, compress_frame_buffer(uts, dev)); + + return 0; +} +DM_TEST(dm_test_video_truetype_bs, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/virtio.c b/roms/u-boot/test/dm/virtio.c new file mode 100644 index 000000000..9a7e658cc --- /dev/null +++ b/roms/u-boot/test/dm/virtio.c @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> + */ + +#include <common.h> +#include <dm.h> +#include <virtio_types.h> +#include <virtio.h> +#include <virtio_ring.h> +#include <dm/device-internal.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +/* Basic test of the virtio uclass */ +static int dm_test_virtio_base(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + u8 status; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-blk device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + ut_assertok(strcmp(dev->name, "virtio-blk#0")); + + /* check driver status */ + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(VIRTIO_CONFIG_S_ACKNOWLEDGE, status); + + return 0; +} +DM_TEST(dm_test_virtio_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test all of the virtio uclass ops */ +static int dm_test_virtio_all_ops(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + struct virtio_dev_priv *uc_priv; + uint offset = 0, len = 0, nvqs = 1; + void *buffer = NULL; + u8 status; + u32 counter; + u64 features; + struct virtqueue *vqs[2]; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-blk device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* + * fake the virtio device probe by filling in uc_priv->vdev + * which is used by virtio_find_vqs/virtio_del_vqs. + */ + uc_priv = dev_get_uclass_priv(bus); + ut_assertnonnull(uc_priv); + uc_priv->vdev = dev; + + /* test virtio_xxx APIs */ + ut_assertok(virtio_get_config(dev, offset, buffer, len)); + ut_assertok(virtio_set_config(dev, offset, buffer, len)); + ut_asserteq(-ENOSYS, virtio_generation(dev, &counter)); + ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(VIRTIO_CONFIG_S_DRIVER_OK, status); + ut_assertok(virtio_reset(dev)); + ut_assertok(virtio_get_status(dev, &status)); + ut_asserteq(0, status); + ut_assertok(virtio_get_features(dev, &features)); + ut_asserteq(VIRTIO_F_VERSION_1, features); + ut_assertok(virtio_set_features(dev)); + ut_assertok(virtio_find_vqs(dev, nvqs, vqs)); + ut_assertok(virtio_del_vqs(dev)); + ut_assertok(virtio_notify(dev, vqs[0])); + + return 0; +} +DM_TEST(dm_test_virtio_all_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test of the virtio driver that does not have required driver ops */ +static int dm_test_virtio_missing_ops(struct unit_test_state *uts) +{ + struct udevice *bus; + + /* find the virtio device */ + ut_assertok(uclass_find_device(UCLASS_VIRTIO, 1, &bus)); + + /* + * Probe the device should fail with error -ENOENT. + * See ops check in virtio_uclass_pre_probe(). + */ + ut_asserteq(-ENOENT, device_probe(bus)); + + return 0; +} +DM_TEST(dm_test_virtio_missing_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test removal of virtio device driver */ +static int dm_test_virtio_remove(struct unit_test_state *uts) +{ + struct udevice *bus, *dev; + + /* check probe success */ + ut_assertok(uclass_first_device(UCLASS_VIRTIO, &bus)); + ut_assertnonnull(bus); + + /* check the child virtio-blk device is bound */ + ut_assertok(device_find_first_child(bus, &dev)); + ut_assertnonnull(dev); + + /* set driver status to VIRTIO_CONFIG_S_DRIVER_OK */ + ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK)); + + /* check the device can be successfully removed */ + dev_or_flags(dev, DM_FLAG_ACTIVATED); + ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL)); + + ut_asserteq(false, device_active(dev)); + + return 0; +} +DM_TEST(dm_test_virtio_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/dm/wdt.c b/roms/u-boot/test/dm/wdt.c new file mode 100644 index 000000000..24b991dff --- /dev/null +++ b/roms/u-boot/test/dm/wdt.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <wdt.h> +#include <asm/state.h> +#include <asm/test.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +/* Test that watchdog driver functions are called */ +static int dm_test_wdt_base(struct unit_test_state *uts) +{ + struct sandbox_state *state = state_get_current(); + struct udevice *dev; + const u64 timeout = 42; + + ut_assertok(uclass_get_device(UCLASS_WDT, 0, &dev)); + ut_assertnonnull(dev); + ut_asserteq(0, state->wdt.counter); + ut_asserteq(false, state->wdt.running); + + ut_assertok(wdt_start(dev, timeout, 0)); + ut_asserteq(timeout, state->wdt.counter); + ut_asserteq(true, state->wdt.running); + + uint reset_count = state->wdt.reset_count; + ut_assertok(wdt_reset(dev)); + ut_asserteq(reset_count + 1, state->wdt.reset_count); + ut_asserteq(true, state->wdt.running); + + ut_assertok(wdt_stop(dev)); + ut_asserteq(false, state->wdt.running); + + return 0; +} +DM_TEST(dm_test_wdt_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/env/Kconfig b/roms/u-boot/test/env/Kconfig new file mode 100644 index 000000000..6cb82337b --- /dev/null +++ b/roms/u-boot/test/env/Kconfig @@ -0,0 +1,9 @@ +config UT_ENV + bool "Enable env unit tests" + depends on UNIT_TEST + default y + help + This enables the 'ut env' command which runs a series of unit + tests on the env code. + If all is well then all tests pass although there will be a few + messages printed along the way. diff --git a/roms/u-boot/test/env/Makefile b/roms/u-boot/test/env/Makefile new file mode 100644 index 000000000..9a98fd479 --- /dev/null +++ b/roms/u-boot/test/env/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2015 National Instruments, Inc + +obj-y += cmd_ut_env.o +obj-y += attr.o +obj-y += hashtable.o +obj-$(CONFIG_ENV_IMPORT_FDT) += fdt.o diff --git a/roms/u-boot/test/env/attr.c b/roms/u-boot/test/env/attr.c new file mode 100644 index 000000000..8d5c0f1c3 --- /dev/null +++ b/roms/u-boot/test/env/attr.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2015 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + */ + +#include <common.h> +#include <command.h> +#include <env_attr.h> +#include <test/env.h> +#include <test/ut.h> + +static int env_test_attrs_lookup(struct unit_test_state *uts) +{ + char attrs[32]; + + ut_assertok(env_attr_lookup("foo:bar", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(",foo:bar", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(",foo:bar,", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(" foo:bar", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup("foo : bar", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(" foo: bar ", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup("foo:bar ", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(",foo:bar,goo:baz", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_asserteq(-ENOENT, env_attr_lookup(",,", "foo", attrs)); + + ut_asserteq(-ENOENT, env_attr_lookup("goo:baz", "foo", attrs)); + + ut_assertok(env_attr_lookup("foo:bar,foo:bat,foo:baz", "foo", attrs)); + ut_asserteq_str("baz", attrs); + + ut_assertok(env_attr_lookup( + " foo : bar , foo : bat , foot : baz ", "foo", attrs)); + ut_asserteq_str("bat", attrs); + + ut_assertok(env_attr_lookup( + " foo : bar , foo : bat , ufoo : baz ", "foo", attrs)); + ut_asserteq_str("bat", attrs); + + ut_asserteq(-EINVAL, env_attr_lookup(NULL, "foo", attrs)); + ut_asserteq(-EINVAL, env_attr_lookup("foo:bar", "foo", NULL)); + + return 0; +} +ENV_TEST(env_test_attrs_lookup, 0); + +#ifdef CONFIG_REGEX +static int env_test_attrs_lookup_regex(struct unit_test_state *uts) +{ + char attrs[32]; + + ut_assertok(env_attr_lookup("foo1?:bar", "foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup("foo1?:bar", "foo1", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(".foo:bar", ".foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup(".foo:bar", "ufoo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_assertok(env_attr_lookup("\\.foo:bar", ".foo", attrs)); + ut_asserteq_str("bar", attrs); + + ut_asserteq(-ENOENT, env_attr_lookup("\\.foo:bar", "ufoo", attrs)); + + return 0; +} +ENV_TEST(env_test_attrs_lookup_regex, 0); +#endif diff --git a/roms/u-boot/test/env/cmd_ut_env.c b/roms/u-boot/test/env/cmd_ut_env.c new file mode 100644 index 000000000..d65a32179 --- /dev/null +++ b/roms/u-boot/test/env/cmd_ut_env.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2015 + * Joe Hershberger, National Instruments, joe.hershberger@ni.com + */ + +#include <common.h> +#include <command.h> +#include <test/env.h> +#include <test/suites.h> +#include <test/ut.h> + +int do_ut_env(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(env_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(env_test); + + return cmd_ut_category("environment", "env_test_", + tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/env/fdt.c b/roms/u-boot/test/env/fdt.c new file mode 100644 index 000000000..30bfa88c3 --- /dev/null +++ b/roms/u-boot/test/env/fdt.c @@ -0,0 +1,20 @@ +#include <common.h> +#include <command.h> +#include <env_attr.h> +#include <test/env.h> +#include <test/ut.h> + +static int env_test_fdt_import(struct unit_test_state *uts) +{ + const char *val; + + val = env_get("from_fdt"); + ut_assertnonnull(val); + ut_asserteq_str("yes", val); + + val = env_get("fdt_env_path"); + ut_assertnull(val); + + return 0; +} +ENV_TEST(env_test_fdt_import, 0); diff --git a/roms/u-boot/test/env/hashtable.c b/roms/u-boot/test/env/hashtable.c new file mode 100644 index 000000000..70102f912 --- /dev/null +++ b/roms/u-boot/test/env/hashtable.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) Copyright 2019 + * Roman Kapl, SYSGO, rka@sysgo.com + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <search.h> +#include <stdio.h> +#include <test/env.h> +#include <test/ut.h> + +#define SIZE 32 +#define ITERATIONS 10000 + +static int htab_fill(struct unit_test_state *uts, + struct hsearch_data *htab, size_t size) +{ + size_t i; + struct env_entry item; + struct env_entry *ritem; + char key[20]; + + for (i = 0; i < size; i++) { + sprintf(key, "%d", (int)i); + item.callback = NULL; + item.data = key; + item.flags = 0; + item.key = key; + ut_asserteq(1, hsearch_r(item, ENV_ENTER, &ritem, htab, 0)); + } + + return 0; +} + +static int htab_check_fill(struct unit_test_state *uts, + struct hsearch_data *htab, size_t size) +{ + size_t i; + struct env_entry item; + struct env_entry *ritem; + char key[20]; + + for (i = 0; i < size; i++) { + sprintf(key, "%d", (int)i); + item.callback = NULL; + item.flags = 0; + item.data = key; + item.key = key; + hsearch_r(item, ENV_FIND, &ritem, htab, 0); + ut_assert(ritem); + ut_asserteq_str(key, ritem->key); + ut_asserteq_str(key, ritem->data); + } + + return 0; +} + +static int htab_create_delete(struct unit_test_state *uts, + struct hsearch_data *htab, size_t iterations) +{ + size_t i; + struct env_entry item; + struct env_entry *ritem; + char key[20]; + + for (i = 0; i < iterations; i++) { + sprintf(key, "cd-%d", (int)i); + item.callback = NULL; + item.flags = 0; + item.data = key; + item.key = key; + hsearch_r(item, ENV_ENTER, &ritem, htab, 0); + ritem = NULL; + + hsearch_r(item, ENV_FIND, &ritem, htab, 0); + ut_assert(ritem); + ut_asserteq_str(key, ritem->key); + ut_asserteq_str(key, ritem->data); + + ut_asserteq(0, hdelete_r(key, htab, 0)); + } + + return 0; +} + +/* Completely fill up the hash table */ +static int env_test_htab_fill(struct unit_test_state *uts) +{ + struct hsearch_data htab; + + memset(&htab, 0, sizeof(htab)); + ut_asserteq(1, hcreate_r(SIZE, &htab)); + + ut_assertok(htab_fill(uts, &htab, SIZE)); + ut_assertok(htab_check_fill(uts, &htab, SIZE)); + ut_asserteq(SIZE, htab.filled); + + hdestroy_r(&htab); + return 0; +} + +ENV_TEST(env_test_htab_fill, 0); + +/* Fill the hashtable up halfway an repeateadly delete/create elements + * and check for corruption + */ +static int env_test_htab_deletes(struct unit_test_state *uts) +{ + struct hsearch_data htab; + + memset(&htab, 0, sizeof(htab)); + ut_asserteq(1, hcreate_r(SIZE, &htab)); + + ut_assertok(htab_fill(uts, &htab, SIZE / 2)); + ut_assertok(htab_create_delete(uts, &htab, ITERATIONS)); + ut_assertok(htab_check_fill(uts, &htab, SIZE / 2)); + ut_asserteq(SIZE / 2, htab.filled); + + hdestroy_r(&htab); + return 0; +} + +ENV_TEST(env_test_htab_deletes, 0); diff --git a/roms/u-boot/test/fs/fat-noncontig-test.sh b/roms/u-boot/test/fs/fat-noncontig-test.sh new file mode 100755 index 000000000..b02dae765 --- /dev/null +++ b/roms/u-boot/test/fs/fat-noncontig-test.sh @@ -0,0 +1,144 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ + +# (C) Copyright 2015 Stephen Warren + +# This script tests U-Boot's FAT filesystem code's ability to read non- +# contiguous files. + +# When porting the ff.c FAT parsing code into U-Boot, it was found that ff.c +# always reads files cluster-by-cluster, which results in poor performance. +# This was solved by adding a patch to ff.c to coalesce reads of adjacent +# clusters. Since this patch needed to correctly handle non-contiguous files, +# this test was written to validate that. +# +# To execute the test, simply run it from the U-Boot source root directory: +# +# cd u-boot +# ./test/fs/fat-noncontig-test.sh +# +# The test will create a FAT filesystem image, record the CRC of a randomly +# generated file in the image, build U-Boot sandbox, invoke U-Boot sandbox to +# read the file and validate that the CRCs match. Expected output is shown +# below. The important part of the log is the penultimate line that contains +# either "PASS" or "FAILURE". +# +# mkfs.fat 3.0.26 (2014-03-07) +# +# +# U-Boot 2015.10-rc4-00018-g4b22a3e5513f (Oct 03 2015 - 13:49:23 -0600) +# +# DRAM: 128 MiB +# Using default environment +# +# In: serial +# Out: lcd +# Err: lcd +# Net: No ethernet found. +# => host bind 0 sandbox/fat-noncontig.img +# => load host 0:0 1000 noncontig.img +# 33584964 bytes read in 18 ms (1.7 GiB/s) +# => crc32 1000 $filesize 0 +# crc32 for 00001000 ... 02008743 ==> 6a080523 +# => if itest.l *0 != 2305086a; then echo FAILURE; else echo PASS; fi +# PASS +# => reset +# +# All temporary files used by this script are created in ./sandbox to avoid +# polluting the source tree. test/fs/fs-test.sh also uses this directory for +# the same purpose. +# +# TODO: Integrate this (and many other corner-cases e.g. different types of +# FAT) with fs-test.sh so that a single script tests everything filesystem- +# related. + +odir=sandbox +img=${odir}/fat-noncontig.img +mnt=${odir}/mnt +fill=/dev/urandom +testfn=noncontig.img +mnttestfn=${mnt}/${testfn} +crcaddr=0 +loadaddr=1000 + +for prereq in fallocate mkfs.fat dd crc32; do + if [ ! -x "`which $prereq`" ]; then + echo "Missing $prereq binary. Exiting!" + exit 1 + fi +done + +make O=${odir} -s sandbox_defconfig && make O=${odir} -s -j8 + +mkdir -p ${mnt} +if [ ! -f ${img} ]; then + fallocate -l 40M ${img} + if [ $? -ne 0 ]; then + echo fallocate failed - using dd instead + dd if=/dev/zero of=${img} bs=1024 count=$((40 * 1024)) + if [ $? -ne 0 ]; then + echo Could not create empty disk image + exit $? + fi + fi + mkfs.fat ${img} + if [ $? -ne 0 ]; then + echo Could not create FAT filesystem + exit $? + fi + + sudo mount -o loop,uid=$(id -u) ${img} ${mnt} + if [ $? -ne 0 ]; then + echo Could not mount test filesystem + exit $? + fi + + for ((sects=8; sects < 512; sects += 8)); do + fn=${mnt}/keep-${sects}.img + dd if=${fill} of=${fn} bs=512 count=${sects} >/dev/null 2>&1 + fn=${mnt}/remove-${sects}.img + dd if=${fill} of=${fn} bs=512 count=${sects} >/dev/null 2>&1 + done + + rm -f ${mnt}/remove-*.img + + # 511 deliberately to trigger a file size that's not a multiple of the + # sector size (ignoring sizes that are multiples of both). + dd if=${fill} of=${mnttestfn} bs=511 >/dev/null 2>&1 + + sudo umount ${mnt} + if [ $? -ne 0 ]; then + echo Could not unmount test filesystem + exit $? + fi +fi + +sudo mount -o ro,loop,uid=$(id -u) ${img} ${mnt} +if [ $? -ne 0 ]; then + echo Could not mount test filesystem + exit $? +fi +crc=0x`crc32 ${mnttestfn}` +sudo umount ${mnt} +if [ $? -ne 0 ]; then + echo Could not unmount test filesystem + exit $? +fi + +crc=`printf %02x%02x%02x%02x \ + $((${crc} & 0xff)) \ + $(((${crc} >> 8) & 0xff)) \ + $(((${crc} >> 16) & 0xff)) \ + $((${crc} >> 24))` + +./sandbox/u-boot << EOF +host bind 0 ${img} +load host 0:0 ${loadaddr} ${testfn} +crc32 ${loadaddr} \$filesize ${crcaddr} +if itest.l *${crcaddr} != ${crc}; then echo FAILURE; else echo PASS; fi +reset +EOF +if [ $? -ne 0 ]; then + echo U-Boot exit status indicates an error + exit $? +fi diff --git a/roms/u-boot/test/fs/fs-test.sh b/roms/u-boot/test/fs/fs-test.sh new file mode 100755 index 000000000..b87748106 --- /dev/null +++ b/roms/u-boot/test/fs/fs-test.sh @@ -0,0 +1,627 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2014 Suriyan Ramasami + +# Invoke this test script from U-Boot base directory as ./test/fs/fs-test.sh +# It currently tests the fs/sb and native commands for ext4 and fat partitions +# Expected results are as follows: +# EXT4 tests: +# fs-test.sb.ext4 Summary: PASS: 24 FAIL: 0 +# fs-test.nonfs.ext4 Summary: PASS: 24 FAIL: 0 +# fs-test.fs.ext4 Summary: PASS: 24 FAIL: 0 +# FAT16 tests: +# fs-test.sb.fat16 Summary: PASS: 24 FAIL: 0 +# fs-test.nonfs.fat16 Summary: PASS: 24 FAIL: 0 +# fs-test.fs.fat16 Summary: PASS: 24 FAIL: 0 +# FAT32 tests: +# fs-test.sb.fat32 Summary: PASS: 24 FAIL: 0 +# fs-test.nonfs.fat32 Summary: PASS: 24 FAIL: 0 +# fs-test.fs.fat32 Summary: PASS: 24 FAIL: 0 +# -------------------------------------------- +# Total Summary: TOTAL PASS: 216 TOTAL FAIL: 0 +# -------------------------------------------- + +# pre-requisite binaries list. +PREREQ_BINS="md5sum mkfs mount umount dd fallocate mkdir" + +# All generated output files from this test will be in $OUT_DIR +# Hence everything is sandboxed. +OUT_DIR="sandbox/test/fs" + +# Location of generated sandbox u-boot +UBOOT="./sandbox/u-boot" + +# Our mount directory will be in the sandbox +MOUNT_DIR="${OUT_DIR}/mnt" + +# The file system image we create will have the $IMG prefix. +IMG="${OUT_DIR}/3GB" + +# $SMALL_FILE is the name of the 1MB file in the file system image +SMALL_FILE="1MB.file" + +# $BIG_FILE is the name of the 2.5GB file in the file system image +BIG_FILE="2.5GB.file" + +# $MD5_FILE will have the expected md5s when we do the test +# They shall have a suffix which represents their file system (ext4/fat16/...) +MD5_FILE="${OUT_DIR}/md5s.list" + +# $OUT shall be the prefix of the test output. Their suffix will be .out +OUT="${OUT_DIR}/fs-test" + +# Full Path of the 1 MB file that shall be created in the fs image. +MB1="${MOUNT_DIR}/${SMALL_FILE}" +GB2p5="${MOUNT_DIR}/${BIG_FILE}" + +# ************************ +# * Functions start here * +# ************************ + +# Check if the prereq binaries exist, or exit +function check_prereq() { + for prereq in $PREREQ_BINS; do + if [ ! -x "`which $prereq`" ]; then + echo "Missing $prereq binary. Exiting!" + exit + fi + done + + # We use /dev/urandom to create files. Check if it exists. + if [ ! -c /dev/urandom ]; then + echo "Missing character special /dev/urandom. Exiting!" + exit + fi +} + +# If 1st param is "clean", then clean out the generated files and exit +function check_clean() { + if [ "$1" = "clean" ]; then + rm -rf "$OUT_DIR" + echo "Cleaned up generated files. Exiting" + exit + fi +} + +# Generate sandbox U-Boot - gleaned from /test/dm/test-dm.sh +function compile_sandbox() { + unset CROSS_COMPILE + NUM_CPUS=$(nproc) + make O=sandbox sandbox_config + make O=sandbox -s -j${NUM_CPUS} + + # Check if U-Boot exists + if [ ! -x "$UBOOT" ]; then + echo "$UBOOT does not exist or is not executable" + echo "Build error?" + echo "Please run this script as ./test/fs/`basename $0`" + exit + fi +} + +# Clean out all generated files other than the file system images +# We save time by not deleting and recreating the file system images +function prepare_env() { + rm -f ${MD5_FILE}.* ${OUT}.* + mkdir -p ${OUT_DIR} +} + +# 1st parameter is the name of the image file to be created +# 2nd parameter is the filesystem - fat16 ext4 etc +# -F cant be used with fat as it means something else. +function create_image() { + # Create image if not already present - saves time, while debugging + case "$2" in + fat16) + MKFS_OPTION="-F 16" + FS_TYPE="fat" + ;; + fat32) + MKFS_OPTION="-F 32" + FS_TYPE="fat" + ;; + ext4) + MKFS_OPTION="-F" + FS_TYPE="ext4" + ;; + esac + + if [ ! -f "$1" ]; then + fallocate -l 3G "$1" &> /dev/null + if [ $? -ne 0 ]; then + echo fallocate failed - using dd instead + dd if=/dev/zero of=$1 bs=1024 count=$((3 * 1024 * 1024)) + if [ $? -ne 0 ]; then + echo Could not create empty disk image + exit $? + fi + fi + mkfs -t "$FS_TYPE" $MKFS_OPTION "$1" &> /dev/null + if [ $? -ne 0 -a "$FS_TYPE" = "fat" ]; then + # If we fail and we did fat, try vfat. + mkfs -t vfat $MKFS_OPTION "$1" &> /dev/null + fi + if [ $? -ne 0 ]; then + echo Could not create filesystem + exit $? + fi + fi +} + +# 1st parameter is image file +# 2nd parameter is file system type - fat16/ext4/... +# 3rd parameter is name of small file +# 4th parameter is name of big file +# 5th parameter is fs/nonfs/sb - to dictate generic fs commands or +# otherwise or sb hostfs +# 6th parameter is the directory path for the files. Its "" for generic +# fs and ext4/fat and full patch for sb hostfs +# UBOOT is set in env +function test_image() { + addr="0x01000008" + length="0x00100000" + + case "$2" in + fat*) + FPATH="" + PREFIX="fat" + WRITE="write" + ;; + + ext4) + # ext4 needs absolute path + FPATH="/" + PREFIX="ext4" + WRITE="write" + ;; + + *) + echo "Unhandled filesystem $2. Exiting!" + exit + ;; + esac + + case "$5" in + fs) + PREFIX="" + WRITE="save" + SUFFIX=" 0:0" + ;; + + nonfs) + SUFFIX=" 0:0" + ;; + + sb) + PREFIX="host " + WRITE="save" + SUFFIX="fs -" + ;; + + *) + echo "Unhandled mode $5. Exiting!" + exit + ;; + + esac + + # sb always uses full path to mointpoint, irrespective of filesystem + if [ "$5" = "sb" ]; then + FPATH=${6}/ + fi + + FILE_WRITE=${3}.w + FILE_SMALL=$3 + FILE_BIG=$4 + + # In u-boot commands, <interface> stands for host or hostfs + # hostfs maps to the host fs. + # host maps to the "host bind" that we do + + $UBOOT << EOF +sb=$5 +setenv bind 'if test "\$sb" != sb; then host bind 0 "$1"; fi' +run bind +# Test Case 1 - ls +${PREFIX}ls host${SUFFIX} $6 +# In addition, test with a nonexistent directory to see if we crash. +${PREFIX}ls host${SUFFIX} invalid_d +# +# We want ${PREFIX}size host 0:0 $3 for host commands and +# host size hostfs - $3 for hostfs commands. +# 1MB is 0x0010 0000 +# Test Case 2a - size of small file +${PREFIX}size host${SUFFIX} ${FPATH}$FILE_SMALL +printenv filesize +setenv filesize +# Test Case 2b - size of small file via a path using '..' +${PREFIX}size host${SUFFIX} ${FPATH}SUBDIR/../$FILE_SMALL +printenv filesize +setenv filesize + +# 2.5GB (1024*1024*2500) is 0x9C40 0000 +# Test Case 3 - size of big file +${PREFIX}size host${SUFFIX} ${FPATH}$FILE_BIG +printenv filesize +setenv filesize + +# Notes about load operation +# If I use 0x01000000 I get DMA misaligned error message +# Last two parameters are size and offset. + +# Test Case 4a - Read full 1MB of small file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL +printenv filesize +# Test Case 4b - Read full 1MB of small file +md5sum $addr \$filesize +setenv filesize + +# Test Case 5a - First 1MB of big file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x0 +printenv filesize +# Test Case 5b - First 1MB of big file +md5sum $addr \$filesize +setenv filesize + +# fails for ext as no offset support +# Test Case 6a - Last 1MB of big file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x9C300000 +printenv filesize +# Test Case 6b - Last 1MB of big file +md5sum $addr \$filesize +setenv filesize + +# fails for ext as no offset support +# Test Case 7a - One from the last 1MB chunk of 2GB +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x7FF00000 +printenv filesize +# Test Case 7b - One from the last 1MB chunk of 2GB +md5sum $addr \$filesize +setenv filesize + +# fails for ext as no offset support +# Test Case 8a - One from the start 1MB chunk from 2GB +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x80000000 +printenv filesize +# Test Case 8b - One from the start 1MB chunk from 2GB +md5sum $addr \$filesize +setenv filesize + +# fails for ext as no offset support +# Test Case 9a - One 1MB chunk crossing the 2GB boundary +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x7FF80000 +printenv filesize +# Test Case 9b - One 1MB chunk crossing the 2GB boundary +md5sum $addr \$filesize +setenv filesize + +# Generic failure case +# Test Case 10 - 2MB chunk from the last 1MB of big file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG 0x00200000 0x9C300000 +printenv filesize +# + +# Read 1MB from small file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL +# Write it back to test the writes +# Test Case 11a - Check that the write succeeded +${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}$FILE_WRITE \$filesize +mw.b $addr 00 100 +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_WRITE +# Test Case 11b - Check md5 of written to is same as the one read from +md5sum $addr \$filesize +setenv filesize +# + +# Next test case checks writing a file whose dirent +# is the first in the block, which is always true for "." +# The write should fail, but the lookup should work +# Test Case 12 - Check directory traversal +${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}. 0x10 + +# Read 1MB from small file +${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL +# Write it via "same directory", i.e. "." dirent +# Test Case 13a - Check directory traversal +${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}./${FILE_WRITE}2 \$filesize +mw.b $addr 00 100 +${PREFIX}load host${SUFFIX} $addr ${FPATH}./${FILE_WRITE}2 +# Test Case 13b - Check md5 of written to is same as the one read from +md5sum $addr \$filesize +setenv filesize +mw.b $addr 00 100 +${PREFIX}load host${SUFFIX} $addr ${FPATH}${FILE_WRITE}2 +# Test Case 13c - Check md5 of written to is same as the one read from +md5sum $addr \$filesize +setenv filesize +# +reset + +EOF +} + +# 1st argument is the name of the image file. +# 2nd argument is the file where we generate the md5s of the files +# generated with the appropriate start and length that we use to test. +# It creates the necessary files in the image to test. +# $GB2p5 is the path of the big file (2.5 GB) +# $MB1 is the path of the small file (1 MB) +# $MOUNT_DIR is the path we can use to mount the image file. +function create_files() { + # Mount the image so we can populate it. + mkdir -p "$MOUNT_DIR" + sudo mount -o loop,rw "$1" "$MOUNT_DIR" + + # Create a subdirectory. + sudo mkdir -p "$MOUNT_DIR/SUBDIR" + + # Create big file in this image. + # Note that we work only on the start 1MB, couple MBs in the 2GB range + # and the last 1 MB of the huge 2.5GB file. + # So, just put random values only in those areas. + if [ ! -f "${GB2p5}" ]; then + sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=1 \ + &> /dev/null + sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=2 seek=2047 \ + &> /dev/null + sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=1 seek=2499 \ + &> /dev/null + fi + + # Create a small file in this image. + if [ ! -f "${MB1}" ]; then + sudo dd if=/dev/urandom of="${MB1}" bs=1M count=1 \ + &> /dev/null + fi + + # Delete the small file copies which possibly are written as part of a + # previous test. + sudo rm -f "${MB1}.w" + sudo rm -f "${MB1}.w2" + + # Generate the md5sums of reads that we will test against small file + dd if="${MB1}" bs=1M skip=0 count=1 2> /dev/null | md5sum > "$2" + + # Generate the md5sums of reads that we will test against big file + # One from beginning of file. + dd if="${GB2p5}" bs=1M skip=0 count=1 \ + 2> /dev/null | md5sum >> "$2" + + # One from end of file. + dd if="${GB2p5}" bs=1M skip=2499 count=1 \ + 2> /dev/null | md5sum >> "$2" + + # One from the last 1MB chunk of 2GB + dd if="${GB2p5}" bs=1M skip=2047 count=1 \ + 2> /dev/null | md5sum >> "$2" + + # One from the start 1MB chunk from 2GB + dd if="${GB2p5}" bs=1M skip=2048 count=1 \ + 2> /dev/null | md5sum >> "$2" + + # One 1MB chunk crossing the 2GB boundary + dd if="${GB2p5}" bs=512K skip=4095 count=2 \ + 2> /dev/null | md5sum >> "$2" + + sync + sudo umount "$MOUNT_DIR" + rmdir "$MOUNT_DIR" +} + +# 1st parameter is the text to print +# if $? is 0 its a pass, else a fail +# As a side effect it shall update env variable PASS and FAIL +function pass_fail() { + if [ $? -eq 0 ]; then + echo pass - "$1" + PASS=$((PASS + 1)) + else + echo FAIL - "$1" + FAIL=$((FAIL + 1)) + fi +} + +# 1st parameter is the string which leads to an md5 generation +# 2nd parameter is the file we grep, for that string +# 3rd parameter is the name of the file which has md5s in it +# 4th parameter is the line # in the md5 file that we match it against +# This function checks if the md5 of the file in the sandbox matches +# that calculated while generating the file +# 5th parameter is the string to print with the result +check_md5() { + # md5sum in u-boot has output of form: + # md5 for 01000008 ... 01100007 ==> <md5> + # the 7th field is the actual md5 + md5_src=`grep -A2 "$1" "$2" | grep "md5 for" | tr -d '\r'` + md5_src=($md5_src) + md5_src=${md5_src[6]} + + # The md5 list, each line is of the form: + # - <md5> + # the 2nd field is the actual md5 + md5_dst=`sed -n $4p $3` + md5_dst=($md5_dst) + md5_dst=${md5_dst[0]} + + # For a pass they should match. + [ "$md5_src" = "$md5_dst" ] + pass_fail "$5" +} + +# 1st parameter is the name of the output file to check +# 2nd parameter is the name of the file containing the md5 expected +# 3rd parameter is the name of the small file +# 4th parameter is the name of the big file +# 5th paramter is the name of the written file +# This function checks the output file for correct results. +function check_results() { + echo "** Start $1" + + PASS=0 + FAIL=0 + + # Check if the ls is showing correct results for 2.5 gb file + grep -A7 "Test Case 1 " "$1" | egrep -iq "2621440000 *$4" + pass_fail "TC1: ls of $4" + + # Check if the ls is showing correct results for 1 mb file + grep -A7 "Test Case 1 " "$1" | egrep -iq "1048576 *$3" + pass_fail "TC1: ls of $3" + + # Check size command on 1MB.file + egrep -A3 "Test Case 2a " "$1" | grep -q "filesize=100000" + pass_fail "TC2: size of $3" + # Check size command on 1MB.file via a path using '..' + egrep -A3 "Test Case 2b " "$1" | grep -q "filesize=100000" + pass_fail "TC2: size of $3 via a path using '..'" + + # Check size command on 2.5GB.file + egrep -A3 "Test Case 3 " "$1" | grep -q "filesize=9c400000" + pass_fail "TC3: size of $4" + + # Check read full mb of 1MB.file + grep -A4 "Test Case 4a " "$1" | grep -q "filesize=100000" + pass_fail "TC4: load of $3 size" + check_md5 "Test Case 4b " "$1" "$2" 1 "TC4: load from $3" + + # Check first mb of 2.5GB.file + grep -A4 "Test Case 5a " "$1" | grep -q "filesize=100000" + pass_fail "TC5: load of 1st MB from $4 size" + check_md5 "Test Case 5b " "$1" "$2" 2 "TC5: load of 1st MB from $4" + + # Check last mb of 2.5GB.file + grep -A4 "Test Case 6a " "$1" | grep -q "filesize=100000" + pass_fail "TC6: load of last MB from $4 size" + check_md5 "Test Case 6b " "$1" "$2" 3 "TC6: load of last MB from $4" + + # Check last 1mb chunk of 2gb from 2.5GB file + grep -A4 "Test Case 7a " "$1" | grep -q "filesize=100000" + pass_fail "TC7: load of last 1mb chunk of 2GB from $4 size" + check_md5 "Test Case 7b " "$1" "$2" 4 \ + "TC7: load of last 1mb chunk of 2GB from $4" + + # Check first 1mb chunk after 2gb from 2.5GB file + grep -A4 "Test Case 8a " "$1" | grep -q "filesize=100000" + pass_fail "TC8: load 1st MB chunk after 2GB from $4 size" + check_md5 "Test Case 8b " "$1" "$2" 5 \ + "TC8: load 1st MB chunk after 2GB from $4" + + # Check 1mb chunk crossing the 2gb boundary from 2.5GB file + grep -A4 "Test Case 9a " "$1" | grep -q "filesize=100000" + pass_fail "TC9: load 1MB chunk crossing 2GB boundary from $4 size" + check_md5 "Test Case 9b " "$1" "$2" 6 \ + "TC9: load 1MB chunk crossing 2GB boundary from $4" + + # Check 2mb chunk from the last 1MB of 2.5GB file loads 1MB + grep -A5 "Test Case 10 " "$1" | grep -q "filesize=100000" + pass_fail "TC10: load 2MB from the last 1MB of $4 loads 1MB" + + # Check 1mb chunk write + grep -A2 "Test Case 11a " "$1" | grep -q '1048576 bytes written' + pass_fail "TC11: 1MB write to $3.w - write succeeded" + check_md5 "Test Case 11b " "$1" "$2" 1 \ + "TC11: 1MB write to $3.w - content verified" + + # Check lookup of 'dot' directory + grep -A4 "Test Case 12 " "$1" | grep -q 'Unable to write' + pass_fail "TC12: 1MB write to . - write denied" + + # Check directory traversal + grep -A2 "Test Case 13a " "$1" | grep -q '1048576 bytes written' + pass_fail "TC13: 1MB write to ./$3.w2 - write succeeded" + check_md5 "Test Case 13b " "$1" "$2" 1 \ + "TC13: 1MB read from ./$3.w2 - content verified" + check_md5 "Test Case 13c " "$1" "$2" 1 \ + "TC13: 1MB read from $3.w2 - content verified" + + echo "** End $1" +} + +# Takes in one parameter which is "fs" or "nonfs", which then dictates +# if a fs test (size/load/save) or a nonfs test (fatread/extread) needs to +# be performed. +function test_fs_nonfs() { + echo "Creating files in $fs image if not already present." + create_files $IMAGE $MD5_FILE_FS + + OUT_FILE="${OUT}.$1.${fs}.out" + test_image $IMAGE $fs $SMALL_FILE $BIG_FILE $1 "" \ + > ${OUT_FILE} 2>&1 + # strip out noise from fs code + grep -v -e "File System is consistent\|update journal finished" \ + -e "reading .*\.file\|writing .*\.file.w" \ + < ${OUT_FILE} > ${OUT_FILE}_clean + check_results ${OUT_FILE}_clean $MD5_FILE_FS $SMALL_FILE \ + $BIG_FILE + TOTAL_FAIL=$((TOTAL_FAIL + FAIL)) + TOTAL_PASS=$((TOTAL_PASS + PASS)) + echo "Summary: PASS: $PASS FAIL: $FAIL" + echo "--------------------------------------------" +} + +# ******************** +# * End of functions * +# ******************** + +check_clean "$1" +check_prereq +compile_sandbox +prepare_env + +# Track TOTAL_FAIL and TOTAL_PASS +TOTAL_FAIL=0 +TOTAL_PASS=0 + +# In each loop, for a given file system image, we test both the +# fs command, like load/size/write, the file system specific command +# like: ext4load/ext4size/ext4write and the host load/ls/save commands. +for fs in ext4 fat16 fat32; do + + echo "Creating $fs image if not already present." + IMAGE=${IMG}.${fs}.img + MD5_FILE_FS="${MD5_FILE}.${fs}" + create_image $IMAGE $fs + + # host commands test + echo "Creating files in $fs image if not already present." + create_files $IMAGE $MD5_FILE_FS + + # Lets mount the image and test host hostfs commands + mkdir -p "$MOUNT_DIR" + case "$fs" in + fat*) + uid="uid=`id -u`" + ;; + *) + uid="" + ;; + esac + sudo mount -o loop,rw,$uid "$IMAGE" "$MOUNT_DIR" + sudo chmod 777 "$MOUNT_DIR" + + OUT_FILE="${OUT}.sb.${fs}.out" + test_image $IMAGE $fs $SMALL_FILE $BIG_FILE sb `pwd`/$MOUNT_DIR \ + > ${OUT_FILE} 2>&1 + sudo umount "$MOUNT_DIR" + rmdir "$MOUNT_DIR" + + check_results $OUT_FILE $MD5_FILE_FS $SMALL_FILE $BIG_FILE + TOTAL_FAIL=$((TOTAL_FAIL + FAIL)) + TOTAL_PASS=$((TOTAL_PASS + PASS)) + echo "Summary: PASS: $PASS FAIL: $FAIL" + echo "--------------------------------------------" + + test_fs_nonfs nonfs + test_fs_nonfs fs +done + +echo "Total Summary: TOTAL PASS: $TOTAL_PASS TOTAL FAIL: $TOTAL_FAIL" +echo "--------------------------------------------" +if [ $TOTAL_FAIL -eq 0 ]; then + echo "PASSED" + exit 0 +else + echo "FAILED" + exit 1 +fi diff --git a/roms/u-boot/test/image/Makefile b/roms/u-boot/test/image/Makefile new file mode 100644 index 000000000..c4039df70 --- /dev/null +++ b/roms/u-boot/test/image/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright 2021 Google LLC + +obj-$(CONFIG_SPL_BUILD) += spl_load.o diff --git a/roms/u-boot/test/image/spl_load.c b/roms/u-boot/test/image/spl_load.c new file mode 100644 index 000000000..851603ddd --- /dev/null +++ b/roms/u-boot/test/image/spl_load.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <image.h> +#include <mapmem.h> +#include <os.h> +#include <spl.h> +#include <test/ut.h> + +/* Declare a new SPL test */ +#define SPL_TEST(_name, _flags) UNIT_TEST(_name, _flags, spl_test) + +/* Context used for this test */ +struct text_ctx { + int fd; +}; + +static ulong read_fit_image(struct spl_load_info *load, ulong sector, + ulong count, void *buf) +{ + struct text_ctx *text_ctx = load->priv; + off_t offset, ret; + ssize_t res; + + offset = sector * load->bl_len; + ret = os_lseek(text_ctx->fd, offset, OS_SEEK_SET); + if (ret != offset) { + printf("Failed to seek to %zx, got %zx (errno=%d)\n", offset, + ret, errno); + return 0; + } + + res = os_read(text_ctx->fd, buf, count * load->bl_len); + if (res == -1) { + printf("Failed to read %lx bytes, got %ld (errno=%d)\n", + count * load->bl_len, res, errno); + return 0; + } + + return count; +} + +int board_fit_config_name_match(const char *name) +{ + return 0; +} + +struct image_header *spl_get_load_buffer(ssize_t offset, size_t size) +{ + return map_sysmem(0x100000, 0); +} + +static int spl_test_load(struct unit_test_state *uts) +{ + struct spl_image_info image; + struct image_header *header; + struct text_ctx text_ctx; + struct spl_load_info load; + char fname[256]; + int ret; + int fd; + + memset(&load, '\0', sizeof(load)); + load.bl_len = 512; + load.read = read_fit_image; + + ret = os_find_u_boot(fname, sizeof(fname), true); + if (ret) { + printf("(%s not found, error %d)\n", fname, ret); + return ret; + } + load.filename = fname; + + header = spl_get_load_buffer(-sizeof(*header), sizeof(*header)); + + fd = os_open(fname, OS_O_RDONLY); + ut_assert(fd >= 0); + ut_asserteq(512, os_read(fd, header, 512)); + text_ctx.fd = fd; + + load.priv = &text_ctx; + + ut_assertok(spl_load_simple_fit(&image, &load, 0, header)); + + return 0; +} +SPL_TEST(spl_test_load, 0); diff --git a/roms/u-boot/test/image/test-imagetools.sh b/roms/u-boot/test/image/test-imagetools.sh new file mode 100755 index 000000000..907f46a7b --- /dev/null +++ b/roms/u-boot/test/image/test-imagetools.sh @@ -0,0 +1,225 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# +# Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com> +# +# Sanity check for mkimage and dumpimage tools +# +# To run this: +# +# make O=sandbox sandbox_config +# make O=sandbox +# ./test/image/test-imagetools.sh + +BASEDIR=sandbox +SRCDIR=${BASEDIR}/boot +IMAGE_NAME="v1.0-test" +IMAGE_MULTI=linux.img +IMAGE_FIT_ITS=linux.its +IMAGE_FIT_ITB=linux.itb +DATAFILE0=vmlinuz +DATAFILE1=initrd.img +DATAFILE2=System.map +DATAFILES="${DATAFILE0} ${DATAFILE1} ${DATAFILE2}" +TEST_OUT=test_output +MKIMAGE=${BASEDIR}/tools/mkimage +DUMPIMAGE=${BASEDIR}/tools/dumpimage +MKIMAGE_LIST=mkimage.list +DUMPIMAGE_LIST=dumpimage.list + +# Remove all the files we created +cleanup() +{ + local file + + for file in ${DATAFILES}; do + rm -f ${file} ${SRCDIR}/${file} + done + rm -f ${IMAGE_MULTI} + rm -f ${DUMPIMAGE_LIST} + rm -f ${MKIMAGE_LIST} + rm -f ${TEST_OUT} + rmdir ${SRCDIR} +} + +# Check that two files are the same +assert_equal() +{ + if ! diff -u $1 $2; then + echo "Failed." + cleanup + exit 1 + fi +} + +# Create some test files +create_files() +{ + local file + + mkdir -p ${SRCDIR} + for file in ${DATAFILES}; do + head -c $RANDOM /dev/urandom >${SRCDIR}/${file} + done +} + +# Run a command, echoing it first +do_cmd() +{ + local cmd="$@" + + echo "# ${cmd}" + ${cmd} 2>&1 +} + +# Run a command, redirecting output +# Args: +# redirect_file +# command... +do_cmd_redir() +{ + local redir="$1" + shift + local cmd="$@" + + echo "# ${cmd}" + ${cmd} >${redir} +} + +# Write files into an multi-file image +create_multi_image() +{ + local files="${SRCDIR}/${DATAFILE0}:${SRCDIR}/${DATAFILE1}" + files+=":${SRCDIR}/${DATAFILE2}" + + echo -e "\nBuilding multi-file image..." + do_cmd ${MKIMAGE} -A x86 -O linux -T multi -n \"${IMAGE_NAME}\" \ + -d ${files} ${IMAGE_MULTI} + echo "done." +} + +# Extract files from an multi-file image +extract_multi_image() +{ + echo -e "\nExtracting multi-file image contents..." + do_cmd ${DUMPIMAGE} -T multi -p 0 -o ${DATAFILE0} ${IMAGE_MULTI} + do_cmd ${DUMPIMAGE} -T multi -p 1 -o ${DATAFILE1} ${IMAGE_MULTI} + do_cmd ${DUMPIMAGE} -T multi -p 2 -o ${DATAFILE2} ${IMAGE_MULTI} + do_cmd ${DUMPIMAGE} -T multi -p 2 -o ${TEST_OUT} ${IMAGE_MULTI} + echo "done." +} + +# Write files into a FIT image +create_fit_image() +{ + echo " \ + /dts-v1/; \ + / { \ + description = \"FIT image\"; \ + #address-cells = <1>; \ + \ + images { \ + kernel@1 { \ + description = \"kernel\"; \ + data = /incbin/(\"${DATAFILE0}\"); \ + type = \"kernel\"; \ + arch = \"sandbox\"; \ + os = \"linux\"; \ + compression = \"gzip\"; \ + load = <0x40000>; \ + entry = <0x8>; \ + }; \ + ramdisk@1 { \ + description = \"filesystem\"; \ + data = /incbin/(\"${DATAFILE1}\"); \ + type = \"ramdisk\"; \ + arch = \"sandbox\"; \ + os = \"linux\"; \ + compression = \"none\"; \ + load = <0x80000>; \ + entry = <0x16>; \ + }; \ + fdt@1 { \ + description = \"device tree\"; \ + data = /incbin/(\"${DATAFILE2}\"); \ + type = \"flat_dt\"; \ + arch = \"sandbox\"; \ + compression = \"none\"; \ + }; \ + }; \ + configurations { \ + default = \"conf@1\"; \ + conf@1 { \ + kernel = \"kernel@1\"; \ + fdt = \"fdt@1\"; \ + }; \ + }; \ + }; \ + " > ${IMAGE_FIT_ITS} + + echo -e "\nBuilding FIT image..." + do_cmd ${MKIMAGE} -f ${IMAGE_FIT_ITS} ${IMAGE_FIT_ITB} + echo "done." +} + +# Extract files from a FIT image +extract_fit_image() +{ + echo -e "\nExtracting FIT image contents..." + do_cmd ${DUMPIMAGE} -T flat_dt -p 0 -o ${DATAFILE0} ${IMAGE_FIT_ITB} + do_cmd ${DUMPIMAGE} -T flat_dt -p 1 -o ${DATAFILE1} ${IMAGE_FIT_ITB} + do_cmd ${DUMPIMAGE} -T flat_dt -p 2 -o ${DATAFILE2} ${IMAGE_FIT_ITB} + do_cmd ${DUMPIMAGE} -T flat_dt -p 2 -o ${TEST_OUT} ${IMAGE_FIT_ITB} + echo "done." +} + +# List the contents of a file +# Args: +# image filename +list_image() +{ + local image="$1" + + echo -e "\nListing image contents..." + do_cmd_redir ${MKIMAGE_LIST} ${MKIMAGE} -l ${image} + do_cmd_redir ${DUMPIMAGE_LIST} ${DUMPIMAGE} -l ${image} + echo "done." +} + +main() +{ + local file + + create_files + + # Compress and extract multi-file images, compare the result + create_multi_image + extract_multi_image + for file in ${DATAFILES}; do + assert_equal ${file} ${SRCDIR}/${file} + done + assert_equal ${TEST_OUT} ${DATAFILE2} + + # List contents of multi-file image and compares output from tools + list_image ${IMAGE_MULTI} + assert_equal ${DUMPIMAGE_LIST} ${MKIMAGE_LIST} + + # Compress and extract FIT images, compare the result + create_fit_image + extract_fit_image + for file in ${DATAFILES}; do + assert_equal ${file} ${SRCDIR}/${file} + done + assert_equal ${TEST_OUT} ${DATAFILE2} + + # List contents of FIT image and compares output from tools + list_image ${IMAGE_FIT_ITB} + assert_equal ${DUMPIMAGE_LIST} ${MKIMAGE_LIST} + + # Remove files created + cleanup + + echo "Tests passed." +} + +main diff --git a/roms/u-boot/test/lib/Makefile b/roms/u-boot/test/lib/Makefile new file mode 100644 index 000000000..aa2e66bc7 --- /dev/null +++ b/roms/u-boot/test/lib/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2018 +# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc +obj-y += cmd_ut_lib.o +obj-$(CONFIG_EFI_LOADER) += efi_device_path.o +obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o +obj-y += hexdump.o +obj-y += lmb.o +obj-y += longjmp.o +obj-$(CONFIG_CONSOLE_RECORD) += test_print.o +obj-$(CONFIG_SSCANF) += sscanf.o +obj-y += string.o +obj-y += strlcat.o +obj-$(CONFIG_ERRNO_STR) += test_errno_str.o +obj-$(CONFIG_UT_LIB_ASN1) += asn1.o +obj-$(CONFIG_UT_LIB_RSA) += rsa.o +obj-$(CONFIG_AES) += test_aes.o +obj-$(CONFIG_GETOPT) += getopt.o diff --git a/roms/u-boot/test/lib/asn1.c b/roms/u-boot/test/lib/asn1.c new file mode 100644 index 000000000..8661fdd30 --- /dev/null +++ b/roms/u-boot/test/lib/asn1.c @@ -0,0 +1,392 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Linaro Limited + * Author: AKASHI Takahiro + * + * Unit test for asn1 compiler and asn1 decoder function via various parsers + */ + +#include <common.h> +#include <command.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +#ifdef CONFIG_PKCS7_MESSAGE_PARSER +#include <crypto/pkcs7_parser.h> +#else +#ifdef CONFIG_X509_CERTIFICATE_PARSER +#include <crypto/x509_parser.h> +#endif +#endif + +#ifdef CONFIG_X509_CERTIFICATE_PARSER +static const unsigned char cert_data[] = { + 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d, 0xeb, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e, + 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b, + 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d, 0x31, + 0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31, 0x5a, + 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33, + 0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e, + 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b, + 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, + 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, + 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, + 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, + 0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7, 0xaf, + 0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3, 0x8b, + 0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd, 0xcb, + 0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1, 0xa2, + 0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea, 0x4d, + 0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97, 0x45, + 0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd, 0xdb, + 0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a, 0x31, + 0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee, 0xc9, + 0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1, 0x15, + 0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e, 0xdc, + 0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b, 0x48, + 0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74, 0x52, + 0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30, 0x77, + 0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8, 0x35, + 0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89, 0xa9, + 0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4, 0x0a, + 0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04, 0x81, + 0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0, 0x0e, + 0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b, 0xf7, + 0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6, 0x25, + 0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01, 0x00, + 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, + 0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, + 0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, + 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02, 0xe1, + 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e, 0x8a, + 0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28, 0xc6, + 0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38, 0xc1, + 0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6, 0x57, + 0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83, 0xfb, + 0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50, 0x40, + 0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50, 0x1d, + 0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33, 0x84, + 0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77, 0x04, + 0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa, 0x91, + 0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e, 0x87, + 0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24, 0xc1, + 0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94, 0x28, + 0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a, 0x2c, + 0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c, 0x3f, + 0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c, 0xd2, + 0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0, 0xd9, + 0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a, 0x77, + 0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30, 0x80, + 0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc, 0x17, + 0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06, 0x5a, + 0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c +}; + +static unsigned int cert_data_len = 971; + +/** + * lib_asn1_x509() - unit test for asn1 decoder function + * with x509 certificate parser + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_asn1_x509(struct unit_test_state *uts) +{ + struct x509_certificate *cert; + + cert = x509_cert_parse(cert_data, cert_data_len); + + ut_assertf(cert != NULL, "decoding failed\n"); + ut_assertf(!strcmp(cert->subject, "Linaro: Tester"), + "subject doesn't match\n"); + ut_assertf(!strcmp(cert->issuer, "Linaro: Tester"), + "issuer doesn't match\n"); + ut_assertf(cert->pub, "public key doesn't exist\n"); + ut_assertf(cert->pub->keylen == 0x10e, "key length doesn't match\n"); + ut_assertf(!strcmp(cert->pub->pkey_algo, "rsa"), "algo isn't rsa\n"); + ut_assertf(cert->valid_from == 0x5da92ddb, + "valid_from doesn't match\n"); + ut_assertf(cert->valid_to == 0x5f8a615b, "valid_to doesn't match\n"); + + x509_free_certificate(cert); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_asn1_x509, 0); +#endif /* CONFIG_X509_CERTIFICATE_PARSER */ + +#ifdef CONFIG_PKCS7_MESSAGE_PARSER +/* + * sbsign --key priv.pem --cert cert.pem --detach --out Image.pk Image + */ +static const unsigned char image_pk7[] = { + 0x30, 0x82, 0x07, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x07, 0x02, 0xa0, 0x82, 0x07, 0x00, 0x30, 0x82, 0x06, 0xfc, 0x02, + 0x01, 0x01, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x30, 0x78, 0x06, 0x0a, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04, 0xa0, 0x6a, 0x30, + 0x68, 0x30, 0x33, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, + 0x02, 0x01, 0x0f, 0x30, 0x25, 0x03, 0x01, 0x00, 0xa0, 0x20, 0xa2, 0x1e, + 0x80, 0x1c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, + 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, + 0x20, 0x9e, 0x90, 0x99, 0x6d, 0xf2, 0xb5, 0x3d, 0x3f, 0xfc, 0x38, 0xb6, + 0xf2, 0x1f, 0xd2, 0x24, 0x88, 0x43, 0x77, 0x7d, 0xc1, 0x2c, 0x9e, 0x8a, + 0xf6, 0xf7, 0xdd, 0x9e, 0x9c, 0x5f, 0x18, 0x36, 0xc5, 0xa0, 0x82, 0x03, + 0xcb, 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d, + 0xeb, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, + 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, + 0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d, + 0x31, 0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31, + 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31, + 0x33, 0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, + 0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, + 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, + 0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, + 0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, + 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, + 0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7, + 0xaf, 0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3, + 0x8b, 0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd, + 0xcb, 0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1, + 0xa2, 0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea, + 0x4d, 0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97, + 0x45, 0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd, + 0xdb, 0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a, + 0x31, 0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee, + 0xc9, 0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1, + 0x15, 0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e, + 0xdc, 0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b, + 0x48, 0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74, + 0x52, 0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30, + 0x77, 0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8, + 0x35, 0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89, + 0xa9, 0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4, + 0x0a, 0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04, + 0x81, 0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0, + 0x0e, 0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b, + 0xf7, 0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6, + 0x25, 0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, + 0xa0, 0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, + 0xcd, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02, + 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c, + 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e, + 0x8a, 0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28, + 0xc6, 0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38, + 0xc1, 0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6, + 0x57, 0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83, + 0xfb, 0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50, + 0x40, 0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50, + 0x1d, 0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33, + 0x84, 0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77, + 0x04, 0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa, + 0x91, 0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e, + 0x87, 0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24, + 0xc1, 0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94, + 0x28, 0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a, + 0x2c, 0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c, + 0x3f, 0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c, + 0xd2, 0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0, + 0xd9, 0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a, + 0x77, 0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30, + 0x80, 0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc, + 0x17, 0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06, + 0x5a, 0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c, + 0x31, 0x82, 0x02, 0x9b, 0x30, 0x82, 0x02, 0x97, 0x02, 0x01, 0x01, 0x30, + 0x81, 0x87, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e, 0x30, + 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, + 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06, + 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, + 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d, 0x06, + 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, + 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65, + 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, + 0x76, 0xd5, 0xd3, 0x4d, 0xeb, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, + 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa0, 0x81, 0xe5, 0x30, + 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x03, + 0x31, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, + 0x01, 0x04, 0x30, 0x1c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x09, 0x05, 0x31, 0x0f, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x31, + 0x38, 0x30, 0x35, 0x35, 0x35, 0x32, 0x36, 0x5a, 0x30, 0x2f, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x04, 0x31, 0x22, 0x04, + 0x20, 0x13, 0xe9, 0x2d, 0xcd, 0x35, 0x43, 0xe0, 0x13, 0x34, 0xc5, 0x67, + 0xde, 0xdd, 0x75, 0xdc, 0x62, 0x97, 0x76, 0x7d, 0x5b, 0xa0, 0xb4, 0x4d, + 0x4f, 0xef, 0xb8, 0xa7, 0x95, 0x50, 0xcb, 0x0f, 0xec, 0x30, 0x79, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x31, 0x6c, + 0x30, 0x6a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x01, 0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, + 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0d, 0x06, + 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x40, + 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0d, 0x06, + 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x28, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x38, 0x40, 0x09, 0xc7, 0xc4, + 0xf7, 0x78, 0x48, 0x75, 0x1e, 0xb2, 0x50, 0x95, 0x0a, 0x52, 0xee, 0x57, + 0x60, 0xc5, 0xf4, 0xdb, 0xca, 0x67, 0xb0, 0x19, 0xad, 0x68, 0xb1, 0xe1, + 0x1e, 0xb7, 0xf6, 0x53, 0x3d, 0x13, 0xb1, 0x11, 0x37, 0xa7, 0x6e, 0x9b, + 0x18, 0x1d, 0x0e, 0xbd, 0xc4, 0xb2, 0xd0, 0x36, 0x6c, 0x0c, 0x5a, 0x11, + 0x50, 0xcc, 0xdb, 0x1f, 0x6b, 0xcb, 0x28, 0x80, 0xd5, 0x3c, 0x4f, 0x93, + 0x0b, 0xd1, 0x45, 0x75, 0xa1, 0x89, 0x00, 0x71, 0x7d, 0x55, 0xcc, 0x1c, + 0x0a, 0xc9, 0xc4, 0xe6, 0x87, 0xf2, 0x87, 0x0d, 0x2e, 0x79, 0x71, 0x85, + 0x01, 0xd7, 0x32, 0x87, 0x9a, 0x11, 0xc6, 0x9a, 0xbb, 0x0a, 0x7b, 0xce, + 0xfe, 0xc8, 0xee, 0x10, 0x3c, 0xa6, 0x47, 0xdd, 0xbb, 0xa7, 0xf5, 0x19, + 0x50, 0xd5, 0x2a, 0x11, 0x44, 0x2f, 0x65, 0x09, 0x69, 0x50, 0xfa, 0xbd, + 0x02, 0xe4, 0x90, 0xdc, 0x2a, 0x7c, 0xdb, 0x82, 0x03, 0xa5, 0x28, 0x91, + 0x74, 0x7c, 0xd3, 0x83, 0xc8, 0x11, 0x1a, 0x14, 0x1b, 0xba, 0xb1, 0x82, + 0xbd, 0x53, 0xad, 0x9c, 0x34, 0x05, 0xfa, 0x2d, 0x14, 0x58, 0x5e, 0x50, + 0x64, 0x60, 0x5c, 0x21, 0x7c, 0xe6, 0xf0, 0x2b, 0xa2, 0xec, 0xe5, 0xeb, + 0xda, 0x88, 0xe2, 0x19, 0x36, 0x96, 0x65, 0xf7, 0x4c, 0x62, 0x9b, 0x75, + 0x24, 0xb4, 0xb1, 0x34, 0x83, 0xba, 0x05, 0x01, 0xd8, 0xe1, 0x33, 0xd3, + 0x1a, 0xd6, 0x09, 0x84, 0x31, 0xd0, 0x67, 0xf3, 0x3b, 0x0e, 0x19, 0x98, + 0x7e, 0x07, 0xdc, 0xe1, 0xd8, 0x45, 0x84, 0xa2, 0xdd, 0x8a, 0x04, 0x6a, + 0x43, 0xcf, 0xff, 0x7c, 0x9e, 0x83, 0xa8, 0x5d, 0xbc, 0x1f, 0x45, 0x86, + 0x5b, 0x2d, 0xcd, 0x9d, 0xa0, 0xba, 0x4d, 0xd2, 0xc6, 0xb9, 0xc5, 0x34, + 0x39, 0x29, 0x20, 0xee, 0x27, 0x60, 0x46, 0x9c, 0x62, 0xbe, 0xf2 +}; + +static unsigned int image_pk7_len = 1811; + +/** + * lib_asn1_pkcs7() - unit test for asn1 decoder function + * with pkcs7 message parser + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_asn1_pkcs7(struct unit_test_state *uts) +{ + struct pkcs7_message *pkcs7; + + pkcs7 = pkcs7_parse_message(image_pk7, image_pk7_len); + + ut_assertf(pkcs7 != NULL, "decoding failed\n"); + ut_assertf(pkcs7->data_len == 104, "signature size doesn't match\n"); + ut_assertf(pkcs7->signed_infos != NULL, "sign-info doesn't exist\n"); + ut_assertf(pkcs7->signed_infos->msgdigest_len == 32, + "digest size doesn't match\n"); + ut_assertf(pkcs7->signed_infos->aa_set == 0xf, + "authenticated attributes doesn't match\n"); + + pkcs7_free_message(pkcs7); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_asn1_pkcs7, 0); +#endif /* CONFIG_PKCS7_MESSAGE_PARSER */ + +#ifdef CONFIG_RSA_PUBLIC_KEY_PARSER +#include <crypto/internal/rsa.h> + +/* + * openssl genrsa 2048 -out private.pem + * openssl rsa -in private.pem -pubout -outform der -out public.der + * dd if=public.der of=public.raw bs=24 skip=1 + */ +static const unsigned char public_key[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23, + 0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43, + 0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f, + 0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c, + 0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a, + 0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e, + 0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8, + 0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05, + 0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68, + 0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67, + 0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19, + 0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92, + 0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7, + 0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4, + 0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf, + 0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36, + 0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99, + 0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8, + 0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35, + 0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e, + 0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60, + 0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79, + 0x75, 0x02, 0x03, 0x01, 0x00, 0x01 +}; + +static unsigned int public_key_len = 270; + +/** + * lib_asn1_pkey() - unit test for asn1 decoder function + * with RSA public key parser + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_asn1_pkey(struct unit_test_state *uts) +{ + struct rsa_key pkey; + int ret; + + ret = rsa_parse_pub_key(&pkey, public_key, public_key_len); + + ut_assertf(ret == 0, "decoding failed (%d)\n", ret); + ut_assertf(pkey.n_sz == 257, "public key modulus size doesn't match\n"); + ut_assertf(pkey.e_sz == 3, "public key exponent size doesn't match\n"); + ut_assertf(pkey.e[0] == 0x01 && pkey.e[1] == 0x00 && pkey.e[2] == 0x01, + "public key exponent doesn't match\n"); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_asn1_pkey, 0); +#endif /* CONFIG_RSA_PUBLIC_KEY_PARSER */ diff --git a/roms/u-boot/test/lib/cmd_ut_lib.c b/roms/u-boot/test/lib/cmd_ut_lib.c new file mode 100644 index 000000000..f1ac015b2 --- /dev/null +++ b/roms/u-boot/test/lib/cmd_ut_lib.c @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Unit tests for library functions + */ + +#include <common.h> +#include <command.h> +#include <test/lib.h> +#include <test/suites.h> +#include <test/ut.h> + +int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(lib_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(lib_test); + + return cmd_ut_category("lib", "lib_test_", tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/lib/efi_device_path.c b/roms/u-boot/test/lib/efi_device_path.c new file mode 100644 index 000000000..24e2f23c5 --- /dev/null +++ b/roms/u-boot/test/lib/efi_device_path.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test device path functions + * + * Copyright (c) 2020 Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <efi_loader.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +static int lib_test_efi_dp_check_length(struct unit_test_state *uts) +{ + /* end of device path */ + u8 d1[] __aligned(2) = { + 0x7f, 0xff, 0x04, 0x00 }; + /* device path node with length less then 4 */ + u8 d2[] __aligned(2) = { + 0x01, 0x02, 0x02, 0x00, 0x04, 0x00, 0x7f, 0xff, 0x04, 0x00 }; + /* well formed device path */ + u8 d3[] __aligned(2) = { + 0x03, 0x02, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, + 0x7f, 0xff, 0x04, 0x00 }; + + struct efi_device_path *p1 = (struct efi_device_path *)d1; + struct efi_device_path *p2 = (struct efi_device_path *)d2; + struct efi_device_path *p3 = (struct efi_device_path *)d3; + + ut_asserteq((ssize_t)-EINVAL, efi_dp_check_length(p1, SIZE_MAX)); + ut_asserteq((ssize_t)sizeof(d1), efi_dp_check_length(p1, sizeof(d1))); + ut_asserteq((ssize_t)sizeof(d1), + efi_dp_check_length(p1, sizeof(d1) + 4)); + ut_asserteq((ssize_t)-1, efi_dp_check_length(p1, sizeof(d1) - 1)); + + ut_asserteq((ssize_t)-1, efi_dp_check_length(p2, sizeof(d2))); + + ut_asserteq((ssize_t)-1, efi_dp_check_length(p3, sizeof(d3) - 1)); + ut_asserteq((ssize_t)sizeof(d3), efi_dp_check_length(p3, sizeof(d3))); + ut_asserteq((ssize_t)sizeof(d3), efi_dp_check_length(p3, SSIZE_MAX)); + ut_asserteq((ssize_t)-EINVAL, + efi_dp_check_length(p3, (size_t)SSIZE_MAX + 1)); + ut_asserteq((ssize_t)sizeof(d3), + efi_dp_check_length(p3, sizeof(d3) + 4)); + + return 0; +} + +LIB_TEST(lib_test_efi_dp_check_length, 0); diff --git a/roms/u-boot/test/lib/efi_image_region.c b/roms/u-boot/test/lib/efi_image_region.c new file mode 100644 index 000000000..0b888f843 --- /dev/null +++ b/roms/u-boot/test/lib/efi_image_region.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <efi_loader.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +#define UT_REG_CAPACITY 6 + +static int lib_test_efi_image_region_add(struct unit_test_state *uts) +{ + struct efi_image_regions *regs; + + regs = calloc(sizeof(*regs) + + sizeof(struct image_region) * UT_REG_CAPACITY, 1); + ut_assert(regs); + + regs->max = UT_REG_CAPACITY; + + ut_asserteq(0, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x4000, + (void *)0x3000, 1)); + ut_asserteq(0, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x3100, + (void *)0x4000, 1)); + ut_asserteq(1, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x2000, + (void *)0x3100, 1)); + ut_asserteq(2, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x1000, + (void *)0x1f00, 1)); + ut_asserteq(3, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x4000, + (void *)0x4e00, 1)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x1f00, + (void *)0x2001, 1)); + ut_asserteq(5, regs->num); + + ut_asserteq_ptr((void *)0x3100, regs->reg[0].data); + ut_asserteq(0x0f00, regs->reg[0].size); + + ut_asserteq_ptr((void *)0x2000, regs->reg[1].data); + ut_asserteq(0x1100, regs->reg[1].size); + + ut_asserteq_ptr((void *)0x1000, regs->reg[2].data); + ut_asserteq(0x0f00, regs->reg[2].size); + + ut_asserteq_ptr((void *)0x4000, regs->reg[3].data); + ut_asserteq(0x0e00, regs->reg[3].size); + + ut_asserteq_ptr((void *)0x1f00, regs->reg[4].data); + ut_asserteq(0x0101, regs->reg[4].size); + + free(regs); + + return 0; +} + +LIB_TEST(lib_test_efi_image_region_add, 0); + +static int lib_test_efi_image_region_sort(struct unit_test_state *uts) +{ + struct efi_image_regions *regs; + + regs = calloc(sizeof(*regs) + + sizeof(struct image_region) * UT_REG_CAPACITY, 1); + ut_assert(regs); + + regs->max = UT_REG_CAPACITY; + + ut_asserteq(0, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x4000, + (void *)0x3000, 0)); + ut_asserteq(0, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x3100, + (void *)0x4000, 0)); + ut_asserteq(1, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x2000, + (void *)0x3100, 0)); + ut_asserteq(2, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x1000, + (void *)0x1f00, 0)); + ut_asserteq(3, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x4000, + (void *)0x4e00, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x1f00, + (void *)0x2001, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x10ff, + (void *)0x11ff, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x0000, + (void *)0x6000, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x3100, + (void *)0x0e00, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x3200, + (void *)0x0e00, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_INVALID_PARAMETER, + efi_image_region_add(regs, (void *)0x3200, + (void *)0x0d00, 0)); + ut_asserteq(4, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x1f00, + (void *)0x2000, 0)); + ut_asserteq(5, regs->num); + ut_asserteq_64(EFI_SUCCESS, + efi_image_region_add(regs, (void *)0x4000, + (void *)0x4000, 0)); + ut_asserteq(6, regs->num); + ut_asserteq_64(EFI_OUT_OF_RESOURCES, + efi_image_region_add(regs, (void *)0x6000, + (void *)0x0100, 0)); + ut_asserteq(6, regs->num); + + ut_asserteq_ptr((void *)0x1000, regs->reg[0].data); + ut_asserteq(0x0f00, regs->reg[0].size); + + ut_asserteq_ptr((void *)0x1f00, regs->reg[1].data); + ut_asserteq(0x0100, regs->reg[1].size); + + ut_asserteq_ptr((void *)0x2000, regs->reg[2].data); + ut_asserteq(0x1100, regs->reg[2].size); + + ut_asserteq_ptr((void *)0x3100, regs->reg[3].data); + ut_asserteq(0x0f00, regs->reg[3].size); + + ut_asserteq_ptr((void *)0x4000, regs->reg[4].data); + ut_asserteq(0x0000, regs->reg[4].size); + + ut_asserteq_ptr((void *)0x4000, regs->reg[5].data); + ut_asserteq(0x0e00, regs->reg[5].size); + + free(regs); + + return 0; +} + +LIB_TEST(lib_test_efi_image_region_sort, 0); diff --git a/roms/u-boot/test/lib/getopt.c b/roms/u-boot/test/lib/getopt.c new file mode 100644 index 000000000..3c68b93c8 --- /dev/null +++ b/roms/u-boot/test/lib/getopt.c @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + * + * Portions of these tests were inspired by glibc's posix/bug-getopt1.c and + * posix/tst-getopt-cancel.c + */ + +#include <common.h> +#include <getopt.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +static int do_test_getopt(struct unit_test_state *uts, int line, + struct getopt_state *gs, const char *optstring, + int args, char *argv[], int expected_count, + int expected[]) +{ + int opt; + + getopt_init_state(gs); + for (int i = 0; i < expected_count; i++) { + opt = getopt_silent(gs, args, argv, optstring); + if (expected[i] != opt) { + /* + * Fudge the line number so we can tell which test + * failed + */ + ut_failf(uts, __FILE__, line, __func__, + "expected[i] == getopt()", + "Expected '%c' (%d) with i=%d, got '%c' (%d)", + expected[i], expected[i], i, opt, opt); + return CMD_RET_FAILURE; + } + } + + opt = getopt_silent(gs, args, argv, optstring); + if (opt != -1) { + ut_failf(uts, __FILE__, line, __func__, + "getopt() != -1", + "Expected -1, got '%c' (%d)", opt, opt); + return CMD_RET_FAILURE; + } + + return 0; +} + +#define test_getopt(optstring, argv, expected) do { \ + int ret = do_test_getopt(uts, __LINE__, &gs, optstring, \ + ARRAY_SIZE(argv) - 1, argv, \ + ARRAY_SIZE(expected), expected); \ + if (ret) \ + return ret; \ +} while (0) + +static int lib_test_getopt(struct unit_test_state *uts) +{ + struct getopt_state gs; + + /* Happy path */ + test_getopt("ab:c", + ((char *[]){ "program", "-cb", "x", "-a", "foo", 0 }), + ((int []){ 'c', 'b', 'a' })); + ut_asserteq(4, gs.index); + + /* Make sure we pick up the optional argument */ + test_getopt("a::b:c", + ((char *[]){ "program", "-cbx", "-a", "foo", 0 }), + ((int []){ 'c', 'b', 'a' })); + ut_asserteq(4, gs.index); + + /* Test required arguments */ + test_getopt("a:b", ((char *[]){ "program", "-a", 0 }), + ((int []){ ':' })); + ut_asserteq('a', gs.opt); + test_getopt("a:b", ((char *[]){ "program", "-b", "-a", 0 }), + ((int []){ 'b', ':' })); + ut_asserteq('a', gs.opt); + + /* Test invalid arguments */ + test_getopt("ab:c", ((char *[]){ "program", "-d", 0 }), + ((int []){ '?' })); + ut_asserteq('d', gs.opt); + + /* Test arg */ + test_getopt("a::b:c", + ((char *[]){ "program", "-a", 0 }), + ((int []){ 'a' })); + ut_asserteq(2, gs.index); + ut_assertnull(gs.arg); + + test_getopt("a::b:c", + ((char *[]){ "program", "-afoo", 0 }), + ((int []){ 'a' })); + ut_asserteq(2, gs.index); + ut_assertnonnull(gs.arg); + ut_asserteq_str("foo", gs.arg); + + test_getopt("a::b:c", + ((char *[]){ "program", "-a", "foo", 0 }), + ((int []){ 'a' })); + ut_asserteq(3, gs.index); + ut_assertnonnull(gs.arg); + ut_asserteq_str("foo", gs.arg); + + test_getopt("a::b:c", + ((char *[]){ "program", "-bfoo", 0 }), + ((int []){ 'b' })); + ut_asserteq(2, gs.index); + ut_assertnonnull(gs.arg); + ut_asserteq_str("foo", gs.arg); + + test_getopt("a::b:c", + ((char *[]){ "program", "-b", "foo", 0 }), + ((int []){ 'b' })); + ut_asserteq(3, gs.index); + ut_assertnonnull(gs.arg); + ut_asserteq_str("foo", gs.arg); + + return 0; +} +LIB_TEST(lib_test_getopt, 0); diff --git a/roms/u-boot/test/lib/hexdump.c b/roms/u-boot/test/lib/hexdump.c new file mode 100644 index 000000000..5dccf4388 --- /dev/null +++ b/roms/u-boot/test/lib/hexdump.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 + * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc + */ + +#include <common.h> +#include <hexdump.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +static int lib_test_hex_to_bin(struct unit_test_state *uts) +{ + ut_asserteq(0x0, hex_to_bin('0')); + ut_asserteq(0x1, hex_to_bin('1')); + ut_asserteq(0x2, hex_to_bin('2')); + ut_asserteq(0x3, hex_to_bin('3')); + ut_asserteq(0x4, hex_to_bin('4')); + ut_asserteq(0x5, hex_to_bin('5')); + ut_asserteq(0x6, hex_to_bin('6')); + ut_asserteq(0x7, hex_to_bin('7')); + ut_asserteq(0x8, hex_to_bin('8')); + ut_asserteq(0x9, hex_to_bin('9')); + ut_asserteq(0xa, hex_to_bin('a')); + ut_asserteq(0xb, hex_to_bin('b')); + ut_asserteq(0xc, hex_to_bin('c')); + ut_asserteq(0xd, hex_to_bin('d')); + ut_asserteq(0xe, hex_to_bin('e')); + ut_asserteq(0xf, hex_to_bin('f')); + ut_asserteq(-1, hex_to_bin('g')); + + return 0; +} + +LIB_TEST(lib_test_hex_to_bin, 0); + +static int lib_test_hex2bin(struct unit_test_state *uts) +{ + u8 dst[4]; + + hex2bin(dst, "649421de", 4); + ut_asserteq_mem("\x64\x94\x21\xde", dst, 4); + hex2bin(dst, "aa2e7545", 4); + ut_asserteq_mem("\xaa\x2e\x75\x45", dst, 4); + hex2bin(dst, "75453bc5", 4); + ut_asserteq_mem("\x75\x45\x3b\xc5", dst, 4); + hex2bin(dst, "a16884c3", 4); + ut_asserteq_mem("\xa1\x68\x84\xc3", dst, 4); + hex2bin(dst, "156b2e5e", 4); + ut_asserteq_mem("\x15\x6b\x2e\x5e", dst, 4); + hex2bin(dst, "2e035fff", 4); + ut_asserteq_mem("\x2e\x03\x5f\xff", dst, 4); + hex2bin(dst, "0ffce99f", 4); + ut_asserteq_mem("\x0f\xfc\xe9\x9f", dst, 4); + hex2bin(dst, "d3999443", 4); + ut_asserteq_mem("\xd3\x99\x94\x43", dst, 4); + hex2bin(dst, "91dd87bc", 4); + ut_asserteq_mem("\x91\xdd\x87\xbc", dst, 4); + hex2bin(dst, "7fec8963", 4); + ut_asserteq_mem("\x7f\xec\x89\x63", dst, 4); + + return 0; +} + +LIB_TEST(lib_test_hex2bin, 0); + +static int lib_test_bin2hex(struct unit_test_state *uts) +{ + char dst[8 + 1] = "\0"; + + bin2hex(dst, "\x64\x94\x21\xde", 4); + ut_asserteq_str("649421de", dst); + bin2hex(dst, "\xaa\x2e\x75\x45", 4); + ut_asserteq_str("aa2e7545", dst); + bin2hex(dst, "\x75\x45\x3b\xc5", 4); + ut_asserteq_str("75453bc5", dst); + bin2hex(dst, "\xa1\x68\x84\xc3", 4); + ut_asserteq_str("a16884c3", dst); + bin2hex(dst, "\x15\x6b\x2e\x5e", 4); + ut_asserteq_str("156b2e5e", dst); + bin2hex(dst, "\x2e\x03\x5f\xff", 4); + ut_asserteq_str("2e035fff", dst); + bin2hex(dst, "\x0f\xfc\xe9\x9f", 4); + ut_asserteq_str("0ffce99f", dst); + bin2hex(dst, "\xd3\x99\x94\x43", 4); + ut_asserteq_str("d3999443", dst); + bin2hex(dst, "\x91\xdd\x87\xbc", 4); + ut_asserteq_str("91dd87bc", dst); + bin2hex(dst, "\x7f\xec\x89\x63", 4); + ut_asserteq_str("7fec8963", dst); + + return 0; +} + +LIB_TEST(lib_test_bin2hex, 0); diff --git a/roms/u-boot/test/lib/lmb.c b/roms/u-boot/test/lib/lmb.c new file mode 100644 index 000000000..0d8963fcb --- /dev/null +++ b/roms/u-boot/test/lib/lmb.c @@ -0,0 +1,725 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2018 Simon Goldschmidt + */ + +#include <common.h> +#include <dm.h> +#include <lmb.h> +#include <log.h> +#include <malloc.h> +#include <dm/test.h> +#include <test/test.h> +#include <test/ut.h> + +static int check_lmb(struct unit_test_state *uts, struct lmb *lmb, + phys_addr_t ram_base, phys_size_t ram_size, + unsigned long num_reserved, + phys_addr_t base1, phys_size_t size1, + phys_addr_t base2, phys_size_t size2, + phys_addr_t base3, phys_size_t size3) +{ + if (ram_size) { + ut_asserteq(lmb->memory.cnt, 1); + ut_asserteq(lmb->memory.region[0].base, ram_base); + ut_asserteq(lmb->memory.region[0].size, ram_size); + } + + ut_asserteq(lmb->reserved.cnt, num_reserved); + if (num_reserved > 0) { + ut_asserteq(lmb->reserved.region[0].base, base1); + ut_asserteq(lmb->reserved.region[0].size, size1); + } + if (num_reserved > 1) { + ut_asserteq(lmb->reserved.region[1].base, base2); + ut_asserteq(lmb->reserved.region[1].size, size2); + } + if (num_reserved > 2) { + ut_asserteq(lmb->reserved.region[2].base, base3); + ut_asserteq(lmb->reserved.region[2].size, size3); + } + return 0; +} + +#define ASSERT_LMB(lmb, ram_base, ram_size, num_reserved, base1, size1, \ + base2, size2, base3, size3) \ + ut_assert(!check_lmb(uts, lmb, ram_base, ram_size, \ + num_reserved, base1, size1, base2, size2, base3, \ + size3)) + +/* + * Test helper function that reserves 64 KiB somewhere in the simulated RAM and + * then does some alloc + free tests. + */ +static int test_multi_alloc(struct unit_test_state *uts, const phys_addr_t ram, + const phys_size_t ram_size, const phys_addr_t ram0, + const phys_size_t ram0_size, + const phys_addr_t alloc_64k_addr) +{ + const phys_addr_t ram_end = ram + ram_size; + const phys_addr_t alloc_64k_end = alloc_64k_addr + 0x10000; + + struct lmb lmb; + long ret; + phys_addr_t a, a2, b, b2, c, d; + + /* check for overflow */ + ut_assert(ram_end == 0 || ram_end > ram); + ut_assert(alloc_64k_end > alloc_64k_addr); + /* check input addresses + size */ + ut_assert(alloc_64k_addr >= ram + 8); + ut_assert(alloc_64k_end <= ram_end - 8); + + lmb_init(&lmb); + + if (ram0_size) { + ret = lmb_add(&lmb, ram0, ram0_size); + ut_asserteq(ret, 0); + } + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + if (ram0_size) { + ut_asserteq(lmb.memory.cnt, 2); + ut_asserteq(lmb.memory.region[0].base, ram0); + ut_asserteq(lmb.memory.region[0].size, ram0_size); + ut_asserteq(lmb.memory.region[1].base, ram); + ut_asserteq(lmb.memory.region[1].size, ram_size); + } else { + ut_asserteq(lmb.memory.cnt, 1); + ut_asserteq(lmb.memory.region[0].base, ram); + ut_asserteq(lmb.memory.region[0].size, ram_size); + } + + /* reserve 64KiB somewhere */ + ret = lmb_reserve(&lmb, alloc_64k_addr, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 1, alloc_64k_addr, 0x10000, + 0, 0, 0, 0); + + /* allocate somewhere, should be at the end of RAM */ + a = lmb_alloc(&lmb, 4, 1); + ut_asserteq(a, ram_end - 4); + ASSERT_LMB(&lmb, 0, 0, 2, alloc_64k_addr, 0x10000, + ram_end - 4, 4, 0, 0); + /* alloc below end of reserved region -> below reserved region */ + b = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end); + ut_asserteq(b, alloc_64k_addr - 4); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 4, 0x10000 + 4, ram_end - 4, 4, 0, 0); + + /* 2nd time */ + c = lmb_alloc(&lmb, 4, 1); + ut_asserteq(c, ram_end - 8); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 4, 0x10000 + 4, ram_end - 8, 8, 0, 0); + d = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end); + ut_asserteq(d, alloc_64k_addr - 8); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 8, 0, 0); + + ret = lmb_free(&lmb, a, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0); + /* allocate again to ensure we get the same address */ + a2 = lmb_alloc(&lmb, 4, 1); + ut_asserteq(a, a2); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 8, 0, 0); + ret = lmb_free(&lmb, a2, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0); + + ret = lmb_free(&lmb, b, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 3, + alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000, + ram_end - 8, 4); + /* allocate again to ensure we get the same address */ + b2 = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end); + ut_asserteq(b, b2); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0); + ret = lmb_free(&lmb, b2, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 3, + alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000, + ram_end - 8, 4); + + ret = lmb_free(&lmb, c, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 2, + alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000, 0, 0); + ret = lmb_free(&lmb, d, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, 0, 0, 1, alloc_64k_addr, 0x10000, + 0, 0, 0, 0); + + if (ram0_size) { + ut_asserteq(lmb.memory.cnt, 2); + ut_asserteq(lmb.memory.region[0].base, ram0); + ut_asserteq(lmb.memory.region[0].size, ram0_size); + ut_asserteq(lmb.memory.region[1].base, ram); + ut_asserteq(lmb.memory.region[1].size, ram_size); + } else { + ut_asserteq(lmb.memory.cnt, 1); + ut_asserteq(lmb.memory.region[0].base, ram); + ut_asserteq(lmb.memory.region[0].size, ram_size); + } + + return 0; +} + +static int test_multi_alloc_512mb(struct unit_test_state *uts, + const phys_addr_t ram) +{ + return test_multi_alloc(uts, ram, 0x20000000, 0, 0, ram + 0x10000000); +} + +static int test_multi_alloc_512mb_x2(struct unit_test_state *uts, + const phys_addr_t ram, + const phys_addr_t ram0) +{ + return test_multi_alloc(uts, ram, 0x20000000, ram0, 0x20000000, + ram + 0x10000000); +} + +/* Create a memory region with one reserved region and allocate */ +static int lib_test_lmb_simple(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_multi_alloc_512mb(uts, 0x40000000); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_multi_alloc_512mb(uts, 0xE0000000); +} + +DM_TEST(lib_test_lmb_simple, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Create two memory regions with one reserved region and allocate */ +static int lib_test_lmb_simple_x2(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 2GiB and 1 GiB */ + ret = test_multi_alloc_512mb_x2(uts, 0x80000000, 0x40000000); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 3.5GiB and 1 GiB */ + return test_multi_alloc_512mb_x2(uts, 0xE0000000, 0x40000000); +} + +DM_TEST(lib_test_lmb_simple_x2, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Simulate 512 MiB RAM, allocate some blocks that fit/don't fit */ +static int test_bigblock(struct unit_test_state *uts, const phys_addr_t ram) +{ + const phys_size_t ram_size = 0x20000000; + const phys_size_t big_block_size = 0x10000000; + const phys_addr_t ram_end = ram + ram_size; + const phys_addr_t alloc_64k_addr = ram + 0x10000000; + struct lmb lmb; + long ret; + phys_addr_t a, b; + + /* check for overflow */ + ut_assert(ram_end == 0 || ram_end > ram); + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + /* reserve 64KiB in the middle of RAM */ + ret = lmb_reserve(&lmb, alloc_64k_addr, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000, + 0, 0, 0, 0); + + /* allocate a big block, should be below reserved */ + a = lmb_alloc(&lmb, big_block_size, 1); + ut_asserteq(a, ram); + ASSERT_LMB(&lmb, ram, ram_size, 1, a, + big_block_size + 0x10000, 0, 0, 0, 0); + /* allocate 2nd big block */ + /* This should fail, printing an error */ + b = lmb_alloc(&lmb, big_block_size, 1); + ut_asserteq(b, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, a, + big_block_size + 0x10000, 0, 0, 0, 0); + + ret = lmb_free(&lmb, a, big_block_size); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000, + 0, 0, 0, 0); + + /* allocate too big block */ + /* This should fail, printing an error */ + a = lmb_alloc(&lmb, ram_size, 1); + ut_asserteq(a, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000, + 0, 0, 0, 0); + + return 0; +} + +static int lib_test_lmb_big(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_bigblock(uts, 0x40000000); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_bigblock(uts, 0xE0000000); +} + +DM_TEST(lib_test_lmb_big, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Simulate 512 MiB RAM, allocate a block without previous reservation */ +static int test_noreserved(struct unit_test_state *uts, const phys_addr_t ram, + const phys_addr_t alloc_size, const ulong align) +{ + const phys_size_t ram_size = 0x20000000; + const phys_addr_t ram_end = ram + ram_size; + struct lmb lmb; + long ret; + phys_addr_t a, b; + const phys_addr_t alloc_size_aligned = (alloc_size + align - 1) & + ~(align - 1); + + /* check for overflow */ + ut_assert(ram_end == 0 || ram_end > ram); + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0); + + /* allocate a block */ + a = lmb_alloc(&lmb, alloc_size, align); + ut_assert(a != 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned, + alloc_size, 0, 0, 0, 0); + /* allocate another block */ + b = lmb_alloc(&lmb, alloc_size, align); + ut_assert(b != 0); + if (alloc_size == alloc_size_aligned) { + ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - + (alloc_size_aligned * 2), alloc_size * 2, 0, 0, 0, + 0); + } else { + ASSERT_LMB(&lmb, ram, ram_size, 2, ram + ram_size - + (alloc_size_aligned * 2), alloc_size, ram + ram_size + - alloc_size_aligned, alloc_size, 0, 0); + } + /* and free them */ + ret = lmb_free(&lmb, b, alloc_size); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned, + alloc_size, 0, 0, 0, 0); + ret = lmb_free(&lmb, a, alloc_size); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0); + + /* allocate a block with base*/ + b = lmb_alloc_base(&lmb, alloc_size, align, ram_end); + ut_assert(a == b); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned, + alloc_size, 0, 0, 0, 0); + /* and free it */ + ret = lmb_free(&lmb, b, alloc_size); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0); + + return 0; +} + +static int lib_test_lmb_noreserved(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_noreserved(uts, 0x40000000, 4, 1); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_noreserved(uts, 0xE0000000, 4, 1); +} + +DM_TEST(lib_test_lmb_noreserved, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int lib_test_lmb_unaligned_size(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_noreserved(uts, 0x40000000, 5, 8); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_noreserved(uts, 0xE0000000, 5, 8); +} + +DM_TEST(lib_test_lmb_unaligned_size, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); +/* + * Simulate a RAM that starts at 0 and allocate down to address 0, which must + * fail as '0' means failure for the lmb_alloc functions. + */ +static int lib_test_lmb_at_0(struct unit_test_state *uts) +{ + const phys_addr_t ram = 0; + const phys_size_t ram_size = 0x20000000; + struct lmb lmb; + long ret; + phys_addr_t a, b; + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + /* allocate nearly everything */ + a = lmb_alloc(&lmb, ram_size - 4, 1); + ut_asserteq(a, ram + 4); + ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4, + 0, 0, 0, 0); + /* allocate the rest */ + /* This should fail as the allocated address would be 0 */ + b = lmb_alloc(&lmb, 4, 1); + ut_asserteq(b, 0); + /* check that this was an error by checking lmb */ + ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4, + 0, 0, 0, 0); + /* check that this was an error by freeing b */ + ret = lmb_free(&lmb, b, 4); + ut_asserteq(ret, -1); + ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4, + 0, 0, 0, 0); + + ret = lmb_free(&lmb, a, ram_size - 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0); + + return 0; +} + +DM_TEST(lib_test_lmb_at_0, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Check that calling lmb_reserve with overlapping regions fails. */ +static int lib_test_lmb_overlapping_reserve(struct unit_test_state *uts) +{ + const phys_addr_t ram = 0x40000000; + const phys_size_t ram_size = 0x20000000; + struct lmb lmb; + long ret; + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + ret = lmb_reserve(&lmb, 0x40010000, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000, + 0, 0, 0, 0); + /* allocate overlapping region should fail */ + ret = lmb_reserve(&lmb, 0x40011000, 0x10000); + ut_asserteq(ret, -1); + ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000, + 0, 0, 0, 0); + /* allocate 3nd region */ + ret = lmb_reserve(&lmb, 0x40030000, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 2, 0x40010000, 0x10000, + 0x40030000, 0x10000, 0, 0); + /* allocate 2nd region */ + ret = lmb_reserve(&lmb, 0x40020000, 0x10000); + ut_assert(ret >= 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x30000, + 0, 0, 0, 0); + + return 0; +} + +DM_TEST(lib_test_lmb_overlapping_reserve, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* + * Simulate 512 MiB RAM, reserve 3 blocks, allocate addresses in between. + * Expect addresses outside the memory range to fail. + */ +static int test_alloc_addr(struct unit_test_state *uts, const phys_addr_t ram) +{ + const phys_size_t ram_size = 0x20000000; + const phys_addr_t ram_end = ram + ram_size; + const phys_size_t alloc_addr_a = ram + 0x8000000; + const phys_size_t alloc_addr_b = ram + 0x8000000 * 2; + const phys_size_t alloc_addr_c = ram + 0x8000000 * 3; + struct lmb lmb; + long ret; + phys_addr_t a, b, c, d, e; + + /* check for overflow */ + ut_assert(ram_end == 0 || ram_end > ram); + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + /* reserve 3 blocks */ + ret = lmb_reserve(&lmb, alloc_addr_a, 0x10000); + ut_asserteq(ret, 0); + ret = lmb_reserve(&lmb, alloc_addr_b, 0x10000); + ut_asserteq(ret, 0); + ret = lmb_reserve(&lmb, alloc_addr_c, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 3, alloc_addr_a, 0x10000, + alloc_addr_b, 0x10000, alloc_addr_c, 0x10000); + + /* allocate blocks */ + a = lmb_alloc_addr(&lmb, ram, alloc_addr_a - ram); + ut_asserteq(a, ram); + ASSERT_LMB(&lmb, ram, ram_size, 3, ram, 0x8010000, + alloc_addr_b, 0x10000, alloc_addr_c, 0x10000); + b = lmb_alloc_addr(&lmb, alloc_addr_a + 0x10000, + alloc_addr_b - alloc_addr_a - 0x10000); + ut_asserteq(b, alloc_addr_a + 0x10000); + ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x10010000, + alloc_addr_c, 0x10000, 0, 0); + c = lmb_alloc_addr(&lmb, alloc_addr_b + 0x10000, + alloc_addr_c - alloc_addr_b - 0x10000); + ut_asserteq(c, alloc_addr_b + 0x10000); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000, + 0, 0, 0, 0); + d = lmb_alloc_addr(&lmb, alloc_addr_c + 0x10000, + ram_end - alloc_addr_c - 0x10000); + ut_asserteq(d, alloc_addr_c + 0x10000); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, ram_size, + 0, 0, 0, 0); + + /* allocating anything else should fail */ + e = lmb_alloc(&lmb, 1, 1); + ut_asserteq(e, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, ram_size, + 0, 0, 0, 0); + + ret = lmb_free(&lmb, d, ram_end - alloc_addr_c - 0x10000); + ut_asserteq(ret, 0); + + /* allocate at 3 points in free range */ + + d = lmb_alloc_addr(&lmb, ram_end - 4, 4); + ut_asserteq(d, ram_end - 4); + ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x18010000, + d, 4, 0, 0); + ret = lmb_free(&lmb, d, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000, + 0, 0, 0, 0); + + d = lmb_alloc_addr(&lmb, ram_end - 128, 4); + ut_asserteq(d, ram_end - 128); + ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x18010000, + d, 4, 0, 0); + ret = lmb_free(&lmb, d, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000, + 0, 0, 0, 0); + + d = lmb_alloc_addr(&lmb, alloc_addr_c + 0x10000, 4); + ut_asserteq(d, alloc_addr_c + 0x10000); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010004, + 0, 0, 0, 0); + ret = lmb_free(&lmb, d, 4); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000, + 0, 0, 0, 0); + + /* allocate at the bottom */ + ret = lmb_free(&lmb, a, alloc_addr_a - ram); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 1, ram + 0x8000000, 0x10010000, + 0, 0, 0, 0); + d = lmb_alloc_addr(&lmb, ram, 4); + ut_asserteq(d, ram); + ASSERT_LMB(&lmb, ram, ram_size, 2, d, 4, + ram + 0x8000000, 0x10010000, 0, 0); + + /* check that allocating outside memory fails */ + if (ram_end != 0) { + ret = lmb_alloc_addr(&lmb, ram_end, 1); + ut_asserteq(ret, 0); + } + if (ram != 0) { + ret = lmb_alloc_addr(&lmb, ram - 1, 1); + ut_asserteq(ret, 0); + } + + return 0; +} + +static int lib_test_lmb_alloc_addr(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_alloc_addr(uts, 0x40000000); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_alloc_addr(uts, 0xE0000000); +} + +DM_TEST(lib_test_lmb_alloc_addr, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Simulate 512 MiB RAM, reserve 3 blocks, check addresses in between */ +static int test_get_unreserved_size(struct unit_test_state *uts, + const phys_addr_t ram) +{ + const phys_size_t ram_size = 0x20000000; + const phys_addr_t ram_end = ram + ram_size; + const phys_size_t alloc_addr_a = ram + 0x8000000; + const phys_size_t alloc_addr_b = ram + 0x8000000 * 2; + const phys_size_t alloc_addr_c = ram + 0x8000000 * 3; + struct lmb lmb; + long ret; + phys_size_t s; + + /* check for overflow */ + ut_assert(ram_end == 0 || ram_end > ram); + + lmb_init(&lmb); + + ret = lmb_add(&lmb, ram, ram_size); + ut_asserteq(ret, 0); + + /* reserve 3 blocks */ + ret = lmb_reserve(&lmb, alloc_addr_a, 0x10000); + ut_asserteq(ret, 0); + ret = lmb_reserve(&lmb, alloc_addr_b, 0x10000); + ut_asserteq(ret, 0); + ret = lmb_reserve(&lmb, alloc_addr_c, 0x10000); + ut_asserteq(ret, 0); + ASSERT_LMB(&lmb, ram, ram_size, 3, alloc_addr_a, 0x10000, + alloc_addr_b, 0x10000, alloc_addr_c, 0x10000); + + /* check addresses in between blocks */ + s = lmb_get_free_size(&lmb, ram); + ut_asserteq(s, alloc_addr_a - ram); + s = lmb_get_free_size(&lmb, ram + 0x10000); + ut_asserteq(s, alloc_addr_a - ram - 0x10000); + s = lmb_get_free_size(&lmb, alloc_addr_a - 4); + ut_asserteq(s, 4); + + s = lmb_get_free_size(&lmb, alloc_addr_a + 0x10000); + ut_asserteq(s, alloc_addr_b - alloc_addr_a - 0x10000); + s = lmb_get_free_size(&lmb, alloc_addr_a + 0x20000); + ut_asserteq(s, alloc_addr_b - alloc_addr_a - 0x20000); + s = lmb_get_free_size(&lmb, alloc_addr_b - 4); + ut_asserteq(s, 4); + + s = lmb_get_free_size(&lmb, alloc_addr_c + 0x10000); + ut_asserteq(s, ram_end - alloc_addr_c - 0x10000); + s = lmb_get_free_size(&lmb, alloc_addr_c + 0x20000); + ut_asserteq(s, ram_end - alloc_addr_c - 0x20000); + s = lmb_get_free_size(&lmb, ram_end - 4); + ut_asserteq(s, 4); + + return 0; +} + +static int lib_test_lmb_get_free_size(struct unit_test_state *uts) +{ + int ret; + + /* simulate 512 MiB RAM beginning at 1GiB */ + ret = test_get_unreserved_size(uts, 0x40000000); + if (ret) + return ret; + + /* simulate 512 MiB RAM beginning at 1.5GiB */ + return test_get_unreserved_size(uts, 0xE0000000); +} + +DM_TEST(lib_test_lmb_get_free_size, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +static int lib_test_lmb_max_regions(struct unit_test_state *uts) +{ + const phys_addr_t ram = 0x00000000; + const phys_size_t ram_size = 0x8000000; + const phys_size_t blk_size = 0x10000; + phys_addr_t offset; + struct lmb lmb; + int ret, i; + + lmb_init(&lmb); + + ut_asserteq(lmb.memory.cnt, 0); + ut_asserteq(lmb.memory.max, 8); + ut_asserteq(lmb.reserved.cnt, 0); + ut_asserteq(lmb.reserved.max, 8); + + /* Add 8 memory regions */ + for (i = 0; i < 8; i++) { + offset = ram + 2 * i * ram_size; + ret = lmb_add(&lmb, offset, ram_size); + ut_asserteq(ret, 0); + } + ut_asserteq(lmb.memory.cnt, 8); + ut_asserteq(lmb.reserved.cnt, 0); + + /* error for the 9th memory regions */ + offset = ram + 2 * 8 * ram_size; + ret = lmb_add(&lmb, offset, ram_size); + ut_asserteq(ret, -1); + + ut_asserteq(lmb.memory.cnt, 8); + ut_asserteq(lmb.reserved.cnt, 0); + + /* reserve 8 regions */ + for (i = 0; i < 8; i++) { + offset = ram + 2 * i * blk_size; + ret = lmb_reserve(&lmb, offset, blk_size); + ut_asserteq(ret, 0); + } + + ut_asserteq(lmb.memory.cnt, 8); + ut_asserteq(lmb.reserved.cnt, 8); + + /* error for the 9th reserved blocks */ + offset = ram + 2 * 8 * blk_size; + ret = lmb_reserve(&lmb, offset, blk_size); + ut_asserteq(ret, -1); + + ut_asserteq(lmb.memory.cnt, 8); + ut_asserteq(lmb.reserved.cnt, 8); + + /* check each regions */ + for (i = 0; i < 8; i++) + ut_asserteq(lmb.memory.region[i].base, ram + 2 * i * ram_size); + + for (i = 0; i < 8; i++) + ut_asserteq(lmb.reserved.region[i].base, ram + 2 * i * blk_size); + + return 0; +} + +DM_TEST(lib_test_lmb_max_regions, + UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); diff --git a/roms/u-boot/test/lib/longjmp.c b/roms/u-boot/test/lib/longjmp.c new file mode 100644 index 000000000..201367a5a --- /dev/null +++ b/roms/u-boot/test/lib/longjmp.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Test setjmp(), longjmp() + * + * Copyright (c) 2021, Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> +#include <asm/setjmp.h> + +struct test_jmp_buf { + jmp_buf env; + int val; +}; + +/** + * test_longjmp() - test longjmp function + * + * @i is passed to longjmp. + * @i << 8 is set in the environment structure. + * + * @env: environment + * @i: value passed to longjmp() + */ +static noinline void test_longjmp(struct test_jmp_buf *env, int i) +{ + env->val = i << 8; + longjmp(env->env, i); +} + +/** + * test_setjmp() - test setjmp function + * + * setjmp() will return the value @i passed to longjmp() if @i is non-zero. + * For @i == 0 we expect return value 1. + * + * @i << 8 will be set by test_longjmp in the environment structure. + * This value can be used to check that the stack frame is restored. + * + * We return the XORed values to allow simply check both at once. + * + * @i: value passed to longjmp() + * Return: values return by longjmp() + */ +static int test_setjmp(int i) +{ + struct test_jmp_buf env; + int ret; + + env.val = -1; + ret = setjmp(env.env); + if (ret) + return ret ^ env.val; + test_longjmp(&env, i); + /* We should not arrive here */ + return 0x1000; +} + +static int lib_test_longjmp(struct unit_test_state *uts) +{ + int i; + + for (i = -3; i < 0; ++i) + ut_asserteq(i ^ (i << 8), test_setjmp(i)); + ut_asserteq(1, test_setjmp(0)); + for (i = 1; i < 4; ++i) + ut_asserteq(i ^ (i << 8), test_setjmp(i)); + return 0; +} +LIB_TEST(lib_test_longjmp, 0); diff --git a/roms/u-boot/test/lib/rsa.c b/roms/u-boot/test/lib/rsa.c new file mode 100644 index 000000000..44f8ade22 --- /dev/null +++ b/roms/u-boot/test/lib/rsa.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Linaro Limited + * Author: AKASHI Takahiro + * + * Unit test for rsa_verify() function + */ + +#include <common.h> +#include <command.h> +#include <image.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> +#include <u-boot/rsa.h> + +#ifdef CONFIG_RSA_VERIFY_WITH_PKEY +/* + * openssl genrsa 2048 -out private.pem + * openssl rsa -in private.pem -pubout -outform der -out public.der + * dd if=public.der of=public.raw bs=24 skip=1 + */ +static unsigned char public_key[] = { + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23, + 0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43, + 0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f, + 0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c, + 0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a, + 0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e, + 0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8, + 0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05, + 0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68, + 0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67, + 0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19, + 0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92, + 0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7, + 0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4, + 0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf, + 0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36, + 0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99, + 0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8, + 0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35, + 0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e, + 0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60, + 0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79, + 0x75, 0x02, 0x03, 0x01, 0x00, 0x01 +}; + +static unsigned int public_key_len = 270; + +/* + * dd if=/dev/urandom of=data.raw bs=512 count=1 + */ +static unsigned char data_raw[] = { + 0x3e, 0x48, 0x6e, 0xef, 0x83, 0xd1, 0x4c, 0xfd, 0x92, 0x47, 0x92, 0xd7, + 0xf6, 0x16, 0x25, 0x0a, 0xdf, 0xe2, 0xb6, 0x6c, 0xe7, 0xe0, 0x55, 0xb2, + 0x70, 0x66, 0xf0, 0xe5, 0xdc, 0xaf, 0xd3, 0x2e, 0xc1, 0x3e, 0x5c, 0x4b, + 0xb5, 0xa7, 0x23, 0x1f, 0x2c, 0xce, 0xf8, 0x83, 0x00, 0x6d, 0xeb, 0xdd, + 0x19, 0x71, 0x13, 0xb4, 0xae, 0x5c, 0xa8, 0xae, 0x52, 0xc8, 0xe1, 0x77, + 0x9e, 0x98, 0x75, 0xbc, 0xef, 0x36, 0x9f, 0x0c, 0x14, 0xed, 0x1a, 0x0a, + 0x4f, 0x6c, 0xa4, 0xb1, 0xbb, 0x0e, 0x43, 0x93, 0x12, 0xfc, 0x2e, 0x82, + 0x93, 0x4e, 0xcb, 0xa2, 0xcd, 0x59, 0x3f, 0xc5, 0x11, 0x38, 0x3a, 0x88, + 0xc3, 0xcf, 0xf9, 0x61, 0xa8, 0x9e, 0x96, 0xb6, 0xbf, 0xa6, 0x5b, 0x0d, + 0xd9, 0xbd, 0x05, 0x4c, 0xbe, 0xed, 0x86, 0xca, 0x10, 0x63, 0x72, 0x75, + 0x4b, 0xbd, 0x86, 0x42, 0x30, 0x9d, 0x54, 0x4e, 0x12, 0xda, 0xf4, 0xb4, + 0xfd, 0xd9, 0x54, 0x95, 0x8f, 0x83, 0xc2, 0x63, 0x44, 0xdd, 0x96, 0x1a, + 0xd0, 0x7c, 0xcf, 0xcb, 0x16, 0xd6, 0xff, 0xa3, 0xbb, 0xeb, 0x24, 0x06, + 0xbf, 0x81, 0xd0, 0x29, 0x76, 0x19, 0x66, 0x84, 0xfc, 0x49, 0xde, 0x7b, + 0x5d, 0xd2, 0x27, 0x58, 0x21, 0x7b, 0xff, 0x4d, 0x64, 0xf3, 0x89, 0xe3, + 0xea, 0xb6, 0x54, 0x4e, 0xb1, 0x62, 0x52, 0x89, 0xe3, 0x22, 0xf2, 0x26, + 0x3e, 0x4f, 0x43, 0x58, 0x78, 0x91, 0x55, 0xbc, 0x1e, 0xd6, 0x97, 0xfc, + 0x0b, 0x85, 0x4c, 0x92, 0x9c, 0xbf, 0xc4, 0xb1, 0x62, 0x93, 0x27, 0xa9, + 0xb2, 0xf4, 0xb4, 0x7a, 0xfb, 0x56, 0xe5, 0x8f, 0xe1, 0x94, 0x4d, 0xfd, + 0xe4, 0x72, 0x8d, 0xa9, 0x71, 0x65, 0xcb, 0x2e, 0x6d, 0x39, 0xd5, 0x95, + 0xe7, 0x3f, 0xab, 0xaa, 0x7a, 0x74, 0x84, 0x25, 0x4b, 0x42, 0x1e, 0xd3, + 0x86, 0xca, 0x47, 0x4a, 0xf0, 0x24, 0x81, 0x24, 0xb0, 0xe1, 0xbb, 0x6c, + 0x3f, 0x2a, 0xa0, 0xb8, 0xeb, 0xd6, 0x01, 0xce, 0x63, 0x51, 0xe1, 0x81, + 0xd2, 0x32, 0x43, 0x56, 0x44, 0x4a, 0x6b, 0x51, 0x24, 0xa2, 0xc7, 0x39, + 0x7c, 0x54, 0xda, 0xf8, 0xd4, 0x93, 0x7c, 0x8e, 0x4e, 0x9d, 0x15, 0x08, + 0xce, 0x27, 0xd8, 0x28, 0xb0, 0x5b, 0x75, 0x32, 0x43, 0xe8, 0xd6, 0xbf, + 0x12, 0xd5, 0xc5, 0x12, 0x8e, 0xeb, 0x77, 0x8f, 0x00, 0xde, 0x45, 0x1e, + 0xdd, 0xf3, 0xef, 0x43, 0x99, 0x79, 0x86, 0xea, 0x01, 0xce, 0xf2, 0x4d, + 0xa0, 0xfe, 0x5a, 0x55, 0xc0, 0x1f, 0xce, 0xe8, 0xbe, 0xc2, 0x66, 0xdb, + 0xcb, 0x3f, 0xa5, 0x48, 0xa1, 0xe2, 0x49, 0xa1, 0x29, 0x65, 0x5b, 0x62, + 0x39, 0xcc, 0xef, 0xbe, 0x86, 0xb7, 0xe3, 0x44, 0x67, 0x04, 0x04, 0xb1, + 0xec, 0xd8, 0xb2, 0xb2, 0x38, 0xbc, 0x10, 0xea, 0x7a, 0x0e, 0xa4, 0xa4, + 0xcb, 0x21, 0xd9, 0xc7, 0xb4, 0x0b, 0xb8, 0x39, 0xb4, 0x07, 0x53, 0x3f, + 0xb9, 0x55, 0x55, 0xa1, 0x6f, 0x11, 0x49, 0xc0, 0x94, 0x77, 0xaf, 0x76, + 0x97, 0x7f, 0x31, 0x08, 0xdd, 0x72, 0x48, 0x72, 0xf8, 0x11, 0x4f, 0x69, + 0x10, 0xef, 0x23, 0x06, 0xf3, 0x34, 0xac, 0xee, 0x97, 0x89, 0x41, 0x1c, + 0x36, 0x38, 0xb1, 0x80, 0x96, 0x7a, 0x9e, 0x72, 0xab, 0x25, 0xeb, 0xce, + 0x7b, 0xb8, 0x5d, 0xc8, 0xef, 0xa4, 0x73, 0xa1, 0xa6, 0x8f, 0x01, 0x54, + 0xce, 0x58, 0x19, 0xe5, 0x7e, 0xfa, 0x77, 0x08, 0x9d, 0x53, 0xc1, 0xcc, + 0x08, 0xe8, 0x1d, 0xe0, 0x82, 0x5e, 0xe1, 0xe6, 0xbd, 0xbb, 0x59, 0x7e, + 0x12, 0x9c, 0x39, 0x60, 0x23, 0xf7, 0xbe, 0x0a, 0x7c, 0x48, 0x12, 0xa0, + 0x84, 0x04, 0x3f, 0xa1, 0x6e, 0x92, 0xcd, 0xa0, 0xac, 0xee, 0x0b, 0xbc, + 0x18, 0x30, 0x28, 0xbd, 0xf5, 0xfa, 0x3a, 0x35 +}; + +static unsigned int data_raw_len = 512; + +/* + * openssl dgst -sha256 -sign private.key -out data.enc data.raw + */ +unsigned char data_enc[] = { + 0xa7, 0x4a, 0x12, 0x8f, 0xee, 0x65, 0x4b, 0xcd, 0x88, 0xca, 0x4d, 0xed, + 0xe3, 0x04, 0xe7, 0x7c, 0x59, 0xbf, 0x2f, 0xad, 0x95, 0x73, 0x5b, 0x2c, + 0x4e, 0xb5, 0xda, 0x5e, 0x3a, 0x6d, 0xb4, 0xc5, 0x84, 0x0c, 0xd2, 0x4a, + 0x62, 0x0d, 0x5f, 0xba, 0x10, 0xee, 0xb1, 0x2a, 0xe1, 0xfe, 0x50, 0x18, + 0x97, 0xcc, 0xea, 0x26, 0x62, 0x33, 0x5a, 0x1d, 0x51, 0x38, 0x52, 0x89, + 0x4d, 0xa7, 0x18, 0xff, 0xa6, 0xc8, 0xd4, 0x7a, 0xc0, 0xa6, 0x22, 0xdf, + 0x41, 0x89, 0x93, 0x9b, 0xe7, 0x9e, 0xc1, 0xc8, 0x80, 0xda, 0x1a, 0x3f, + 0xa4, 0x7a, 0xd0, 0x07, 0xcb, 0x5c, 0xa4, 0x75, 0x12, 0x54, 0x78, 0x67, + 0xbf, 0xe6, 0xae, 0x1e, 0x56, 0x33, 0x9e, 0xe0, 0x6e, 0x33, 0xa7, 0x58, + 0xb0, 0x47, 0x49, 0xa8, 0x37, 0xdb, 0x82, 0x4b, 0xbd, 0x32, 0x9c, 0xdc, + 0xf4, 0x67, 0x17, 0x24, 0x55, 0xfd, 0x83, 0x1e, 0xc8, 0xb4, 0x5c, 0xf9, + 0x15, 0x6c, 0x5e, 0xaa, 0x72, 0x03, 0x9e, 0x7c, 0x17, 0xf5, 0x7c, 0x37, + 0x96, 0x00, 0xb0, 0xd8, 0xaa, 0x05, 0xfa, 0xaa, 0xa1, 0x78, 0x77, 0xd5, + 0x09, 0xdd, 0x05, 0xc7, 0xe2, 0x9f, 0x68, 0xc7, 0xf8, 0xfb, 0x0b, 0x6f, + 0x18, 0x1e, 0xcc, 0x93, 0xd3, 0x3f, 0xc9, 0x26, 0x29, 0x64, 0xe7, 0x15, + 0xdc, 0xb8, 0x19, 0x10, 0x24, 0x55, 0x55, 0x3b, 0x79, 0xa1, 0x65, 0x12, + 0xe0, 0x0b, 0x88, 0x44, 0x4c, 0xea, 0x85, 0x5a, 0x6b, 0x2d, 0x45, 0x6e, + 0xe7, 0x83, 0x6f, 0x3a, 0xaa, 0x1e, 0xf1, 0x9c, 0x8f, 0xdc, 0xb9, 0x37, + 0xa2, 0x15, 0x61, 0x93, 0x06, 0x23, 0xf5, 0xfe, 0xf0, 0xf8, 0x2b, 0xf7, + 0xc0, 0x68, 0x67, 0xf6, 0x4e, 0x08, 0x0d, 0x0d, 0x08, 0xbe, 0xfb, 0x2c, + 0x4c, 0xe7, 0xd7, 0x1a, 0xad, 0xd9, 0x98, 0xa1, 0x8d, 0x94, 0x1c, 0xd1, + 0x89, 0x06, 0xc9, 0x3a +}; + +static unsigned int data_enc_len = 256; + +/** + * lib_rsa_verify_valid() - unit test for rsa_verify() + * + * Test rsa_verify() with valid hash + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_rsa_verify_valid(struct unit_test_state *uts) +{ + struct image_sign_info info; + struct image_region reg; + int ret; + + memset(&info, '\0', sizeof(info)); + info.name = "sha256,rsa2048"; + info.padding = image_get_padding_algo("pkcs-1.5"); + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.crypto = image_get_crypto_algo(info.name); + + info.key = public_key; + info.keylen = public_key_len; + + reg.data = data_raw; + reg.size = data_raw_len; + ret = rsa_verify(&info, ®, 1, data_enc, data_enc_len); + ut_assertf(ret == 0, "verification unexpectedly failed (%d)\n", ret); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_rsa_verify_valid, 0); + +/** + * lib_rsa_verify_invalid() - unit test for rsa_verify() + * + * Test rsa_verify() with invalid hash + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_rsa_verify_invalid(struct unit_test_state *uts) +{ + struct image_sign_info info; + struct image_region reg; + unsigned char ctmp; + int ret; + + memset(&info, '\0', sizeof(info)); + info.name = "sha256,rsa2048"; + info.padding = image_get_padding_algo("pkcs-1.5"); + info.checksum = image_get_checksum_algo("sha256,rsa2048"); + info.crypto = image_get_crypto_algo(info.name); + + info.key = public_key; + info.keylen = public_key_len; + + /* randomly corrupt enc'ed data */ + ctmp = data_enc[data_enc_len - 10]; + data_enc[data_enc_len - 10] = 0x12; + + reg.data = data_raw; + reg.size = data_raw_len; + ret = rsa_verify(&info, ®, 1, data_enc, data_enc_len); + + /* revert a change */ + data_enc[data_enc_len - 10] = ctmp; + + ut_assertf(ret != 0, "verification unexpectedly succeeded\n"); + + return CMD_RET_SUCCESS; +} + +LIB_TEST(lib_rsa_verify_invalid, 0); +#endif /* RSA_VERIFY_WITH_PKEY */ diff --git a/roms/u-boot/test/lib/sscanf.c b/roms/u-boot/test/lib/sscanf.c new file mode 100644 index 000000000..772e4b920 --- /dev/null +++ b/roms/u-boot/test/lib/sscanf.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2002, Uwe Bonnes + * Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. + * Copyright (c) 2020, EPAM Systems Inc. + * + * Unit tests for sscanf() function + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +#define EOF -1 + +/** + * lib_sscanf() - unit test for sscanf() + * @uts: unit test state + * + * Test sscanf() with varied parameters in different combinations passed + * as arguments. + * + * Return: 0 - success + * 1 - failure + */ +static int lib_sscanf(struct unit_test_state *uts) +{ + char buffer[100], buffer1[100]; + int result, ret; + static const char pname[] = " Hello World!\n"; + int hour = 21, min = 59, sec = 20; + int number, number_so_far; + unsigned int u1, u2, u3; + char s1[20], s2[10], s3[10], ch; + int r, int1, int2; + long lng1; + + /* check EOF */ + strcpy(buffer, ""); + ret = sscanf(buffer, "%d", &result); + ut_asserteq(ret, EOF); + + /* check %x */ + strcpy(buffer, "0x519"); + ut_asserteq(sscanf(buffer, "%x", &result), 1); + ut_asserteq(result, 0x519); + + strcpy(buffer, "0x51a"); + ut_asserteq(sscanf(buffer, "%x", &result), 1); + ut_asserteq(result, 0x51a); + + strcpy(buffer, "0x51g"); + ut_asserteq(sscanf(buffer, "%x", &result), 1); + ut_asserteq(result, 0x51); + + /* check strings */ + ret = sprintf(buffer, " %s", pname); + ret = sscanf(buffer, "%*c%[^\n]", buffer1); + ut_asserteq(ret, 1); + ut_asserteq(strncmp(pname, buffer1, strlen(buffer1)), 0); + + /* check digits */ + ret = sprintf(buffer, "%d:%d:%d", hour, min, sec); + ret = sscanf(buffer, "%d%n", &number, &number_so_far); + ut_asserteq(ret, 1); + ut_asserteq(number, hour); + ut_asserteq(number_so_far, 2); + + ret = sscanf(buffer + 2, "%*c%n", &number_so_far); + ut_asserteq(ret, 0); + ut_asserteq(number_so_far, 1); + + /* Check %i */ + strcpy(buffer, "123"); + ret = sscanf(buffer, "%i", &result); + ut_asserteq(ret, 1); + ut_asserteq(result, 123); + ret = sscanf(buffer, "%d", &result); + ut_asserteq(ret, 1); + ut_asserteq(result, 123); + + ut_asserteq(0, sscanf("hello world", "hello world")); + ut_asserteq(0, sscanf("hello world", "good bye")); + /* Excess data */ + ut_asserteq(0, sscanf("hello 3", "%u", &u1)); /* have to match the start */ + ut_asserteq(1, sscanf("3 hello", "%u", &u1)); /* but trailing is alright */ + + /* Numbers (ie. %u) */ + ut_asserteq(0, sscanf("hello world 3", "hello worlb %u", &u1)); /* d vs b */ + ut_asserteq(1, sscanf("12345", "%u", &u1)); + ut_asserteq(12345u, u1); + ut_asserteq(1, sscanf("0", "%u", &u1)); + ut_asserteq(0u, u1); + ut_asserteq(1, sscanf("0000", "%u", &u2)); + ut_asserteq(0u, u2); + ut_asserteq(0, sscanf("A", "%u", &u1)); /* bogus number */ + + /* Numbers with size (eg. %2u) */ + ut_asserteq(2, sscanf("123456", "%2u%u", &u1, &u2)); + ut_asserteq(12u, u1); + ut_asserteq(3456u, u2); + ut_asserteq(1, sscanf("123456", "%8u", &u1)); + ut_asserteq(123456u, u1); + ut_asserteq(1, sscanf("123457 ", "%8u", &u1)); + ut_asserteq(123457u, u1); + ut_asserteq(3, sscanf("!12:3:456", "!%2u:%2u:%3u", &u1, &u2, &u3)); + ut_asserteq(12u, u1); + ut_asserteq(3u, u2); + ut_asserteq(456u, u3); + ut_asserteq(3, sscanf("67:8:099", "%2u:%2u:%3u", &u1, &u2, &u3)); /* 0s */ + ut_asserteq(67u, u1); + ut_asserteq(8u, u2); + ut_asserteq(99u, u3); + /* Arbitrary amounts of 0-padding are okay */ + ut_asserteq(3, sscanf("12:03:000000000000000099", "%2u:%2u:%u", &u1, &u2, &u3)); + ut_asserteq(12u, u1); + ut_asserteq(3u, u2); + ut_asserteq(99u, u3); + + /* Hex (ie. %x) */ + ut_asserteq(3, sscanf("1234 02aBcdEf ff", "%x %x %x", &u1, &u2, &u3)); + ut_asserteq(0x1234, u1); + ut_asserteq(0x2ABCDEF, u2); + ut_asserteq(0xFF, u3); + /* Width works on %x */ + ut_asserteq(3, sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3)); + ut_asserteq(0xf00d, u1); + ut_asserteq(0xcafe, u2); + ut_asserteq(444, u3); + + /* Literal '%' (ie. '%%') */ + ut_asserteq(1, sscanf("99% fresh", "%3u%% fresh", &u1)); + ut_asserteq(99, u1); + ut_asserteq(0, sscanf("99 fresh", "%% %3u %s", &u1, s1)); + ut_asserteq(1, sscanf("99 fresh", "%3u%% %s", &u1, s1)); + ut_asserteq(2, sscanf("99 fresh", "%3u %5s %%", &u1, s1)); + ut_asserteq(99, u1); + ut_asserteq_str(s1, "fresh"); + ut_asserteq(1, sscanf("% boo", "%% %3s", s1)); + ut_asserteq_str("boo", s1); + + /* Strings (ie. %s) */ + ut_asserteq(2, sscanf("hello", "%3s%7s", s1, s2)); + ut_asserteq_str(s1, "hel"); + ut_asserteq_str(s2, "lo"); + ut_asserteq(2, sscanf("WD40", "%2s%u", s3, &u1)); /* %s%u */ + ut_asserteq_str(s3, "WD"); + ut_asserteq(40, u1); + ut_asserteq(2, sscanf("WD40", "%3s%u", s3, &u1)); /* %s%u */ + ut_asserteq_str(s3, "WD4"); + ut_asserteq(0, u1); + ut_asserteq(2, sscanf("76trombones", "%6u%9s", &u1, s1)); /* %u%s */ + ut_asserteq(76, u1); + ut_asserteq_str(s1, "trombones"); + + ut_asserteq(3, sscanf("1.2.3", "%u.%u.%u%c", &u1, &u2, &u3, &ch)); + ut_asserteq(4, sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch)); + ut_asserteq(' ', ch); + + r = sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2); + ut_asserteq(r, 3); + ut_asserteq(int1, 12345); + ut_asserteq(lng1, -67890); + ut_asserteq(int2, -1); + + return 0; +} + +LIB_TEST(lib_sscanf, 0); diff --git a/roms/u-boot/test/lib/string.c b/roms/u-boot/test/lib/string.c new file mode 100644 index 000000000..64234bef3 --- /dev/null +++ b/roms/u-boot/test/lib/string.c @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Unit tests for memory functions + * + * The architecture dependent implementations run through different lines of + * code depending on the alignment and length of memory regions copied or set. + * This has to be considered in testing. + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/* Xor mask used for marking memory regions */ +#define MASK 0xA5 +/* Number of different alignment values */ +#define SWEEP 16 +/* Allow for copying up to 32 bytes */ +#define BUFLEN (SWEEP + 33) + +/** + * init_buffer() - initialize buffer + * + * The buffer is filled with incrementing values xor'ed with the mask. + * + * @buf: buffer + * @mask: xor mask + */ +static void init_buffer(u8 buf[], u8 mask) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) + buf[i] = i ^ mask; +} + +/** + * test_memset() - test result of memset() + * + * @uts: unit test state + * @buf: buffer + * @mask: value set by memset() + * @offset: relative start of region changed by memset() in buffer + * @len: length of region changed by memset() + * Return: 0 = success, 1 = failure + */ +static int test_memset(struct unit_test_state *uts, u8 buf[], u8 mask, + int offset, int len) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) { + if (i < offset || i >= offset + len) { + ut_asserteq(i, buf[i]); + } else { + ut_asserteq(mask, buf[i]); + } + } + return 0; +} + +/** + * lib_memset() - unit test for memset() + * + * Test memset() with varied alignment and length of the changed buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memset(struct unit_test_state *uts) +{ + u8 buf[BUFLEN]; + int offset, len; + void *ptr; + + for (offset = 0; offset <= SWEEP; ++offset) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf, 0); + ptr = memset(buf + offset, MASK, len); + ut_asserteq_ptr(buf + offset, (u8 *)ptr); + if (test_memset(uts, buf, MASK, offset, len)) { + debug("%s: failure %d, %d\n", + __func__, offset, len); + return CMD_RET_FAILURE; + } + } + } + return 0; +} + +LIB_TEST(lib_memset, 0); + +/** + * test_memmove() - test result of memcpy() or memmove() + * + * @uts: unit test state + * @buf: buffer + * @mask: xor mask used to initialize source buffer + * @offset1: relative start of copied region in source buffer + * @offset2: relative start of copied region in destination buffer + * @len: length of region changed by memset() + * Return: 0 = success, 1 = failure + */ +static int test_memmove(struct unit_test_state *uts, u8 buf[], u8 mask, + int offset1, int offset2, int len) +{ + int i; + + for (i = 0; i < BUFLEN; ++i) { + if (i < offset2 || i >= offset2 + len) { + ut_asserteq(i, buf[i]); + } else { + ut_asserteq((i + offset1 - offset2) ^ mask, buf[i]); + } + } + return 0; +} + +/** + * lib_memcpy() - unit test for memcpy() + * + * Test memcpy() with varied alignment and length of the copied buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memcpy(struct unit_test_state *uts) +{ + u8 buf1[BUFLEN]; + u8 buf2[BUFLEN]; + int offset1, offset2, len; + void *ptr; + + init_buffer(buf1, MASK); + + for (offset1 = 0; offset1 <= SWEEP; ++offset1) { + for (offset2 = 0; offset2 <= SWEEP; ++offset2) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf2, 0); + ptr = memcpy(buf2 + offset2, buf1 + offset1, + len); + ut_asserteq_ptr(buf2 + offset2, (u8 *)ptr); + if (test_memmove(uts, buf2, MASK, offset1, + offset2, len)) { + debug("%s: failure %d, %d, %d\n", + __func__, offset1, offset2, len); + return CMD_RET_FAILURE; + } + } + } + } + return 0; +} + +LIB_TEST(lib_memcpy, 0); + +/** + * lib_memmove() - unit test for memmove() + * + * Test memmove() with varied alignment and length of the copied buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_memmove(struct unit_test_state *uts) +{ + u8 buf[BUFLEN]; + int offset1, offset2, len; + void *ptr; + + for (offset1 = 0; offset1 <= SWEEP; ++offset1) { + for (offset2 = 0; offset2 <= SWEEP; ++offset2) { + for (len = 1; len < BUFLEN - SWEEP; ++len) { + init_buffer(buf, 0); + ptr = memmove(buf + offset2, buf + offset1, + len); + ut_asserteq_ptr(buf + offset2, (u8 *)ptr); + if (test_memmove(uts, buf, 0, offset1, offset2, + len)) { + debug("%s: failure %d, %d, %d\n", + __func__, offset1, offset2, len); + return CMD_RET_FAILURE; + } + } + } + } + return 0; +} + +LIB_TEST(lib_memmove, 0); diff --git a/roms/u-boot/test/lib/strlcat.c b/roms/u-boot/test/lib/strlcat.c new file mode 100644 index 000000000..ee61684c4 --- /dev/null +++ b/roms/u-boot/test/lib/strlcat.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.1+ +/* + * Copyright (C) 2021 Sean Anderson <seanga2@gmail.com> + * Copyright (C) 2011-2021 Free Software Foundation, Inc. + * + * These tests adapted from glibc's string/test-strncat.c + */ + +#include <common.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +#define BUF_SIZE 4096 +char buf1[BUF_SIZE], buf2[BUF_SIZE]; + +static int do_test_strlcat(struct unit_test_state *uts, int line, size_t align1, + size_t align2, size_t len1, size_t len2, size_t n) +{ + char *s1, *s2; + size_t i, len, expected, actual; + + align1 &= 7; + if (align1 + len1 >= BUF_SIZE) + return 0; + if (align1 + n > BUF_SIZE) + return 0; + + align2 &= 7; + if (align2 + len1 + len2 >= BUF_SIZE) + return 0; + if (align2 + len1 + n > BUF_SIZE) + return 0; + + s1 = buf1 + align1; + s2 = buf2 + align2; + + for (i = 0; i < len1 - 1; i++) + s1[i] = 32 + 23 * i % (127 - 32); + s1[len1 - 1] = '\0'; + + for (i = 0; i < len2 - 1; i++) + s2[i] = 32 + 23 * i % (127 - 32); + s2[len2 - 1] = '\0'; + + expected = len2 < n ? min(len1 + len2 - 1, n) : n; + actual = strlcat(s2, s1, n); + if (expected != actual) { + ut_failf(uts, __FILE__, line, __func__, + "strlcat(s2, s1, 2) == len2 < n ? min(len1 + len2, n) : n", + "Expected %#lx (%ld), got %#lx (%ld)", + expected, expected, actual, actual); + return CMD_RET_FAILURE; + } + + len = min3(len1, n - len2, (size_t)0); + if (memcmp(s2 + len2, s1, len)) { + ut_failf(uts, __FILE__, line, __func__, + "s2 + len1 == s1", + "Expected \"%.*s\", got \"%.*s\"", + (int)len, s1, (int)len, s2 + len2); + return CMD_RET_FAILURE; + } + + i = min(n, len1 + len2 - 1) - 1; + if (len2 < n && s2[i] != '\0') { + ut_failf(uts, __FILE__, line, __func__, + "n < len1 && s2[len2 + n] == '\\0'", + "Expected s2[%ld] = '\\0', got %d ('%c')", + i, s2[i], s2[i]); + return CMD_RET_FAILURE; + } + + return 0; +} + +#define test_strlcat(align1, align2, len1, len2, n) do { \ + int ret = do_test_strlcat(uts, __LINE__, align1, align2, len1, len2, \ + n); \ + if (ret) \ + return ret; \ +} while (0) + +static int lib_test_strlcat(struct unit_test_state *uts) +{ + size_t i, n; + + test_strlcat(0, 2, 2, 2, SIZE_MAX); + test_strlcat(0, 0, 4, 4, SIZE_MAX); + test_strlcat(4, 0, 4, 4, SIZE_MAX); + test_strlcat(0, 0, 8, 8, SIZE_MAX); + test_strlcat(0, 8, 8, 8, SIZE_MAX); + + for (i = 1; i < 8; i++) { + test_strlcat(0, 0, 8 << i, 8 << i, SIZE_MAX); + test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, SIZE_MAX); + test_strlcat(0, 0, 8 << i, 2 << i, SIZE_MAX); + test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, SIZE_MAX); + + test_strlcat(i, 2 * i, 8 << i, 1, SIZE_MAX); + test_strlcat(2 * i, i, 8 << i, 1, SIZE_MAX); + test_strlcat(i, i, 8 << i, 10, SIZE_MAX); + } + + for (n = 2; n <= 2048; n *= 4) { + test_strlcat(0, 2, 2, 2, n); + test_strlcat(0, 0, 4, 4, n); + test_strlcat(4, 0, 4, 4, n); + test_strlcat(0, 0, 8, 8, n); + test_strlcat(0, 8, 8, 8, n); + + for (i = 1; i < 8; i++) { + test_strlcat(0, 0, 8 << i, 8 << i, n); + test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, n); + test_strlcat(0, 0, 8 << i, 2 << i, n); + test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, n); + + test_strlcat(i, 2 * i, 8 << i, 1, n); + test_strlcat(2 * i, i, 8 << i, 1, n); + test_strlcat(i, i, 8 << i, 10, n); + } + } + + return 0; +} +LIB_TEST(lib_test_strlcat, 0); diff --git a/roms/u-boot/test/lib/test_aes.c b/roms/u-boot/test/lib/test_aes.c new file mode 100644 index 000000000..cbc712f7e --- /dev/null +++ b/roms/u-boot/test/lib/test_aes.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Philippe Reynes <philippe.reynes@softathome.com> + * + * Unit tests for aes functions + */ + +#include <common.h> +#include <command.h> +#include <hexdump.h> +#include <rand.h> +#include <uboot_aes.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +#define TEST_AES_ONE_BLOCK 0 +#define TEST_AES_CBC_CHAIN 1 + +struct test_aes_s { + int key_len; + int key_exp_len; + int type; + int num_block; +}; + +static struct test_aes_s test_aes[] = { + { AES128_KEY_LENGTH, AES128_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 }, + { AES128_KEY_LENGTH, AES128_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 }, + { AES192_KEY_LENGTH, AES192_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 }, + { AES192_KEY_LENGTH, AES192_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 }, + { AES256_KEY_LENGTH, AES256_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 }, + { AES256_KEY_LENGTH, AES256_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 }, +}; + +static void rand_buf(u8 *buf, int size) +{ + int i; + + for (i = 0; i < size; i++) + buf[i] = rand() & 0xff; +} + +static int lib_test_aes_one_block(struct unit_test_state *uts, int key_len, + u8 *key_exp, u8 *iv, int num_block, + u8 *nocipher, u8 *ciphered, u8 *uncipher) +{ + aes_encrypt(key_len, nocipher, key_exp, ciphered); + aes_decrypt(key_len, ciphered, key_exp, uncipher); + + ut_asserteq_mem(nocipher, uncipher, AES_BLOCK_LENGTH); + + /* corrupt the expanded key */ + key_exp[0]++; + aes_decrypt(key_len, ciphered, key_exp, uncipher); + ut_assertf(memcmp(nocipher, uncipher, AES_BLOCK_LENGTH), + "nocipher and uncipher should be different\n"); + + return 0; +} + +static int lib_test_aes_cbc_chain(struct unit_test_state *uts, int key_len, + u8 *key_exp, u8 *iv, int num_block, + u8 *nocipher, u8 *ciphered, u8 *uncipher) +{ + aes_cbc_encrypt_blocks(key_len, key_exp, iv, + nocipher, ciphered, num_block); + aes_cbc_decrypt_blocks(key_len, key_exp, iv, + ciphered, uncipher, num_block); + + ut_asserteq_mem(nocipher, uncipher, num_block * AES_BLOCK_LENGTH); + + /* corrupt the expanded key */ + key_exp[0]++; + aes_cbc_decrypt_blocks(key_len, key_exp, iv, + ciphered, uncipher, num_block); + ut_assertf(memcmp(nocipher, uncipher, num_block * AES_BLOCK_LENGTH), + "nocipher and uncipher should be different\n"); + + return 0; +} + +static int _lib_test_aes_run(struct unit_test_state *uts, int key_len, + int key_exp_len, int type, int num_block) +{ + u8 *key, *key_exp, *iv; + u8 *nocipher, *ciphered, *uncipher; + int ret; + + /* Allocate all the buffer */ + key = malloc(key_len); + key_exp = malloc(key_exp_len); + iv = malloc(AES_BLOCK_LENGTH); + nocipher = malloc(num_block * AES_BLOCK_LENGTH); + ciphered = malloc((num_block + 1) * AES_BLOCK_LENGTH); + uncipher = malloc((num_block + 1) * AES_BLOCK_LENGTH); + + if (!key || !key_exp || !iv || !nocipher || !ciphered || !uncipher) { + printf("%s: can't allocate memory\n", __func__); + ret = -1; + goto out; + } + + /* Initialize all buffer */ + rand_buf(key, key_len); + rand_buf(iv, AES_BLOCK_LENGTH); + rand_buf(nocipher, num_block * AES_BLOCK_LENGTH); + memset(ciphered, 0, (num_block + 1) * AES_BLOCK_LENGTH); + memset(uncipher, 0, (num_block + 1) * AES_BLOCK_LENGTH); + + /* Expand the key */ + aes_expand_key(key, key_len, key_exp); + + /* Encrypt and decrypt */ + switch (type) { + case TEST_AES_ONE_BLOCK: + ret = lib_test_aes_one_block(uts, key_len, key_exp, iv, + num_block, nocipher, + ciphered, uncipher); + break; + case TEST_AES_CBC_CHAIN: + ret = lib_test_aes_cbc_chain(uts, key_len, key_exp, iv, + num_block, nocipher, + ciphered, uncipher); + break; + default: + printf("%s: unknown type (type=%d)\n", __func__, type); + ret = -1; + }; + + out: + /* Free all the data */ + free(key); + free(key_exp); + free(iv); + free(nocipher); + free(ciphered); + free(uncipher); + + return ret; +} + +static int lib_test_aes_run(struct unit_test_state *uts, + struct test_aes_s *test) +{ + int key_len = test->key_len; + int key_exp_len = test->key_exp_len; + int type = test->type; + int num_block = test->num_block; + + return _lib_test_aes_run(uts, key_len, key_exp_len, + type, num_block); +} + +static int lib_test_aes(struct unit_test_state *uts) +{ + int i, ret = 0; + + for (i = 0; i < ARRAY_SIZE(test_aes); i++) { + ret = lib_test_aes_run(uts, &test_aes[i]); + if (ret) + break; + } + + return ret; +} + +LIB_TEST(lib_test_aes, 0); diff --git a/roms/u-boot/test/lib/test_errno_str.c b/roms/u-boot/test/lib/test_errno_str.c new file mode 100644 index 000000000..8a9f1fd98 --- /dev/null +++ b/roms/u-boot/test/lib/test_errno_str.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Unit tests for memory functions + * + * The architecture dependent implementations run through different lines of + * code depending on the alignment and length of memory regions copied or set. + * This has to be considered in testing. + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +/** + * lib_errno_str() - unit test for errno_str() + * + * Test errno_str() with varied alignment and length of the copied buffer. + * + * @uts: unit test state + * Return: 0 = success, 1 = failure + */ +static int lib_errno_str(struct unit_test_state *uts) +{ + const char *msg; + + msg = errno_str(1); + ut_asserteq_str("Success", msg); + + msg = errno_str(0); + ut_asserteq_str("Success", msg); + + msg = errno_str(-ENOMEM); + ut_asserteq_str("Out of memory", msg); + + msg = errno_str(-99999); + ut_asserteq_str("Unknown error", msg); + + return 0; +} + +LIB_TEST(lib_errno_str, 0); diff --git a/roms/u-boot/test/lib/test_print.c b/roms/u-boot/test/lib/test_print.c new file mode 100644 index 000000000..a60a5a51f --- /dev/null +++ b/roms/u-boot/test/lib/test_print.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Tests for print functions + * + * Copyright 2020, Heinrich Schuchadt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <command.h> +#include <display_options.h> +#include <asm/global_data.h> +#include <test/lib.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int test_print_freq(struct unit_test_state *uts, + uint64_t freq, char *expected) +{ + ut_silence_console(uts); + console_record_reset_enable(); + print_freq(freq, ";\n"); + ut_unsilence_console(uts); + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + ut_asserteq_str(expected, uts->actual_str); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} + +static int lib_test_print_freq(struct unit_test_state *uts) +{ + ut_assertok(test_print_freq(uts, 321, "321 Hz;")); + ut_assertok(test_print_freq(uts, 4321, "4.32 kHz;")); + ut_assertok(test_print_freq(uts, 54321, "54.32 kHz;")); + ut_assertok(test_print_freq(uts, 654321, "654.32 kHz;")); + ut_assertok(test_print_freq(uts, 7654321, "7.66 MHz;")); + ut_assertok(test_print_freq(uts, 87654321, "87.66 MHz;")); + ut_assertok(test_print_freq(uts, 987654321, "987.66 MHz;")); + ut_assertok(test_print_freq(uts, 1987654321, "1.99 GHz;")); + ut_assertok(test_print_freq(uts, 54321987654321, "54321.99 GHz;")); + return 0; +} + +LIB_TEST(lib_test_print_freq, 0); + +static int test_print_size(struct unit_test_state *uts, + uint64_t freq, char *expected) +{ + ut_silence_console(uts); + console_record_reset_enable(); + print_size(freq, ";\n"); + ut_unsilence_console(uts); + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + ut_asserteq_str(expected, uts->actual_str); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} + +static int lib_test_print_size(struct unit_test_state *uts) +{ + ut_assertok(test_print_size(uts, 321, "321 Bytes;")); + ut_assertok(test_print_size(uts, 4321, "4.2 KiB;")); + ut_assertok(test_print_size(uts, 54321, "53 KiB;")); + ut_assertok(test_print_size(uts, 654321, "639 KiB;")); + ut_assertok(test_print_size(uts, 7654321, "7.3 MiB;")); + ut_assertok(test_print_size(uts, 87654321, "83.6 MiB;")); + ut_assertok(test_print_size(uts, 987654321, "941.9 MiB;")); + ut_assertok(test_print_size(uts, 1987654321, "1.9 GiB;")); + ut_assertok(test_print_size(uts, 54321987654321, "49.4 TiB;")); + return 0; +} + +LIB_TEST(lib_test_print_size, 0); diff --git a/roms/u-boot/test/log/Makefile b/roms/u-boot/test/log/Makefile new file mode 100644 index 000000000..a3dedace0 --- /dev/null +++ b/roms/u-boot/test/log/Makefile @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2017 Google, Inc + +obj-$(CONFIG_LOG_TEST) += log_test.o +obj-$(CONFIG_CMD_LOG) += log_filter.o + +ifdef CONFIG_UT_LOG + +obj-y += log_ut.o + +ifdef CONFIG_SANDBOX +obj-$(CONFIG_LOG_SYSLOG) += syslog_test.o +obj-$(CONFIG_LOG_SYSLOG) += syslog_test_ndebug.o +endif + +ifdef CONFIG_LOG +obj-y += pr_cont_test.o +obj-$(CONFIG_CONSOLE_RECORD) += cont_test.o +else +obj-$(CONFIG_CONSOLE_RECORD) += nolog_test.o +endif + +endif # CONFIG_UT_LOG diff --git a/roms/u-boot/test/log/cont_test.c b/roms/u-boot/test/log/cont_test.c new file mode 100644 index 000000000..de7b7f064 --- /dev/null +++ b/roms/u-boot/test/log/cont_test.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Test continuation of log messages. + */ + +#include <common.h> +#include <console.h> +#include <asm/global_data.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int log_test_cont(struct unit_test_state *uts) +{ + int log_fmt; + int log_level; + + log_fmt = gd->log_fmt; + log_level = gd->default_log_level; + + /* Write two messages, the second continuing the first */ + gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG); + gd->default_log_level = LOGL_INFO; + console_record_reset_enable(); + log(LOGC_ARCH, LOGL_ERR, "ea%d\n", 1); + log(LOGC_CONT, LOGL_CONT, "cc%d\n", 2); + gd->default_log_level = log_level; + gd->log_fmt = log_fmt; + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "ERR.arch, ea1")); + ut_assertok(ut_check_console_line(uts, "ERR.arch, cc2")); + ut_assertok(ut_check_console_end(uts)); + + /* Write a third message which is not a continuation */ + gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG); + gd->default_log_level = LOGL_INFO; + console_record_reset_enable(); + log(LOGC_EFI, LOGL_INFO, "ie%d\n", 3); + gd->default_log_level = log_level; + gd->log_fmt = log_fmt; + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "INFO.efi, ie3")); + ut_assertok(ut_check_console_end(uts)); + + /* Write two messages without a newline between them */ + gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG); + gd->default_log_level = LOGL_INFO; + console_record_reset_enable(); + log(LOGC_ARCH, LOGL_ERR, "ea%d ", 1); + log(LOGC_CONT, LOGL_CONT, "cc%d\n", 2); + gd->default_log_level = log_level; + gd->log_fmt = log_fmt; + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "ERR.arch, ea1 cc2")); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} +LOG_TEST(log_test_cont); diff --git a/roms/u-boot/test/log/log_filter.c b/roms/u-boot/test/log/log_filter.c new file mode 100644 index 000000000..b644b40a8 --- /dev/null +++ b/roms/u-boot/test/log/log_filter.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com> + */ + +#include <common.h> +#include <console.h> +#include <log.h> +#include <asm/global_data.h> +#include <test/log.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* Test invalid options */ +static int log_test_filter_invalid(struct unit_test_state *uts) +{ + ut_asserteq(1, run_command("log filter-add -AD", 0)); + ut_asserteq(1, run_command("log filter-add -l1 -L1", 0)); + ut_asserteq(1, run_command("log filter-add -l1 -L1", 0)); + ut_asserteq(1, run_command("log filter-add -lfoo", 0)); + ut_asserteq(1, run_command("log filter-add -cfoo", 0)); + ut_asserteq(1, run_command("log filter-add -ccore -ccore -ccore -ccore " + "-ccore -ccore", 0)); + + return 0; +} +LOG_TEST_FLAGS(log_test_filter_invalid, UT_TESTF_CONSOLE_REC); + +/* Test adding and removing filters */ +static int log_test_filter(struct unit_test_state *uts) +{ + bool any_found = false; + bool filt1_found = false; + bool filt2_found = false; + char cmd[32]; + struct log_filter *filt; + struct log_device *ldev; + ulong filt1, filt2; + +#define create_filter(args, filter_num) do {\ + ut_assertok(console_record_reset_enable()); \ + ut_assertok(run_command("log filter-add -p " args, 0)); \ + ut_assert_skipline(); \ + ut_assertok(strict_strtoul(uts->actual_str, 10, &(filter_num))); \ + ut_assert_console_end(); \ +} while (0) + + create_filter("", filt1); + create_filter("-DL warning -cmmc -cspi -ffile", filt2); + + ldev = log_device_find_by_name("console"); + ut_assertnonnull(ldev); + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (filt->filter_num == filt1) { + filt1_found = true; + ut_asserteq(0, filt->flags); + ut_asserteq(LOGL_MAX, filt->level); + ut_assertnull(filt->file_list); + } else if (filt->filter_num == filt2) { + filt2_found = true; + ut_asserteq(LOGFF_HAS_CAT | LOGFF_DENY | + LOGFF_LEVEL_MIN, filt->flags); + ut_asserteq(true, log_has_cat(filt->cat_list, + log_uc_cat(UCLASS_MMC))); + ut_asserteq(true, log_has_cat(filt->cat_list, + log_uc_cat(UCLASS_SPI))); + ut_asserteq(LOGL_WARNING, filt->level); + ut_asserteq_str("file", filt->file_list); + } + } + ut_asserteq(true, filt1_found); + ut_asserteq(true, filt2_found); + +#define remove_filter(filter_num) do { \ + ut_assertok(console_record_reset_enable()); \ + snprintf(cmd, sizeof(cmd), "log filter-remove %lu", filter_num); \ + ut_assertok(run_command(cmd, 0)); \ + ut_assert_console_end(); \ +} while (0) + + remove_filter(filt1); + remove_filter(filt2); + + filt1_found = false; + filt2_found = false; + list_for_each_entry(filt, &ldev->filter_head, sibling_node) { + if (filt->filter_num == filt1) + filt1_found = true; + else if (filt->filter_num == filt2) + filt2_found = true; + } + ut_asserteq(false, filt1_found); + ut_asserteq(false, filt2_found); + + create_filter("", filt1); + create_filter("", filt2); + + ut_assertok(console_record_reset_enable()); + ut_assertok(run_command("log filter-remove -a", 0)); + ut_assert_console_end(); + + list_for_each_entry(filt, &ldev->filter_head, sibling_node) + any_found = true; + ut_asserteq(false, any_found); + + return 0; +} +LOG_TEST_FLAGS(log_test_filter, UT_TESTF_CONSOLE_REC); diff --git a/roms/u-boot/test/log/log_test.c b/roms/u-boot/test/log/log_test.c new file mode 100644 index 000000000..4a814ff41 --- /dev/null +++ b/roms/u-boot/test/log/log_test.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Logging support test program + * + * Copyright (c) 2017 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <log.h> +#include <asm/global_data.h> +#include <test/log.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* emit some sample log records in different ways, for testing */ +static int do_log_run(struct unit_test_state *uts, int cat, const char *file) +{ + int i; + int ret, expected_ret; + + if (gd->flags & GD_FLG_LOG_READY) + expected_ret = 0; + else + expected_ret = -ENOSYS; + + gd->log_fmt = LOGF_TEST; + debug("debug\n"); + for (i = LOGL_FIRST; i < LOGL_COUNT; i++) { + log(cat, i, "log %d\n", i); + ret = _log(log_uc_cat(cat), i, file, 100 + i, + "func", "_log %d\n", i); + ut_asserteq(ret, expected_ret); + } + /* test with LOGL_COUNT flag */ + for (i = LOGL_FIRST; i < LOGL_COUNT; i++) { + ret = _log(log_uc_cat(cat), i | LOGL_FORCE_DEBUG, file, 100 + i, + "func", "_log force %d\n", i); + ut_asserteq(ret, expected_ret); + } + + gd->log_fmt = log_get_default_format(); + return 0; +} + +#define log_run_cat(cat) do_log_run(uts, cat, "file") +#define log_run_file(file) do_log_run(uts, UCLASS_SPI, file) +#define log_run() do_log_run(uts, UCLASS_SPI, "file") + +#define EXPECT_LOG BIT(0) +#define EXPECT_DIRECT BIT(1) +#define EXPECT_EXTRA BIT(2) +#define EXPECT_FORCE BIT(3) +#define EXPECT_DEBUG BIT(4) + +static int do_check_log_entries(struct unit_test_state *uts, int flags, int min, + int max) +{ + int i; + + for (i = min; i <= max; i++) { + if (flags & EXPECT_LOG) + ut_assert_nextline("do_log_run() log %d", i); + if (flags & EXPECT_DIRECT) + ut_assert_nextline("func() _log %d", i); + if (flags & EXPECT_DEBUG) { + ut_assert_nextline("log %d", i); + ut_assert_nextline("_log %d", i); + } + } + if (flags & EXPECT_EXTRA) + for (; i <= LOGL_MAX ; i++) + ut_assert_nextline("func() _log %d", i); + + for (i = LOGL_FIRST; i < LOGL_COUNT; i++) { + if (flags & EXPECT_FORCE) + ut_assert_nextline("func() _log force %d", i); + if (flags & EXPECT_DEBUG) + ut_assert_nextline("_log force %d", i); + } + + ut_assert_console_end(); + return 0; +} + +#define check_log_entries_flags_levels(flags, min, max) do {\ + int ret = do_check_log_entries(uts, flags, min, max); \ + if (ret) \ + return ret; \ +} while (0) + +#define check_log_entries_flags(flags) \ + check_log_entries_flags_levels(flags, LOGL_FIRST, _LOG_MAX_LEVEL) +#define check_log_entries() check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE) +#define check_log_entries_extra() \ + check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE) +#define check_log_entries_none() check_log_entries_flags(EXPECT_FORCE) + +/* Check a category filter using the first category */ +int log_test_cat_allow(struct unit_test_state *uts) +{ + enum log_category_t cat_list[] = { + log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI), + LOGC_NONE, LOGC_END + }; + int filt; + + filt = log_add_filter("console", cat_list, LOGL_MAX, NULL); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_cat(UCLASS_MMC); + check_log_entries_extra(); + + ut_assertok(console_record_reset_enable()); + log_run_cat(UCLASS_SPI); + check_log_entries_extra(); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_cat_allow, UT_TESTF_CONSOLE_REC); + +/* Check a category filter that should block log entries */ +int log_test_cat_deny_implicit(struct unit_test_state *uts) +{ + enum log_category_t cat_list[] = { + log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END + }; + int filt; + + filt = log_add_filter("console", cat_list, LOGL_MAX, NULL); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_cat(UCLASS_SPI); + check_log_entries_none(); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_cat_deny_implicit, UT_TESTF_CONSOLE_REC); + +/* Check passing and failing file filters */ +int log_test_file(struct unit_test_state *uts) +{ + int filt; + + filt = log_add_filter("console", NULL, LOGL_MAX, "file"); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_file("file"); + check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE); + + ut_assertok(console_record_reset_enable()); + log_run_file("file2"); + check_log_entries_none(); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_file, UT_TESTF_CONSOLE_REC); + +/* Check a passing file filter (second in list) */ +int log_test_file_second(struct unit_test_state *uts) +{ + int filt; + + filt = log_add_filter("console", NULL, LOGL_MAX, "file,file2"); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_file("file2"); + check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_file_second, UT_TESTF_CONSOLE_REC); + +/* Check a passing file filter (middle of list) */ +int log_test_file_mid(struct unit_test_state *uts) +{ + int filt; + + filt = log_add_filter("console", NULL, LOGL_MAX, + "file,file2,log/log_test.c"); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_file("file2"); + check_log_entries_extra(); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_file_mid, UT_TESTF_CONSOLE_REC); + +/* Check a log level filter */ +int log_test_level(struct unit_test_state *uts) +{ + int filt; + + filt = log_add_filter("console", NULL, LOGL_WARNING, NULL); + ut_assert(filt >= 0); + + ut_assertok(console_record_reset_enable()); + log_run(); + check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE, + LOGL_FIRST, LOGL_WARNING); + + ut_assertok(log_remove_filter("console", filt)); + return 0; +} +LOG_TEST_FLAGS(log_test_level, UT_TESTF_CONSOLE_REC); + +/* Check two filters, one of which passes everything */ +int log_test_double(struct unit_test_state *uts) +{ + int filt1, filt2; + + filt1 = log_add_filter("console", NULL, LOGL_WARNING, NULL); + ut_assert(filt1 >= 0); + filt2 = log_add_filter("console", NULL, LOGL_MAX, NULL); + ut_assert(filt2 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run(); + check_log_entries_extra(); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + return 0; +} +LOG_TEST_FLAGS(log_test_double, UT_TESTF_CONSOLE_REC); + +/* Check three filters, which together pass everything */ +int log_test_triple(struct unit_test_state *uts) +{ + int filt1, filt2, filt3; + + filt1 = log_add_filter("console", NULL, LOGL_MAX, "file)"); + ut_assert(filt1 >= 0); + filt2 = log_add_filter("console", NULL, LOGL_MAX, "file2"); + ut_assert(filt2 >= 0); + filt3 = log_add_filter("console", NULL, LOGL_MAX, "log/log_test.c"); + ut_assert(filt3 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_file("file2"); + check_log_entries_extra(); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + ut_assertok(log_remove_filter("console", filt3)); + return 0; +} +LOG_TEST_FLAGS(log_test_triple, UT_TESTF_CONSOLE_REC); + +int do_log_test_helpers(struct unit_test_state *uts) +{ + int i; + + ut_assertok(console_record_reset_enable()); + log_err("level %d\n", LOGL_EMERG); + log_err("level %d\n", LOGL_ALERT); + log_err("level %d\n", LOGL_CRIT); + log_err("level %d\n", LOGL_ERR); + log_warning("level %d\n", LOGL_WARNING); + log_notice("level %d\n", LOGL_NOTICE); + log_info("level %d\n", LOGL_INFO); + log_debug("level %d\n", LOGL_DEBUG); + log_content("level %d\n", LOGL_DEBUG_CONTENT); + log_io("level %d\n", LOGL_DEBUG_IO); + + for (i = LOGL_EMERG; i <= _LOG_MAX_LEVEL; i++) + ut_assert_nextline("%s() level %d", __func__, i); + ut_assert_console_end(); + return 0; +} + +int log_test_helpers(struct unit_test_state *uts) +{ + int ret; + + gd->log_fmt = LOGF_TEST; + ret = do_log_test_helpers(uts); + gd->log_fmt = log_get_default_format(); + return ret; +} +LOG_TEST_FLAGS(log_test_helpers, UT_TESTF_CONSOLE_REC); + +int do_log_test_disable(struct unit_test_state *uts) +{ + ut_assertok(console_record_reset_enable()); + log_err("default\n"); + ut_assert_nextline("%s() default", __func__); + + ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false)); + log_err("disabled\n"); + + ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true)); + log_err("enabled\n"); + ut_assert_nextline("%s() enabled", __func__); + ut_assert_console_end(); + return 0; +} + +int log_test_disable(struct unit_test_state *uts) +{ + int ret; + + gd->log_fmt = LOGF_TEST; + ret = do_log_test_disable(uts); + gd->log_fmt = log_get_default_format(); + return ret; +} +LOG_TEST_FLAGS(log_test_disable, UT_TESTF_CONSOLE_REC); + +/* Check denying based on category */ +int log_test_cat_deny(struct unit_test_state *uts) +{ + int filt1, filt2; + enum log_category_t cat_list[] = { + log_uc_cat(UCLASS_SPI), LOGC_END + }; + + filt1 = log_add_filter("console", cat_list, LOGL_MAX, NULL); + ut_assert(filt1 >= 0); + filt2 = log_add_filter_flags("console", cat_list, LOGL_MAX, NULL, + LOGFF_DENY); + ut_assert(filt2 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_cat(UCLASS_SPI); + check_log_entries_none(); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + return 0; +} +LOG_TEST_FLAGS(log_test_cat_deny, UT_TESTF_CONSOLE_REC); + +/* Check denying based on file */ +int log_test_file_deny(struct unit_test_state *uts) +{ + int filt1, filt2; + + filt1 = log_add_filter("console", NULL, LOGL_MAX, "file"); + ut_assert(filt1 >= 0); + filt2 = log_add_filter_flags("console", NULL, LOGL_MAX, "file", + LOGFF_DENY); + ut_assert(filt2 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run_file("file"); + check_log_entries_none(); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + return 0; +} +LOG_TEST_FLAGS(log_test_file_deny, UT_TESTF_CONSOLE_REC); + +/* Check denying based on level */ +int log_test_level_deny(struct unit_test_state *uts) +{ + int filt1, filt2; + + filt1 = log_add_filter("console", NULL, LOGL_INFO, NULL); + ut_assert(filt1 >= 0); + filt2 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL, + LOGFF_DENY); + ut_assert(filt2 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run(); + check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE, + LOGL_WARNING + 1, _LOG_MAX_LEVEL); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + return 0; +} +LOG_TEST_FLAGS(log_test_level_deny, UT_TESTF_CONSOLE_REC); + +/* Check matching based on minimum level */ +int log_test_min(struct unit_test_state *uts) +{ + int filt1, filt2; + + filt1 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL, + LOGFF_LEVEL_MIN); + ut_assert(filt1 >= 0); + filt2 = log_add_filter_flags("console", NULL, LOGL_INFO, NULL, + LOGFF_DENY | LOGFF_LEVEL_MIN); + ut_assert(filt2 >= 0); + + ut_assertok(console_record_reset_enable()); + log_run(); + check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE, + LOGL_WARNING, LOGL_INFO - 1); + + ut_assertok(log_remove_filter("console", filt1)); + ut_assertok(log_remove_filter("console", filt2)); + return 0; +} +LOG_TEST_FLAGS(log_test_min, UT_TESTF_CONSOLE_REC); + +/* Check dropped traces */ +int log_test_dropped(struct unit_test_state *uts) +{ + /* force LOG not ready */ + gd->flags &= ~(GD_FLG_LOG_READY); + gd->log_drop_count = 0; + + ut_assertok(console_record_reset_enable()); + log_run(); + + ut_asserteq(gd->log_drop_count, 3 * (LOGL_COUNT - LOGL_FIRST - 1)); + check_log_entries_flags_levels(EXPECT_DEBUG, LOGL_FIRST, CONFIG_LOG_DEFAULT_LEVEL); + + gd->flags |= GD_FLG_LOG_READY; + gd->log_drop_count = 0; + + return 0; +} +LOG_TEST_FLAGS(log_test_dropped, UT_TESTF_CONSOLE_REC); diff --git a/roms/u-boot/test/log/log_ut.c b/roms/u-boot/test/log/log_ut.c new file mode 100644 index 000000000..5aa3a1840 --- /dev/null +++ b/roms/u-boot/test/log/log_ut.c @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests. + */ + +#include <common.h> +#include <console.h> +#include <log.h> +#include <test/log.h> +#include <test/suites.h> + +int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(log_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(log_test); + + return cmd_ut_category("log", "log_test_", + tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/log/nolog_test.c b/roms/u-boot/test/log/nolog_test.c new file mode 100644 index 000000000..cb4fb3db9 --- /dev/null +++ b/roms/u-boot/test/log/nolog_test.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests for CONFIG_LOG=n. + */ + +/* Needed for testing log_debug() */ +#define DEBUG 1 + +#include <common.h> +#include <console.h> +#include <asm/global_data.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define BUFFSIZE 32 + +static int log_test_nolog_err(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_err("testing %s\n", "log_err"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_err")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_err); + +static int log_test_nolog_warning(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_warning("testing %s\n", "log_warning"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_warning")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_warning); + +static int log_test_nolog_notice(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_notice("testing %s\n", "log_notice"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_notice")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_notice); + +static int log_test_nolog_info(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_err("testing %s\n", "log_info"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_info")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_info); + +#undef _DEBUG +#define _DEBUG 0 +static int nolog_test_nodebug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + debug("testing %s\n", "debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_nodebug); + +static int log_test_nolog_nodebug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_debug("testing %s\n", "log_debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assert(!strcmp(buf, "")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_nodebug); + +#undef _DEBUG +#define _DEBUG 1 +static int nolog_test_debug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + debug("testing %s\n", "debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing debug")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(nolog_test_debug); + +static int log_test_nolog_debug(struct unit_test_state *uts) +{ + char buf[BUFFSIZE]; + + memset(buf, 0, BUFFSIZE); + console_record_reset_enable(); + log_debug("testing %s\n", "log_debug"); + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "testing log_debug")); + ut_assertok(ut_check_console_end(uts)); + return 0; +} +LOG_TEST(log_test_nolog_debug); diff --git a/roms/u-boot/test/log/pr_cont_test.c b/roms/u-boot/test/log/pr_cont_test.c new file mode 100644 index 000000000..6abddf7a1 --- /dev/null +++ b/roms/u-boot/test/log/pr_cont_test.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Test continuation of log messages using pr_cont(). + */ + +#include <common.h> +#include <console.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> +#include <asm/global_data.h> +#include <linux/printk.h> + +#define BUFFSIZE 64 + +#undef CONFIG_LOGLEVEL +#define CONFIG_LOGLEVEL 4 + +DECLARE_GLOBAL_DATA_PTR; + +static int log_test_pr_cont(struct unit_test_state *uts) +{ + int log_fmt; + int log_level; + + log_fmt = gd->log_fmt; + log_level = gd->default_log_level; + + /* Write two messages, the second continuing the first */ + gd->log_fmt = BIT(LOGF_MSG); + gd->default_log_level = LOGL_INFO; + console_record_reset_enable(); + pr_err("ea%d ", 1); + pr_cont("cc%d\n", 2); + gd->default_log_level = log_level; + gd->log_fmt = log_fmt; + gd->flags &= ~GD_FLG_RECORD; + ut_assertok(ut_check_console_line(uts, "ea1 cc2")); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} +LOG_TEST(log_test_pr_cont); diff --git a/roms/u-boot/test/log/syslog_test.c b/roms/u-boot/test/log/syslog_test.c new file mode 100644 index 000000000..4db649db8 --- /dev/null +++ b/roms/u-boot/test/log/syslog_test.c @@ -0,0 +1,241 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests for CONFIG_LOG_SYSLOG=y. + * + * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb + */ + +/* Override CONFIG_LOG_MAX_LEVEL */ +#define LOG_DEBUG + +#include <common.h> +#include <asm/global_data.h> +#include <dm/device.h> +#include <hexdump.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> +#include <asm/eth.h> +#include "syslog_test.h" + +DECLARE_GLOBAL_DATA_PTR; + +int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len) +{ + struct eth_sandbox_priv *priv = dev_get_priv(dev); + struct sb_log_env *env = priv->priv; + /* uts is updated by the ut_assert* macros */ + struct unit_test_state *uts = env->uts; + char *buf = packet; + struct ethernet_hdr *eth_hdr = packet; + struct ip_udp_hdr *ip_udp_hdr; + + /* Check Ethernet header */ + ut_asserteq_mem(ð_hdr->et_dest, net_bcast_ethaddr, ARP_HLEN); + ut_asserteq(ntohs(eth_hdr->et_protlen), PROT_IP); + + /* Check IP header */ + buf += sizeof(struct ethernet_hdr); + ip_udp_hdr = (struct ip_udp_hdr *)buf; + ut_asserteq(ip_udp_hdr->ip_p, IPPROTO_UDP); + ut_asserteq(ip_udp_hdr->ip_dst.s_addr, 0xffffffff); + ut_asserteq(ntohs(ip_udp_hdr->udp_dst), 514); + ut_asserteq(UDP_HDR_SIZE + strlen(env->expected) + 1, + ntohs(ip_udp_hdr->udp_len)); + + /* Check payload */ + buf += sizeof(struct ip_udp_hdr); + ut_asserteq_mem(env->expected, buf, + ntohs(ip_udp_hdr->udp_len) - UDP_HDR_SIZE); + + /* Signal that the callback function has been executed */ + env->expected = NULL; + + return 0; +} + +int syslog_test_setup(struct unit_test_state *uts) +{ + ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), true)); + + return 0; +} + +int syslog_test_finish(struct unit_test_state *uts) +{ + ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), false)); + + return 0; +} + +/** + * log_test_syslog_err() - test log_err() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_err(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<3>sandbox uboot: log_test_syslog_err() " + "testing log_err\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_err("testing %s\n", "log_err"); + /* Check that the callback function was called */ + sandbox_eth_set_tx_handler(0, NULL); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_err); + +/** + * log_test_syslog_warning() - test log_warning() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_warning(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<4>sandbox uboot: log_test_syslog_warning() " + "testing log_warning\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_warning("testing %s\n", "log_warning"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_warning); + +/** + * log_test_syslog_notice() - test log_notice() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_notice(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<5>sandbox uboot: log_test_syslog_notice() " + "testing log_notice\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_notice("testing %s\n", "log_notice"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_notice); + +/** + * log_test_syslog_info() - test log_info() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_info(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<6>sandbox uboot: log_test_syslog_info() " + "testing log_info\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_info("testing %s\n", "log_info"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_info); + +/** + * log_test_syslog_debug() - test log_debug() function + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_debug(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_DEBUG; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<7>sandbox uboot: log_test_syslog_debug() " + "testing log_debug\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_debug("testing %s\n", "log_debug"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was called */ + ut_assertnull(env.expected); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_debug); diff --git a/roms/u-boot/test/log/syslog_test.h b/roms/u-boot/test/log/syslog_test.h new file mode 100644 index 000000000..bfaa6daef --- /dev/null +++ b/roms/u-boot/test/log/syslog_test.h @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Header file for logging tests + */ + +#ifndef __SYSLOG_TEST_H +#define __SYSLOG_TEST_H + +/** + * struct sb_log_env - private data for sandbox ethernet driver + * + * This structure is used for the private data of the sandbox ethernet + * driver. + * + * @expected: string expected to be written by the syslog driver + * @uts: unit test state + */ +struct sb_log_env { + const char *expected; + struct unit_test_state *uts; +}; + +/** + * sb_log_tx_handler() - transmit callback function + * + * This callback function is invoked when a network package is sent using the + * sandbox Ethernet driver. The private data of the driver holds a sb_log_env + * structure with the unit test state and the expected UDP payload. + * + * The following checks are executed: + * + * * the Ethernet packet indicates a IP broadcast message + * * the IP header is for a local UDP broadcast message to port 514 + * * the UDP payload matches the expected string + * + * After testing the pointer to the expected string is set to NULL to signal + * that the callback function has been called. + * + * @dev: sandbox ethernet device + * @packet: Ethernet packet + * @len: length of Ethernet packet + * Return: 0 = success + */ +int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len); + +/** + * syslog_test_setup() - Enable syslog logging ready for tests + * + * @uts: Test state + * @return 0 if OK, -ENOENT if the syslog log driver is not found + */ +int syslog_test_setup(struct unit_test_state *uts); + +/** + * syslog_test_finish() - Disable syslog logging after tests + * + * @uts: Test state + * @return 0 if OK, -ENOENT if the syslog log driver is not found + */ +int syslog_test_finish(struct unit_test_state *uts); + +#endif diff --git a/roms/u-boot/test/log/syslog_test_ndebug.c b/roms/u-boot/test/log/syslog_test_ndebug.c new file mode 100644 index 000000000..443879104 --- /dev/null +++ b/roms/u-boot/test/log/syslog_test_ndebug.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de> + * + * Logging function tests for CONFIG_LOG_SYSLOG=y. + * + * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb + */ + +#include <common.h> +#include <asm/global_data.h> +#include <dm/device.h> +#include <hexdump.h> +#include <test/log.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> +#include <asm/eth.h> +#include "syslog_test.h" + +DECLARE_GLOBAL_DATA_PTR; + +/** + * log_test_syslog_nodebug() - test logging level filter + * + * Verify that log_debug() does not lead to a log message if the logging level + * is set to LOGL_INFO. + * + * @uts: unit test state + * Return: 0 = success + */ +static int log_test_syslog_nodebug(struct unit_test_state *uts) +{ + int old_log_level = gd->default_log_level; + struct sb_log_env env; + + ut_assertok(syslog_test_setup(uts)); + gd->log_fmt = LOGF_TEST; + gd->default_log_level = LOGL_INFO; + env_set("ethact", "eth@10002000"); + env_set("log_hostname", "sandbox"); + env.expected = "<7>sandbox uboot: log_test_syslog_nodebug() " + "testing log_debug\n"; + env.uts = uts; + sandbox_eth_set_tx_handler(0, sb_log_tx_handler); + /* Used by ut_assert macros in the tx_handler */ + sandbox_eth_set_priv(0, &env); + log_debug("testing %s\n", "log_debug"); + sandbox_eth_set_tx_handler(0, NULL); + /* Check that the callback function was not called */ + ut_assertnonnull(env.expected); + gd->default_log_level = old_log_level; + gd->log_fmt = log_get_default_format(); + ut_assertok(syslog_test_finish(uts)); + + return 0; +} +LOG_TEST(log_test_syslog_nodebug); diff --git a/roms/u-boot/test/nokia_rx51_test.sh b/roms/u-boot/test/nokia_rx51_test.sh new file mode 100755 index 000000000..ee45e8d6d --- /dev/null +++ b/roms/u-boot/test/nokia_rx51_test.sh @@ -0,0 +1,296 @@ +#!/bin/bash -e +# SPDX-License-Identifier: GPL-2.0+ +# (C) 2020 Pali Rohár <pali@kernel.org> + +# External tools needed for this test: +echo ' + wget + git + truncate + tar + dpkg + dd + make + gcc + arm-linux-gnueabi-gcc + fakeroot (homepage http://fakeroot-ng.lingnu.com/) + mcopy (from mtools, homepage http://www.gnu.org/software/mtools/) + mformat (from mtools, homepage http://www.gnu.org/software/mtools/) + /usr/sbin/mkfs.ubifs (from mtd-utils, homepage http://www.linux-mtd.infradead.org/) + /usr/sbin/ubinize (from mtd-utils, homepage http://www.linux-mtd.infradead.org/) +' | while read tool info; do + if test -z "$tool"; then continue; fi + if ! which $tool 1>/dev/null 2>&1; then + echo "Tool $tool was not found and is required to run this test" + echo "First install $tool $info" + exit 1 + fi +done || exit 1 + +echo +echo "============================================================" +echo "========== Compiling U-Boot for Nokia RX-51 board ==========" +echo "============================================================" +echo + +# First compile u-boot.bin binary for Nokia RX-51 board +make nokia_rx51_config +make -j4 u-boot.bin ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- + +# And then do all stuff in temporary directory +mkdir -p nokia_rx51_tmp +cd nokia_rx51_tmp + +test -f mkimage || ln -s ../tools/mkimage . +test -f u-boot.bin || ln -s ../u-boot.bin . + +echo +echo "==========================================================================" +echo "========== Downloading and compiling qemu from qemu-linaro fork ==========" +echo "==========================================================================" +echo + +# Download and compile linaro version qemu which has support for n900 machine +# Last working commit is 8f8d8e0796efe1a6f34cdd83fb798f3c41217ec1 +if ! test -f qemu-system-arm; then + test -d qemu-linaro || git clone https://git.linaro.org/qemu/qemu-linaro.git + cd qemu-linaro + git checkout 8f8d8e0796efe1a6f34cdd83fb798f3c41217ec1 + ./configure --enable-system --target-list=arm-softmmu --disable-sdl --disable-gtk --disable-curses --audio-drv-list= --audio-card-list= --disable-werror --disable-xen --disable-xen-pci-passthrough --disable-brlapi --disable-vnc --disable-curl --disable-slirp --disable-kvm --disable-user --disable-linux-user --disable-bsd-user --disable-guest-base --disable-uuid --disable-vde --disable-linux-aio --disable-cap-ng --disable-attr --disable-blobs --disable-docs --disable-spice --disable-libiscsi --disable-smartcard-nss --disable-usb-redir --disable-guest-agent --disable-seccomp --disable-glusterfs --disable-nptl --disable-fdt + make -j4 + cd .. + ln -s qemu-linaro/arm-softmmu/qemu-system-arm . +fi + +echo +echo "===================================================" +echo "========== Downloading external binaries ==========" +echo "===================================================" +echo + +# Download qflasher and nolo images +# This is proprietary qemu flasher tool with first stage images, but license allows non-commercial redistribution +wget -c http://repository.maemo.org/qemu-n900/qemu-n900.tar.gz +tar -xf qemu-n900.tar.gz + +# Download Maemo script u-boot-gen-combined +if ! test -f u-boot-gen-combined; then + test -d u-boot-maemo || git clone https://github.com/pali/u-boot-maemo.git + chmod +x u-boot-maemo/debian/u-boot-gen-combined + ln -s u-boot-maemo/debian/u-boot-gen-combined . +fi + +# Download Maemo fiasco kernel +wget -c http://repository.maemo.org/pool/maemo5.0/free/k/kernel/kernel_2.6.28-20103103+0m5_armel.deb +dpkg -x kernel_2.6.28-20103103+0m5_armel.deb kernel_2.6.28 + +# Download Maemo libc +wget -c http://repository.maemo.org/pool/maemo5.0/free/g/glibc/libc6_2.5.1-1eglibc27+0m5_armel.deb +dpkg -x libc6_2.5.1-1eglibc27+0m5_armel.deb libc6_2.5.1 + +# Download Maemo busybox +wget -c http://repository.maemo.org/pool/maemo5.0/free/b/busybox/busybox_1.10.2.legal-1osso30+0m5_armel.deb +dpkg -x busybox_1.10.2.legal-1osso30+0m5_armel.deb busybox_1.10.2 + +echo +echo "=======================================" +echo "========== Generating images ==========" +echo "=======================================" +echo + +# Generate rootfs directory +mkdir -p rootfs +mkdir -p rootfs/dev/ +mkdir -p rootfs/bin/ +mkdir -p rootfs/sbin/ +mkdir -p rootfs/lib/ +cp -a busybox_1.10.2/bin/busybox rootfs/bin/ +cp -a libc6_2.5.1/lib/ld-linux.so.3 rootfs/lib/ +cp -a libc6_2.5.1/lib/ld-2.5.so rootfs/lib/ +cp -a libc6_2.5.1/lib/libc.so.6 rootfs/lib/ +cp -a libc6_2.5.1/lib/libc-2.5.so rootfs/lib/ +cp -a libc6_2.5.1/lib/libcrypt.so.1 rootfs/lib/ +cp -a libc6_2.5.1/lib/libcrypt-2.5.so rootfs/lib/ +test -f rootfs/bin/sh || ln -sf busybox rootfs/bin/sh +test -f rootfs/sbin/poweroff || ln -sf ../bin/busybox rootfs/sbin/poweroff +cat > rootfs/sbin/preinit << EOF +#!/bin/sh +echo +echo "Successfully booted" +echo +/sbin/poweroff -f +EOF +chmod +x rootfs/sbin/preinit + +# Generate ubi config file for ubi rootfs image +cat > ubi.ini << EOF +[rootfs] +mode=ubi +image=ubifs.img +vol_id=0 +vol_size=160MiB +vol_type=dynamic +vol_name=rootfs +vol_alignment=1 +vol_flags=autoresize +EOF + +# Generate ubi rootfs image from rootfs directory +# NOTE: Character device on host filesystem can be created only by root +# But we do not need it on host filesystem, just in ubifs image +# So run mknod and mkfs.ubifs commands under fakeroot program +# which via LD_PRELOAD simulate mknod() and stat() functions +# so mkfs.ubifs will see dev/console as character device and +# put it correctly as character device into final ubifs image +# Therefore we can run whole script as non-root nobody user +fakeroot sh -c ' + rm -f rootfs/dev/console; + mknod rootfs/dev/console c 5 1; + /usr/sbin/mkfs.ubifs -m 2048 -e 129024 -c 2047 -r rootfs ubifs.img; +' +/usr/sbin/ubinize -o ubi.img -p 128KiB -m 2048 -s 512 ubi.ini + +# Generate bootmenu for U-Boot serial console testing +cat > bootmenu_uboot << EOF +setenv bootmenu_0 'Serial console test=echo; echo "Testing serial console"; echo; echo "Successfully booted"; echo; poweroff'; +setenv bootmenu_1; +setenv bootmenu_delay 1; +setenv bootdelay 1; +EOF +./mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n bootmenu_uboot -d bootmenu_uboot bootmenu_uboot.scr + +# Generate bootmenu for eMMC booting +cat > bootmenu_emmc << EOF +setenv bootmenu_0 'uImage-2.6.28-omap1 from eMMC=setenv mmcnum 1; setenv mmcpart 1; setenv mmctype fat; setenv bootargs; setenv setup_omap_atag 1; setenv mmckernfile uImage-2.6.28-omap1; run trymmckernboot'; +setenv bootmenu_1; +setenv bootmenu_delay 1; +setenv bootdelay 1; +EOF +./mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n bootmenu_emmc -d bootmenu_emmc bootmenu_emmc.scr + +# Generate bootmenu for OneNAND booting +cat > bootmenu_nand << EOF +setenv bootmenu_0 'uImage-2.6.28-omap1 from OneNAND=mtd read initfs \${kernaddr}; setenv bootargs; setenv setup_omap_atag 1; bootm \${kernaddr}'; +setenv bootmenu_1; +setenv bootmenu_delay 1; +setenv bootdelay 1; +EOF +./mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n bootmenu_nand -d bootmenu_nand bootmenu_nand.scr + +# Generate combined image from u-boot and Maemo fiasco kernel +dd if=kernel_2.6.28/boot/zImage-2.6.28-20103103+0m5.fiasco of=zImage-2.6.28-omap1 skip=95 bs=1 +./mkimage -A arm -O linux -T kernel -C none -a 80008000 -e 80008000 -n zImage-2.6.28-omap1 -d zImage-2.6.28-omap1 uImage-2.6.28-omap1 +./u-boot-gen-combined u-boot.bin uImage-2.6.28-omap1 combined.bin + +# Generate combined hack image from u-boot and Maemo fiasco kernel (kernel starts at 2MB offset and qflasher puts 2kB header before supplied image) +cp u-boot.bin combined_hack.bin +dd if=uImage-2.6.28-omap1 of=combined_hack.bin bs=1024 seek=$((2048-2)) + +# Generate FAT32 eMMC image for U-Boot serial console testing +truncate -s 50MiB emmc_uboot.img +mformat -m 0xf8 -F -h 4 -s 16 -c 1 -t $((50*1024*1024/(4*16*512))) :: -i emmc_uboot.img +mcopy bootmenu_uboot.scr ::/bootmenu.scr -i emmc_uboot.img + +# Generate FAT32 eMMC image for eMMC booting +truncate -s 50MiB emmc_emmc.img +mformat -m 0xf8 -F -h 4 -s 16 -c 1 -t $((50*1024*1024/(4*16*512))) :: -i emmc_emmc.img +mcopy uImage-2.6.28-omap1 ::/uImage-2.6.28-omap1 -i emmc_emmc.img +mcopy bootmenu_emmc.scr ::/bootmenu.scr -i emmc_emmc.img + +# Generate FAT32 eMMC image for OneNAND booting +truncate -s 50MiB emmc_nand.img +mformat -m 0xf8 -F -h 4 -s 16 -c 1 -t $((50*1024*1024/(4*16*512))) :: -i emmc_nand.img +mcopy bootmenu_nand.scr ::/bootmenu.scr -i emmc_nand.img + +# Generate MTD image for U-Boot serial console testing +rm -f mtd_uboot.img +./qflasher -v -x xloader-qemu.bin -s secondary-qemu.bin -k u-boot.bin -m rx51 -o mtd_uboot.img + +# Generate MTD image for RAM booting from bootloader nolo images, compiled image and rootfs image +rm -f mtd_ram.img +./qflasher -v -x xloader-qemu.bin -s secondary-qemu.bin -k combined.bin -r ubi.img -m rx51 -o mtd_ram.img + +# Generate MTD image for eMMC booting from bootloader nolo images, u-boot image and rootfs image +rm -f mtd_emmc.img +./qflasher -v -x xloader-qemu.bin -s secondary-qemu.bin -k u-boot.bin -r ubi.img -m rx51 -o mtd_emmc.img + +# Generate MTD image for OneNAND booting from bootloader nolo images, combined hacked image and rootfs image +# Kernel image is put into initfs area, but qflasher reject to copy kernel image into initfs area because it does not have initfs signature +# This is hack to workaround this problem, tell qflasher that kernel area for u-boot is bigger and put big combined hacked image (u-boot + kernel with correct offset) +rm -f mtd_nand.img +./qflasher -v -x xloader-qemu.bin -s secondary-qemu.bin -k combined_hack.bin -r ubi.img -m rx51 -p k=4094,i=2 -o mtd_nand.img + +echo +echo "======================================================" +echo "========== Running test images in n900 qemu ==========" +echo "======================================================" +echo + +# Run MTD image in qemu and wait for 300s if U-Boot prints testing string to serial console and poweroff +rm -f qemu_uboot.log +./qemu-system-arm -M n900 -mtdblock mtd_uboot.img -sd emmc_uboot.img -serial /dev/stdout -display none > qemu_uboot.log & +qemu_pid=$! +tail -F qemu_uboot.log & +tail_pid=$! +sleep 300 & +sleep_pid=$! +wait -n $sleep_pid $qemu_pid || true +kill -9 $tail_pid $sleep_pid $qemu_pid 2>/dev/null || true +wait || true + +# Run MTD image in qemu and wait for 300s if kernel from RAM is correctly booted +rm -f qemu_ram.log +./qemu-system-arm -M n900 -mtdblock mtd_ram.img -serial /dev/stdout -display none > qemu_ram.log & +qemu_pid=$! +tail -F qemu_ram.log & +tail_pid=$! +sleep 300 & +sleep_pid=$! +wait -n $sleep_pid $qemu_pid || true +kill -9 $tail_pid $sleep_pid $qemu_pid 2>/dev/null || true +wait || true + +# Run MTD image in qemu and wait for 300s if kernel from eMMC is correctly booted +rm -f qemu_emmc.log +./qemu-system-arm -M n900 -mtdblock mtd_emmc.img -sd emmc_emmc.img -serial /dev/stdout -display none > qemu_emmc.log & +qemu_pid=$! +tail -F qemu_emmc.log & +tail_pid=$! +sleep 300 & +sleep_pid=$! +wait -n $sleep_pid $qemu_pid || true +kill -9 $tail_pid $sleep_pid $qemu_pid 2>/dev/null || true +wait || true + +# Run MTD image in qemu and wait for 300s if kernel from OneNAND is correctly booted +rm -f qemu_nand.log +./qemu-system-arm -M n900 -mtdblock mtd_nand.img -sd emmc_nand.img -serial /dev/stdout -display none > qemu_nand.log & +qemu_pid=$! +tail -F qemu_nand.log & +tail_pid=$! +sleep 300 & +sleep_pid=$! +wait -n $sleep_pid $qemu_pid || true +kill -9 $tail_pid $sleep_pid $qemu_pid 2>/dev/null || true +wait || true + +echo +echo "=============================" +echo "========== Results ==========" +echo "=============================" +echo + +if grep -q 'Successfully booted' qemu_uboot.log; then echo "U-Boot serial console is working"; else echo "U-Boot serial console test failed"; fi +if grep -q 'Successfully booted' qemu_ram.log; then echo "Kernel was successfully booted from RAM"; else echo "Failed to boot kernel from RAM"; fi +if grep -q 'Successfully booted' qemu_emmc.log; then echo "Kernel was successfully booted from eMMC"; else echo "Failed to boot kernel from eMMC"; fi +if grep -q 'Successfully booted' qemu_nand.log; then echo "Kernel was successfully booted from OneNAND"; else echo "Failed to boot kernel from OneNAND"; fi + +echo + +if grep -q 'Successfully booted' qemu_uboot.log && grep -q 'Successfully booted' qemu_ram.log && grep -q 'Successfully booted' qemu_emmc.log && grep -q 'Successfully booted' qemu_nand.log; then + echo "All tests passed" + exit 0 +else + echo "Some tests failed" + exit 1 +fi diff --git a/roms/u-boot/test/optee/Kconfig b/roms/u-boot/test/optee/Kconfig new file mode 100644 index 000000000..2f6834aa3 --- /dev/null +++ b/roms/u-boot/test/optee/Kconfig @@ -0,0 +1,7 @@ +config UT_OPTEE + bool "Enable OP-TEE Unit Tests" + depends on UNIT_TEST && OF_CONTROL && OPTEE + default y + help + This enables the 'ut optee' command which runs a series of unit + tests on the optee library code.. diff --git a/roms/u-boot/test/optee/Makefile b/roms/u-boot/test/optee/Makefile new file mode 100644 index 000000000..8793fd7ad --- /dev/null +++ b/roms/u-boot/test/optee/Makefile @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH + +# Test files +obj-y += cmd_ut_optee.o + +DTC_FLAGS += -@ + +# DT overlays +obj-y += test-optee-base.dtb.o +obj-y += test-optee-optee.dtb.o +obj-y += test-optee-no-optee.dtb.o diff --git a/roms/u-boot/test/optee/cmd_ut_optee.c b/roms/u-boot/test/optee/cmd_ut_optee.c new file mode 100644 index 000000000..c3887ab11 --- /dev/null +++ b/roms/u-boot/test/optee/cmd_ut_optee.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <fdt_support.h> +#include <log.h> +#include <malloc.h> +#include <tee/optee.h> + +#include <linux/sizes.h> + +#include <test/ut.h> +#include <test/optee.h> +#include <test/suites.h> + +/* 4k ought to be enough for anybody */ +#define FDT_COPY_SIZE (4 * SZ_1K) + +extern u32 __dtb_test_optee_base_begin; +extern u32 __dtb_test_optee_optee_begin; +extern u32 __dtb_test_optee_no_optee_begin; + +static void *fdt; +static bool expect_success; + +static int optee_fdt_firmware(struct unit_test_state *uts) +{ + const void *prop; + int offs, len; + + offs = fdt_path_offset(fdt, "/firmware/optee"); + ut_assert(expect_success ? offs >= 0 : offs < 0); + + /* only continue if we have an optee node */ + if (offs < 0) + return CMD_RET_SUCCESS; + + prop = fdt_getprop(fdt, offs, "compatible", &len); + ut_assertok(strncmp((const char *)prop, "linaro,optee-tz", len)); + + prop = fdt_getprop(fdt, offs, "method", &len); + ut_assert(strncmp(prop, "hvc", 3) == 0 || strncmp(prop, "smc", 3) == 0); + + return CMD_RET_SUCCESS; +} +OPTEE_TEST(optee_fdt_firmware, 0); + +static int optee_fdt_protected_memory(struct unit_test_state *uts) +{ + int offs, subnode; + bool found; + + offs = fdt_path_offset(fdt, "/firmware/optee"); + ut_assert(expect_success ? offs >= 0 : offs < 0); + + /* only continue if we have an optee node */ + if (offs < 0) + return CMD_RET_SUCCESS; + + /* optee inserts its memory regions as reserved-memory nodes */ + offs = fdt_subnode_offset(fdt, 0, "reserved-memory"); + ut_assert(offs >= 0); + + subnode = fdt_first_subnode(fdt, offs); + ut_assert(subnode); + + found = 0; + while (subnode >= 0) { + const char *name = fdt_get_name(fdt, subnode, NULL); + struct fdt_resource res; + + ut_assert(name); + + /* only handle optee reservations */ + if (strncmp(name, "optee", 5)) + continue; + + found = true; + + /* check if this subnode has a reg property */ + ut_assertok(fdt_get_resource(fdt, subnode, "reg", 0, &res)); + subnode = fdt_next_subnode(fdt, subnode); + } + + ut_assert(found); + + return CMD_RET_SUCCESS; +} +OPTEE_TEST(optee_fdt_protected_memory, 0); + +int do_ut_optee(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(optee_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(optee_test); + struct unit_test_state *uts; + void *fdt_optee = &__dtb_test_optee_optee_begin; + void *fdt_no_optee = &__dtb_test_optee_no_optee_begin; + void *fdt_base = &__dtb_test_optee_base_begin; + int ret = -ENOMEM; + + uts = calloc(1, sizeof(*uts)); + if (!uts) + return -ENOMEM; + + ut_assertok(fdt_check_header(fdt_base)); + ut_assertok(fdt_check_header(fdt_optee)); + ut_assertok(fdt_check_header(fdt_no_optee)); + + fdt = malloc(FDT_COPY_SIZE); + if (!fdt) + return ret; + + /* + * Resize the FDT to 4k so that we have room to operate on + * + * (and relocate it since the memory might be mapped + * read-only) + */ + ut_assertok(fdt_open_into(fdt_base, fdt, FDT_COPY_SIZE)); + + /* + * (1) Try to copy optee nodes from empty dt. + * This should still run successfully. + */ + ut_assertok(optee_copy_fdt_nodes(fdt_no_optee, fdt)); + + expect_success = false; + ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv); + + /* (2) Try to copy optee nodes from prefilled dt */ + ut_assertok(optee_copy_fdt_nodes(fdt_optee, fdt)); + + expect_success = true; + ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv); + + /* (3) Try to copy OP-TEE nodes into a already filled DT */ + ut_assertok(fdt_open_into(fdt_optee, fdt, FDT_COPY_SIZE)); + ut_assertok(optee_copy_fdt_nodes(fdt_optee, fdt)); + + expect_success = true; + ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv); + + free(fdt); + return ret; +} diff --git a/roms/u-boot/test/optee/test-optee-base.dts b/roms/u-boot/test/optee/test-optee-base.dts new file mode 100644 index 000000000..3c1d0c60e --- /dev/null +++ b/roms/u-boot/test/optee/test-optee-base.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; +}; + + diff --git a/roms/u-boot/test/optee/test-optee-no-optee.dts b/roms/u-boot/test/optee/test-optee-no-optee.dts new file mode 100644 index 000000000..3c1d0c60e --- /dev/null +++ b/roms/u-boot/test/optee/test-optee-no-optee.dts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; +}; + + diff --git a/roms/u-boot/test/optee/test-optee-optee.dts b/roms/u-boot/test/optee/test-optee-optee.dts new file mode 100644 index 000000000..11e26a272 --- /dev/null +++ b/roms/u-boot/test/optee/test-optee-optee.dts @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH + */ + +/dts-v1/; + +/ { + #address-cells = <2>; + #size-cells = <2>; + + firmware { + optee { + compatible = "linaro,optee-tz"; + method = "smc"; + }; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + optee_shm@32000000 { + reg = <0x00 0x32000000 0x00 0x400000>; + }; + + optee_core@30000000 { + reg = <0x00 0x30000000 0x00 0x2000000>; + }; + }; +}; diff --git a/roms/u-boot/test/overlay/Kconfig b/roms/u-boot/test/overlay/Kconfig new file mode 100644 index 000000000..a4f154415 --- /dev/null +++ b/roms/u-boot/test/overlay/Kconfig @@ -0,0 +1,10 @@ +config UT_OVERLAY + bool "Enable Device Tree Overlays Unit Tests" + depends on UNIT_TEST && OF_CONTROL + default y + select OF_LIBFDT_OVERLAY + help + This enables the 'ut overlay' command which runs a series of unit + tests on the fdt overlay code. + If all is well then all tests pass although there will be a few + messages printed along the way. diff --git a/roms/u-boot/test/overlay/Makefile b/roms/u-boot/test/overlay/Makefile new file mode 100644 index 000000000..2deec929a --- /dev/null +++ b/roms/u-boot/test/overlay/Makefile @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2016 NextThing Co +# Copyright (c) 2016 Free Electrons + +# Test files +obj-y += cmd_ut_overlay.o + +DTC_FLAGS += -@ + +# DT overlays +obj-y += test-fdt-base.dtb.o +obj-y += test-fdt-overlay.dtb.o +obj-y += test-fdt-overlay-stacked.dtb.o diff --git a/roms/u-boot/test/overlay/cmd_ut_overlay.c b/roms/u-boot/test/overlay/cmd_ut_overlay.c new file mode 100644 index 000000000..56a3df171 --- /dev/null +++ b/roms/u-boot/test/overlay/cmd_ut_overlay.c @@ -0,0 +1,285 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <fdt_support.h> +#include <image.h> +#include <log.h> +#include <malloc.h> + +#include <linux/sizes.h> + +#include <test/ut.h> +#include <test/overlay.h> +#include <test/suites.h> + +/* 4k ought to be enough for anybody */ +#define FDT_COPY_SIZE (4 * SZ_1K) + +extern u32 __dtb_test_fdt_base_begin; +extern u32 __dtb_test_fdt_overlay_begin; +extern u32 __dtb_test_fdt_overlay_stacked_begin; + +static void *fdt; + +static int ut_fdt_getprop_u32_by_index(void *fdt, const char *path, + const char *name, int index, + u32 *out) +{ + const fdt32_t *val; + int node_off; + int len; + + node_off = fdt_path_offset(fdt, path); + if (node_off < 0) + return node_off; + + val = fdt_getprop(fdt, node_off, name, &len); + if (!val || (len < (sizeof(uint32_t) * (index + 1)))) + return -FDT_ERR_NOTFOUND; + + *out = fdt32_to_cpu(*(val + index)); + + return 0; +} + +static int ut_fdt_getprop_u32(void *fdt, const char *path, const char *name, + u32 *out) +{ + return ut_fdt_getprop_u32_by_index(fdt, path, name, 0, out); +} + +static int fdt_getprop_str(void *fdt, const char *path, const char *name, + const char **out) +{ + int node_off; + int len; + + node_off = fdt_path_offset(fdt, path); + if (node_off < 0) + return node_off; + + *out = fdt_stringlist_get(fdt, node_off, name, 0, &len); + + return len < 0 ? len : 0; +} + +static int fdt_overlay_change_int_property(struct unit_test_state *uts) +{ + u32 val = 0; + + ut_assertok(ut_fdt_getprop_u32(fdt, "/test-node", "test-int-property", + &val)); + ut_asserteq(43, val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_change_int_property, 0); + +static int fdt_overlay_change_str_property(struct unit_test_state *uts) +{ + const char *val = NULL; + + ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property", + &val)); + ut_asserteq_str("foobar", val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_change_str_property, 0); + +static int fdt_overlay_add_str_property(struct unit_test_state *uts) +{ + const char *val = NULL; + + ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property-2", + &val)); + ut_asserteq_str("foobar2", val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_add_str_property, 0); + +static int fdt_overlay_add_node_by_phandle(struct unit_test_state *uts) +{ + int off; + + off = fdt_path_offset(fdt, "/test-node/new-node"); + ut_assert(off >= 0); + + ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL)); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_add_node_by_phandle, 0); + +static int fdt_overlay_add_node_by_path(struct unit_test_state *uts) +{ + int off; + + off = fdt_path_offset(fdt, "/new-node"); + ut_assert(off >= 0); + + ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL)); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_add_node_by_path, 0); + +static int fdt_overlay_add_subnode_property(struct unit_test_state *uts) +{ + int off; + + off = fdt_path_offset(fdt, "/test-node/sub-test-node"); + ut_assert(off >= 0); + + ut_assertnonnull(fdt_getprop(fdt, off, "sub-test-property", NULL)); + ut_assertnonnull(fdt_getprop(fdt, off, "new-sub-test-property", NULL)); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_add_subnode_property, 0); + +static int fdt_overlay_local_phandle(struct unit_test_state *uts) +{ + uint32_t local_phandle; + u32 val = 0; + int off; + + off = fdt_path_offset(fdt, "/new-local-node"); + ut_assert(off >= 0); + + local_phandle = fdt_get_phandle(fdt, off); + ut_assert(local_phandle); + + ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle", + 0, &val)); + ut_asserteq(local_phandle, val); + + ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle", + 1, &val)); + ut_asserteq(local_phandle, val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_local_phandle, 0); + +static int fdt_overlay_local_phandles(struct unit_test_state *uts) +{ + uint32_t local_phandle, test_phandle; + u32 val = 0; + int off; + + off = fdt_path_offset(fdt, "/new-local-node"); + ut_assert(off >= 0); + + local_phandle = fdt_get_phandle(fdt, off); + ut_assert(local_phandle); + + off = fdt_path_offset(fdt, "/test-node"); + ut_assert(off >= 0); + + test_phandle = fdt_get_phandle(fdt, off); + ut_assert(test_phandle); + + ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 0, + &val)); + ut_asserteq(test_phandle, val); + + ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 1, + &val)); + ut_asserteq(local_phandle, val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_local_phandles, 0); + +static int fdt_overlay_stacked(struct unit_test_state *uts) +{ + u32 val = 0; + + ut_assertok(ut_fdt_getprop_u32(fdt, "/new-local-node", + "stacked-test-int-property", &val)); + ut_asserteq(43, val); + + return CMD_RET_SUCCESS; +} +OVERLAY_TEST(fdt_overlay_stacked, 0); + +int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(overlay_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(overlay_test); + struct unit_test_state *uts; + void *fdt_base = &__dtb_test_fdt_base_begin; + void *fdt_overlay = &__dtb_test_fdt_overlay_begin; + void *fdt_overlay_stacked = &__dtb_test_fdt_overlay_stacked_begin; + void *fdt_overlay_copy, *fdt_overlay_stacked_copy; + int ret = -ENOMEM; + + uts = calloc(1, sizeof(*uts)); + if (!uts) + return -ENOMEM; + + ut_assertok(fdt_check_header(fdt_base)); + ut_assertok(fdt_check_header(fdt_overlay)); + + fdt = malloc(FDT_COPY_SIZE); + if (!fdt) + goto err1; + + fdt_overlay_copy = malloc(FDT_COPY_SIZE); + if (!fdt_overlay_copy) + goto err2; + + fdt_overlay_stacked_copy = malloc(FDT_COPY_SIZE); + if (!fdt_overlay_stacked_copy) + goto err3; + + /* + * Resize the FDT to 4k so that we have room to operate on + * + * (and relocate it since the memory might be mapped + * read-only) + */ + ut_assertok(fdt_open_into(fdt_base, fdt, FDT_COPY_SIZE)); + + /* + * Resize the overlay to 4k so that we have room to operate on + * + * (and relocate it since the memory might be mapped + * read-only) + */ + ut_assertok(fdt_open_into(fdt_overlay, fdt_overlay_copy, + FDT_COPY_SIZE)); + + /* + * Resize the stacked overlay to 4k so that we have room to operate on + * + * (and relocate it since the memory might be mapped + * read-only) + */ + ut_assertok(fdt_open_into(fdt_overlay_stacked, fdt_overlay_stacked_copy, + FDT_COPY_SIZE)); + + /* Apply the overlay */ + ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_copy)); + + /* Apply the stacked overlay */ + ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_stacked_copy)); + + ret = cmd_ut_category("overlay", "", tests, n_ents, argc, argv); + + free(fdt_overlay_stacked_copy); +err3: + free(fdt_overlay_copy); +err2: + free(fdt); +err1: + return ret; +} diff --git a/roms/u-boot/test/overlay/test-fdt-base.dts b/roms/u-boot/test/overlay/test-fdt-base.dts new file mode 100644 index 000000000..38278334e --- /dev/null +++ b/roms/u-boot/test/overlay/test-fdt-base.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + */ + +/dts-v1/; + +/ { + test: test-node { + test-int-property = <42>; + test-str-property = "foo"; + + subtest: sub-test-node { + sub-test-property; + }; + }; +}; + + diff --git a/roms/u-boot/test/overlay/test-fdt-overlay-stacked.dts b/roms/u-boot/test/overlay/test-fdt-overlay-stacked.dts new file mode 100644 index 000000000..6411adec5 --- /dev/null +++ b/roms/u-boot/test/overlay/test-fdt-overlay-stacked.dts @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + * Copyright (c) 2018 Konsulko Group + */ + +/dts-v1/; +/plugin/; + +/ { + /* Test that we can reference an overlay symbol */ + fragment@0 { + target = <&local>; + + __overlay__ { + stacked-test-int-property = <43>; + }; + }; +}; diff --git a/roms/u-boot/test/overlay/test-fdt-overlay.dts b/roms/u-boot/test/overlay/test-fdt-overlay.dts new file mode 100644 index 000000000..5a21b346d --- /dev/null +++ b/roms/u-boot/test/overlay/test-fdt-overlay.dts @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2016 NextThing Co + * Copyright (c) 2016 Free Electrons + */ + +/dts-v1/; +/plugin/; + +/ { + /* Test that we can change an int by another */ + fragment@0 { + target = <&test>; + + __overlay__ { + test-int-property = <43>; + }; + }; + + /* Test that we can replace a string by a longer one */ + fragment@1 { + target = <&test>; + + __overlay__ { + test-str-property = "foobar"; + }; + }; + + /* Test that we add a new property */ + fragment@2 { + target = <&test>; + + __overlay__ { + test-str-property-2 = "foobar2"; + }; + }; + + /* Test that we add a new node (by phandle) */ + fragment@3 { + target = <&test>; + + __overlay__ { + new-node { + new-property; + }; + }; + }; + + /* Test that we add a new node (by path) */ + fragment@4 { + target-path = "/"; + + __overlay__ { + new-node { + new-property; + }; + }; + }; + + fragment@5 { + target-path = "/"; + + __overlay__ { + local: new-local-node { + new-property; + }; + }; + }; + + fragment@6 { + target-path = "/"; + + __overlay__ { + test-phandle = <&test>, <&local>; + }; + }; + + fragment@7 { + target-path = "/"; + + __overlay__ { + test-several-phandle = <&local>, <&local>; + }; + }; + + fragment@8 { + target = <&test>; + + __overlay__ { + sub-test-node { + new-sub-test-property; + }; + }; + }; +}; diff --git a/roms/u-boot/test/print_ut.c b/roms/u-boot/test/print_ut.c new file mode 100644 index 000000000..5b0a46de9 --- /dev/null +++ b/roms/u-boot/test/print_ut.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2012, The Chromium Authors + */ + +#define DEBUG + +#include <common.h> +#include <command.h> +#include <efi_api.h> +#include <display_options.h> +#include <log.h> +#include <version.h> + +#define FAKE_BUILD_TAG "jenkins-u-boot-denx_uboot_dm-master-build-aarch64" \ + "and a lot more text to come" + +/* Test printing GUIDs */ +static void guid_ut_print(void) +{ +#if CONFIG_IS_ENABLED(LIB_UUID) + unsigned char guid[16] = { + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 + }; + char str[40]; + + sprintf(str, "%pUb", guid); + assert(!strcmp("01020304-0506-0708-090a-0b0c0d0e0f10", str)); + sprintf(str, "%pUB", guid); + assert(!strcmp("01020304-0506-0708-090A-0B0C0D0E0F10", str)); + sprintf(str, "%pUl", guid); + assert(!strcmp("04030201-0605-0807-090a-0b0c0d0e0f10", str)); + sprintf(str, "%pUL", guid); + assert(!strcmp("04030201-0605-0807-090A-0B0C0D0E0F10", str)); +#endif +} + +/* Test efi_loader specific printing */ +static void efi_ut_print(void) +{ +#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) + char str[10]; + u8 buf[sizeof(struct efi_device_path_sd_mmc_path) + + sizeof(struct efi_device_path)]; + u8 *pos = buf; + struct efi_device_path *dp_end; + struct efi_device_path_sd_mmc_path *dp_sd = + (struct efi_device_path_sd_mmc_path *)pos; + + /* Create a device path for an SD card */ + dp_sd->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE; + dp_sd->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SD; + dp_sd->dp.length = sizeof(struct efi_device_path_sd_mmc_path); + dp_sd->slot_number = 3; + pos += sizeof(struct efi_device_path_sd_mmc_path); + /* Append end node */ + dp_end = (struct efi_device_path *)pos; + dp_end->type = DEVICE_PATH_TYPE_END; + dp_end->sub_type = DEVICE_PATH_SUB_TYPE_END; + dp_end->length = sizeof(struct efi_device_path); + + snprintf(str, sizeof(str), "_%pD_", buf); + assert(!strcmp("_/SD(3)_", str)); + + /* NULL device path */ + snprintf(str, sizeof(str), "_%pD_", NULL); + assert(!strcmp("_<NULL>_", str)); +#endif +} + +static int do_ut_print(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + char big_str[400]; + int big_str_len; + char str[10], *s; + int len; + + printf("%s: Testing print\n", __func__); + + snprintf(str, sizeof(str), "testing"); + assert(!strcmp("testing", str)); + + snprintf(str, sizeof(str), "testing but too long"); + assert(!strcmp("testing b", str)); + + snprintf(str, 1, "testing none"); + assert(!strcmp("", str)); + + *str = 'x'; + snprintf(str, 0, "testing none"); + assert(*str == 'x'); + + sprintf(big_str, "_%ls_", L"foo"); + assert(!strcmp("_foo_", big_str)); + + /* Test the banner function */ + s = display_options_get_banner(true, str, sizeof(str)); + assert(s == str); + assert(!strcmp("\n\nU-Boo\n\n", s)); + + /* Assert that we do not overwrite memory before the buffer */ + str[0] = '`'; + s = display_options_get_banner(true, str + 1, 1); + assert(s == str + 1); + assert(!strcmp("`", str)); + + str[0] = '~'; + s = display_options_get_banner(true, str + 1, 2); + assert(s == str + 1); + assert(!strcmp("~\n", str)); + + /* The last two characters are set to \n\n for all buffer sizes > 2 */ + s = display_options_get_banner(false, str, sizeof(str)); + assert(s == str); + assert(!strcmp("U-Boot \n\n", s)); + + /* Give it enough space for some of the version */ + big_str_len = strlen(version_string) - 5; + s = display_options_get_banner_priv(false, FAKE_BUILD_TAG, big_str, + big_str_len); + assert(s == big_str); + assert(!strncmp(version_string, s, big_str_len - 3)); + assert(!strcmp("\n\n", s + big_str_len - 3)); + + /* Give it enough space for the version and some of the build tag */ + big_str_len = strlen(version_string) + 9 + 20; + s = display_options_get_banner_priv(false, FAKE_BUILD_TAG, big_str, + big_str_len); + assert(s == big_str); + len = strlen(version_string); + assert(!strncmp(version_string, s, len)); + assert(!strncmp(", Build: ", s + len, 9)); + assert(!strncmp(FAKE_BUILD_TAG, s + 9 + len, 12)); + assert(!strcmp("\n\n", s + big_str_len - 3)); + + /* Test efi_loader specific printing */ + efi_ut_print(); + + /* Test printing GUIDs */ + guid_ut_print(); + + printf("%s: Everything went swimmingly\n", __func__); + return 0; +} + +U_BOOT_CMD( + ut_print, 1, 1, do_ut_print, + "Very basic test of printf(), etc.", + "" +); diff --git a/roms/u-boot/test/py/.gitignore b/roms/u-boot/test/py/.gitignore new file mode 100644 index 000000000..0d20b6487 --- /dev/null +++ b/roms/u-boot/test/py/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/roms/u-boot/test/py/conftest.py b/roms/u-boot/test/py/conftest.py new file mode 100644 index 000000000..11a3f307e --- /dev/null +++ b/roms/u-boot/test/py/conftest.py @@ -0,0 +1,629 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Implementation of pytest run-time hook functions. These are invoked by +# pytest at certain points during operation, e.g. startup, for each executed +# test, at shutdown etc. These hooks perform functions such as: +# - Parsing custom command-line options. +# - Pullilng in user-specified board configuration. +# - Creating the U-Boot console test fixture. +# - Creating the HTML log file. +# - Monitoring each test's results. +# - Implementing custom pytest markers. + +import atexit +import configparser +import errno +import io +import os +import os.path +import pytest +import re +from _pytest.runner import runtestprotocol +import sys + +# Globals: The HTML log file, and the connection to the U-Boot console. +log = None +console = None + +def mkdir_p(path): + """Create a directory path. + + This includes creating any intermediate/parent directories. Any errors + caused due to already extant directories are ignored. + + Args: + path: The directory path to create. + + Returns: + Nothing. + """ + + try: + os.makedirs(path) + except OSError as exc: + if exc.errno == errno.EEXIST and os.path.isdir(path): + pass + else: + raise + +def pytest_addoption(parser): + """pytest hook: Add custom command-line options to the cmdline parser. + + Args: + parser: The pytest command-line parser. + + Returns: + Nothing. + """ + + parser.addoption('--build-dir', default=None, + help='U-Boot build directory (O=)') + parser.addoption('--result-dir', default=None, + help='U-Boot test result/tmp directory') + parser.addoption('--persistent-data-dir', default=None, + help='U-Boot test persistent generated data directory') + parser.addoption('--board-type', '--bd', '-B', default='sandbox', + help='U-Boot board type') + parser.addoption('--board-identity', '--id', default='na', + help='U-Boot board identity/instance') + parser.addoption('--build', default=False, action='store_true', + help='Compile U-Boot before running tests') + parser.addoption('--buildman', default=False, action='store_true', + help='Use buildman to build U-Boot (assuming --build is given)') + parser.addoption('--gdbserver', default=None, + help='Run sandbox under gdbserver. The argument is the channel '+ + 'over which gdbserver should communicate, e.g. localhost:1234') + +def pytest_configure(config): + """pytest hook: Perform custom initialization at startup time. + + Args: + config: The pytest configuration. + + Returns: + Nothing. + """ + def parse_config(conf_file): + """Parse a config file, loading it into the ubconfig container + + Args: + conf_file: Filename to load (within build_dir) + + Raises + Exception if the file does not exist + """ + dot_config = build_dir + '/' + conf_file + if not os.path.exists(dot_config): + raise Exception(conf_file + ' does not exist; ' + + 'try passing --build option?') + + with open(dot_config, 'rt') as f: + ini_str = '[root]\n' + f.read() + ini_sio = io.StringIO(ini_str) + parser = configparser.RawConfigParser() + parser.read_file(ini_sio) + ubconfig.buildconfig.update(parser.items('root')) + + global log + global console + global ubconfig + + test_py_dir = os.path.dirname(os.path.abspath(__file__)) + source_dir = os.path.dirname(os.path.dirname(test_py_dir)) + + board_type = config.getoption('board_type') + board_type_filename = board_type.replace('-', '_') + + board_identity = config.getoption('board_identity') + board_identity_filename = board_identity.replace('-', '_') + + build_dir = config.getoption('build_dir') + if not build_dir: + build_dir = source_dir + '/build-' + board_type + mkdir_p(build_dir) + + result_dir = config.getoption('result_dir') + if not result_dir: + result_dir = build_dir + mkdir_p(result_dir) + + persistent_data_dir = config.getoption('persistent_data_dir') + if not persistent_data_dir: + persistent_data_dir = build_dir + '/persistent-data' + mkdir_p(persistent_data_dir) + + gdbserver = config.getoption('gdbserver') + if gdbserver and not board_type.startswith('sandbox'): + raise Exception('--gdbserver only supported with sandbox targets') + + import multiplexed_log + log = multiplexed_log.Logfile(result_dir + '/test-log.html') + + if config.getoption('build'): + if config.getoption('buildman'): + if build_dir != source_dir: + dest_args = ['-o', build_dir, '-w'] + else: + dest_args = ['-i'] + cmds = (['buildman', '--board', board_type] + dest_args,) + name = 'buildman' + else: + if build_dir != source_dir: + o_opt = 'O=%s' % build_dir + else: + o_opt = '' + cmds = ( + ['make', o_opt, '-s', board_type + '_defconfig'], + ['make', o_opt, '-s', '-j{}'.format(os.cpu_count())], + ) + name = 'make' + + with log.section(name): + runner = log.get_runner(name, sys.stdout) + for cmd in cmds: + runner.run(cmd, cwd=source_dir) + runner.close() + log.status_pass('OK') + + class ArbitraryAttributeContainer(object): + pass + + ubconfig = ArbitraryAttributeContainer() + ubconfig.brd = dict() + ubconfig.env = dict() + + modules = [ + (ubconfig.brd, 'u_boot_board_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename), + (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' + + board_identity_filename), + ] + for (dict_to_fill, module_name) in modules: + try: + module = __import__(module_name) + except ImportError: + continue + dict_to_fill.update(module.__dict__) + + ubconfig.buildconfig = dict() + + # buildman -k puts autoconf.mk in the rootdir, so handle this as well + # as the standard U-Boot build which leaves it in include/autoconf.mk + parse_config('.config') + if os.path.exists(build_dir + '/' + 'autoconf.mk'): + parse_config('autoconf.mk') + else: + parse_config('include/autoconf.mk') + + ubconfig.test_py_dir = test_py_dir + ubconfig.source_dir = source_dir + ubconfig.build_dir = build_dir + ubconfig.result_dir = result_dir + ubconfig.persistent_data_dir = persistent_data_dir + ubconfig.board_type = board_type + ubconfig.board_identity = board_identity + ubconfig.gdbserver = gdbserver + ubconfig.dtb = build_dir + '/arch/sandbox/dts/test.dtb' + + env_vars = ( + 'board_type', + 'board_identity', + 'source_dir', + 'test_py_dir', + 'build_dir', + 'result_dir', + 'persistent_data_dir', + ) + for v in env_vars: + os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v) + + if board_type.startswith('sandbox'): + import u_boot_console_sandbox + console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig) + else: + import u_boot_console_exec_attach + console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig) + +re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_\1_test_(.*)\s*$') +def generate_ut_subtest(metafunc, fixture_name, sym_path): + """Provide parametrization for a ut_subtest fixture. + + Determines the set of unit tests built into a U-Boot binary by parsing the + list of symbols generated by the build process. Provides this information + to test functions by parameterizing their ut_subtest fixture parameter. + + Args: + metafunc: The pytest test function. + fixture_name: The fixture name to test. + sym_path: Relative path to the symbol file with preceding '/' + (e.g. '/u-boot.sym') + + Returns: + Nothing. + """ + fn = console.config.build_dir + sym_path + try: + with open(fn, 'rt') as f: + lines = f.readlines() + except: + lines = [] + lines.sort() + + vals = [] + for l in lines: + m = re_ut_test_list.search(l) + if not m: + continue + vals.append(m.group(1) + ' ' + m.group(2)) + + ids = ['ut_' + s.replace(' ', '_') for s in vals] + metafunc.parametrize(fixture_name, vals, ids=ids) + +def generate_config(metafunc, fixture_name): + """Provide parametrization for {env,brd}__ fixtures. + + If a test function takes parameter(s) (fixture names) of the form brd__xxx + or env__xxx, the brd and env configuration dictionaries are consulted to + find the list of values to use for those parameters, and the test is + parametrized so that it runs once for each combination of values. + + Args: + metafunc: The pytest test function. + fixture_name: The fixture name to test. + + Returns: + Nothing. + """ + + subconfigs = { + 'brd': console.config.brd, + 'env': console.config.env, + } + parts = fixture_name.split('__') + if len(parts) < 2: + return + if parts[0] not in subconfigs: + return + subconfig = subconfigs[parts[0]] + vals = [] + val = subconfig.get(fixture_name, []) + # If that exact name is a key in the data source: + if val: + # ... use the dict value as a single parameter value. + vals = (val, ) + else: + # ... otherwise, see if there's a key that contains a list of + # values to use instead. + vals = subconfig.get(fixture_name+ 's', []) + def fixture_id(index, val): + try: + return val['fixture_id'] + except: + return fixture_name + str(index) + ids = [fixture_id(index, val) for (index, val) in enumerate(vals)] + metafunc.parametrize(fixture_name, vals, ids=ids) + +def pytest_generate_tests(metafunc): + """pytest hook: parameterize test functions based on custom rules. + + Check each test function parameter (fixture name) to see if it is one of + our custom names, and if so, provide the correct parametrization for that + parameter. + + Args: + metafunc: The pytest test function. + + Returns: + Nothing. + """ + for fn in metafunc.fixturenames: + if fn == 'ut_subtest': + generate_ut_subtest(metafunc, fn, '/u-boot.sym') + continue + if fn == 'ut_spl_subtest': + generate_ut_subtest(metafunc, fn, '/spl/u-boot-spl.sym') + continue + generate_config(metafunc, fn) + +@pytest.fixture(scope='session') +def u_boot_log(request): + """Generate the value of a test's log fixture. + + Args: + request: The pytest request. + + Returns: + The fixture value. + """ + + return console.log + +@pytest.fixture(scope='session') +def u_boot_config(request): + """Generate the value of a test's u_boot_config fixture. + + Args: + request: The pytest request. + + Returns: + The fixture value. + """ + + return console.config + +@pytest.fixture(scope='function') +def u_boot_console(request): + """Generate the value of a test's u_boot_console fixture. + + Args: + request: The pytest request. + + Returns: + The fixture value. + """ + + console.ensure_spawned() + return console + +anchors = {} +tests_not_run = [] +tests_failed = [] +tests_xpassed = [] +tests_xfailed = [] +tests_skipped = [] +tests_warning = [] +tests_passed = [] + +def pytest_itemcollected(item): + """pytest hook: Called once for each test found during collection. + + This enables our custom result analysis code to see the list of all tests + that should eventually be run. + + Args: + item: The item that was collected. + + Returns: + Nothing. + """ + + tests_not_run.append(item.name) + +def cleanup(): + """Clean up all global state. + + Executed (via atexit) once the entire test process is complete. This + includes logging the status of all tests, and the identity of any failed + or skipped tests. + + Args: + None. + + Returns: + Nothing. + """ + + if console: + console.close() + if log: + with log.section('Status Report', 'status_report'): + log.status_pass('%d passed' % len(tests_passed)) + if tests_warning: + log.status_warning('%d passed with warning' % len(tests_warning)) + for test in tests_warning: + anchor = anchors.get(test, None) + log.status_warning('... ' + test, anchor) + if tests_skipped: + log.status_skipped('%d skipped' % len(tests_skipped)) + for test in tests_skipped: + anchor = anchors.get(test, None) + log.status_skipped('... ' + test, anchor) + if tests_xpassed: + log.status_xpass('%d xpass' % len(tests_xpassed)) + for test in tests_xpassed: + anchor = anchors.get(test, None) + log.status_xpass('... ' + test, anchor) + if tests_xfailed: + log.status_xfail('%d xfail' % len(tests_xfailed)) + for test in tests_xfailed: + anchor = anchors.get(test, None) + log.status_xfail('... ' + test, anchor) + if tests_failed: + log.status_fail('%d failed' % len(tests_failed)) + for test in tests_failed: + anchor = anchors.get(test, None) + log.status_fail('... ' + test, anchor) + if tests_not_run: + log.status_fail('%d not run' % len(tests_not_run)) + for test in tests_not_run: + anchor = anchors.get(test, None) + log.status_fail('... ' + test, anchor) + log.close() +atexit.register(cleanup) + +def setup_boardspec(item): + """Process any 'boardspec' marker for a test. + + Such a marker lists the set of board types that a test does/doesn't + support. If tests are being executed on an unsupported board, the test is + marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + """ + + required_boards = [] + for boards in item.iter_markers('boardspec'): + board = boards.args[0] + if board.startswith('!'): + if ubconfig.board_type == board[1:]: + pytest.skip('board "%s" not supported' % ubconfig.board_type) + return + else: + required_boards.append(board) + if required_boards and ubconfig.board_type not in required_boards: + pytest.skip('board "%s" not supported' % ubconfig.board_type) + +def setup_buildconfigspec(item): + """Process any 'buildconfigspec' marker for a test. + + Such a marker lists some U-Boot configuration feature that the test + requires. If tests are being executed on an U-Boot build that doesn't + have the required feature, the test is marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + """ + + for options in item.iter_markers('buildconfigspec'): + option = options.args[0] + if not ubconfig.buildconfig.get('config_' + option.lower(), None): + pytest.skip('.config feature "%s" not enabled' % option.lower()) + for options in item.iter_markers('notbuildconfigspec'): + option = options.args[0] + if ubconfig.buildconfig.get('config_' + option.lower(), None): + pytest.skip('.config feature "%s" enabled' % option.lower()) + +def tool_is_in_path(tool): + for path in os.environ["PATH"].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +def setup_requiredtool(item): + """Process any 'requiredtool' marker for a test. + + Such a marker lists some external tool (binary, executable, application) + that the test requires. If tests are being executed on a system that + doesn't have the required tool, the test is marked to be skipped. + + Args: + item: The pytest test item. + + Returns: + Nothing. + """ + + for tools in item.iter_markers('requiredtool'): + tool = tools.args[0] + if not tool_is_in_path(tool): + pytest.skip('tool "%s" not in $PATH' % tool) + +def start_test_section(item): + anchors[item.name] = log.start_section(item.name) + +def pytest_runtest_setup(item): + """pytest hook: Configure (set up) a test item. + + Called once for each test to perform any custom configuration. This hook + is used to skip the test if certain conditions apply. + + Args: + item: The pytest test item. + + Returns: + Nothing. + """ + + start_test_section(item) + setup_boardspec(item) + setup_buildconfigspec(item) + setup_requiredtool(item) + +def pytest_runtest_protocol(item, nextitem): + """pytest hook: Called to execute a test. + + This hook wraps the standard pytest runtestprotocol() function in order + to acquire visibility into, and record, each test function's result. + + Args: + item: The pytest test item to execute. + nextitem: The pytest test item that will be executed after this one. + + Returns: + A list of pytest reports (test result data). + """ + + log.get_and_reset_warning() + ihook = item.ihook + ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location) + reports = runtestprotocol(item, nextitem=nextitem) + ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location) + was_warning = log.get_and_reset_warning() + + # In pytest 3, runtestprotocol() may not call pytest_runtest_setup() if + # the test is skipped. That call is required to create the test's section + # in the log file. The call to log.end_section() requires that the log + # contain a section for this test. Create a section for the test if it + # doesn't already exist. + if not item.name in anchors: + start_test_section(item) + + failure_cleanup = False + if not was_warning: + test_list = tests_passed + msg = 'OK' + msg_log = log.status_pass + else: + test_list = tests_warning + msg = 'OK (with warning)' + msg_log = log.status_warning + for report in reports: + if report.outcome == 'failed': + if hasattr(report, 'wasxfail'): + test_list = tests_xpassed + msg = 'XPASSED' + msg_log = log.status_xpass + else: + failure_cleanup = True + test_list = tests_failed + msg = 'FAILED:\n' + str(report.longrepr) + msg_log = log.status_fail + break + if report.outcome == 'skipped': + if hasattr(report, 'wasxfail'): + failure_cleanup = True + test_list = tests_xfailed + msg = 'XFAILED:\n' + str(report.longrepr) + msg_log = log.status_xfail + break + test_list = tests_skipped + msg = 'SKIPPED:\n' + str(report.longrepr) + msg_log = log.status_skipped + + if failure_cleanup: + console.drain_console() + + test_list.append(item.name) + tests_not_run.remove(item.name) + + try: + msg_log(msg) + except: + # If something went wrong with logging, it's better to let the test + # process continue, which may report other exceptions that triggered + # the logging issue (e.g. console.log wasn't created). Hence, just + # squash the exception. If the test setup failed due to e.g. syntax + # error somewhere else, this won't be seen. However, once that issue + # is fixed, if this exception still exists, it will then be logged as + # part of the test's stdout. + import traceback + print('Exception occurred while logging runtest status:') + traceback.print_exc() + # FIXME: Can we force a test failure here? + + log.end_section(item.name) + + if failure_cleanup: + console.cleanup_spawn() + + return True diff --git a/roms/u-boot/test/py/multiplexed_log.css b/roms/u-boot/test/py/multiplexed_log.css new file mode 100644 index 000000000..3db992722 --- /dev/null +++ b/roms/u-boot/test/py/multiplexed_log.css @@ -0,0 +1,108 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2015 Stephen Warren + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + */ + +/* + * This provides pretty formatting of the HTML log file, e.g. + * - colored bars beside/above log sections for easily parsed delineation. + * - color highlighting of various messages. + */ + +body { + background-color: black; + color: #ffffff; +} + +pre { + margin-top: 0px; + margin-bottom: 0px; +} + +.implicit { + color: #808080; +} + +.block { + border-style: solid; + border-color: #303030; + border-width: 0px 0px 0px 5px; + padding-left: 5px +} + +.block-header { + background-color: #303030; + margin-left: -5px; + margin-top: 5px; +} + +.block-header:hover { + text-decoration: underline; +} + +.block-trailer { + display: none; +} + +.error { + color: #ff0000 +} + +.warning { + color: #ffff00 +} + +.info { + color: #808080 +} + +.action { + color: #8080ff +} + +.timestamp { + color: #8080ff +} + +.status-pass { + color: #00ff00 +} + +.status-warning { + color: #ffff00 +} + +.status-skipped { + color: #ffff00 +} + +.status-xfail { + color: #ff7f00 +} + +.status-xpass { + color: #ff7f00 +} + +.status-fail { + color: #ff0000 +} + +.hidden { + display: none; +} + +a:link { + text-decoration: inherit; + color: inherit; +} + +a:visited { + text-decoration: inherit; + color: inherit; +} + +a:hover { + text-decoration: underline; +} diff --git a/roms/u-boot/test/py/multiplexed_log.py b/roms/u-boot/test/py/multiplexed_log.py new file mode 100644 index 000000000..545a77430 --- /dev/null +++ b/roms/u-boot/test/py/multiplexed_log.py @@ -0,0 +1,709 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Generate an HTML-formatted log file containing multiple streams of data, +# each represented in a well-delineated/-structured fashion. + +import datetime +import html +import os.path +import shutil +import subprocess + +mod_dir = os.path.dirname(os.path.abspath(__file__)) + +class LogfileStream(object): + """A file-like object used to write a single logical stream of data into + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.""" + + def __init__(self, logfile, name, chained_file): + """Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream. + chained_file: The file-like object to which all stream data should be + logged to in addition to logfile. Can be None. + + Returns: + Nothing. + """ + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + + def close(self): + """Dummy function so that this class is "file-like". + + Args: + None. + + Returns: + Nothing. + """ + + pass + + def write(self, data, implicit=False): + """Write data to the log stream. + + Args: + data: The data to write to the file. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + """ + + self.logfile.write(self, data, implicit) + if self.chained_file: + # Chained file is console, convert things a little + self.chained_file.write((data.encode('ascii', 'replace')).decode()) + + def flush(self): + """Flush the log stream, to ensure correct log interleaving. + + Args: + None. + + Returns: + Nothing. + """ + + self.logfile.flush() + if self.chained_file: + self.chained_file.flush() + +class RunAndLog(object): + """A utility object used to execute sub-processes and log their output to + a multiplexed log file. Objects of this type should be created by factory + functions in the Logfile class rather than directly.""" + + def __init__(self, logfile, name, chained_file): + """Initialize a new object. + + Args: + logfile: The Logfile object to log to. + name: The name of this log stream or sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + Nothing. + """ + + self.logfile = logfile + self.name = name + self.chained_file = chained_file + self.output = None + self.exit_status = None + + def close(self): + """Clean up any resources managed by this object.""" + pass + + def run(self, cmd, cwd=None, ignore_errors=False): + """Run a command as a sub-process, and log the results. + + The output is available at self.output which can be useful if there is + an exception. + + Args: + cmd: The command to execute. + cwd: The directory to run the command in. Can be None to use the + current directory. + ignore_errors: Indicate whether to ignore errors. If True, the + function will simply return if the command cannot be executed + or exits with an error code, otherwise an exception will be + raised if such problems occur. + + Returns: + The output as a string. + """ + + msg = '+' + ' '.join(cmd) + '\n' + if self.chained_file: + self.chained_file.write(msg) + self.logfile.write(self, msg) + + try: + p = subprocess.Popen(cmd, cwd=cwd, + stdin=None, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + (stdout, stderr) = p.communicate() + if stdout is not None: + stdout = stdout.decode('utf-8') + if stderr is not None: + stderr = stderr.decode('utf-8') + output = '' + if stdout: + if stderr: + output += 'stdout:\n' + output += stdout + if stderr: + if stdout: + output += 'stderr:\n' + output += stderr + exit_status = p.returncode + exception = None + except subprocess.CalledProcessError as cpe: + output = cpe.output + exit_status = cpe.returncode + exception = cpe + except Exception as e: + output = '' + exit_status = 0 + exception = e + if output and not output.endswith('\n'): + output += '\n' + if exit_status and not exception and not ignore_errors: + exception = Exception('Exit code: ' + str(exit_status)) + if exception: + output += str(exception) + '\n' + self.logfile.write(self, output) + if self.chained_file: + self.chained_file.write(output) + self.logfile.timestamp() + + # Store the output so it can be accessed if we raise an exception. + self.output = output + self.exit_status = exit_status + if exception: + raise exception + return output + +class SectionCtxMgr(object): + """A context manager for Python's "with" statement, which allows a certain + portion of test code to be logged to a separate section of the log file. + Objects of this type should be created by factory functions in the Logfile + class rather than directly.""" + + def __init__(self, log, marker, anchor): + """Initialize a new object. + + Args: + log: The Logfile object to log to. + marker: The name of the nested log section. + anchor: The anchor value to pass to start_section(). + + Returns: + Nothing. + """ + + self.log = log + self.marker = marker + self.anchor = anchor + + def __enter__(self): + self.anchor = self.log.start_section(self.marker, self.anchor) + + def __exit__(self, extype, value, traceback): + self.log.end_section(self.marker) + +class Logfile(object): + """Generates an HTML-formatted log file containing multiple streams of + data, each represented in a well-delineated/-structured fashion.""" + + def __init__(self, fn): + """Initialize a new object. + + Args: + fn: The filename to write to. + + Returns: + Nothing. + """ + + self.f = open(fn, 'wt', encoding='utf-8') + self.last_stream = None + self.blocks = [] + self.cur_evt = 1 + self.anchor = 0 + self.timestamp_start = self._get_time() + self.timestamp_prev = self.timestamp_start + self.timestamp_blocks = [] + self.seen_warning = False + + shutil.copy(mod_dir + '/multiplexed_log.css', os.path.dirname(fn)) + self.f.write('''\ +<html> +<head> +<link rel="stylesheet" type="text/css" href="multiplexed_log.css"> +<script src="http://code.jquery.com/jquery.min.js"></script> +<script> +$(document).ready(function () { + // Copy status report HTML to start of log for easy access + sts = $(".block#status_report")[0].outerHTML; + $("tt").prepend(sts); + + // Add expand/contract buttons to all block headers + btns = "<span class=\\\"block-expand hidden\\\">[+] </span>" + + "<span class=\\\"block-contract\\\">[-] </span>"; + $(".block-header").prepend(btns); + + // Pre-contract all blocks which passed, leaving only problem cases + // expanded, to highlight issues the user should look at. + // Only top-level blocks (sections) should have any status + passed_bcs = $(".block-content:has(.status-pass)"); + // Some blocks might have multiple status entries (e.g. the status + // report), so take care not to hide blocks with partial success. + passed_bcs = passed_bcs.not(":has(.status-fail)"); + passed_bcs = passed_bcs.not(":has(.status-xfail)"); + passed_bcs = passed_bcs.not(":has(.status-xpass)"); + passed_bcs = passed_bcs.not(":has(.status-skipped)"); + passed_bcs = passed_bcs.not(":has(.status-warning)"); + // Hide the passed blocks + passed_bcs.addClass("hidden"); + // Flip the expand/contract button hiding for those blocks. + bhs = passed_bcs.parent().children(".block-header") + bhs.children(".block-expand").removeClass("hidden"); + bhs.children(".block-contract").addClass("hidden"); + + // Add click handler to block headers. + // The handler expands/contracts the block. + $(".block-header").on("click", function (e) { + var header = $(this); + var content = header.next(".block-content"); + var expanded = !content.hasClass("hidden"); + if (expanded) { + content.addClass("hidden"); + header.children(".block-expand").first().removeClass("hidden"); + header.children(".block-contract").first().addClass("hidden"); + } else { + header.children(".block-contract").first().removeClass("hidden"); + header.children(".block-expand").first().addClass("hidden"); + content.removeClass("hidden"); + } + }); + + // When clicking on a link, expand the target block + $("a").on("click", function (e) { + var block = $($(this).attr("href")); + var header = block.children(".block-header"); + var content = block.children(".block-content").first(); + header.children(".block-contract").first().removeClass("hidden"); + header.children(".block-expand").first().addClass("hidden"); + content.removeClass("hidden"); + }); +}); +</script> +</head> +<body> +<tt> +''') + + def close(self): + """Close the log file. + + After calling this function, no more data may be written to the log. + + Args: + None. + + Returns: + Nothing. + """ + + self.f.write('''\ +</tt> +</body> +</html> +''') + self.f.close() + + # The set of characters that should be represented as hexadecimal codes in + # the log file. + _nonprint = {ord('%')} + _nonprint.update({c for c in range(0, 32) if c not in (9, 10)}) + _nonprint.update({c for c in range(127, 256)}) + + def _escape(self, data): + """Render data format suitable for inclusion in an HTML document. + + This includes HTML-escaping certain characters, and translating + control characters to a hexadecimal representation. + + Args: + data: The raw string data to be escaped. + + Returns: + An escaped version of the data. + """ + + data = data.replace(chr(13), '') + data = ''.join((ord(c) in self._nonprint) and ('%%%02x' % ord(c)) or + c for c in data) + data = html.escape(data) + return data + + def _terminate_stream(self): + """Write HTML to the log file to terminate the current stream's data. + + Args: + None. + + Returns: + Nothing. + """ + + self.cur_evt += 1 + if not self.last_stream: + return + self.f.write('</pre>\n') + self.f.write('<div class="stream-trailer block-trailer">End stream: ' + + self.last_stream.name + '</div>\n') + self.f.write('</div>\n') + self.f.write('</div>\n') + self.last_stream = None + + def _note(self, note_type, msg, anchor=None): + """Write a note or one-off message to the log file. + + Args: + note_type: The type of note. This must be a value supported by the + accompanying multiplexed_log.css. + msg: The note/message to log. + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._terminate_stream() + self.f.write('<div class="' + note_type + '">\n') + self.f.write('<pre>') + if anchor: + self.f.write('<a href="#%s">' % anchor) + self.f.write(self._escape(msg)) + if anchor: + self.f.write('</a>') + self.f.write('\n</pre>\n') + self.f.write('</div>\n') + + def start_section(self, marker, anchor=None): + """Begin a new nested section in the log file. + + Args: + marker: The name of the section that is starting. + anchor: The value to use for the anchor. If None, a unique value + will be calculated and used + + Returns: + Name of the HTML anchor emitted before section. + """ + + self._terminate_stream() + self.blocks.append(marker) + self.timestamp_blocks.append(self._get_time()) + if not anchor: + self.anchor += 1 + anchor = str(self.anchor) + blk_path = '/'.join(self.blocks) + self.f.write('<div class="section block" id="' + anchor + '">\n') + self.f.write('<div class="section-header block-header">Section: ' + + blk_path + '</div>\n') + self.f.write('<div class="section-content block-content">\n') + self.timestamp() + + return anchor + + def end_section(self, marker): + """Terminate the current nested section in the log file. + + This function validates proper nesting of start_section() and + end_section() calls. If a mismatch is found, an exception is raised. + + Args: + marker: The name of the section that is ending. + + Returns: + Nothing. + """ + + if (not self.blocks) or (marker != self.blocks[-1]): + raise Exception('Block nesting mismatch: "%s" "%s"' % + (marker, '/'.join(self.blocks))) + self._terminate_stream() + timestamp_now = self._get_time() + timestamp_section_start = self.timestamp_blocks.pop() + delta_section = timestamp_now - timestamp_section_start + self._note("timestamp", + "TIME: SINCE-SECTION: " + str(delta_section)) + blk_path = '/'.join(self.blocks) + self.f.write('<div class="section-trailer block-trailer">' + + 'End section: ' + blk_path + '</div>\n') + self.f.write('</div>\n') + self.f.write('</div>\n') + self.blocks.pop() + + def section(self, marker, anchor=None): + """Create a temporary section in the log file. + + This function creates a context manager for Python's "with" statement, + which allows a certain portion of test code to be logged to a separate + section of the log file. + + Usage: + with log.section("somename"): + some test code + + Args: + marker: The name of the nested section. + anchor: The anchor value to pass to start_section(). + + Returns: + A context manager object. + """ + + return SectionCtxMgr(self, marker, anchor) + + def error(self, msg): + """Write an error note to the log file. + + Args: + msg: A message describing the error. + + Returns: + Nothing. + """ + + self._note("error", msg) + + def warning(self, msg): + """Write an warning note to the log file. + + Args: + msg: A message describing the warning. + + Returns: + Nothing. + """ + + self.seen_warning = True + self._note("warning", msg) + + def get_and_reset_warning(self): + """Get and reset the log warning flag. + + Args: + None + + Returns: + Whether a warning was seen since the last call. + """ + + ret = self.seen_warning + self.seen_warning = False + return ret + + def info(self, msg): + """Write an informational note to the log file. + + Args: + msg: An informational message. + + Returns: + Nothing. + """ + + self._note("info", msg) + + def action(self, msg): + """Write an action note to the log file. + + Args: + msg: A message describing the action that is being logged. + + Returns: + Nothing. + """ + + self._note("action", msg) + + def _get_time(self): + return datetime.datetime.now() + + def timestamp(self): + """Write a timestamp to the log file. + + Args: + None + + Returns: + Nothing. + """ + + timestamp_now = self._get_time() + delta_prev = timestamp_now - self.timestamp_prev + delta_start = timestamp_now - self.timestamp_start + self.timestamp_prev = timestamp_now + + self._note("timestamp", + "TIME: NOW: " + timestamp_now.strftime("%Y/%m/%d %H:%M:%S.%f")) + self._note("timestamp", + "TIME: SINCE-PREV: " + str(delta_prev)) + self._note("timestamp", + "TIME: SINCE-START: " + str(delta_start)) + + def status_pass(self, msg, anchor=None): + """Write a note to the log file describing test(s) which passed. + + Args: + msg: A message describing the passed test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-pass", msg, anchor) + + def status_warning(self, msg, anchor=None): + """Write a note to the log file describing test(s) which passed. + + Args: + msg: A message describing the passed test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-warning", msg, anchor) + + def status_skipped(self, msg, anchor=None): + """Write a note to the log file describing skipped test(s). + + Args: + msg: A message describing the skipped test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-skipped", msg, anchor) + + def status_xfail(self, msg, anchor=None): + """Write a note to the log file describing xfailed test(s). + + Args: + msg: A message describing the xfailed test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-xfail", msg, anchor) + + def status_xpass(self, msg, anchor=None): + """Write a note to the log file describing xpassed test(s). + + Args: + msg: A message describing the xpassed test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-xpass", msg, anchor) + + def status_fail(self, msg, anchor=None): + """Write a note to the log file describing failed test(s). + + Args: + msg: A message describing the failed test(s). + anchor: Optional internal link target. + + Returns: + Nothing. + """ + + self._note("status-fail", msg, anchor) + + def get_stream(self, name, chained_file=None): + """Create an object to log a single stream's data into the log file. + + This creates a "file-like" object that can be written to in order to + write a single stream's data to the log file. The implementation will + handle any required interleaving of data (from multiple streams) in + the log, in a way that makes it obvious which stream each bit of data + came from. + + Args: + name: The name of the stream. + chained_file: The file-like object to which all stream data should + be logged to in addition to this log. Can be None. + + Returns: + A file-like object. + """ + + return LogfileStream(self, name, chained_file) + + def get_runner(self, name, chained_file=None): + """Create an object that executes processes and logs their output. + + Args: + name: The name of this sub-process. + chained_file: The file-like object to which all stream data should + be logged to in addition to logfile. Can be None. + + Returns: + A RunAndLog object. + """ + + return RunAndLog(self, name, chained_file) + + def write(self, stream, data, implicit=False): + """Write stream data into the log file. + + This function should only be used by instances of LogfileStream or + RunAndLog. + + Args: + stream: The stream whose data is being logged. + data: The data to log. + implicit: Boolean indicating whether data actually appeared in the + stream, or was implicitly generated. A valid use-case is to + repeat a shell prompt at the start of each separate log + section, which makes the log sections more readable in + isolation. + + Returns: + Nothing. + """ + + if stream != self.last_stream: + self._terminate_stream() + self.f.write('<div class="stream block">\n') + self.f.write('<div class="stream-header block-header">Stream: ' + + stream.name + '</div>\n') + self.f.write('<div class="stream-content block-content">\n') + self.f.write('<pre>') + if implicit: + self.f.write('<span class="implicit">') + self.f.write(self._escape(data)) + if implicit: + self.f.write('</span>') + self.last_stream = stream + + def flush(self): + """Flush the log stream, to ensure correct log interleaving. + + Args: + None. + + Returns: + Nothing. + """ + + self.f.flush() diff --git a/roms/u-boot/test/py/pytest.ini b/roms/u-boot/test/py/pytest.ini new file mode 100644 index 000000000..e93d010f1 --- /dev/null +++ b/roms/u-boot/test/py/pytest.ini @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Static configuration data for pytest. pytest reads this at startup time. + +[pytest] +markers = + boardspec: U-Boot: Describes the set of boards a test can/can't run on. + buildconfigspec: U-Boot: Describes Kconfig/config-header constraints. + notbuildconfigspec: U-Boot: Describes required disabled Kconfig options. + requiredtool: U-Boot: Required host tools for a test. + slow: U-Boot: Specific test will run slowly. diff --git a/roms/u-boot/test/py/requirements.txt b/roms/u-boot/test/py/requirements.txt new file mode 100644 index 000000000..33c5c0bbc --- /dev/null +++ b/roms/u-boot/test/py/requirements.txt @@ -0,0 +1,26 @@ +atomicwrites==1.3.0 +attrs==19.3.0 +coverage==4.5.4 +extras==1.0.0 +fixtures==3.0.0 +importlib-metadata==0.23 +linecache2==1.0.0 +more-itertools==7.2.0 +packaging==19.2 +pbr==5.4.3 +pluggy==0.13.0 +py==1.10.0 +pycryptodomex==3.9.8 +pyelftools==0.27 +pygit2==0.28.2 +pyparsing==2.4.2 +pytest==5.2.1 +python-mimeparse==1.6.0 +python-subunit==1.3.0 +requests==2.25.1 +six==1.12.0 +testtools==2.3.0 +traceback2==1.4.0 +unittest2==1.1.0 +wcwidth==0.1.7 +zipp==0.6.0 diff --git a/roms/u-boot/test/py/test.py b/roms/u-boot/test/py/test.py new file mode 100755 index 000000000..285fda542 --- /dev/null +++ b/roms/u-boot/test/py/test.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 + +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Wrapper script to invoke pytest with the directory name that contains the +# U-Boot tests. + +import os +import os.path +import sys +import pytest +from pkg_resources import load_entry_point + +if __name__ == '__main__': + # argv; py.test test_directory_name user-supplied-arguments + args = [os.path.dirname(__file__) + '/tests'] + args.extend(sys.argv) + sys.exit(pytest.main(args)) diff --git a/roms/u-boot/test/py/tests/test_000_version.py b/roms/u-boot/test/py/tests/test_000_version.py new file mode 100644 index 000000000..bd089ab54 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_000_version.py @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +# pytest runs tests the order of their module path, which is related to the +# filename containing the test. This file is named such that it is sorted +# first, simply as a very basic sanity check of the functionality of the U-Boot +# command prompt. + +def test_version(u_boot_console): + """Test that the "version" command prints the U-Boot version.""" + + # "version" prints the U-Boot sign-on message. This is usually considered + # an error, so that any unexpected reboot causes an error. Here, this + # error detection is disabled since the sign-on message is expected. + with u_boot_console.disable_check('main_signon'): + response = u_boot_console.run_command('version') + # Ensure "version" printed what we expected. + u_boot_console.validate_version_string_in_text(response) diff --git a/roms/u-boot/test/py/tests/test_android/test_ab.py b/roms/u-boot/test/py/tests/test_android/test_ab.py new file mode 100644 index 000000000..c79cb07fd --- /dev/null +++ b/roms/u-boot/test/py/tests/test_android/test_ab.py @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2018 Texas Instruments, <www.ti.com> + +# Test A/B update commands. + +import os +import pytest +import u_boot_utils + +class ABTestDiskImage(object): + """Disk Image used by the A/B tests.""" + + def __init__(self, u_boot_console): + """Initialize a new ABTestDiskImage object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + """ + + filename = 'test_ab_disk_image.bin' + + persistent = u_boot_console.config.persistent_data_dir + '/' + filename + self.path = u_boot_console.config.result_dir + '/' + filename + + with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): + if os.path.exists(persistent): + u_boot_console.log.action('Disk image file ' + persistent + + ' already exists') + else: + u_boot_console.log.action('Generating ' + persistent) + fd = os.open(persistent, os.O_RDWR | os.O_CREAT) + os.ftruncate(fd, 524288) + os.close(fd) + cmd = ('sgdisk', persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('sgdisk', '--new=1:64:512', '--change-name=1:misc', + persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + cmd = ('sgdisk', '--load-backup=' + persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('cp', persistent, self.path) + u_boot_utils.run_and_log(u_boot_console, cmd) + +di = None +@pytest.fixture(scope='function') +def ab_disk_image(u_boot_console): + global di + if not di: + di = ABTestDiskImage(u_boot_console) + return di + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('android_ab') +@pytest.mark.buildconfigspec('cmd_ab_select') +@pytest.mark.requiredtool('sgdisk') +def test_ab(ab_disk_image, u_boot_console): + """Test the 'ab_select' command.""" + + u_boot_console.run_command('host bind 0 ' + ab_disk_image.path) + + output = u_boot_console.run_command('ab_select slot_name host 0#misc') + assert 're-initializing A/B metadata' in output + assert 'Attempting slot a, tries remaining 7' in output + output = u_boot_console.run_command('printenv slot_name') + assert 'slot_name=a' in output + + output = u_boot_console.run_command('ab_select slot_name host 0:1') + assert 'Attempting slot b, tries remaining 7' in output + output = u_boot_console.run_command('printenv slot_name') + assert 'slot_name=b' in output diff --git a/roms/u-boot/test/py/tests/test_android/test_abootimg.py b/roms/u-boot/test/py/tests/test_android/test_abootimg.py new file mode 100644 index 000000000..43a7099c4 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_android/test_abootimg.py @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020 +# Author: Sam Protsenko <joe.skb7@gmail.com> + +# Test U-Boot's "abootimg" commands. + +import os +import pytest +import u_boot_utils + +""" +These tests rely on disk image (boot.img), which is automatically created by +the test from the stored hex dump. This is done to avoid the dependency on the +most recent mkbootimg tool from AOSP/master. Here is the list of commands which +was used to generate the boot.img and obtain compressed hex dump from it: + + $ echo '/dts-v1/; / { model = "x1"; compatible = "y1,z1"; };' > test1.dts + $ echo '/dts-v1/; / { model = "x2"; compatible = "y2,z2"; };' > test2.dts + $ dtc test1.dts > dt1.dtb + $ dtc test2.dts > dt2.dtb + $ cat dt1.dtb dt2.dtb > dtb.img + $ echo 'kernel payload' > kernel + $ echo 'ramdisk payload' > ramdisk.img + $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \ + --cmdline "cmdline test" --dtb ./dtb.img \ + --os_version R --os_patch_level 2019-06-05 \ + --header_version 2 --output boot.img + $ gzip -9 boot.img + $ xxd -p boot.img.gz > boot.img.gz.hex + +Now one can obtain original boot.img from this hex dump like this: + + $ xxd -r -p boot.img.gz.hex boot.img.gz + $ gunzip -9 boot.img.gz +""" + +# boot.img.gz hex dump +img_hex = """1f8b08084844af5d0203626f6f742e696d670073f47309f2f77451e46700 +820606010106301084501f04181819041838181898803c3346060c909c9b +92939997aa50925a5cc2300a461c3078b2e1793c4b876fd92db97939fb6c +b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253 +8bf25273140a122b73f21353b8460364148c8251300a46c1281801a02831 +3725b3387bb401300a46c1281805a360148c207081f7df5b20550bc41640 +9c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34 +d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5 +463a5546f82c1f05a360148c8251300a462e000085bf67f200200000""" +# Expected response for "abootimg dtb_dump" command +dtb_dump_resp="""## DTB area contents (concat format): + - DTB #0: + (DTB)size = 125 + (DTB)model = x1 + (DTB)compatible = y1,z1 + - DTB #1: + (DTB)size = 125 + (DTB)model = x2 + (DTB)compatible = y2,z2""" +# Address in RAM where to load the boot image ('abootimg' looks in $loadaddr) +loadaddr = 0x1000 +# Expected DTB #1 offset from the boot image start address +dtb1_offset = 0x187d +# DTB #1 start address in RAM +dtb1_addr = loadaddr + dtb1_offset + +class AbootimgTestDiskImage(object): + """Disk image used by abootimg tests.""" + + def __init__(self, u_boot_console): + """Initialize a new AbootimgDiskImage object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + """ + + gz_hex = u_boot_console.config.persistent_data_dir + '/boot.img.gz.hex' + gz = u_boot_console.config.persistent_data_dir + '/boot.img.gz' + + filename = 'boot.img' + persistent = u_boot_console.config.persistent_data_dir + '/' + filename + self.path = u_boot_console.config.result_dir + '/' + filename + + with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): + if os.path.exists(persistent): + u_boot_console.log.action('Disk image file ' + persistent + + ' already exists') + else: + u_boot_console.log.action('Generating ' + persistent) + + f = open(gz_hex, "w") + f.write(img_hex) + f.close() + + cmd = ('xxd', '-r', '-p', gz_hex, gz) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('gunzip', '-9', gz) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('cp', persistent, self.path) + u_boot_utils.run_and_log(u_boot_console, cmd) + +gtdi = None +@pytest.fixture(scope='function') +def abootimg_disk_image(u_boot_console): + """pytest fixture to provide a AbootimgTestDiskImage object to tests. + This is function-scoped because it uses u_boot_console, which is also + function-scoped. However, we don't need to actually do any function-scope + work, so this simply returns the same object over and over each time.""" + + global gtdi + if not gtdi: + gtdi = AbootimgTestDiskImage(u_boot_console) + return gtdi + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('android_boot_image') +@pytest.mark.buildconfigspec('cmd_abootimg') +@pytest.mark.buildconfigspec('cmd_fdt') +@pytest.mark.requiredtool('xxd') +@pytest.mark.requiredtool('gunzip') +def test_abootimg(abootimg_disk_image, u_boot_console): + """Test the 'abootimg' command.""" + + u_boot_console.log.action('Loading disk image to RAM...') + u_boot_console.run_command('setenv loadaddr 0x%x' % (loadaddr)) + u_boot_console.run_command('host load hostfs - 0x%x %s' % (loadaddr, + abootimg_disk_image.path)) + + u_boot_console.log.action('Testing \'abootimg get ver\'...') + response = u_boot_console.run_command('abootimg get ver') + assert response == "2" + u_boot_console.run_command('abootimg get ver v') + response = u_boot_console.run_command('env print v') + assert response == 'v=2' + + u_boot_console.log.action('Testing \'abootimg get recovery_dtbo\'...') + response = u_boot_console.run_command('abootimg get recovery_dtbo a') + assert response == 'Error: recovery_dtbo_size is 0' + + u_boot_console.log.action('Testing \'abootimg dump dtb\'...') + response = u_boot_console.run_command('abootimg dump dtb').replace('\r', '') + assert response == dtb_dump_resp + + u_boot_console.log.action('Testing \'abootimg get dtb_load_addr\'...') + u_boot_console.run_command('abootimg get dtb_load_addr a') + response = u_boot_console.run_command('env print a') + assert response == 'a=11f00000' + + u_boot_console.log.action('Testing \'abootimg get dtb --index\'...') + u_boot_console.run_command('abootimg get dtb --index=1 dtb1_start') + response = u_boot_console.run_command('env print dtb1_start') + correct_str = "dtb1_start=%x" % (dtb1_addr) + assert response == correct_str + u_boot_console.run_command('fdt addr $dtb1_start') + u_boot_console.run_command('fdt get value v / model') + response = u_boot_console.run_command('env print v') + assert response == 'v=x2' diff --git a/roms/u-boot/test/py/tests/test_android/test_avb.py b/roms/u-boot/test/py/tests/test_android/test_avb.py new file mode 100644 index 000000000..a04a7ff26 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_android/test_avb.py @@ -0,0 +1,137 @@ +# Copyright (c) 2018, Linaro Limited +# +# SPDX-License-Identifier: GPL-2.0+ +# +# Android Verified Boot 2.0 Test + +""" +This tests Android Verified Boot 2.0 support in U-boot: + +For additional details about how to build proper vbmeta partition +check doc/android/avb2.rst + +For configuration verification: +- Corrupt boot partition and check for failure +- Corrupt vbmeta partition and check for failure +""" + +import pytest +import u_boot_utils as util + +# defauld mmc id +mmc_dev = 1 +temp_addr = 0x90000000 +temp_addr2 = 0x90002000 + +@pytest.mark.buildconfigspec('cmd_avb') +@pytest.mark.buildconfigspec('cmd_mmc') +def test_avb_verify(u_boot_console): + """Run AVB 2.0 boot verification chain with avb subset of commands + """ + + success_str = "Verification passed successfully" + + response = u_boot_console.run_command('avb init %s' %str(mmc_dev)) + assert response == '' + response = u_boot_console.run_command('avb verify') + assert response.find(success_str) + + +@pytest.mark.buildconfigspec('cmd_avb') +@pytest.mark.buildconfigspec('cmd_mmc') +def test_avb_mmc_uuid(u_boot_console): + """Check if 'avb get_uuid' works, compare results with + 'part list mmc 1' output + """ + + response = u_boot_console.run_command('avb init %s' % str(mmc_dev)) + assert response == '' + + response = u_boot_console.run_command('mmc rescan; mmc dev %s' % + str(mmc_dev)) + assert response.find('is current device') + + part_lines = u_boot_console.run_command('mmc part').splitlines() + part_list = {} + cur_partname = '' + + for line in part_lines: + if '"' in line: + start_pt = line.find('"') + end_pt = line.find('"', start_pt + 1) + cur_partname = line[start_pt + 1: end_pt] + + if 'guid:' in line: + guid_to_check = line.split('guid:\t') + part_list[cur_partname] = guid_to_check[1] + + # lets check all guids with avb get_guid + for part, guid in part_list.iteritems(): + avb_guid_resp = u_boot_console.run_command('avb get_uuid %s' % part) + assert guid == avb_guid_resp.split('UUID: ')[1] + + +@pytest.mark.buildconfigspec('cmd_avb') +def test_avb_read_rb(u_boot_console): + """Test reading rollback indexes + """ + + response = u_boot_console.run_command('avb init %s' % str(mmc_dev)) + assert response == '' + + response = u_boot_console.run_command('avb read_rb 1') + assert response == 'Rollback index: 0' + + +@pytest.mark.buildconfigspec('cmd_avb') +def test_avb_is_unlocked(u_boot_console): + """Test if device is in the unlocked state + """ + + response = u_boot_console.run_command('avb init %s' % str(mmc_dev)) + assert response == '' + + response = u_boot_console.run_command('avb is_unlocked') + assert response == 'Unlocked = 1' + + +@pytest.mark.buildconfigspec('cmd_avb') +@pytest.mark.buildconfigspec('cmd_mmc') +def test_avb_mmc_read(u_boot_console): + """Test mmc read operation + """ + + response = u_boot_console.run_command('mmc rescan; mmc dev %s 0' % + str(mmc_dev)) + assert response.find('is current device') + + response = u_boot_console.run_command('mmc read 0x%x 0x100 0x1' % temp_addr) + assert response.find('read: OK') + + response = u_boot_console.run_command('avb init %s' % str(mmc_dev)) + assert response == '' + + response = u_boot_console.run_command('avb read_part xloader 0 100 0x%x' % + temp_addr2) + assert response.find('Read 512 bytes') + + # Now lets compare two buffers + response = u_boot_console.run_command('cmp 0x%x 0x%x 40' % + (temp_addr, temp_addr2)) + assert response.find('64 word') + + +@pytest.mark.buildconfigspec('cmd_avb') +@pytest.mark.buildconfigspec('optee_ta_avb') +def test_avb_persistent_values(u_boot_console): + """Test reading/writing persistent storage to avb + """ + + response = u_boot_console.run_command('avb init %s' % str(mmc_dev)) + assert response == '' + + response = u_boot_console.run_command('avb write_pvalue test value_value') + assert response == 'Wrote 12 bytes' + + response = u_boot_console.run_command('avb read_pvalue test 12') + assert response == 'Read 12 bytes, value = value_value' diff --git a/roms/u-boot/test/py/tests/test_bind.py b/roms/u-boot/test/py/tests/test_bind.py new file mode 100644 index 000000000..6703325c0 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_bind.py @@ -0,0 +1,179 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +import os.path +import pytest +import re + +def in_tree(response, name, uclass, drv, depth, last_child): + lines = [x.strip() for x in response.splitlines()] + leaf = '' + if depth != 0: + leaf = ' ' + ' ' * (depth - 1) ; + if not last_child: + leaf = leaf + r'\|' + else: + leaf = leaf + '`' + + leaf = leaf + '-- ' + name + line = (r' *{:10.10} *[0-9]* \[ [ +] \] {:20.20} [` |]{}$' + .format(uclass, drv, leaf)) + prog = re.compile(line) + for l in lines: + if prog.match(l): + return True + return False + + +@pytest.mark.buildconfigspec('cmd_bind') +def test_bind_unbind_with_node(u_boot_console): + + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False) + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + + #Unbind child #1. No error expected and all devices should be there except for bind-test-child1 + response = u_boot_console.run_command('unbind /bind-test/bind-test-child1') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert 'bind-test-child1' not in tree + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + + #bind child #1. No error expected and all devices should be there + response = u_boot_console.run_command('bind /bind-test/bind-test-child1 phy_sandbox') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True) + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False) + + #Unbind child #2. No error expected and all devices should be there except for bind-test-child2 + response = u_boot_console.run_command('unbind /bind-test/bind-test-child2') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True) + assert 'bind-test-child2' not in tree + + + #Bind child #2. No error expected and all devices should be there + response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False) + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + + #Unbind parent. No error expected. All devices should be removed and unbound + response = u_boot_console.run_command('unbind /bind-test') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert 'bind-test' not in tree + assert 'bind-test-child1' not in tree + assert 'bind-test-child2' not in tree + + #try binding invalid node with valid driver + response = u_boot_console.run_command('bind /not-a-valid-node simple_bus') + assert response != '' + tree = u_boot_console.run_command('dm tree') + assert 'not-a-valid-node' not in tree + + #try binding valid node with invalid driver + response = u_boot_console.run_command('bind /bind-test not_a_driver') + assert response != '' + tree = u_boot_console.run_command('dm tree') + assert 'bind-test' not in tree + + #bind /bind-test. Device should come up as well as its children + response = u_boot_console.run_command('bind /bind-test simple_bus') + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True) + assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False) + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + + response = u_boot_console.run_command('unbind /bind-test') + assert response == '' + +def get_next_line(tree, name): + treelines = [x.strip() for x in tree.splitlines() if x.strip()] + child_line = '' + for idx, line in enumerate(treelines): + if ('-- ' + name) in line: + try: + child_line = treelines[idx+1] + except: + pass + break + return child_line + +@pytest.mark.buildconfigspec('cmd_bind') +def test_bind_unbind_with_uclass(u_boot_console): + #bind /bind-test + response = u_boot_console.run_command('bind /bind-test simple_bus') + assert response == '' + + #make sure bind-test-child2 is there and get its uclass/index pair + tree = u_boot_console.run_command('dm tree') + child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x] + assert len(child2_line) == 1 + + child2_uclass = child2_line[0].split()[0] + child2_index = int(child2_line[0].split()[1]) + + #bind simple_bus as a child of bind-test-child2 + response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus')) + + #check that the child is there and its uclass/index pair is right + tree = u_boot_console.run_command('dm tree') + + child_of_child2_line = get_next_line(tree, 'bind-test-child2') + assert child_of_child2_line + child_of_child2_index = int(child_of_child2_line.split()[1]) + assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True) + assert child_of_child2_index == child2_index + 1 + + #unbind the child and check it has been removed + response = u_boot_console.run_command('unbind simple_bus {}'.format(child_of_child2_index)) + assert response == '' + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True) + child_of_child2_line = get_next_line(tree, 'bind-test-child2') + assert child_of_child2_line == '' + + #bind simple_bus as a child of bind-test-child2 + response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus')) + + #check that the child is there and its uclass/index pair is right + tree = u_boot_console.run_command('dm tree') + treelines = [x.strip() for x in tree.splitlines() if x.strip()] + + child_of_child2_line = get_next_line(tree, 'bind-test-child2') + assert child_of_child2_line + child_of_child2_index = int(child_of_child2_line.split()[1]) + assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True) + assert child_of_child2_index == child2_index + 1 + + #unbind the child and check it has been removed + response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus')) + assert response == '' + + tree = u_boot_console.run_command('dm tree') + assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True) + + child_of_child2_line = get_next_line(tree, 'bind-test-child2') + assert child_of_child2_line == '' + + #unbind the child again and check it doesn't change the tree + tree_old = u_boot_console.run_command('dm tree') + response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus')) + tree_new = u_boot_console.run_command('dm tree') + + assert response == '' + assert tree_old == tree_new + + response = u_boot_console.run_command('unbind /bind-test') + assert response == '' diff --git a/roms/u-boot/test/py/tests/test_button.py b/roms/u-boot/test/py/tests/test_button.py new file mode 100644 index 000000000..3b7f148c8 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_button.py @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0+ + +import pytest + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_button') +def test_button_list(u_boot_console): + """Test listing buttons""" + + response = u_boot_console.run_command('button list; echo rc:$?') + assert('button1' in response) + assert('button2' in response) + assert('rc:0' in response) + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_button') +@pytest.mark.buildconfigspec('cmd_gpio') +def test_button_return_code(u_boot_console): + """Test correct reporting of the button status + + The sandbox gpio driver reports the last output value as input value. + We can use this in our test to emulate different input statuses. + """ + + u_boot_console.run_command('gpio set a3; gpio input a3'); + response = u_boot_console.run_command('button button1; echo rc:$?') + assert('on' in response) + assert('rc:0' in response) + + u_boot_console.run_command('gpio clear a3; gpio input a3'); + response = u_boot_console.run_command('button button1; echo rc:$?') + assert('off' in response) + assert('rc:1' in response) + + response = u_boot_console.run_command('button nonexistent-button; echo rc:$?') + assert('not found' in response) + assert('rc:1' in response) diff --git a/roms/u-boot/test/py/tests/test_dfu.py b/roms/u-boot/test/py/tests/test_dfu.py new file mode 100644 index 000000000..5d87eb349 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_dfu.py @@ -0,0 +1,320 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB +# device enumeration on the host, executes dfu-util multiple times to test +# various transfer sizes, many of which trigger USB driver edge cases, and +# finally aborts the "dfu" command in U-Boot. + +import os +import os.path +import pytest +import u_boot_utils + +""" +Note: This test relies on: + +a) boardenv_* to contain configuration values to define which USB ports are +available for testing. Without this, this test will be automatically skipped. +For example: + +env__usb_dev_ports = ( + { + 'fixture_id': 'micro_b', + 'tgt_usb_ctlr': '0', + 'host_usb_dev_node': '/dev/usbdev-p2371-2180', + # This parameter is optional /if/ you only have a single board + # attached to your host at a time. + 'host_usb_port_path': '3-13', + }, +) + +# Optional entries (required only when 'alt_id_test_file' and +# 'alt_id_dummy_file' are specified). +test_file_name = '/dfu_test.bin' +dummy_file_name = '/dfu_dummy.bin' +# Above files are used to generate proper 'alt_info' entry +'alt_info': '/%s ext4 0 2;/%s ext4 0 2' % (test_file_name, dummy_file_name), + +env__dfu_configs = ( + # eMMC, partition 1 + { + 'fixture_id': 'emmc', + 'alt_info': '/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1', + 'cmd_params': 'mmc 0', + # This value is optional. + # If present, it specified the set of transfer sizes tested. + # If missing, a default list of sizes will be used, which covers + # various useful corner cases. + # Manually specifying test sizes is useful if you wish to test 4 DFU + # configurations, but don't want to test every single transfer size + # on each, to avoid bloating the overall time taken by testing. + 'test_sizes': (63, 64, 65), + # This value is optional. + # The name of the environment variable that the the dfu command reads + # alt info from. If unspecified, this defaults to dfu_alt_info, which is + # valid for most systems. Some systems use a different variable name. + # One example is the Odroid XU3, which automatically generates + # $dfu_alt_info, each time the dfu command is run, by concatenating + # $dfu_alt_boot and $dfu_alt_system. + 'alt_info_env_name': 'dfu_alt_system', + # This value is optional. + # For boards which require the 'test file' alt setting number other than + # default (0) it is possible to specify exact file name to be used as + # this parameter. + 'alt_id_test_file': test_file_name, + # This value is optional. + # For boards which require the 'dummy file' alt setting number other + # than default (1) it is possible to specify exact file name to be used + # as this parameter. + 'alt_id_dummy_file': dummy_file_name, + }, +) + +b) udev rules to set permissions on devices nodes, so that sudo is not +required. For example: + +ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666" + +(You may wish to change the group ID instead of setting the permissions wide +open. All that matters is that the user ID running the test can access the +device.) + +c) An optional udev rule to give you a persistent value to use in +host_usb_dev_node. For example: + +IMPORT{builtin}="path_id" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="bus/usb/by-path/$env{ID_PATH}" +ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="bus/usb/by-path/$env{ID_PATH}-port$env{.ID_PORT}" +""" + +# The set of file sizes to test. These values trigger various edge-cases such +# as one less than, equal to, and one greater than typical USB max packet +# sizes, and similar boundary conditions. +test_sizes_default = ( + 64 - 1, + 64, + 64 + 1, + 128 - 1, + 128, + 128 + 1, + 960 - 1, + 960, + 960 + 1, + 4096 - 1, + 4096, + 4096 + 1, + 1024 * 1024 - 1, + 1024 * 1024, + 8 * 1024 * 1024, +) + +first_usb_dev_port = None + +@pytest.mark.buildconfigspec('cmd_dfu') +@pytest.mark.requiredtool('dfu-util') +def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config): + """Test the "dfu" command; the host system must be able to enumerate a USB + device when "dfu" is running, various DFU transfers are tested, and the + USB device must disappear when "dfu" is aborted. + + Args: + u_boot_console: A U-Boot console connection. + env__usb_dev_port: The single USB device-mode port specification on + which to run the test. See the file-level comment above for + details of the format. + env__dfu_config: The single DFU (memory region) configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + def start_dfu(): + """Start U-Boot's dfu shell command. + + This also waits for the host-side USB enumeration process to complete. + + Args: + None. + + Returns: + Nothing. + """ + + u_boot_utils.wait_until_file_open_fails( + env__usb_dev_port['host_usb_dev_node'], True) + fh = u_boot_utils.attempt_to_open_file( + env__usb_dev_port['host_usb_dev_node']) + if fh: + fh.close() + raise Exception('USB device present before dfu command invoked') + + u_boot_console.log.action( + 'Starting long-running U-Boot dfu shell command') + + dfu_alt_info_env = env__dfu_config.get('alt_info_env_name', \ + 'dfu_alt_info') + + cmd = 'setenv "%s" "%s"' % (dfu_alt_info_env, + env__dfu_config['alt_info']) + u_boot_console.run_command(cmd) + + cmd = 'dfu 0 ' + env__dfu_config['cmd_params'] + u_boot_console.run_command(cmd, wait_for_prompt=False) + u_boot_console.log.action('Waiting for DFU USB device to appear') + fh = u_boot_utils.wait_until_open_succeeds( + env__usb_dev_port['host_usb_dev_node']) + fh.close() + + def stop_dfu(ignore_errors): + """Stop U-Boot's dfu shell command from executing. + + This also waits for the host-side USB de-enumeration process to + complete. + + Args: + ignore_errors: Ignore any errors. This is useful if an error has + already been detected, and the code is performing best-effort + cleanup. In this case, we do not want to mask the original + error by "honoring" any new errors. + + Returns: + Nothing. + """ + + try: + u_boot_console.log.action( + 'Stopping long-running U-Boot dfu shell command') + u_boot_console.ctrlc() + u_boot_console.log.action( + 'Waiting for DFU USB device to disappear') + u_boot_utils.wait_until_file_open_fails( + env__usb_dev_port['host_usb_dev_node'], ignore_errors) + except: + if not ignore_errors: + raise + + def run_dfu_util(alt_setting, fn, up_dn_load_arg): + """Invoke dfu-util on the host. + + Args: + alt_setting: The DFU "alternate setting" identifier to interact + with. + fn: The host-side file name to transfer. + up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or + download operation should be performed. + + Returns: + Nothing. + """ + + cmd = ['dfu-util', '-a', alt_setting, up_dn_load_arg, fn] + if 'host_usb_port_path' in env__usb_dev_port: + cmd += ['-p', env__usb_dev_port['host_usb_port_path']] + u_boot_utils.run_and_log(u_boot_console, cmd) + u_boot_console.wait_for('Ctrl+C to exit ...') + + def dfu_write(alt_setting, fn): + """Write a file to the target board using DFU. + + Args: + alt_setting: The DFU "alternate setting" identifier to interact + with. + fn: The host-side file name to transfer. + + Returns: + Nothing. + """ + + run_dfu_util(alt_setting, fn, '-D') + + def dfu_read(alt_setting, fn): + """Read a file from the target board using DFU. + + Args: + alt_setting: The DFU "alternate setting" identifier to interact + with. + fn: The host-side file name to transfer. + + Returns: + Nothing. + """ + + # dfu-util fails reads/uploads if the host file already exists + if os.path.exists(fn): + os.remove(fn) + run_dfu_util(alt_setting, fn, '-U') + + def dfu_write_read_check(size): + """Test DFU transfers of a specific size of data + + This function first writes data to the board then reads it back and + compares the written and read back data. Measures are taken to avoid + certain types of false positives. + + Args: + size: The data size to test. + + Returns: + Nothing. + """ + + test_f = u_boot_utils.PersistentRandomFile(u_boot_console, + 'dfu_%d.bin' % size, size) + readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin' + + u_boot_console.log.action('Writing test data to DFU primary ' + + 'altsetting') + dfu_write(alt_setting_test_file, test_f.abs_fn) + + u_boot_console.log.action('Writing dummy data to DFU secondary ' + + 'altsetting to clear DFU buffers') + dfu_write(alt_setting_dummy_file, dummy_f.abs_fn) + + u_boot_console.log.action('Reading DFU primary altsetting for ' + + 'comparison') + dfu_read(alt_setting_test_file, readback_fn) + + u_boot_console.log.action('Comparing written and read data') + written_hash = test_f.content_hash + read_back_hash = u_boot_utils.md5sum_file(readback_fn, size) + assert(written_hash == read_back_hash) + + # This test may be executed against multiple USB ports. The test takes a + # long time, so we don't want to do the whole thing each time. Instead, + # execute the full test on the first USB port, and perform a very limited + # test on other ports. In the limited case, we solely validate that the + # host PC can enumerate the U-Boot USB device. + global first_usb_dev_port + if not first_usb_dev_port: + first_usb_dev_port = env__usb_dev_port + if env__usb_dev_port == first_usb_dev_port: + sizes = env__dfu_config.get('test_sizes', test_sizes_default) + else: + sizes = [] + + dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console, + 'dfu_dummy.bin', 1024) + + alt_setting_test_file = env__dfu_config.get('alt_id_test_file', '0') + alt_setting_dummy_file = env__dfu_config.get('alt_id_dummy_file', '1') + + ignore_cleanup_errors = True + try: + start_dfu() + + u_boot_console.log.action( + 'Overwriting DFU primary altsetting with dummy data') + dfu_write(alt_setting_test_file, dummy_f.abs_fn) + + for size in sizes: + with u_boot_console.log.section('Data size %d' % size): + dfu_write_read_check(size) + # Make the status of each sub-test obvious. If the test didn't + # pass, an exception was thrown so this code isn't executed. + u_boot_console.log.status_pass('OK') + ignore_cleanup_errors = False + finally: + stop_dfu(ignore_cleanup_errors) diff --git a/roms/u-boot/test/py/tests/test_dm.py b/roms/u-boot/test/py/tests/test_dm.py new file mode 100644 index 000000000..97203b536 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_dm.py @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 Sean Anderson + +import pytest + +@pytest.mark.buildconfigspec('cmd_dm') +def test_dm_compat(u_boot_console): + """Test that each driver in `dm tree` is also listed in `dm compat`.""" + response = u_boot_console.run_command('dm tree') + driver_index = response.find('Driver') + assert driver_index != -1 + drivers = (line[driver_index:].split()[0] + for line in response[:-1].split('\n')[2:]) + + response = u_boot_console.run_command('dm compat') + for driver in drivers: + assert driver in response + +@pytest.mark.buildconfigspec('cmd_dm') +def test_dm_drivers(u_boot_console): + """Test that each driver in `dm compat` is also listed in `dm drivers`.""" + response = u_boot_console.run_command('dm compat') + drivers = (line[:20].rstrip() for line in response[:-1].split('\n')[2:]) + response = u_boot_console.run_command('dm drivers') + for driver in drivers: + assert driver in response + +@pytest.mark.buildconfigspec('cmd_dm') +def test_dm_static(u_boot_console): + """Test that each driver in `dm static` is also listed in `dm drivers`.""" + response = u_boot_console.run_command('dm static') + drivers = (line[:25].rstrip() for line in response[:-1].split('\n')[2:]) + response = u_boot_console.run_command('dm drivers') + for driver in drivers: + assert driver in response diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py b/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py new file mode 100644 index 000000000..4fd6353c2 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Directories +CAPSULE_DATA_DIR = '/EFI/CapsuleTestData' +CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule' diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py b/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py new file mode 100644 index 000000000..6ad5608cd --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py @@ -0,0 +1,74 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + +import os +import os.path +import re +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from capsule_defs import * + +# +# Fixture for UEFI secure boot test +# + + +@pytest.fixture(scope='session') +def efi_capsule_data(request, u_boot_config): + """Set up a file system to be used in UEFI capsule test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR + + mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule' + data_dir = mnt_point + CAPSULE_DATA_DIR + install_dir = mnt_point + CAPSULE_INSTALL_DIR + image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img' + + try: + # Create a target device + check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True) + + check_call('rm -rf %s' % mnt_point, shell=True) + check_call('mkdir -p %s' % data_dir, shell=True) + check_call('mkdir -p %s' % install_dir, shell=True) + + # Create capsule files + # two regions: one for u-boot.bin and the other for u-boot.env + check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old -> u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir, + shell=True) + check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' % + (u_boot_config.source_dir, data_dir), + shell=True) + check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --fit uboot_bin_env.itb --index 1 Test01' % + (data_dir, u_boot_config.build_dir), + shell=True) + check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 Test02' % + (data_dir, u_boot_config.build_dir), + shell=True) + + # Create a disk image with EFI system partition + check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' % + (mnt_point, image_path), shell=True) + check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' % + image_path, shell=True) + + except CalledProcessError as exception: + pytest.skip('Setup failed: %s' % exception.cmd) + return + else: + yield image_path + finally: + call('rm -rf %s' % mnt_point, shell=True) + call('rm -f %s' % image_path, shell=True) + call('rm -f ./spi.bin', shell=True) diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py new file mode 100644 index 000000000..4697ca6f1 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py @@ -0,0 +1,249 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Firmware Update Test + +""" +This test verifies capsule-on-disk firmware update +""" + +from subprocess import check_call, check_output, CalledProcessError +import pytest +from capsule_defs import * + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_capsule_firmware_fit') +@pytest.mark.buildconfigspec('efi_capsule_firmware_raw') +@pytest.mark.buildconfigspec('efi_capsule_on_disk') +@pytest.mark.buildconfigspec('dfu') +@pytest.mark.buildconfigspec('dfu_sf') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.slow +class TestEfiCapsuleFirmwareFit(object): + def test_efi_capsule_fw1( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 1 - Update U-Boot and U-Boot environment on SPI Flash + but with OsIndications unset + No update should happen + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 1-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e OsIndications', + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize contents + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 150000 10', + 'sf read 5000000 150000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + with u_boot_console.log.section('Test Case 1-b, after reboot'): + if not capsule_early: + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e -all Capsule0000') + + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:Old' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 150000 10', + 'md.b 4000000 10']) + assert 'u-boot-env:Old' in ''.join(output) + + def test_efi_capsule_fw2( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 2 - Update U-Boot and U-Boot environment on SPI Flash + 0x100000-0x150000: U-Boot binary (but dummy) + 0x150000-0x200000: U-Boot environment (but dummy) + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 2-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize contents + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 150000 10', + 'sf read 5000000 150000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + with u_boot_console.log.section('Test Case 2-b, after reboot'): + if not capsule_early: + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e -all Capsule0000') + + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test01' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:New' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf read 4000000 150000 10', + 'md.b 4000000 10']) + assert 'u-boot-env:New' in ''.join(output) + + def test_efi_capsule_fw3( + self, u_boot_config, u_boot_console, efi_capsule_data): + """ + Test Case 3 - Update U-Boot on SPI Flash, raw image format + 0x100000-0x150000: U-Boot binary (but dummy) + """ + disk_img = efi_capsule_data + with u_boot_console.log.section('Test Case 3-a, before reboot'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""', + 'efidebug boot order 1', + 'env set -e -nv -bs -rt OsIndications =0x0000000000000004', + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'env save']) + + # initialize content + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR, + 'sf write 4000000 100000 10', + 'sf read 5000000 100000 10', + 'md.b 5000000 10']) + assert 'Old' in ''.join(output) + + # place a capsule file + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR, + 'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' in ''.join(output) + + # reboot + u_boot_console.restart_uboot() + + capsule_early = u_boot_config.buildconfig.get( + 'config_efi_capsule_on_disk_early') + with u_boot_console.log.section('Test Case 3-b, after reboot'): + if not capsule_early: + # make sure that dfu_alt_info exists even persistent variables + # are not available. + output = u_boot_console.run_command_list([ + 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"', + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' in ''.join(output) + + # need to run uefi command to initiate capsule handling + output = u_boot_console.run_command( + 'env print -e -all Capsule0000') + + output = u_boot_console.run_command_list(['efidebug capsule esrt']) + + # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID is in the ESRT. + assert 'AE13FF2D-9AD4-4E25-9AC8-6D80B3B22147' in ''.join(output) + + # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID is in the ESRT. + assert 'E2BB9C06-70E9-4B14-97A3-5A7913176E3F' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR]) + assert 'Test02' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'sf probe 0:0', + 'sf read 4000000 100000 10', + 'md.b 4000000 10']) + assert 'u-boot:New' in ''.join(output) diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its b/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its new file mode 100644 index 000000000..fc6590748 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its @@ -0,0 +1,36 @@ +/* + * Automatic software update for U-Boot + * Make sure the flashing addresses ('load' prop) is correct for your board! + */ + +/dts-v1/; + +/ { + description = "Automatic U-Boot environment update"; + #address-cells = <2>; + + images { + u-boot-bin { + description = "U-Boot binary on SPI Flash"; + data = /incbin/("BINFILE1"); + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + hash-1 { + algo = "sha1"; + }; + }; + u-boot-env { + description = "U-Boot environment on SPI Flash"; + data = /incbin/("BINFILE2"); + compression = "none"; + type = "firmware"; + arch = "sandbox"; + load = <0>; + hash-1 { + algo = "sha1"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/test_efi_fit.py b/roms/u-boot/test/py/tests/test_efi_fit.py new file mode 100644 index 000000000..068a35a55 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_fit.py @@ -0,0 +1,458 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Cristian Ciocaltea <cristian.ciocaltea@gmail.com> +# +# Work based on: +# - test_net.py +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# - test_fit.py +# Copyright (c) 2013, Google Inc. +# +# Test launching UEFI binaries from FIT images. + +""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped. + +For example: + +# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False + +# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True + +# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +# Additionally, when the 'size' is not available, the file will be generated +# automatically in the TFTP root directory, as specified by the 'dn' field. +env__efi_fit_tftp_file = { + 'fn': 'test-efi-fit.img', # File path relative to TFTP root + 'size': 3831, # File size + 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional + 'addr': 0x40400000, # Loading address, integer, optional + 'dn': 'tftp/root/dir', # TFTP root directory path, optional +} +""" + +import os.path +import pytest +import u_boot_utils as util + +# Define the parametrized ITS data to be used for FIT images generation. +ITS_DATA = ''' +/dts-v1/; + +/ { + description = "EFI image with FDT blob"; + #address-cells = <1>; + + images { + efi { + description = "Test EFI"; + data = /incbin/("%(efi-bin)s"); + type = "%(kernel-type)s"; + arch = "%(sys-arch)s"; + os = "efi"; + compression = "%(efi-comp)s"; + load = <0x0>; + entry = <0x0>; + }; + fdt { + description = "Test FDT"; + data = /incbin/("%(fdt-bin)s"); + type = "flat_dt"; + arch = "%(sys-arch)s"; + compression = "%(fdt-comp)s"; + }; + }; + + configurations { + default = "config-efi-fdt"; + config-efi-fdt { + description = "EFI FIT w/ FDT"; + kernel = "efi"; + fdt = "fdt"; + }; + config-efi-nofdt { + description = "EFI FIT w/o FDT"; + kernel = "efi"; + }; + }; +}; +''' + +# Define the parametrized FDT data to be used for DTB images generation. +FDT_DATA = ''' +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test"; + compatible = "%(sys-arch)s"; + + reset@0 { + compatible = "%(sys-arch)s,reset"; + reg = <0 4>; + }; +}; +''' + +@pytest.mark.buildconfigspec('bootm_efi') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +@pytest.mark.requiredtool('dtc') +def test_efi_fit_launch(u_boot_console): + """Test handling of UEFI binaries inside FIT images. + + The tests are trying to launch U-Boot's helloworld.efi embedded into + FIT images, in uncompressed or gzip compressed format. + + Additionally, a sample FDT blob is created and embedded into the above + mentioned FIT images, in uncompressed or gzip compressed format. + + For more details, see launch_efi(). + + The following test cases are currently defined and enabled: + - Launch uncompressed FIT EFI & internal FDT + - Launch uncompressed FIT EFI & FIT FDT + - Launch compressed FIT EFI & internal FDT + - Launch compressed FIT EFI & FIT FDT + """ + + def net_pre_commands(): + """Execute any commands required to enable network hardware. + + These commands are provided by the boardenv_* file; see the comment + at the beginning of this file. + """ + + init_usb = cons.config.env.get('env__net_uses_usb', False) + if init_usb: + cons.run_command('usb start') + + init_pci = cons.config.env.get('env__net_uses_pci', False) + if init_pci: + cons.run_command('pci enum') + + def net_dhcp(): + """Execute the dhcp command. + + The boardenv_* file may be used to enable/disable DHCP; see the + comment at the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup') + return False + + test_dhcp = cons.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + cons.log.info('No DHCP server available') + return False + + cons.run_command('setenv autoload no') + output = cons.run_command('dhcp') + assert 'DHCP client bound to address ' in output + return True + + def net_setup_static(): + """Set up a static IP configuration. + + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' + if not has_dhcp: + cons.log.warning('CONFIG_NET != y: Skipping static network setup') + return False + + env_vars = cons.config.env.get('env__net_static_env_vars', None) + if not env_vars: + cons.log.info('No static network configuration is defined') + return False + + for (var, val) in env_vars: + cons.run_command('setenv %s %s' % (var, val)) + return True + + def make_fpath(file_name): + """Compute the path of a given (temporary) file. + + Args: + file_name: The name of a file within U-Boot build dir. + Return: + The computed file path. + """ + + return os.path.join(cons.config.build_dir, file_name) + + def make_efi(fname, comp): + """Create an UEFI binary. + + This simply copies lib/efi_loader/helloworld.efi into U-Boot + build dir and, optionally, compresses the file using gzip. + + Args: + fname: The target file name within U-Boot build dir. + comp: Flag to enable gzip compression. + Return: + The path of the created file. + """ + + bin_path = make_fpath(fname) + util.run_and_log(cons, + ['cp', make_fpath('lib/efi_loader/helloworld.efi'), + bin_path]) + if comp: + util.run_and_log(cons, ['gzip', '-f', bin_path]) + bin_path += '.gz' + return bin_path + + def make_dtb(fdt_type, comp): + """Create a sample DTB file. + + Creates a DTS file and compiles it to a DTB. + + Args: + fdt_type: The type of the FDT, i.e. internal, user. + comp: Flag to enable gzip compression. + Return: + The path of the created file. + """ + + # Generate resources referenced by FDT. + fdt_params = { + 'sys-arch': sys_arch, + 'fdt_type': fdt_type, + } + + # Generate a test FDT file. + dts = make_fpath('test-efi-fit-%s.dts' % fdt_type) + with open(dts, 'w') as file: + file.write(FDT_DATA % fdt_params) + + # Build the test FDT. + dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type) + util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts]) + if comp: + util.run_and_log(cons, ['gzip', '-f', dtb]) + dtb += '.gz' + return dtb + + def make_fit(comp): + """Create a sample FIT image. + + Runs 'mkimage' to create a FIT image within U-Boot build dir. + Args: + comp: Enable gzip compression for the EFI binary and FDT blob. + Return: + The path of the created file. + """ + + # Generate resources referenced by ITS. + its_params = { + 'sys-arch': sys_arch, + 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)), + 'kernel-type': 'kernel' if comp else 'kernel_noload', + 'efi-comp': 'gzip' if comp else 'none', + 'fdt-bin': os.path.basename(make_dtb('user', comp)), + 'fdt-comp': 'gzip' if comp else 'none', + } + + # Generate a test ITS file. + its_path = make_fpath('test-efi-fit-helloworld.its') + with open(its_path, 'w') as file: + file.write(ITS_DATA % its_params) + + # Build the test ITS. + fit_path = make_fpath('test-efi-fit-helloworld.fit') + util.run_and_log( + cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path]) + return fit_path + + def load_fit_from_host(fit): + """Load the FIT image using the 'host load' command and return its address. + + Args: + fit: Dictionary describing the FIT image to load, see env__efi_fit_test_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = fit.get('addr', None) + if not addr: + addr = util.find_ram_base(cons) + + output = cons.run_command( + 'host load hostfs - %x %s/%s' % (addr, fit['dn'], fit['fn'])) + expected_text = ' bytes read' + size = fit.get('size', None) + if size: + expected_text = '%d' % size + expected_text + assert expected_text in output + + return addr + + def load_fit_from_tftp(fit): + """Load the FIT image using the tftpboot command and return its address. + + The file is downloaded from the TFTP server, its size and optionally its + CRC32 are validated. + + Args: + fit: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file + in the comment at the beginning of this file. + Return: + The address where the file has been loaded. + """ + + addr = fit.get('addr', None) + if not addr: + addr = util.find_ram_base(cons) + + file_name = fit['fn'] + output = cons.run_command('tftpboot %x %s' % (addr, file_name)) + expected_text = 'Bytes transferred = ' + size = fit.get('size', None) + if size: + expected_text += '%d' % size + assert expected_text in output + + expected_crc = fit.get('crc32', None) + if not expected_crc: + return addr + + if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return addr + + output = cons.run_command('crc32 $fileaddr $filesize') + assert expected_crc in output + + return addr + + def launch_efi(enable_fdt, enable_comp): + """Launch U-Boot's helloworld.efi binary from a FIT image. + + An external image file can be downloaded from TFTP, when related + details are provided by the boardenv_* file; see the comment at the + beginning of this file. + + If the size of the TFTP file is not provided within env__efi_fit_tftp_file, + the test image is generated automatically and placed in the TFTP root + directory specified via the 'dn' field. + + When running the tests on Sandbox, the image file is loaded directly + from the host filesystem. + + Once the load address is available on U-Boot console, the 'bootm' + command is executed for either 'config-efi-fdt' or 'config-efi-nofdt' + FIT configuration, depending on the value of the 'enable_fdt' function + argument. + + Eventually the 'Hello, world' message is expected in the U-Boot console. + + Args: + enable_fdt: Flag to enable using the FDT blob inside FIT image. + enable_comp: Flag to enable GZIP compression on EFI and FDT + generated content. + """ + + with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)): + if is_sandbox: + fit = { + 'dn': cons.config.build_dir, + } + else: + # Init networking. + net_pre_commands() + net_set_up = net_dhcp() + net_set_up = net_setup_static() or net_set_up + if not net_set_up: + pytest.skip('Network not initialized') + + fit = cons.config.env.get('env__efi_fit_tftp_file', None) + if not fit: + pytest.skip('No env__efi_fit_tftp_file binary specified in environment') + + size = fit.get('size', None) + if not size: + if not fit.get('dn', None): + pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file') + + # Create test FIT image. + fit_path = make_fit(enable_comp) + fit['fn'] = os.path.basename(fit_path) + fit['size'] = os.path.getsize(fit_path) + + # Copy image to TFTP root directory. + if fit['dn'] != cons.config.build_dir: + util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']]) + + # Load FIT image. + addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit) + + # Select boot configuration. + fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt' + + # Try booting. + output = cons.run_command('bootm %x#%s' % (addr, fit_config)) + if enable_fdt: + assert 'Booting using the fdt blob' in output + assert 'Hello, world' in output + assert '## Application failed' not in output + cons.restart_uboot() + + cons = u_boot_console + # Array slice removes leading/trailing quotes. + sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1] + is_sandbox = sys_arch == 'sandbox' + + try: + if is_sandbox: + # Use our own device tree file, will be restored afterwards. + control_dtb = make_dtb('internal', False) + old_dtb = cons.config.dtb + cons.config.dtb = control_dtb + + # Run tests + # - fdt OFF, gzip OFF + launch_efi(False, False) + # - fdt ON, gzip OFF + launch_efi(True, False) + + if is_sandbox: + # - fdt OFF, gzip ON + launch_efi(False, True) + # - fdt ON, gzip ON + launch_efi(True, True) + + finally: + if is_sandbox: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot() diff --git a/roms/u-boot/test/py/tests/test_efi_loader.py b/roms/u-boot/test/py/tests/test_efi_loader.py new file mode 100644 index 000000000..fc8d6b865 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_loader.py @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2016, Alexander Graf <agraf@suse.de> +# +# based on test_net.py. + +# Test efi loader implementation + +import pytest +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +which network environment is available for testing. Without this, the parts +that rely on network will be automatically skipped. + +For example: + +# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False + +# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True + +# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +env__efi_loader_helloworld_file = { + 'fn': 'lib/efi_loader/helloworld.efi', # file name + 'size': 5058624, # file length in bytes + 'crc32': 'c2244b26', # CRC32 check sum + 'addr': 0x40400000, # load address +} +""" + +net_set_up = False + +def test_efi_pre_commands(u_boot_console): + """Execute any commands required to enable network hardware. + + These commands are provided by the boardenv_* file; see the comment at the + beginning of this file. + """ + + init_usb = u_boot_console.config.env.get('env__net_uses_usb', False) + if init_usb: + u_boot_console.run_command('usb start') + + init_pci = u_boot_console.config.env.get('env__net_uses_pci', False) + if init_pci: + u_boot_console.run_command('pci enum') + +@pytest.mark.buildconfigspec('cmd_dhcp') +def test_efi_setup_dhcp(u_boot_console): + """Set up the network using DHCP. + + The boardenv_* file may be used to enable/disable this test; see the + comment at the beginning of this file. + """ + + test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None) + if not env_vars: + pytest.skip('No DHCP server available') + return None + + u_boot_console.run_command('setenv autoload no') + output = u_boot_console.run_command('dhcp') + assert 'DHCP client bound to address ' in output + + global net_set_up + net_set_up = True + +@pytest.mark.buildconfigspec('net') +def test_efi_setup_static(u_boot_console): + """Set up the network using a static IP configuration. + + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None) + if not env_vars: + test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + pytest.skip('No static network configuration is defined') + return None + + for (var, val) in env_vars: + u_boot_console.run_command('setenv %s %s' % (var, val)) + + global net_set_up + net_set_up = True + +def fetch_tftp_file(u_boot_console, env_conf): + """Grab an env described file via TFTP and return its address + + A file as described by an env config <env_conf> is downloaded from the TFTP + server. The address to that file is returned. + """ + if not net_set_up: + pytest.skip('Network not initialized') + + f = u_boot_console.config.env.get(env_conf, None) + if not f: + pytest.skip('No %s binary specified in environment' % env_conf) + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output + + expected_crc = f.get('crc32', None) + if not expected_crc: + return addr + + if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return addr + + output = u_boot_console.run_command('crc32 %x $filesize' % addr) + assert expected_crc in output + + return addr + +@pytest.mark.buildconfigspec('of_control') +@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile') +def test_efi_helloworld_net(u_boot_console): + """Run the helloworld.efi binary via TFTP. + + The helloworld.efi file is downloaded from the TFTP server and is executed + using the fallback device tree at $fdtcontroladdr. + """ + + addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_helloworld_file') + + output = u_boot_console.run_command('bootefi %x' % addr) + expected_text = 'Hello, world' + assert expected_text in output + expected_text = '## Application failed' + assert expected_text not in output + +@pytest.mark.buildconfigspec('cmd_bootefi_hello') +def test_efi_helloworld_builtin(u_boot_console): + """Run the builtin helloworld.efi binary. + + The helloworld.efi file is included in U-Boot, execute it using the + special "bootefi hello" command. + """ + + output = u_boot_console.run_command('bootefi hello') + expected_text = 'Hello, world' + assert expected_text in output + +@pytest.mark.buildconfigspec('of_control') +@pytest.mark.buildconfigspec('cmd_bootefi') +def test_efi_grub_net(u_boot_console): + """Run the grub.efi binary via TFTP. + + The grub.efi file is downloaded from the TFTP server and gets + executed. + """ + + addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_grub_file') + + u_boot_console.run_command('bootefi %x' % addr, wait_for_prompt=False) + + # Verify that we have an SMBIOS table + check_smbios = u_boot_console.config.env.get('env__efi_loader_check_smbios', False) + if check_smbios: + u_boot_console.wait_for('grub>') + output = u_boot_console.run_command('lsefisystab', wait_for_prompt=False, wait_for_echo=False) + u_boot_console.wait_for('SMBIOS') + + # Then exit cleanly + u_boot_console.wait_for('grub>') + u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False) + u_boot_console.wait_for(u_boot_console.prompt) + # And give us our U-Boot prompt back + u_boot_console.run_command('') diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py b/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py new file mode 100644 index 000000000..69a498ca0 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py @@ -0,0 +1,239 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> + +import os +import os.path +from subprocess import call, check_call, check_output, CalledProcessError +import pytest +from defs import * + + +# +# Fixture for UEFI secure boot test +# + + +@pytest.fixture(scope='session') +def efi_boot_env(request, u_boot_config): + """Set up a file system to be used in UEFI secure boot test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + image_path = u_boot_config.persistent_data_dir + image_path = image_path + '/test_efi_secboot.img' + + try: + mnt_point = u_boot_config.build_dir + '/mnt_efisecure' + check_call('rm -rf {}'.format(mnt_point), shell=True) + check_call('mkdir -p {}'.format(mnt_point), shell=True) + + # suffix + # *.key: RSA private key in PEM + # *.crt: X509 certificate (self-signed) in PEM + # *.esl: signature list + # *.hash: message digest of image as signature list + # *.auth: signed signature list in signature database format + # *.efi: UEFI image + # *.efi.signed: signed UEFI image + + # Create signature database + # PK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -t "2020-04-01" -c PK.crt -k PK.key PK PK.esl PK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # PK_null for deletion + check_call('cd %s; touch PK_null.esl; %ssign-efi-sig-list -t "2020-04-02" -c PK.crt -k PK.key PK PK_null.esl PK_null.auth' + % (mnt_point, EFITOOLS_PATH), shell=True) + # KEK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -t "2020-04-03" -c PK.crt -k PK.key KEK KEK.esl KEK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # db + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -t "2020-04-04" -c KEK.crt -k KEK.key db db.esl db.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # db1 + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key db db1.esl db1.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # dbx (TEST_dbx certificate) + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # dbx_hash (digest of TEST_db certificate) + check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db.crt dbx_hash.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash.crl dbx_hash.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # dbx_hash1 (digest of TEST_db1 certificate) + check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db1.crt dbx_hash1.crl; %ssign-efi-sig-list -t "2020-04-06" -c KEK.crt -k KEK.key dbx dbx_hash1.crl dbx_hash1.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # dbx_db (with TEST_db certificate) + check_call('cd %s; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx db.esl dbx_db.auth' + % (mnt_point, EFITOOLS_PATH), + shell=True) + + # Copy image + check_call('cp %s/lib/efi_loader/helloworld.efi %s' % + (u_boot_config.build_dir, mnt_point), shell=True) + + # Sign image + check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi' + % mnt_point, shell=True) + # Sign already-signed image with another key + check_call('cd %s; sbsign --key db1.key --cert db1.crt --output helloworld.efi.signed_2sigs helloworld.efi.signed' + % mnt_point, shell=True) + # Digest image + check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth' + % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH), + shell=True) + check_call('cd %s; %shash-to-efi-sig-list helloworld.efi.signed db_hello_signed.hash; %ssign-efi-sig-list -t "2020-04-03" -c KEK.crt -k KEK.key db db_hello_signed.hash db_hello_signed.auth' + % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH), + shell=True) + check_call('cd %s; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key dbx db_hello_signed.hash dbx_hello_signed.auth' + % (mnt_point, EFITOOLS_PATH), + shell=True) + + check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format( + mnt_point, image_path), shell=True) + check_call('rm -rf {}'.format(mnt_point), shell=True) + + except CalledProcessError as exception: + pytest.skip('Setup failed: %s' % exception.cmd) + return + else: + yield image_path + finally: + call('rm -f %s' % image_path, shell=True) + +# +# Fixture for UEFI secure boot test of intermediate certificates +# + + +@pytest.fixture(scope='session') +def efi_boot_env_intca(request, u_boot_config): + """Set up a file system to be used in UEFI secure boot test + of intermediate certificates. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A path to disk image to be used for testing + """ + image_path = u_boot_config.persistent_data_dir + image_path = image_path + '/test_efi_secboot_intca.img' + + try: + mnt_point = u_boot_config.persistent_data_dir + '/mnt_efi_secboot_intca' + check_call('rm -rf {}'.format(mnt_point), shell=True) + check_call('mkdir -p {}'.format(mnt_point), shell=True) + + # Create signature database + # PK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # KEK + check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365' + % mnt_point, shell=True) + check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + + # We will have three-tier hierarchy of certificates: + # TestRoot: Root CA (self-signed) + # TestSub: Intermediate CA (signed by Root CA) + # TestCert: User certificate (signed by Intermediate CA, and used + # for signing an image) + # + # NOTE: + # I consulted the following EDK2 document for certificate options: + # BaseTools/Source/Python/Pkcs7Sign/Readme.md + # Please not use them as they are in product system. They are + # for test purpose only. + + # TestRoot + check_call('cp %s/test/py/tests/test_efi_secboot/openssl.cnf %s' + % (u_boot_config.source_dir, mnt_point), shell=True) + check_call('cd %s; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestRoot.key 2048; openssl req -extensions v3_ca -new -x509 -days 365 -key TestRoot.key -out TestRoot.crt -subj "/CN=TEST_root/"; touch index.txt; touch index.txt.attr' + % mnt_point, shell=True) + # TestSub + check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestSub.key 2048; openssl req -new -key TestSub.key -out TestSub.csr -subj "/CN=TEST_sub/"; openssl ca -in TestSub.csr -out TestSub.crt -extensions v3_int_ca -days 365 -batch -rand_serial -cert TestRoot.crt -keyfile TestRoot.key' + % mnt_point, shell=True) + # TestCert + check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestCert.key 2048; openssl req -new -key TestCert.key -out TestCert.csr -subj "/CN=TEST_cert/"; openssl ca -in TestCert.csr -out TestCert.crt -extensions usr_cert -days 365 -batch -rand_serial -cert TestSub.crt -keyfile TestSub.key' + % mnt_point, shell=True) + # db + # for TestCert + check_call('cd %s; %scert-to-efi-sig-list -g %s TestCert.crt TestCert.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db TestCert.esl db_a.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # for TestSub + check_call('cd %s; %scert-to-efi-sig-list -g %s TestSub.crt TestSub.esl; %ssign-efi-sig-list -t "2020-07-16" -c KEK.crt -k KEK.key db TestSub.esl db_b.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # for TestRoot + check_call('cd %s; %scert-to-efi-sig-list -g %s TestRoot.crt TestRoot.esl; %ssign-efi-sig-list -t "2020-07-17" -c KEK.crt -k KEK.key db TestRoot.esl db_c.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + ## dbx (hash of certificate with revocation time) + # for TestCert + check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-20" -s 256 TestCert.crt TestCert.crl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx TestCert.crl dbx_a.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # for TestSub + check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-21" -s 256 TestSub.crt TestSub.crl; %ssign-efi-sig-list -t "2020-07-18" -c KEK.crt -k KEK.key dbx TestSub.crl dbx_b.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + # for TestRoot + check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-22" -s 256 TestRoot.crt TestRoot.crl; %ssign-efi-sig-list -t "2020-07-19" -c KEK.crt -k KEK.key dbx TestRoot.crl dbx_c.auth' + % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH), + shell=True) + + # Sign image + # additional intermediate certificates may be included + # in SignedData + + check_call('cp %s/lib/efi_loader/helloworld.efi %s' % + (u_boot_config.build_dir, mnt_point), shell=True) + # signed by TestCert + check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --out helloworld.efi.signed_a helloworld.efi' + % (mnt_point, SBSIGN_PATH), shell=True) + # signed by TestCert with TestSub in signature + check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSub.crt --out helloworld.efi.signed_ab helloworld.efi' + % (mnt_point, SBSIGN_PATH), shell=True) + # signed by TestCert with TestSub and TestRoot in signature + check_call('cd %s; cat TestSub.crt TestRoot.crt > TestSubRoot.crt; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSubRoot.crt --out helloworld.efi.signed_abc helloworld.efi' + % (mnt_point, SBSIGN_PATH), shell=True) + + check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True) + check_call('rm -rf {}'.format(mnt_point), shell=True) + + except CalledProcessError as e: + pytest.skip('Setup failed: %s' % e.cmd) + return + else: + yield image_path + finally: + call('rm -f %s' % image_path, shell=True) diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/defs.py b/roms/u-boot/test/py/tests/test_efi_secboot/defs.py new file mode 100644 index 000000000..b7a2a1185 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/defs.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# Owner guid +GUID = '11111111-2222-3333-4444-123456789abc' + +# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and +# you need build a newer version on your own. +# The path must terminate with '/'. +EFITOOLS_PATH = '' + +# "--addcert" option of sbsign must be available, otherwise +# you need build a newer version on your own. +# The path must terminate with '/'. +SBSIGN_PATH = '' diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf b/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf new file mode 100644 index 000000000..f684f1df7 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf @@ -0,0 +1,48 @@ +[ ca ] +default_ca = CA_default + +[ CA_default ] +new_certs_dir = . +database = ./index.txt +serial = ./serial +default_md = sha256 +policy = policy_min + +[ req ] +distinguished_name = def_distinguished_name + +[def_distinguished_name] + +# Extensions +# -addext " ... = ..." +# +[ v3_ca ] + # Extensions for a typical Root CA. + basicConstraints = critical,CA:TRUE + keyUsage = critical, digitalSignature, cRLSign, keyCertSign + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + +[ v3_int_ca ] + # Extensions for a typical intermediate CA. + basicConstraints = critical, CA:TRUE + keyUsage = critical, digitalSignature, cRLSign, keyCertSign + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid:always,issuer + +[ usr_cert ] + # Extensions for user end certificates. + basicConstraints = CA:FALSE + keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment + extendedKeyUsage = clientAuth, emailProtection + subjectKeyIdentifier = hash + authorityKeyIdentifier = keyid,issuer + +[ policy_min ] + countryName = optional + stateOrProvinceName = optional + localityName = optional + organizationName = optional + organizationalUnitName = optional + commonName = supplied + emailAddress = optional diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py new file mode 100644 index 000000000..f99b8270a --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py @@ -0,0 +1,281 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Variable Authentication Test + +""" +This test verifies variable authentication +""" + +import pytest + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiAuthVar(object): + def test_efi_var_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - Install signature database + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, Initial secure state + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'printenv -e SecureBoot']) + assert '00000000: 00' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert '00000000: 01' in output + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 1c'): + # Test Case 1c, install PK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'printenv -e -n PK']) + assert 'PK:' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert '00000000: 01' in output + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert '00000000: 00' in output + + with u_boot_console.log.section('Test Case 1d'): + # Test Case 1d, db/dbx without KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 1e'): + # Test Case 1e, install KEK + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -i 4000000:$filesize KEK']) + assert 'Failed to set EFI variable' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'printenv -e -n KEK']) + assert 'KEK:' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert '00000000: 01' in output + + with u_boot_console.log.section('Test Case 1f'): + # Test Case 1f, install db + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert '00000000: 01' in output + + with u_boot_console.log.section('Test Case 1g'): + # Test Case 1g, install dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx.auth', + 'setenv -e -nv -bs -rt -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f dbx']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'dbx:' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert '00000000: 01' in output + + def test_efi_var_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - Update database by overwriting + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.esl', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 2c'): + # Test Case 2c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + def test_efi_var_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - Append database + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -a -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, update without correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.esl', + 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' in ''.join(output) + + with u_boot_console.log.section('Test Case 3c'): + # Test Case 3c, update with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + def test_efi_var_auth4(self, u_boot_console, efi_boot_env): + """ + Test Case 4 - Delete database without authentication + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4a'): + # Test Case 4a, update without AUTHENTICATED_WRITE_ACCESS + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'db:' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' in ''.join(output) + assert 'db:' in ''.join(output) + + with u_boot_console.log.section('Test Case 4b'): + # Test Case 4b, update without correct signature/data + output = u_boot_console.run_command_list([ + 'setenv -e -nv -bs -rt -at db', + 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db']) + assert 'Failed to set EFI variable' in ''.join(output) + assert 'db:' in ''.join(output) + + def test_efi_var_auth5(self, u_boot_console, efi_boot_env): + """ + Test Case 5 - Uninstall(delete) PK + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 5a'): + # Test Case 5a, Uninstall PK without correct signature + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'printenv -e -n PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert 'PK:' in ''.join(output) + + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK_null.esl', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'printenv -e -n PK']) + assert 'Failed to set EFI variable' in ''.join(output) + assert 'PK:' in ''.join(output) + + with u_boot_console.log.section('Test Case 5b'): + # Test Case 5b, Uninstall PK with correct signature + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 PK_null.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK', + 'printenv -e -n PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + assert '\"PK\" not defined' in ''.join(output) + + output = u_boot_console.run_command( + 'printenv -e SecureBoot') + assert '00000000: 00' in output + output = u_boot_console.run_command( + 'printenv -e SetupMode') + assert '00000000: 01' in output diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py new file mode 100644 index 000000000..0aee34479 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py @@ -0,0 +1,259 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for signed images. +""" + +import pytest + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiSignedImage(object): + def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - Secure boot is not in force + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, run signed image if no PK + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, run unsigned image if no PK + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - Secure boot is in force, + authenticated by db (TEST_db certificate in db) + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, db is not yet installed + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert('\'HELLO1\' failed' in ''.join(output)) + assert('efi_start_image() returned: 26' in ''.join(output)) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 2', + 'efidebug test bootmgr']) + assert '\'HELLO2\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, authenticated by db + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 2', + 'efidebug test bootmgr']) + assert '\'HELLO2\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + def test_efi_signed_image_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - rejected by dbx (TEST_db certificate in dbx) + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, rejected by dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, rejected by dbx even if db allows + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + def test_efi_signed_image_auth4(self, u_boot_console, efi_boot_env): + """ + Test Case 4 - revoked by dbx (digest of TEST_db certificate in dbx) + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 4'): + # Test Case 4, rejected by dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 dbx_hash.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + def test_efi_signed_image_auth5(self, u_boot_console, efi_boot_env): + """ + Test Case 5 - multiple signatures + one signed with TEST_db, and + one signed with TEST_db1 + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 5a'): + # Test Case 5a, authenticated even if only one of signatures + # is verified + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'Hello, world!' in ''.join(output) + + with u_boot_console.log.section('Test Case 5b'): + # Test Case 5b, authenticated if both signatures are verified + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db1.auth', + 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'Hello, world!' in ''.join(output) + + with u_boot_console.log.section('Test Case 5c'): + # Test Case 5c, not rejected if one of signatures (digest of + # certificate) is revoked + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx_hash.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'Hello, world!' in ''.join(output) + + with u_boot_console.log.section('Test Case 5d'): + # Test Case 5d, rejected if both of signatures are revoked + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx_hash1.auth', + 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + def test_efi_signed_image_auth6(self, u_boot_console, efi_boot_env): + """ + Test Case 6 - using digest of signed image in database + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 6a'): + # Test Case 6a, verified by image's digest in db + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_hello_signed.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + with u_boot_console.log.section('Test Case 6b'): + # Test Case 6b, rejected by TEST_db certificate in dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx_db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 6c'): + # Test Case 6c, rejected by image's digest in dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 dbx_hello_signed.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx']) + assert 'Failed to set EFI variable' not in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py new file mode 100644 index 000000000..d8d599d22 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py @@ -0,0 +1,135 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Image Authentication Test (signature with certificates chain) + +""" +This test verifies image authentication for a signed image which is signed +by user certificate and contains additional intermediate certificates in its +signature. +""" + +import pytest + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiSignedImageIntca(object): + def test_efi_signed_image_intca1(self, u_boot_console, efi_boot_env_intca): + """ + Test Case 1 - authenticated by root CA in db + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env_intca + with u_boot_console.log.section('Test Case 1a'): + # Test Case 1a, with no Int CA and not authenticated by root CA + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_c.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO_a\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 1b'): + # Test Case 1b, signed and authenticated by root CA + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab -s ""', + 'efidebug boot next 2', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + def test_efi_signed_image_intca2(self, u_boot_console, efi_boot_env_intca): + """ + Test Case 2 - authenticated by root CA in db + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env_intca + with u_boot_console.log.section('Test Case 2a'): + # Test Case 2a, unsigned and not authenticated by root CA + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO_abc\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 2b'): + # Test Case 2b, signed and authenticated by root CA + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db_b.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO_abc\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 2c'): + # Test Case 2c, signed and authenticated by root CA + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db_c.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'Hello, world!' in ''.join(output) + + def test_efi_signed_image_intca3(self, u_boot_console, efi_boot_env_intca): + """ + Test Case 3 - revoked by dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env_intca + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, revoked by int CA in dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 dbx_b.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'fatload host 0:1 4000000 db_c.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'Hello, world!' in ''.join(output) + # Or, + # assert '\'HELLO_abc\' failed' in ''.join(output) + # assert 'efi_start_image() returned: 26' in ''.join(output) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, revoked by root CA in dbx + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 dbx_c.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert '\'HELLO_abc\' failed' in ''.join(output) + assert 'efi_start_image() returned: 26' in ''.join(output) diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py new file mode 100644 index 000000000..df63f0df0 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py @@ -0,0 +1,117 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Linaro Limited +# Author: AKASHI Takahiro <takahiro.akashi@linaro.org> +# +# U-Boot UEFI: Signed Image Authentication Test + +""" +This test verifies image authentication for unsigned images. +""" + +import pytest + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('efi_secure_boot') +@pytest.mark.buildconfigspec('cmd_efidebug') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_nvedit_efi') +@pytest.mark.slow +class TestEfiUnsignedImage(object): + def test_efi_unsigned_image_auth1(self, u_boot_console, efi_boot_env): + """ + Test Case 1 - rejected when not digest in db or dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 1'): + # Test Case 1 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'efi_start_image() returned: 26' in ''.join(output) + assert 'Hello, world!' not in ''.join(output) + + def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env): + """ + Test Case 2 - authenticated by digest in db + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 2'): + # Test Case 2 + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert 'Hello, world!' in ''.join(output) + + def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env): + """ + Test Case 3 - rejected by digest in dbx + """ + u_boot_console.restart_uboot() + disk_img = efi_boot_env + with u_boot_console.log.section('Test Case 3a'): + # Test Case 3a, rejected by dbx + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % disk_img, + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx', + 'fatload host 0:1 4000000 KEK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK', + 'fatload host 0:1 4000000 PK.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'efi_start_image() returned: 26' in ''.join(output) + assert 'Hello, world!' not in ''.join(output) + + with u_boot_console.log.section('Test Case 3b'): + # Test Case 3b, rejected by dbx even if db allows + output = u_boot_console.run_command_list([ + 'fatload host 0:1 4000000 db_hello.auth', + 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db']) + assert 'Failed to set EFI variable' not in ''.join(output) + + output = u_boot_console.run_command_list([ + 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""', + 'efidebug boot next 1', + 'bootefi bootmgr']) + assert '\'HELLO\' failed' in ''.join(output) + output = u_boot_console.run_command_list([ + 'efidebug boot next 1', + 'efidebug test bootmgr']) + assert 'efi_start_image() returned: 26' in ''.join(output) + assert 'Hello, world!' not in ''.join(output) diff --git a/roms/u-boot/test/py/tests/test_efi_selftest.py b/roms/u-boot/test/py/tests/test_efi_selftest.py new file mode 100644 index 000000000..63218efbc --- /dev/null +++ b/roms/u-boot/test/py/tests/test_efi_selftest.py @@ -0,0 +1,214 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de> + +""" +Test UEFI API implementation +""" + +import pytest + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest(u_boot_console): + """Run UEFI unit tests + + :param u_boot_console: U-Boot console + + This function executes all selftests that are not marked as on request. + """ + u_boot_console.run_command(cmd='setenv efi_selftest') + u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False) + m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) + if m != 0: + raise Exception('Failures occurred during the EFI selftest') + u_boot_console.restart_uboot() + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +@pytest.mark.buildconfigspec('hush_parser') +@pytest.mark.buildconfigspec('of_control') +@pytest.mark.notbuildconfigspec('generate_acpi_table') +def test_efi_selftest_device_tree(u_boot_console): + """Test the device tree support in the UEFI sub-system + + :param u_boot_console: U-Boot console + + This test executes the UEFI unit test by calling 'bootefi selftest'. + """ + u_boot_console.run_command(cmd='setenv efi_selftest list') + output = u_boot_console.run_command('bootefi selftest') + assert '\'device tree\'' in output + u_boot_console.run_command(cmd='setenv efi_selftest device tree') + # Set serial# if it is not already set. + u_boot_console.run_command(cmd='setenv efi_test "${serial#}x"') + u_boot_console.run_command(cmd='test "${efi_test}" = x && setenv serial# 0') + u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False) + m = u_boot_console.p.expect(['serial-number:', 'U-Boot']) + if m != 0: + raise Exception('serial-number missing in device tree') + u_boot_console.restart_uboot() + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest_watchdog_reboot(u_boot_console): + """Test the watchdog timer + + :param u_boot_console: U-Boot console + + This function executes the 'watchdog reboot' unit test. + """ + u_boot_console.run_command(cmd='setenv efi_selftest list') + output = u_boot_console.run_command('bootefi selftest') + assert '\'watchdog reboot\'' in output + u_boot_console.run_command(cmd='setenv efi_selftest watchdog reboot') + u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False) + m = u_boot_console.p.expect(['resetting', 'U-Boot']) + if m != 0: + raise Exception('Reset failed in \'watchdog reboot\' test') + u_boot_console.restart_uboot() + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest_text_input(u_boot_console): + """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL + + :param u_boot_console: U-Boot console + + This function calls the text input EFI selftest. + """ + u_boot_console.run_command(cmd='setenv efi_selftest text input') + output = u_boot_console.run_command(cmd='bootefi selftest', + wait_for_prompt=False) + m = u_boot_console.p.expect([r'To terminate type \'x\'']) + if m != 0: + raise Exception('No prompt for \'text input\' test') + u_boot_console.drain_console() + u_boot_console.p.timeout = 500 + # EOT + u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']) + if m != 0: + raise Exception('EOT failed in \'text input\' test') + u_boot_console.drain_console() + # BS + u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 8 \(BS\), scan code 0 \(Null\)']) + if m != 0: + raise Exception('BS failed in \'text input\' test') + u_boot_console.drain_console() + # TAB + u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']) + if m != 0: + raise Exception('BS failed in \'text input\' test') + u_boot_console.drain_console() + # a + u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']) + if m != 0: + raise Exception('\'a\' failed in \'text input\' test') + u_boot_console.drain_console() + # UP escape sequence + u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 0 \(Null\), scan code 1 \(Up\)']) + if m != 0: + raise Exception('UP failed in \'text input\' test') + u_boot_console.drain_console() + # Euro sign + u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect([r'Unicode char 8364 \(\'']) + if m != 0: + raise Exception('Euro sign failed in \'text input\' test') + u_boot_console.drain_console() + u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) + if m != 0: + raise Exception('Failures occurred during the EFI selftest') + u_boot_console.restart_uboot() + +@pytest.mark.buildconfigspec('cmd_bootefi_selftest') +def test_efi_selftest_text_input_ex(u_boot_console): + """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL + + :param u_boot_console: U-Boot console + + This function calls the extended text input EFI selftest. + """ + u_boot_console.run_command(cmd='setenv efi_selftest extended text input') + output = u_boot_console.run_command(cmd='bootefi selftest', + wait_for_prompt=False) + m = u_boot_console.p.expect([r'To terminate type \'CTRL\+x\'']) + if m != 0: + raise Exception('No prompt for \'text input\' test') + u_boot_console.drain_console() + u_boot_console.p.timeout = 500 + # EOT + u_boot_console.run_command(cmd=chr(4), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']) + if m != 0: + raise Exception('EOT failed in \'text input\' test') + u_boot_console.drain_console() + # BS + u_boot_console.run_command(cmd=chr(8), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']) + if m != 0: + raise Exception('BS failed in \'text input\' test') + u_boot_console.drain_console() + # TAB + u_boot_console.run_command(cmd=chr(9), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']) + if m != 0: + raise Exception('TAB failed in \'text input\' test') + u_boot_console.drain_console() + # a + u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']) + if m != 0: + raise Exception('\'a\' failed in \'text input\' test') + u_boot_console.drain_console() + # UP escape sequence + u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']) + if m != 0: + raise Exception('UP failed in \'text input\' test') + u_boot_console.drain_console() + # Euro sign + u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False, + send_nl=False, wait_for_prompt=False) + m = u_boot_console.p.expect([r'Unicode char 8364 \(\'']) + if m != 0: + raise Exception('Euro sign failed in \'text input\' test') + u_boot_console.drain_console() + # SHIFT+ALT+FN 5 + u_boot_console.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(), + wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect( + [r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']) + if m != 0: + raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test') + u_boot_console.drain_console() + u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False, + wait_for_prompt=False) + m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']) + if m != 0: + raise Exception('Failures occurred during the EFI selftest') + u_boot_console.restart_uboot() diff --git a/roms/u-boot/test/py/tests/test_env.py b/roms/u-boot/test/py/tests/test_env.py new file mode 100644 index 000000000..9bed2f48d --- /dev/null +++ b/roms/u-boot/test/py/tests/test_env.py @@ -0,0 +1,517 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Test operation of shell commands relating to environment variables. + +import os +import os.path +from subprocess import call, check_call, CalledProcessError + +import pytest +import u_boot_utils + +# FIXME: This might be useful for other tests; +# perhaps refactor it into ConsoleBase or some other state object? +class StateTestEnv(object): + """Container that represents the state of all U-Boot environment variables. + This enables quick determination of existant/non-existant variable + names. + """ + + def __init__(self, u_boot_console): + """Initialize a new StateTestEnv object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + """ + + self.u_boot_console = u_boot_console + self.get_env() + self.set_var = self.get_non_existent_var() + + def get_env(self): + """Read all current environment variables from U-Boot. + + Args: + None. + + Returns: + Nothing. + """ + + if self.u_boot_console.config.buildconfig.get( + 'config_version_variable', 'n') == 'y': + with self.u_boot_console.disable_check('main_signon'): + response = self.u_boot_console.run_command('printenv') + else: + response = self.u_boot_console.run_command('printenv') + self.env = {} + for l in response.splitlines(): + if not '=' in l: + continue + (var, value) = l.split('=', 1) + self.env[var] = value + + def get_existent_var(self): + """Return the name of an environment variable that exists. + + Args: + None. + + Returns: + The name of an environment variable. + """ + + for var in self.env: + return var + + def get_non_existent_var(self): + """Return the name of an environment variable that does not exist. + + Args: + None. + + Returns: + The name of an environment variable. + """ + + n = 0 + while True: + var = 'test_env_' + str(n) + if var not in self.env: + return var + n += 1 + +ste = None +@pytest.fixture(scope='function') +def state_test_env(u_boot_console): + """pytest fixture to provide a StateTestEnv object to tests.""" + + global ste + if not ste: + ste = StateTestEnv(u_boot_console) + return ste + +def unset_var(state_test_env, var): + """Unset an environment variable. + + This both executes a U-Boot shell command and updates a StateTestEnv + object. + + Args: + state_test_env: The StateTestEnv object to update. + var: The variable name to unset. + + Returns: + Nothing. + """ + + state_test_env.u_boot_console.run_command('setenv %s' % var) + if var in state_test_env.env: + del state_test_env.env[var] + +def set_var(state_test_env, var, value): + """Set an environment variable. + + This both executes a U-Boot shell command and updates a StateTestEnv + object. + + Args: + state_test_env: The StateTestEnv object to update. + var: The variable name to set. + value: The value to set the variable to. + + Returns: + Nothing. + """ + + bc = state_test_env.u_boot_console.config.buildconfig + if bc.get('config_hush_parser', None): + quote = '"' + else: + quote = '' + if ' ' in value: + pytest.skip('Space in variable value on non-Hush shell') + + state_test_env.u_boot_console.run_command( + 'setenv %s %s%s%s' % (var, quote, value, quote)) + state_test_env.env[var] = value + +def validate_empty(state_test_env, var): + """Validate that a variable is not set, using U-Boot shell commands. + + Args: + var: The variable name to test. + + Returns: + Nothing. + """ + + response = state_test_env.u_boot_console.run_command('echo ${%s}' % var) + assert response == '' + +def validate_set(state_test_env, var, value): + """Validate that a variable is set, using U-Boot shell commands. + + Args: + var: The variable name to test. + value: The value the variable is expected to have. + + Returns: + Nothing. + """ + + # echo does not preserve leading, internal, or trailing whitespace in the + # value. printenv does, and hence allows more complete testing. + response = state_test_env.u_boot_console.run_command('printenv %s' % var) + assert response == ('%s=%s' % (var, value)) + +def test_env_echo_exists(state_test_env): + """Test echoing a variable that exists.""" + + var = state_test_env.get_existent_var() + value = state_test_env.env[var] + validate_set(state_test_env, var, value) + +@pytest.mark.buildconfigspec('cmd_echo') +def test_env_echo_non_existent(state_test_env): + """Test echoing a variable that doesn't exist.""" + + var = state_test_env.set_var + validate_empty(state_test_env, var) + +def test_env_printenv_non_existent(state_test_env): + """Test printenv error message for non-existant variables.""" + + var = state_test_env.set_var + c = state_test_env.u_boot_console + with c.disable_check('error_notification'): + response = c.run_command('printenv %s' % var) + assert(response == '## Error: "%s" not defined' % var) + +@pytest.mark.buildconfigspec('cmd_echo') +def test_env_unset_non_existent(state_test_env): + """Test unsetting a nonexistent variable.""" + + var = state_test_env.get_non_existent_var() + unset_var(state_test_env, var) + validate_empty(state_test_env, var) + +def test_env_set_non_existent(state_test_env): + """Test set a non-existant variable.""" + + var = state_test_env.set_var + value = 'foo' + set_var(state_test_env, var, value) + validate_set(state_test_env, var, value) + +def test_env_set_existing(state_test_env): + """Test setting an existant variable.""" + + var = state_test_env.set_var + value = 'bar' + set_var(state_test_env, var, value) + validate_set(state_test_env, var, value) + +@pytest.mark.buildconfigspec('cmd_echo') +def test_env_unset_existing(state_test_env): + """Test unsetting a variable.""" + + var = state_test_env.set_var + unset_var(state_test_env, var) + validate_empty(state_test_env, var) + +def test_env_expansion_spaces(state_test_env): + """Test expanding a variable that contains a space in its value.""" + + var_space = None + var_test = None + try: + var_space = state_test_env.get_non_existent_var() + set_var(state_test_env, var_space, ' ') + + var_test = state_test_env.get_non_existent_var() + value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals() + set_var(state_test_env, var_test, value) + value = ' 1 2 ' + validate_set(state_test_env, var_test, value) + finally: + if var_space: + unset_var(state_test_env, var_space) + if var_test: + unset_var(state_test_env, var_test) + +@pytest.mark.buildconfigspec('cmd_importenv') +def test_env_import_checksum_no_size(state_test_env): + """Test that omitted ('-') size parameter with checksum validation fails the + env import function. + """ + c = state_test_env.u_boot_console + ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console) + addr = '%08x' % ram_base + + with c.disable_check('error_notification'): + response = c.run_command('env import -c %s -' % addr) + assert(response == '## Error: external checksum format must pass size') + +@pytest.mark.buildconfigspec('cmd_importenv') +def test_env_import_whitelist_checksum_no_size(state_test_env): + """Test that omitted ('-') size parameter with checksum validation fails the + env import function when variables are passed as parameters. + """ + c = state_test_env.u_boot_console + ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console) + addr = '%08x' % ram_base + + with c.disable_check('error_notification'): + response = c.run_command('env import -c %s - foo1 foo2 foo4' % addr) + assert(response == '## Error: external checksum format must pass size') + +@pytest.mark.buildconfigspec('cmd_exportenv') +@pytest.mark.buildconfigspec('cmd_importenv') +def test_env_import_whitelist(state_test_env): + """Test importing only a handful of env variables from an environment.""" + c = state_test_env.u_boot_console + ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console) + addr = '%08x' % ram_base + + set_var(state_test_env, 'foo1', 'bar1') + set_var(state_test_env, 'foo2', 'bar2') + set_var(state_test_env, 'foo3', 'bar3') + + c.run_command('env export %s' % addr) + + unset_var(state_test_env, 'foo1') + set_var(state_test_env, 'foo2', 'test2') + set_var(state_test_env, 'foo4', 'bar4') + + # no foo1 in current env, foo2 overridden, foo3 should be of the value + # before exporting and foo4 should be of the value before importing. + c.run_command('env import %s - foo1 foo2 foo4' % addr) + + validate_set(state_test_env, 'foo1', 'bar1') + validate_set(state_test_env, 'foo2', 'bar2') + validate_set(state_test_env, 'foo3', 'bar3') + validate_set(state_test_env, 'foo4', 'bar4') + + # Cleanup test environment + unset_var(state_test_env, 'foo1') + unset_var(state_test_env, 'foo2') + unset_var(state_test_env, 'foo3') + unset_var(state_test_env, 'foo4') + +@pytest.mark.buildconfigspec('cmd_exportenv') +@pytest.mark.buildconfigspec('cmd_importenv') +def test_env_import_whitelist_delete(state_test_env): + + """Test importing only a handful of env variables from an environment, with. + deletion if a var A that is passed to env import is not in the + environment to be imported. + """ + c = state_test_env.u_boot_console + ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console) + addr = '%08x' % ram_base + + set_var(state_test_env, 'foo1', 'bar1') + set_var(state_test_env, 'foo2', 'bar2') + set_var(state_test_env, 'foo3', 'bar3') + + c.run_command('env export %s' % addr) + + unset_var(state_test_env, 'foo1') + set_var(state_test_env, 'foo2', 'test2') + set_var(state_test_env, 'foo4', 'bar4') + + # no foo1 in current env, foo2 overridden, foo3 should be of the value + # before exporting and foo4 should be empty. + c.run_command('env import -d %s - foo1 foo2 foo4' % addr) + + validate_set(state_test_env, 'foo1', 'bar1') + validate_set(state_test_env, 'foo2', 'bar2') + validate_set(state_test_env, 'foo3', 'bar3') + validate_empty(state_test_env, 'foo4') + + # Cleanup test environment + unset_var(state_test_env, 'foo1') + unset_var(state_test_env, 'foo2') + unset_var(state_test_env, 'foo3') + unset_var(state_test_env, 'foo4') + +@pytest.mark.buildconfigspec('cmd_nvedit_info') +def test_env_info(state_test_env): + + """Test 'env info' command with all possible options. + """ + c = state_test_env.u_boot_console + + response = c.run_command('env info') + nb_line = 0 + for l in response.split('\n'): + if 'env_valid = ' in l: + assert '= invalid' in l or '= valid' in l or '= redundant' in l + nb_line += 1 + elif 'env_ready =' in l or 'env_use_default =' in l: + assert '= true' in l or '= false' in l + nb_line += 1 + else: + assert true + assert nb_line == 3 + + response = c.run_command('env info -p -d') + assert 'Default environment is used' in response or "Environment was loaded from persistent storage" in response + assert 'Environment can be persisted' in response or "Environment cannot be persisted" in response + + response = c.run_command('env info -p -d -q') + assert response == "" + + response = c.run_command('env info -p -q') + assert response == "" + + response = c.run_command('env info -d -q') + assert response == "" + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_nvedit_info') +@pytest.mark.buildconfigspec('cmd_echo') +def test_env_info_sandbox(state_test_env): + """Test 'env info' command result with several options on sandbox + with a known ENV configuration: ready & default & persistent + """ + c = state_test_env.u_boot_console + + response = c.run_command('env info') + assert 'env_ready = true' in response + assert 'env_use_default = true' in response + + response = c.run_command('env info -p -d') + assert 'Default environment is used' in response + assert 'Environment cannot be persisted' in response + + response = c.run_command('env info -d -q') + response = c.run_command('echo $?') + assert response == "0" + + response = c.run_command('env info -p -q') + response = c.run_command('echo $?') + assert response == "1" + + response = c.run_command('env info -d -p -q') + response = c.run_command('echo $?') + assert response == "1" + +def mk_env_ext4(state_test_env): + + """Create a empty ext4 file system volume.""" + c = state_test_env.u_boot_console + filename = 'env.ext4.img' + persistent = c.config.persistent_data_dir + '/' + filename + fs_img = c.config.result_dir + '/' + filename + + if os.path.exists(persistent): + c.log.action('Disk image file ' + persistent + ' already exists') + else: + # Some distributions do not add /sbin to the default PATH, where mkfs.ext4 lives + os.environ["PATH"] += os.pathsep + '/sbin' + try: + u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent) + u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent) + sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent) + if 'metadata_csum' in sb_content: + u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent) + except CalledProcessError: + call('rm -f %s' % persistent, shell=True) + raise + + u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img]) + return fs_img + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('cmd_nvedit_info') +@pytest.mark.buildconfigspec('cmd_nvedit_load') +@pytest.mark.buildconfigspec('cmd_nvedit_select') +@pytest.mark.buildconfigspec('env_is_in_ext4') +def test_env_ext4(state_test_env): + + """Test ENV in EXT4 on sandbox.""" + c = state_test_env.u_boot_console + fs_img = '' + try: + fs_img = mk_env_ext4(state_test_env) + + c.run_command('host bind 0 %s' % fs_img) + + response = c.run_command('ext4ls host 0:0') + assert 'uboot.env' not in response + + # force env location: EXT4 (prio 1 in sandbox) + response = c.run_command('env select EXT4') + assert 'Select Environment on EXT4: OK' in response + + response = c.run_command('env save') + assert 'Saving Environment to EXT4' in response + + response = c.run_command('env load') + assert 'Loading Environment from EXT4... OK' in response + + response = c.run_command('ext4ls host 0:0') + assert '8192 uboot.env' in response + + response = c.run_command('env info') + assert 'env_valid = valid' in response + assert 'env_ready = true' in response + assert 'env_use_default = false' in response + + response = c.run_command('env info -p -d') + assert 'Environment was loaded from persistent storage' in response + assert 'Environment can be persisted' in response + + response = c.run_command('env info -d -q') + assert response == "" + response = c.run_command('echo $?') + assert response == "1" + + response = c.run_command('env info -p -q') + assert response == "" + response = c.run_command('echo $?') + assert response == "0" + + response = c.run_command('env erase') + assert 'OK' in response + + response = c.run_command('env load') + assert 'Loading Environment from EXT4... ' in response + assert 'bad CRC, using default environment' in response + + response = c.run_command('env info') + assert 'env_valid = invalid' in response + assert 'env_ready = true' in response + assert 'env_use_default = true' in response + + response = c.run_command('env info -p -d') + assert 'Default environment is used' in response + assert 'Environment can be persisted' in response + + # restore env location: NOWHERE (prio 0 in sandbox) + response = c.run_command('env select nowhere') + assert 'Select Environment on nowhere: OK' in response + + response = c.run_command('env load') + assert 'Loading Environment from nowhere... OK' in response + + response = c.run_command('env info') + assert 'env_valid = invalid' in response + assert 'env_ready = true' in response + assert 'env_use_default = true' in response + + response = c.run_command('env info -p -d') + assert 'Default environment is used' in response + assert 'Environment cannot be persisted' in response + + finally: + if fs_img: + call('rm -f %s' % fs_img, shell=True) diff --git a/roms/u-boot/test/py/tests/test_extension.py b/roms/u-boot/test/py/tests/test_extension.py new file mode 100644 index 000000000..267cf2ff2 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_extension.py @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020 +# Author: Kory Maincent <kory.maincent@bootlin.com> + +# Test U-Boot's "extension" commands. + +import os +import pytest +import u_boot_utils + +overlay_addr = 0x1000 + +SANDBOX_DTB='arch/sandbox/dts/sandbox.dtb' +OVERLAY_DIR='arch/sandbox/dts/' + +def load_dtb(u_boot_console): + u_boot_console.log.action('Loading devicetree to RAM...') + u_boot_console.run_command('host load hostfs - $fdt_addr_r %s' % (os.path.join(u_boot_console.config.build_dir, SANDBOX_DTB))) + u_boot_console.run_command('fdt addr $fdt_addr_r') + +@pytest.mark.buildconfigspec('cmd_fdt') +@pytest.mark.boardspec('sandbox') +def test_extension(u_boot_console): + """Test the 'extension' command.""" + + load_dtb(u_boot_console) + + output = u_boot_console.run_command('extension list') + assert('No extension' in output) + + output = u_boot_console.run_command('extension scan') + assert output == 'Found 2 extension board(s).' + + output = u_boot_console.run_command('extension list') + assert('overlay0.dtbo' in output) + assert('overlay1.dtbo' in output) + + u_boot_console.run_command_list([ + 'setenv extension_overlay_addr %s' % (overlay_addr), + 'setenv extension_overlay_cmd \'host load hostfs - ${extension_overlay_addr} %s${extension_overlay_name}\'' % (os.path.join(u_boot_console.config.build_dir, OVERLAY_DIR))]) + + output = u_boot_console.run_command('extension apply 0') + assert('bytes read' in output) + + output = u_boot_console.run_command('fdt print') + assert('button3' in output) + + output = u_boot_console.run_command('extension apply all') + assert('bytes read' in output) + + output = u_boot_console.run_command('fdt print') + assert('button4' in output) + diff --git a/roms/u-boot/test/py/tests/test_fit.py b/roms/u-boot/test/py/tests/test_fit.py new file mode 100755 index 000000000..6d5b43c3b --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fit.py @@ -0,0 +1,460 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013, Google Inc. +# +# Sanity check of the FIT handling in U-Boot + +import os +import pytest +import struct +import u_boot_utils as util + +# Define a base ITS which we can adjust using % and a dictionary +base_its = ''' +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel-1 { + data = /incbin/("%(kernel)s"); + type = "kernel"; + arch = "sandbox"; + os = "linux"; + compression = "%(compression)s"; + load = <0x40000>; + entry = <0x8>; + }; + kernel-2 { + data = /incbin/("%(loadables1)s"); + type = "kernel"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + %(loadables1_load)s + entry = <0x0>; + }; + fdt-1 { + description = "snow"; + data = /incbin/("%(fdt)s"); + type = "flat_dt"; + arch = "sandbox"; + %(fdt_load)s + compression = "%(compression)s"; + signature-1 { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + ramdisk-1 { + description = "snow"; + data = /incbin/("%(ramdisk)s"); + type = "ramdisk"; + arch = "sandbox"; + os = "linux"; + %(ramdisk_load)s + compression = "%(compression)s"; + }; + ramdisk-2 { + description = "snow"; + data = /incbin/("%(loadables2)s"); + type = "ramdisk"; + arch = "sandbox"; + os = "linux"; + %(loadables2_load)s + compression = "none"; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel-1"; + fdt = "fdt-1"; + %(ramdisk_config)s + %(loadables_config)s + }; + }; +}; +''' + +# Define a base FDT - currently we don't use anything in this +base_fdt = ''' +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <0>; + + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + reset@0 { + compatible = "sandbox,reset"; + reg = <0>; + }; +}; +''' + +# This is the U-Boot script that is run for each test. First load the FIT, +# then run the 'bootm' command, then save out memory from the places where +# we expect 'bootm' to write things. Then quit. +base_script = ''' +host load hostfs 0 %(fit_addr)x %(fit)s +fdt addr %(fit_addr)x +bootm start %(fit_addr)x +bootm loados +host save hostfs 0 %(kernel_addr)x %(kernel_out)s %(kernel_size)x +host save hostfs 0 %(fdt_addr)x %(fdt_out)s %(fdt_size)x +host save hostfs 0 %(ramdisk_addr)x %(ramdisk_out)s %(ramdisk_size)x +host save hostfs 0 %(loadables1_addr)x %(loadables1_out)s %(loadables1_size)x +host save hostfs 0 %(loadables2_addr)x %(loadables2_out)s %(loadables2_size)x +''' + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('fit_signature') +@pytest.mark.requiredtool('dtc') +def test_fit(u_boot_console): + def make_fname(leaf): + """Make a temporary filename + + Args: + leaf: Leaf name of file to create (within temporary directory) + Return: + Temporary filename + """ + + return os.path.join(cons.config.build_dir, leaf) + + def filesize(fname): + """Get the size of a file + + Args: + fname: Filename to check + Return: + Size of file in bytes + """ + return os.stat(fname).st_size + + def read_file(fname): + """Read the contents of a file + + Args: + fname: Filename to read + Returns: + Contents of file as a string + """ + with open(fname, 'rb') as fd: + return fd.read() + + def make_dtb(): + """Make a sample .dts file and compile it to a .dtb + + Returns: + Filename of .dtb file created + """ + src = make_fname('u-boot.dts') + dtb = make_fname('u-boot.dtb') + with open(src, 'w') as fd: + fd.write(base_fdt) + util.run_and_log(cons, ['dtc', src, '-O', 'dtb', '-o', dtb]) + return dtb + + def make_its(params): + """Make a sample .its file with parameters embedded + + Args: + params: Dictionary containing parameters to embed in the %() strings + Returns: + Filename of .its file created + """ + its = make_fname('test.its') + with open(its, 'w') as fd: + print(base_its % params, file=fd) + return its + + def make_fit(mkimage, params): + """Make a sample .fit file ready for loading + + This creates a .its script with the selected parameters and uses mkimage to + turn this into a .fit image. + + Args: + mkimage: Filename of 'mkimage' utility + params: Dictionary containing parameters to embed in the %() strings + Return: + Filename of .fit file created + """ + fit = make_fname('test.fit') + its = make_its(params) + util.run_and_log(cons, [mkimage, '-f', its, fit]) + with open(make_fname('u-boot.dts'), 'w') as fd: + fd.write(base_fdt) + return fit + + def make_kernel(filename, text): + """Make a sample kernel with test data + + Args: + filename: the name of the file you want to create + Returns: + Full path and filename of the kernel it created + """ + fname = make_fname(filename) + data = '' + for i in range(100): + data += 'this %s %d is unlikely to boot\n' % (text, i) + with open(fname, 'w') as fd: + print(data, file=fd) + return fname + + def make_ramdisk(filename, text): + """Make a sample ramdisk with test data + + Returns: + Filename of ramdisk created + """ + fname = make_fname(filename) + data = '' + for i in range(100): + data += '%s %d was seldom used in the middle ages\n' % (text, i) + with open(fname, 'w') as fd: + print(data, file=fd) + return fname + + def make_compressed(filename): + util.run_and_log(cons, ['gzip', '-f', '-k', filename]) + return filename + '.gz' + + def find_matching(text, match): + """Find a match in a line of text, and return the unmatched line portion + + This is used to extract a part of a line from some text. The match string + is used to locate the line - we use the first line that contains that + match text. + + Once we find a match, we discard the match string itself from the line, + and return what remains. + + TODO: If this function becomes more generally useful, we could change it + to use regex and return groups. + + Args: + text: Text to check (list of strings, one for each command issued) + match: String to search for + Return: + String containing unmatched portion of line + Exceptions: + ValueError: If match is not found + + >>> find_matching(['first line:10', 'second_line:20'], 'first line:') + '10' + >>> find_matching(['first line:10', 'second_line:20'], 'second line') + Traceback (most recent call last): + ... + ValueError: Test aborted + >>> find_matching('first line:10\', 'second_line:20'], 'second_line:') + '20' + >>> find_matching('first line:10\', 'second_line:20\nthird_line:30'], + 'third_line:') + '30' + """ + __tracebackhide__ = True + for line in '\n'.join(text).splitlines(): + pos = line.find(match) + if pos != -1: + return line[:pos] + line[pos + len(match):] + + pytest.fail("Expected '%s' but not found in output") + + def check_equal(expected_fname, actual_fname, failure_msg): + """Check that a file matches its expected contents + + This is always used on out-buffers whose size is decided by the test + script anyway, which in some cases may be larger than what we're + actually looking for. So it's safe to truncate it to the size of the + expected data. + + Args: + expected_fname: Filename containing expected contents + actual_fname: Filename containing actual contents + failure_msg: Message to print on failure + """ + expected_data = read_file(expected_fname) + actual_data = read_file(actual_fname) + if len(expected_data) < len(actual_data): + actual_data = actual_data[:len(expected_data)] + assert expected_data == actual_data, failure_msg + + def check_not_equal(expected_fname, actual_fname, failure_msg): + """Check that a file does not match its expected contents + + Args: + expected_fname: Filename containing expected contents + actual_fname: Filename containing actual contents + failure_msg: Message to print on failure + """ + expected_data = read_file(expected_fname) + actual_data = read_file(actual_fname) + assert expected_data != actual_data, failure_msg + + def run_fit_test(mkimage): + """Basic sanity check of FIT loading in U-Boot + + TODO: Almost everything: + - hash algorithms - invalid hash/contents should be detected + - signature algorithms - invalid sig/contents should be detected + - compression + - checking that errors are detected like: + - image overwriting + - missing images + - invalid configurations + - incorrect os/arch/type fields + - empty data + - images too large/small + - invalid FDT (e.g. putting a random binary in instead) + - default configuration selection + - bootm command line parameters should have desired effect + - run code coverage to make sure we are testing all the code + """ + # Set up invariant files + control_dtb = make_dtb() + kernel = make_kernel('test-kernel.bin', 'kernel') + ramdisk = make_ramdisk('test-ramdisk.bin', 'ramdisk') + loadables1 = make_kernel('test-loadables1.bin', 'lenrek') + loadables2 = make_ramdisk('test-loadables2.bin', 'ksidmar') + kernel_out = make_fname('kernel-out.bin') + fdt = make_fname('u-boot.dtb') + fdt_out = make_fname('fdt-out.dtb') + ramdisk_out = make_fname('ramdisk-out.bin') + loadables1_out = make_fname('loadables1-out.bin') + loadables2_out = make_fname('loadables2-out.bin') + + # Set up basic parameters with default values + params = { + 'fit_addr' : 0x1000, + + 'kernel' : kernel, + 'kernel_out' : kernel_out, + 'kernel_addr' : 0x40000, + 'kernel_size' : filesize(kernel), + + 'fdt' : fdt, + 'fdt_out' : fdt_out, + 'fdt_addr' : 0x80000, + 'fdt_size' : filesize(control_dtb), + 'fdt_load' : '', + + 'ramdisk' : ramdisk, + 'ramdisk_out' : ramdisk_out, + 'ramdisk_addr' : 0xc0000, + 'ramdisk_size' : filesize(ramdisk), + 'ramdisk_load' : '', + 'ramdisk_config' : '', + + 'loadables1' : loadables1, + 'loadables1_out' : loadables1_out, + 'loadables1_addr' : 0x100000, + 'loadables1_size' : filesize(loadables1), + 'loadables1_load' : '', + + 'loadables2' : loadables2, + 'loadables2_out' : loadables2_out, + 'loadables2_addr' : 0x140000, + 'loadables2_size' : filesize(loadables2), + 'loadables2_load' : '', + + 'loadables_config' : '', + 'compression' : 'none', + } + + # Make a basic FIT and a script to load it + fit = make_fit(mkimage, params) + params['fit'] = fit + cmd = base_script % params + + # First check that we can load a kernel + # We could perhaps reduce duplication with some loss of readability + cons.config.dtb = control_dtb + cons.restart_uboot() + with cons.log.section('Kernel load'): + output = cons.run_command_list(cmd.splitlines()) + check_equal(kernel, kernel_out, 'Kernel not loaded') + check_not_equal(control_dtb, fdt_out, + 'FDT loaded but should be ignored') + check_not_equal(ramdisk, ramdisk_out, + 'Ramdisk loaded but should not be') + + # Find out the offset in the FIT where U-Boot has found the FDT + line = find_matching(output, 'Booting using the fdt blob at ') + fit_offset = int(line, 16) - params['fit_addr'] + fdt_magic = struct.pack('>L', 0xd00dfeed) + data = read_file(fit) + + # Now find where it actually is in the FIT (skip the first word) + real_fit_offset = data.find(fdt_magic, 4) + assert fit_offset == real_fit_offset, ( + 'U-Boot loaded FDT from offset %#x, FDT is actually at %#x' % + (fit_offset, real_fit_offset)) + + # Now a kernel and an FDT + with cons.log.section('Kernel + FDT load'): + params['fdt_load'] = 'load = <%#x>;' % params['fdt_addr'] + fit = make_fit(mkimage, params) + cons.restart_uboot() + output = cons.run_command_list(cmd.splitlines()) + check_equal(kernel, kernel_out, 'Kernel not loaded') + check_equal(control_dtb, fdt_out, 'FDT not loaded') + check_not_equal(ramdisk, ramdisk_out, + 'Ramdisk loaded but should not be') + + # Try a ramdisk + with cons.log.section('Kernel + FDT + Ramdisk load'): + params['ramdisk_config'] = 'ramdisk = "ramdisk-1";' + params['ramdisk_load'] = 'load = <%#x>;' % params['ramdisk_addr'] + fit = make_fit(mkimage, params) + cons.restart_uboot() + output = cons.run_command_list(cmd.splitlines()) + check_equal(ramdisk, ramdisk_out, 'Ramdisk not loaded') + + # Configuration with some Loadables + with cons.log.section('Kernel + FDT + Ramdisk load + Loadables'): + params['loadables_config'] = 'loadables = "kernel-2", "ramdisk-2";' + params['loadables1_load'] = ('load = <%#x>;' % + params['loadables1_addr']) + params['loadables2_load'] = ('load = <%#x>;' % + params['loadables2_addr']) + fit = make_fit(mkimage, params) + cons.restart_uboot() + output = cons.run_command_list(cmd.splitlines()) + check_equal(loadables1, loadables1_out, + 'Loadables1 (kernel) not loaded') + check_equal(loadables2, loadables2_out, + 'Loadables2 (ramdisk) not loaded') + + # Kernel, FDT and Ramdisk all compressed + with cons.log.section('(Kernel + FDT + Ramdisk) compressed'): + params['compression'] = 'gzip' + params['kernel'] = make_compressed(kernel) + params['fdt'] = make_compressed(fdt) + params['ramdisk'] = make_compressed(ramdisk) + fit = make_fit(mkimage, params) + cons.restart_uboot() + output = cons.run_command_list(cmd.splitlines()) + check_equal(kernel, kernel_out, 'Kernel not loaded') + check_equal(control_dtb, fdt_out, 'FDT not loaded') + check_not_equal(ramdisk, ramdisk_out, 'Ramdisk got decompressed?') + check_equal(ramdisk + '.gz', ramdisk_out, 'Ramdist not loaded') + + + cons = u_boot_console + try: + # We need to use our own device tree file. Remember to restore it + # afterwards. + old_dtb = cons.config.dtb + mkimage = cons.config.build_dir + '/tools/mkimage' + run_fit_test(mkimage) + finally: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot() diff --git a/roms/u-boot/test/py/tests/test_fit_ecdsa.py b/roms/u-boot/test/py/tests/test_fit_ecdsa.py new file mode 100644 index 000000000..87b608122 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fit_ecdsa.py @@ -0,0 +1,111 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2020,2021 Alexandru Gagniuc <mr.nuke.me@gmail.com> + +""" +Test ECDSA signing of FIT images + +This test uses mkimage to sign an existing FIT image with an ECDSA key. The +signature is then extracted, and verified against pyCryptodome. +This test doesn't run the sandbox. It only checks the host tool 'mkimage' +""" + +import pytest +import u_boot_utils as util +from Cryptodome.Hash import SHA256 +from Cryptodome.PublicKey import ECC +from Cryptodome.Signature import DSS + +class SignableFitImage(object): + """ Helper to manipulate a FIT image on disk """ + def __init__(self, cons, file_name): + self.fit = file_name + self.cons = cons + self.signable_nodes = set() + + def __fdt_list(self, path): + return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}') + + def __fdt_set(self, node, **prop_value): + for prop, value in prop_value.items(): + util.run_and_log(self.cons, f'fdtput -ts {self.fit} {node} {prop} {value}') + + def __fdt_get_binary(self, node, prop): + numbers = util.run_and_log(self.cons, f'fdtget -tbi {self.fit} {node} {prop}') + + bignum = bytearray() + for little_num in numbers.split(): + bignum.append(int(little_num)) + + return bignum + + def find_signable_image_nodes(self): + for node in self.__fdt_list('/images').split(): + image = f'/images/{node}' + if 'signature' in self.__fdt_list(image): + self.signable_nodes.add(image) + + return self.signable_nodes + + def change_signature_algo_to_ecdsa(self): + for image in self.signable_nodes: + self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256') + + def sign(self, mkimage, key_file): + util.run_and_log(self.cons, [mkimage, '-F', self.fit, f'-G{key_file}']) + + def check_signatures(self, key): + for image in self.signable_nodes: + raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value') + raw_bin = self.__fdt_get_binary(image, 'data') + + sha = SHA256.new(raw_bin) + verifier = DSS.new(key, 'fips-186-3') + verifier.verify(sha, bytes(raw_sig)) + + +@pytest.mark.buildconfigspec('fit_signature') +@pytest.mark.requiredtool('dtc') +@pytest.mark.requiredtool('fdtget') +@pytest.mark.requiredtool('fdtput') +def test_fit_ecdsa(u_boot_console): + """ Test that signatures generated by mkimage are legible. """ + def generate_ecdsa_key(): + return ECC.generate(curve='prime256v1') + + def assemble_fit_image(dest_fit, its, destdir): + dtc_args = f'-I dts -O dtb -i {destdir}' + util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit]) + + def dtc(dts): + dtb = dts.replace('.dts', '.dtb') + util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}') + + cons = u_boot_console + mkimage = cons.config.build_dir + '/tools/mkimage' + datadir = cons.config.source_dir + '/test/py/tests/vboot/' + tempdir = cons.config.result_dir + key_file = f'{tempdir}/ecdsa-test-key.pem' + fit_file = f'{tempdir}/test.fit' + dtc('sandbox-kernel.dts') + + key = generate_ecdsa_key() + + # Create a fake kernel image -- zeroes will do just fine + with open(f'{tempdir}/test-kernel.bin', 'w') as fd: + fd.write(500 * chr(0)) + + # invocations of mkimage expect to read the key from disk + with open(key_file, 'w') as f: + f.write(key.export_key(format='PEM')) + + assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir) + + fit = SignableFitImage(cons, fit_file) + nodes = fit.find_signable_image_nodes() + if len(nodes) == 0: + raise ValueError('FIT image has no "/image" nodes with "signature"') + + fit.change_signature_algo_to_ecdsa() + fit.sign(mkimage, key_file) + fit.check_signatures(key) diff --git a/roms/u-boot/test/py/tests/test_fpga.py b/roms/u-boot/test/py/tests/test_fpga.py new file mode 100644 index 000000000..ca7ef8ea4 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fpga.py @@ -0,0 +1,565 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2018, Xilinx Inc. +# +# Michal Simek +# Siva Durga Prasad Paladugu + +import pytest +import re +import random +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +the network available and files to be used for testing. Without this, this test +will be automatically skipped. + +For example: + +# True if a DHCP server is attached to the network, and should be tested. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. In this test case we atleast need serverip for performing tftpb +# to get required files. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding the files that may be read from a TFTP server. . +env__fpga_secure_readable_file = { + 'fn': 'auth_bhdr_ppk1_bit.bin', + 'enckupfn': 'auth_bhdr_enc_kup_load_bit.bin', + 'addr': 0x1000000, + 'keyaddr': 0x100000, + 'keyfn': 'key.txt', +} + +env__fpga_under_test = { + 'dev': 0, + 'addr' : 0x1000000, + 'bitstream_load': 'compress.bin', + 'bitstream_load_size': 1831960, + 'bitstream_loadp': 'compress_pr.bin', + 'bitstream_loadp_size': 423352, + 'bitstream_loadb': 'compress.bit', + 'bitstream_loadb_size': 1832086, + 'bitstream_loadbp': 'compress_pr.bit', + 'bitstream_loadbp_size': 423491, + 'mkimage_legacy': 'download.ub', + 'mkimage_legacy_size': 13321468, + 'mkimage_legacy_gz': 'download.gz.ub', + 'mkimage_legacy_gz_size': 53632, + 'mkimage_fit': 'download-fit.ub', + 'mkimage_fit_size': 13322784, + 'loadfs': 'mmc 0 compress.bin', + 'loadfs_size': 1831960, + 'loadfs_block_size': 0x10000, +} +""" + +import test_net + +def check_dev(u_boot_console): + f = u_boot_console.config.env.get('env__fpga_under_test', None) + if not f: + pytest.skip('No FPGA to test') + + dev = f.get('dev', -1) + if dev < 0: + pytest.fail('No dev specified via env__fpga_under_test') + + return dev, f + +def load_file_from_var(u_boot_console, name): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + bit = f['%s' % (name)] + bit_size = f['%s_size' % (name)] + + expected_tftp = 'Bytes transferred = %d' % bit_size + output = u_boot_console.run_command('tftpboot %x %s' % (addr, bit)) + assert expected_tftp in output + + return f, dev, addr, bit, bit_size + +###### FPGA FAIL test ###### +expected_usage = 'fpga - loadable FPGA image support' + +@pytest.mark.xfail +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_fail(u_boot_console): + # Test non valid fpga subcommand + expected = 'fpga: non existing command' + output = u_boot_console.run_command('fpga broken 0') + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_help(u_boot_console): + # Just show help + output = u_boot_console.run_command('fpga') + assert expected_usage in output + + +###### FPGA DUMP tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_dump(u_boot_console): + pytest.skip('Not implemented now') + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_dump_variable(u_boot_console): + # Same as above but via "fpga" variable + pytest.skip('Not implemented now') + +###### FPGA INFO tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_fail(u_boot_console): + # Maybe this can be skipped completely + dev, f = check_dev(u_boot_console) + + # Multiple parameters to fpga info should fail + expected = 'fpga: more parameters passed' + output = u_boot_console.run_command('fpga info 0 0') + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_list(u_boot_console): + # Maybe this can be skipped completely + dev, f = check_dev(u_boot_console) + + # Code is design in a way that if fpga dev is not passed it should + # return list of all fpga devices in the system + u_boot_console.run_command('setenv fpga') + output = u_boot_console.run_command('fpga info') + assert expected_usage not in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info(u_boot_console): + dev, f = check_dev(u_boot_console) + + output = u_boot_console.run_command('fpga info %x' % (dev)) + assert expected_usage not in output + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_info_variable(u_boot_console): + dev, f = check_dev(u_boot_console) + + # + # fpga variable is storing device number which doesn't need to be passed + # + u_boot_console.run_command('setenv fpga %x' % (dev)) + + output = u_boot_console.run_command('fpga info') + # Variable cleanup + u_boot_console.run_command('setenv fpga') + assert expected_usage not in output + +###### FPGA LOAD tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_load_fail(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + for cmd in ['dump', 'load', 'loadb']: + # missing dev parameter + expected = 'fpga: incorrect parameters passed' + output = u_boot_console.run_command('fpga %s %x $filesize' % (cmd, addr)) + #assert expected in output + assert expected_usage in output + + # more parameters - 0 at the end + expected = 'fpga: more parameters passed' + output = u_boot_console.run_command('fpga %s %x %x $filesize 0' % (cmd, dev, addr)) + #assert expected in output + assert expected_usage in output + + # 0 address + expected = 'fpga: zero fpga_data address' + output = u_boot_console.run_command('fpga %s %x 0 $filesize' % (cmd, dev)) + #assert expected in output + assert expected_usage in output + + # 0 filesize + expected = 'fpga: zero size' + output = u_boot_console.run_command('fpga %s %x %x 0' % (cmd, dev, addr)) + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_load(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadp') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadp(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + + # And load also partial bistream + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadp') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadp %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadb(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadbp') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadbp(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + + # And load also partial bistream in bit format + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadbp') + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadbp %x %x $filesize && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +###### FPGA LOADMK tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_fail(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + # load image but pass incorrect address to show error message + expected = 'Unknown image type' + output = u_boot_console.run_command('fpga loadmk %x %x' % (dev, addr + 0x10)) + assert expected in output + + # Pass more parameters then command expects - 0 at the end + output = u_boot_console.run_command('fpga loadmk %x %x 0' % (dev, addr)) + #assert expected in output + assert expected_usage in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.xfail +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable_fpga(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (addr, expected_text)) + u_boot_console.run_command('setenv fpga') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable_fpgadata(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpgadata %x' % (addr)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text)) + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +def test_fpga_loadmk_legacy_variable(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + u_boot_console.run_command('setenv fpgadata %x' % (addr)) + + # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text)) + u_boot_console.run_command('setenv fpga') + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.buildconfigspec('image_format_legacy') +@pytest.mark.buildconfigspec('gzip') +def test_fpga_loadmk_legacy_gz(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy_gz') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_external(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit_external') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable_fpga(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + # FIXME this should fail - broken support in past + u_boot_console.run_command('setenv fpga %x' % (dev)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x:fpga && echo %s' % (addr, expected_text)) + u_boot_console.run_command('setenv fpga') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable_fpgadata(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + # FIXME this should fail - broken support in past + u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text)) + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_loadmk') +@pytest.mark.buildconfigspec('fit') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadmk_fit_variable(u_boot_console): + f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit') + + u_boot_console.run_command('imi %x' % (addr)) + + u_boot_console.run_command('setenv fpga %x' % (dev)) + u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr)) + + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text)) + u_boot_console.run_command('setenv fpga') + u_boot_console.run_command('setenv fpgadata') + assert expected_text in output + +###### FPGA LOAD tests ###### + +@pytest.mark.buildconfigspec('cmd_fpga') +def test_fpga_loadfs_fail(u_boot_console): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + bit = f['loadfs'] + bit_size = f['loadfs_size'] + block_size = f['loadfs_block_size'] + + # less params - dev number removed + expected = 'fpga: incorrect parameters passed' + output = u_boot_console.run_command('fpga loadfs %x %x %x %s' % (addr, bit_size, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # one more param - 0 at the end + # This is the longest command that's why there is no message from cmd/fpga.c + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s 0' % (dev, addr, bit_size, block_size, bit)) + assert expected_usage in output + + # zero address 0 + expected = 'fpga: zero fpga_data address' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, 0, bit_size, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # bit_size 0 + expected = 'fpga: zero size' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, 0, block_size, bit)) + #assert expected in output + assert expected_usage in output + + # block size 0 + # FIXME this should pass but it failing too + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, bit_size, 0, bit)) + assert expected_usage in output + + # non existing bitstream name + expected = 'Unable to read file noname' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x mmc 0 noname' % (dev, addr, bit_size, block_size)) + assert expected in output + assert expected_usage in output + + # -1 dev number + expected = 'fpga_fsload: Invalid device number -1' + output = u_boot_console.run_command('fpga loadfs %d %x %x %x mmc 0 noname' % (-1, addr, bit_size, block_size)) + assert expected in output + assert expected_usage in output + + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_echo') +def test_fpga_loadfs(u_boot_console): + dev, f = check_dev(u_boot_console) + + addr = f.get('addr', -1) + if addr < 0: + pytest.fail('No address specified via env__fpga_under_test') + + bit = f['loadfs'] + bit_size = f['loadfs_size'] + block_size = f['loadfs_block_size'] + + # This should be done better + expected_text = 'FPGA loaded successfully' + output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s && echo %s' % (dev, addr, bit_size, block_size, bit, expected_text)) + assert expected_text in output + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_load_secure') +@pytest.mark.buildconfigspec('cmd_net') +@pytest.mark.buildconfigspec('cmd_dhcp') +@pytest.mark.buildconfigspec('net') +def test_fpga_secure_bit_auth(u_boot_console): + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + + f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file to read') + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + expected_zynqmpsecure = 'Bitstream successfully loaded' + output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 2' % (addr)) + assert expected_zynqmpsecure in output + + +@pytest.mark.buildconfigspec('cmd_fpga') +@pytest.mark.buildconfigspec('cmd_fpga_load_secure') +@pytest.mark.buildconfigspec('cmd_net') +@pytest.mark.buildconfigspec('cmd_dhcp') +@pytest.mark.buildconfigspec('net') +def test_fpga_secure_bit_img_auth_kup(u_boot_console): + + test_net.test_net_dhcp(u_boot_console) + test_net.test_net_setup_static(u_boot_console) + + f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file to read') + + keyaddr = f.get('keyaddr', None) + if not keyaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + keyfn = f['keyfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn)) + assert expected_tftp in output + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + fn = f['enckupfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + expected_zynqmpsecure = 'Bitstream successfully loaded' + output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 1 %x' % (addr, keyaddr)) + assert expected_zynqmpsecure in output diff --git a/roms/u-boot/test/py/tests/test_fs/conftest.py b/roms/u-boot/test/py/tests/test_fs/conftest.py new file mode 100644 index 000000000..7325486cd --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/conftest.py @@ -0,0 +1,662 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Linaro Limited +# Author: Takahiro Akashi <takahiro.akashi@linaro.org> + +import os +import os.path +import pytest +import re +from subprocess import call, check_call, check_output, CalledProcessError +from fstest_defs import * + +supported_fs_basic = ['fat16', 'fat32', 'ext4'] +supported_fs_ext = ['fat16', 'fat32'] +supported_fs_mkdir = ['fat16', 'fat32'] +supported_fs_unlink = ['fat16', 'fat32'] +supported_fs_symlink = ['ext4'] + +# +# Filesystem test specific setup +# +def pytest_addoption(parser): + """Enable --fs-type option. + + See pytest_configure() about how it works. + + Args: + parser: Pytest command-line parser. + + Returns: + Nothing. + """ + parser.addoption('--fs-type', action='append', default=None, + help='Targeting Filesystem Types') + +def pytest_configure(config): + """Restrict a file system(s) to be tested. + + A file system explicitly named with --fs-type option is selected + if it belongs to a default supported_fs_xxx list. + Multiple options can be specified. + + Args: + config: Pytest configuration. + + Returns: + Nothing. + """ + global supported_fs_basic + global supported_fs_ext + global supported_fs_mkdir + global supported_fs_unlink + global supported_fs_symlink + + def intersect(listA, listB): + return [x for x in listA if x in listB] + + supported_fs = config.getoption('fs_type') + if supported_fs: + print('*** FS TYPE modified: %s' % supported_fs) + supported_fs_basic = intersect(supported_fs, supported_fs_basic) + supported_fs_ext = intersect(supported_fs, supported_fs_ext) + supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir) + supported_fs_unlink = intersect(supported_fs, supported_fs_unlink) + supported_fs_symlink = intersect(supported_fs, supported_fs_symlink) + +def pytest_generate_tests(metafunc): + """Parametrize fixtures, fs_obj_xxx + + Each fixture will be parametrized with a corresponding support_fs_xxx + list. + + Args: + metafunc: Pytest test function. + + Returns: + Nothing. + """ + if 'fs_obj_basic' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_basic', supported_fs_basic, + indirect=True, scope='module') + if 'fs_obj_ext' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_ext', supported_fs_ext, + indirect=True, scope='module') + if 'fs_obj_mkdir' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir, + indirect=True, scope='module') + if 'fs_obj_unlink' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_unlink', supported_fs_unlink, + indirect=True, scope='module') + if 'fs_obj_symlink' in metafunc.fixturenames: + metafunc.parametrize('fs_obj_symlink', supported_fs_symlink, + indirect=True, scope='module') + +# +# Helper functions +# +def fstype_to_ubname(fs_type): + """Convert a file system type to an U-boot specific string + + A generated string can be used as part of file system related commands + or a config name in u-boot. Currently fat16 and fat32 are handled + specifically. + + Args: + fs_type: File system type. + + Return: + A corresponding string for file system type. + """ + if re.match('fat', fs_type): + return 'fat' + else: + return fs_type + +def check_ubconfig(config, fs_type): + """Check whether a file system is enabled in u-boot configuration. + + This function is assumed to be called in a fixture function so that + the whole test cases will be skipped if a given file system is not + enabled. + + Args: + fs_type: File system type. + + Return: + Nothing. + """ + if not config.buildconfig.get('config_cmd_%s' % fs_type, None): + pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper()) + if not config.buildconfig.get('config_%s_write' % fs_type, None): + pytest.skip('.config feature "%s_WRITE" not enabled' + % fs_type.upper()) + +def mk_fs(config, fs_type, size, id): + """Create a file system volume. + + Args: + fs_type: File system type. + size: Size of file system in MiB. + id: Prefix string of volume's file name. + + Return: + Nothing. + """ + fs_img = '%s.%s.img' % (id, fs_type) + fs_img = config.persistent_data_dir + '/' + fs_img + + if fs_type == 'fat16': + mkfs_opt = '-F 16' + elif fs_type == 'fat32': + mkfs_opt = '-F 32' + else: + mkfs_opt = '' + + if re.match('fat', fs_type): + fs_lnxtype = 'vfat' + else: + fs_lnxtype = fs_type + + count = (size + 1048576 - 1) / 1048576 + + # Some distributions do not add /sbin to the default PATH, where mkfs lives + if '/sbin' not in os.environ["PATH"].split(os.pathsep): + os.environ["PATH"] += os.pathsep + '/sbin' + + try: + check_call('rm -f %s' % fs_img, shell=True) + check_call('dd if=/dev/zero of=%s bs=1M count=%d' + % (fs_img, count), shell=True) + check_call('mkfs.%s %s %s' + % (fs_lnxtype, mkfs_opt, fs_img), shell=True) + if fs_type == 'ext4': + sb_content = check_output('tune2fs -l %s' % fs_img, shell=True).decode() + if 'metadata_csum' in sb_content: + check_call('tune2fs -O ^metadata_csum %s' % fs_img, shell=True) + return fs_img + except CalledProcessError: + call('rm -f %s' % fs_img, shell=True) + raise + +# from test/py/conftest.py +def tool_is_in_path(tool): + """Check whether a given command is available on host. + + Args: + tool: Command name. + + Return: + True if available, False if not. + """ + for path in os.environ['PATH'].split(os.pathsep): + fn = os.path.join(path, tool) + if os.path.isfile(fn) and os.access(fn, os.X_OK): + return True + return False + +fuse_mounted = False + +def mount_fs(fs_type, device, mount_point): + """Mount a volume. + + Args: + fs_type: File system type. + device: Volume's file name. + mount_point: Mount point. + + Return: + Nothing. + """ + global fuse_mounted + + fuse_mounted = False + try: + if tool_is_in_path('guestmount'): + fuse_mounted = True + check_call('guestmount -a %s -m /dev/sda %s' + % (device, mount_point), shell=True) + else: + mount_opt = 'loop,rw' + if re.match('fat', fs_type): + mount_opt += ',umask=0000' + + check_call('sudo mount -o %s %s %s' + % (mount_opt, device, mount_point), shell=True) + + # may not be effective for some file systems + check_call('sudo chmod a+rw %s' % mount_point, shell=True) + except CalledProcessError: + raise + +def umount_fs(mount_point): + """Unmount a volume. + + Args: + mount_point: Mount point. + + Return: + Nothing. + """ + if fuse_mounted: + call('sync') + call('guestunmount %s' % mount_point, shell=True) + else: + call('sudo umount %s' % mount_point, shell=True) + +# +# Fixture for basic fs test +# derived from test/fs/fs-test.sh +# +@pytest.fixture() +def fs_obj_basic(request, u_boot_config): + """Set up a file system to be used in basic fs test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for basic fs test, i.e. a triplet of file system type, + volume file name and a list of MD5 hashes. + """ + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + mount_dir = u_boot_config.persistent_data_dir + '/mnt' + + small_file = mount_dir + '/' + SMALL_FILE + big_file = mount_dir + '/' + BIG_FILE + + try: + + # 3GiB volume + fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB') + except CalledProcessError as err: + pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) + return + + try: + check_call('mkdir -p %s' % mount_dir, shell=True) + except CalledProcessError as err: + pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Mount the image so we can populate it. + mount_fs(fs_type, fs_img, mount_dir) + except CalledProcessError as err: + pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Create a subdirectory. + check_call('mkdir %s/SUBDIR' % mount_dir, shell=True) + + # Create big file in this image. + # Note that we work only on the start 1MB, couple MBs in the 2GB range + # and the last 1 MB of the huge 2.5GB file. + # So, just put random values only in those areas. + check_call('dd if=/dev/urandom of=%s bs=1M count=1' + % big_file, shell=True) + check_call('dd if=/dev/urandom of=%s bs=1M count=2 seek=2047' + % big_file, shell=True) + check_call('dd if=/dev/urandom of=%s bs=1M count=1 seek=2499' + % big_file, shell=True) + + # Create a small file in this image. + check_call('dd if=/dev/urandom of=%s bs=1M count=1' + % small_file, shell=True) + + # Delete the small file copies which possibly are written as part of a + # previous test. + # check_call('rm -f "%s.w"' % MB1, shell=True) + # check_call('rm -f "%s.w2"' % MB1, shell=True) + + # Generate the md5sums of reads that we will test against small file + out = check_output( + 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum' + % small_file, shell=True).decode() + md5val = [ out.split()[0] ] + + # Generate the md5sums of reads that we will test against big file + # One from beginning of file. + out = check_output( + 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum' + % big_file, shell=True).decode() + md5val.append(out.split()[0]) + + # One from end of file. + out = check_output( + 'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum' + % big_file, shell=True).decode() + md5val.append(out.split()[0]) + + # One from the last 1MB chunk of 2GB + out = check_output( + 'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum' + % big_file, shell=True).decode() + md5val.append(out.split()[0]) + + # One from the start 1MB chunk from 2GB + out = check_output( + 'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum' + % big_file, shell=True).decode() + md5val.append(out.split()[0]) + + # One 1MB chunk crossing the 2GB boundary + out = check_output( + 'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum' + % big_file, shell=True).decode() + md5val.append(out.split()[0]) + + except CalledProcessError as err: + pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err)) + umount_fs(mount_dir) + return + else: + umount_fs(mount_dir) + yield [fs_ubtype, fs_img, md5val] + finally: + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + +# +# Fixture for extended fs test +# +@pytest.fixture() +def fs_obj_ext(request, u_boot_config): + """Set up a file system to be used in extended fs test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for extended fs test, i.e. a triplet of file system type, + volume file name and a list of MD5 hashes. + """ + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + mount_dir = u_boot_config.persistent_data_dir + '/mnt' + + min_file = mount_dir + '/' + MIN_FILE + tmp_file = mount_dir + '/tmpfile' + + try: + + # 128MiB volume + fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB') + except CalledProcessError as err: + pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) + return + + try: + check_call('mkdir -p %s' % mount_dir, shell=True) + except CalledProcessError as err: + pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Mount the image so we can populate it. + mount_fs(fs_type, fs_img, mount_dir) + except CalledProcessError as err: + pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Create a test directory + check_call('mkdir %s/dir1' % mount_dir, shell=True) + + # Create a small file and calculate md5 + check_call('dd if=/dev/urandom of=%s bs=1K count=20' + % min_file, shell=True) + out = check_output( + 'dd if=%s bs=1K 2> /dev/null | md5sum' + % min_file, shell=True).decode() + md5val = [ out.split()[0] ] + + # Calculate md5sum of Test Case 4 + check_call('dd if=%s of=%s bs=1K count=20' + % (min_file, tmp_file), shell=True) + check_call('dd if=%s of=%s bs=1K seek=5 count=20' + % (min_file, tmp_file), shell=True) + out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum' + % tmp_file, shell=True).decode() + md5val.append(out.split()[0]) + + # Calculate md5sum of Test Case 5 + check_call('dd if=%s of=%s bs=1K count=20' + % (min_file, tmp_file), shell=True) + check_call('dd if=%s of=%s bs=1K seek=5 count=5' + % (min_file, tmp_file), shell=True) + out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum' + % tmp_file, shell=True).decode() + md5val.append(out.split()[0]) + + # Calculate md5sum of Test Case 7 + check_call('dd if=%s of=%s bs=1K count=20' + % (min_file, tmp_file), shell=True) + check_call('dd if=%s of=%s bs=1K seek=20 count=20' + % (min_file, tmp_file), shell=True) + out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum' + % tmp_file, shell=True).decode() + md5val.append(out.split()[0]) + + check_call('rm %s' % tmp_file, shell=True) + except CalledProcessError: + pytest.skip('Setup failed for filesystem: ' + fs_type) + umount_fs(mount_dir) + return + else: + umount_fs(mount_dir) + yield [fs_ubtype, fs_img, md5val] + finally: + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + +# +# Fixture for mkdir test +# +@pytest.fixture() +def fs_obj_mkdir(request, u_boot_config): + """Set up a file system to be used in mkdir test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for mkdir test, i.e. a duplet of file system type and + volume file name. + """ + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + try: + # 128MiB volume + fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB') + except: + pytest.skip('Setup failed for filesystem: ' + fs_type) + return + else: + yield [fs_ubtype, fs_img] + call('rm -f %s' % fs_img, shell=True) + +# +# Fixture for unlink test +# +@pytest.fixture() +def fs_obj_unlink(request, u_boot_config): + """Set up a file system to be used in unlink test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for unlink test, i.e. a duplet of file system type and + volume file name. + """ + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + mount_dir = u_boot_config.persistent_data_dir + '/mnt' + + try: + + # 128MiB volume + fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB') + except CalledProcessError as err: + pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) + return + + try: + check_call('mkdir -p %s' % mount_dir, shell=True) + except CalledProcessError as err: + pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Mount the image so we can populate it. + mount_fs(fs_type, fs_img, mount_dir) + except CalledProcessError as err: + pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Test Case 1 & 3 + check_call('mkdir %s/dir1' % mount_dir, shell=True) + check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1' + % mount_dir, shell=True) + check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1' + % mount_dir, shell=True) + + # Test Case 2 + check_call('mkdir %s/dir2' % mount_dir, shell=True) + for i in range(0, 20): + check_call('mkdir %s/dir2/0123456789abcdef%02x' + % (mount_dir, i), shell=True) + + # Test Case 4 + check_call('mkdir %s/dir4' % mount_dir, shell=True) + + # Test Case 5, 6 & 7 + check_call('mkdir %s/dir5' % mount_dir, shell=True) + check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1' + % mount_dir, shell=True) + + except CalledProcessError: + pytest.skip('Setup failed for filesystem: ' + fs_type) + umount_fs(mount_dir) + return + else: + umount_fs(mount_dir) + yield [fs_ubtype, fs_img] + finally: + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + +# +# Fixture for symlink fs test +# +@pytest.fixture() +def fs_obj_symlink(request, u_boot_config): + """Set up a file system to be used in symlink fs test. + + Args: + request: Pytest request object. + u_boot_config: U-boot configuration. + + Return: + A fixture for basic fs test, i.e. a triplet of file system type, + volume file name and a list of MD5 hashes. + """ + fs_type = request.param + fs_img = '' + + fs_ubtype = fstype_to_ubname(fs_type) + check_ubconfig(u_boot_config, fs_ubtype) + + mount_dir = u_boot_config.persistent_data_dir + '/mnt' + + small_file = mount_dir + '/' + SMALL_FILE + medium_file = mount_dir + '/' + MEDIUM_FILE + + try: + + # 1GiB volume + fs_img = mk_fs(u_boot_config, fs_type, 0x40000000, '1GB') + except CalledProcessError as err: + pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) + return + + try: + check_call('mkdir -p %s' % mount_dir, shell=True) + except CalledProcessError as err: + pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Mount the image so we can populate it. + mount_fs(fs_type, fs_img, mount_dir) + except CalledProcessError as err: + pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err)) + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) + return + + try: + # Create a subdirectory. + check_call('mkdir %s/SUBDIR' % mount_dir, shell=True) + + # Create a small file in this image. + check_call('dd if=/dev/urandom of=%s bs=1M count=1' + % small_file, shell=True) + + # Create a medium file in this image. + check_call('dd if=/dev/urandom of=%s bs=10M count=1' + % medium_file, shell=True) + + # Generate the md5sums of reads that we will test against small file + out = check_output( + 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum' + % small_file, shell=True).decode() + md5val = [out.split()[0]] + out = check_output( + 'dd if=%s bs=10M skip=0 count=1 2> /dev/null | md5sum' + % medium_file, shell=True).decode() + md5val.extend([out.split()[0]]) + + except CalledProcessError: + pytest.skip('Setup failed for filesystem: ' + fs_type) + umount_fs(mount_dir) + return + else: + umount_fs(mount_dir) + yield [fs_ubtype, fs_img, md5val] + finally: + call('rmdir %s' % mount_dir, shell=True) + call('rm -f %s' % fs_img, shell=True) diff --git a/roms/u-boot/test/py/tests/test_fs/fstest_defs.py b/roms/u-boot/test/py/tests/test_fs/fstest_defs.py new file mode 100644 index 000000000..35b2bb651 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/fstest_defs.py @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ + +# $MIN_FILE is the name of the 20KB file in the file system image +MIN_FILE='testfile' + +# $SMALL_FILE is the name of the 1MB file in the file system image +SMALL_FILE='1MB.file' + +# $MEDIUM_FILE is the name of the 10MB file in the file system image +MEDIUM_FILE='10MB.file' + +# $BIG_FILE is the name of the 2.5GB file in the file system image +BIG_FILE='2.5GB.file' + +ADDR=0x01000008 +LENGTH=0x00100000 diff --git a/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py b/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py new file mode 100644 index 000000000..faec29824 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Texas Instrument +# Author: JJ Hiblot <jjhiblot@ti.com> +# + +from subprocess import check_call, CalledProcessError + +def assert_fs_integrity(fs_type, fs_img): + try: + if fs_type == 'ext4': + check_call('fsck.ext4 -n -f %s' % fs_img, shell=True) + except CalledProcessError: + raise diff --git a/roms/u-boot/test/py/tests/test_fs/test_basic.py b/roms/u-boot/test/py/tests/test_fs/test_basic.py new file mode 100644 index 000000000..71f3e86fb --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_basic.py @@ -0,0 +1,292 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Linaro Limited +# Author: Takahiro Akashi <takahiro.akashi@linaro.org> +# +# U-Boot File System:Basic Test + +""" +This test verifies basic read/write operation on file system. +""" + +import pytest +import re +from fstest_defs import * +from fstest_helpers import assert_fs_integrity + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestFsBasic(object): + def test_fs1(self, u_boot_console, fs_obj_basic): + """ + Test Case 1 - ls command, listing a root directory and invalid directory + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 1a - ls'): + # Test Case 1 - ls + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sls host 0:0' % fs_type]) + assert(re.search('2621440000 *%s' % BIG_FILE, ''.join(output))) + assert(re.search('1048576 *%s' % SMALL_FILE, ''.join(output))) + + with u_boot_console.log.section('Test Case 1b - ls (invalid dir)'): + # In addition, test with a nonexistent directory to see if we crash. + output = u_boot_console.run_command( + '%sls host 0:0 invalid_d' % fs_type) + if fs_type == 'ext4': + assert('Can not find directory' in output) + else: + assert('' == output) + + def test_fs2(self, u_boot_console, fs_obj_basic): + """ + Test Case 2 - size command for a small file + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 2a - size (small)'): + # 1MB is 0x0010 0000 + # Test Case 2a - size of small file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%ssize host 0:0 /%s' % (fs_type, SMALL_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=100000' in ''.join(output)) + + with u_boot_console.log.section('Test Case 2b - size (/../<file>)'): + # Test Case 2b - size of small file via a path using '..' + output = u_boot_console.run_command_list([ + '%ssize host 0:0 /SUBDIR/../%s' % (fs_type, SMALL_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=100000' in ''.join(output)) + + def test_fs3(self, u_boot_console, fs_obj_basic): + """ + Test Case 3 - size command for a large file + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 3 - size (large)'): + # 2.5GB (1024*1024*2500) is 0x9C40 0000 + # Test Case 3 - size of big file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%ssize host 0:0 /%s' % (fs_type, BIG_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=9c400000' in ''.join(output)) + + def test_fs4(self, u_boot_console, fs_obj_basic): + """ + Test Case 4 - load a small file, 1MB + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 4 - load (small)'): + # Test Case 4a - Read full 1MB of small file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 4b - Read full 1MB of small file + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + + def test_fs5(self, u_boot_console, fs_obj_basic): + """ + Test Case 5 - load, reading first 1MB of 3GB file + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 5 - load (first 1MB)'): + # Test Case 5a - First 1MB of big file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s %x 0x0' % (fs_type, ADDR, BIG_FILE, LENGTH), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 5b - First 1MB of big file + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[1] in ''.join(output)) + + def test_fs6(self, u_boot_console, fs_obj_basic): + """ + Test Case 6 - load, reading last 1MB of 3GB file + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 6 - load (last 1MB)'): + # fails for ext as no offset support + # Test Case 6a - Last 1MB of big file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s %x 0x9c300000' + % (fs_type, ADDR, BIG_FILE, LENGTH), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 6b - Last 1MB of big file + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[2] in ''.join(output)) + + def test_fs7(self, u_boot_console, fs_obj_basic): + """ + Test Case 7 - load, 1MB from the last 1MB in 2GB + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 7 - load (last 1MB in 2GB)'): + # fails for ext as no offset support + # Test Case 7a - One from the last 1MB chunk of 2GB + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s %x 0x7ff00000' + % (fs_type, ADDR, BIG_FILE, LENGTH), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 7b - One from the last 1MB chunk of 2GB + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[3] in ''.join(output)) + + def test_fs8(self, u_boot_console, fs_obj_basic): + """ + Test Case 8 - load, reading first 1MB in 2GB + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 8 - load (first 1MB in 2GB)'): + # fails for ext as no offset support + # Test Case 8a - One from the start 1MB chunk from 2GB + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s %x 0x80000000' + % (fs_type, ADDR, BIG_FILE, LENGTH), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 8b - One from the start 1MB chunk from 2GB + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[4] in ''.join(output)) + + def test_fs9(self, u_boot_console, fs_obj_basic): + """ + Test Case 9 - load, 1MB crossing 2GB boundary + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 9 - load (crossing 2GB boundary)'): + # fails for ext as no offset support + # Test Case 9a - One 1MB chunk crossing the 2GB boundary + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s %x 0x7ff80000' + % (fs_type, ADDR, BIG_FILE, LENGTH), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 9b - One 1MB chunk crossing the 2GB boundary + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[5] in ''.join(output)) + + def test_fs10(self, u_boot_console, fs_obj_basic): + """ + Test Case 10 - load, reading beyond file end'): + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 10 - load (beyond file end)'): + # Generic failure case + # Test Case 10 - 2MB chunk from the last 1MB of big file + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s 0x00200000 0x9c300000' + % (fs_type, ADDR, BIG_FILE), + 'printenv filesize', + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert('filesize=100000' in ''.join(output)) + + def test_fs11(self, u_boot_console, fs_obj_basic): + """ + Test Case 11 - write' + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 11 - write'): + # Read 1MB from small file + # Write it back to test the writes + # Test Case 11a - Check that the write succeeded + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE), + '%swrite host 0:0 %x /%s.w $filesize' + % (fs_type, ADDR, SMALL_FILE)]) + assert('1048576 bytes written' in ''.join(output)) + + # Test Case 11b - Check md5 of written to is same + # as the one read from + output = u_boot_console.run_command_list([ + '%sload host 0:0 %x /%s.w' % (fs_type, ADDR, SMALL_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs12(self, u_boot_console, fs_obj_basic): + """ + Test Case 12 - write to "." directory + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 12 - write (".")'): + # Next test case checks writing a file whose dirent + # is the first in the block, which is always true for "." + # The write should fail, but the lookup should work + # Test Case 12 - Check directory traversal + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%swrite host 0:0 %x /. 0x10' % (fs_type, ADDR)]) + assert('Unable to write' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs13(self, u_boot_console, fs_obj_basic): + """ + Test Case 13 - write to a file with "/./<filename>" + """ + fs_type,fs_img,md5val = fs_obj_basic + with u_boot_console.log.section('Test Case 13 - write ("./<file>")'): + # Read 1MB from small file + # Write it via "same directory", i.e. "." dirent + # Test Case 13a - Check directory traversal + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE), + '%swrite host 0:0 %x /./%s2 $filesize' + % (fs_type, ADDR, SMALL_FILE)]) + assert('1048576 bytes written' in ''.join(output)) + + # Test Case 13b - Check md5 of written to is same + # as the one read from + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /./%s2' % (fs_type, ADDR, SMALL_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + + # Test Case 13c - Check md5 of written to is same + # as the one read from + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /%s2' % (fs_type, ADDR, SMALL_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) diff --git a/roms/u-boot/test/py/tests/test_fs/test_ext.py b/roms/u-boot/test/py/tests/test_fs/test_ext.py new file mode 100644 index 000000000..dba874fc5 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_ext.py @@ -0,0 +1,319 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Linaro Limited +# Author: Takahiro Akashi <takahiro.akashi@linaro.org> +# +# U-Boot File System:Exntented Test + +""" +This test verifies extended write operation on file system. +""" + +import pytest +import re +from fstest_defs import * +from fstest_helpers import assert_fs_integrity + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestFsExt(object): + def test_fs_ext1(self, u_boot_console, fs_obj_ext): + """ + Test Case 1 - write a file with absolute path + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 1 - write with abs path'): + # Test Case 1a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w1 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + assert('20480 bytes written' in ''.join(output)) + + # Test Case 1b - Check md5 of file content + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /dir1/%s.w1' % (fs_type, ADDR, MIN_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext2(self, u_boot_console, fs_obj_ext): + """ + Test Case 2 - write to a file with relative path + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 2 - write with rel path'): + # Test Case 2a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x dir1/%s.w2 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + assert('20480 bytes written' in ''.join(output)) + + # Test Case 2b - Check md5 of file content + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x dir1/%s.w2' % (fs_type, ADDR, MIN_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext3(self, u_boot_console, fs_obj_ext): + """ + Test Case 3 - write to a file with invalid path + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 3 - write with invalid path'): + # Test Case 3 - Check if command expectedly failed + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/none/%s.w3 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + assert('Unable to write file /dir1/none/' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext4(self, u_boot_console, fs_obj_ext): + """ + Test Case 4 - write at non-zero offset, enlarging file size + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 4 - write at non-zero offset, enlarging file size'): + # Test Case 4a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w4 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/%s.w4 $filesize 0x1400' + % (fs_type, ADDR, MIN_FILE)) + assert('20480 bytes written' in output) + + # Test Case 4b - Check size of written file + output = u_boot_console.run_command_list([ + '%ssize host 0:0 /dir1/%s.w4' % (fs_type, MIN_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=6400' in ''.join(output)) + + # Test Case 4c - Check md5 of file content + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /dir1/%s.w4' % (fs_type, ADDR, MIN_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[1] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext5(self, u_boot_console, fs_obj_ext): + """ + Test Case 5 - write at non-zero offset, shrinking file size + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 5 - write at non-zero offset, shrinking file size'): + # Test Case 5a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w5 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/%s.w5 0x1400 0x1400' + % (fs_type, ADDR, MIN_FILE)) + assert('5120 bytes written' in output) + + # Test Case 5b - Check size of written file + output = u_boot_console.run_command_list([ + '%ssize host 0:0 /dir1/%s.w5' % (fs_type, MIN_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=2800' in ''.join(output)) + + # Test Case 5c - Check md5 of file content + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /dir1/%s.w5' % (fs_type, ADDR, MIN_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[2] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext6(self, u_boot_console, fs_obj_ext): + """ + Test Case 6 - write nothing at the start, truncating to zero + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 6 - write nothing at the start, truncating to zero'): + # Test Case 6a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w6 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/%s.w6 0 0' + % (fs_type, ADDR, MIN_FILE)) + assert('0 bytes written' in output) + + # Test Case 6b - Check size of written file + output = u_boot_console.run_command_list([ + '%ssize host 0:0 /dir1/%s.w6' % (fs_type, MIN_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=0' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext7(self, u_boot_console, fs_obj_ext): + """ + Test Case 7 - write at the end (append) + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 7 - write at the end (append)'): + # Test Case 7a - Check if command successfully returned + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w7 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/%s.w7 $filesize $filesize' + % (fs_type, ADDR, MIN_FILE)) + assert('20480 bytes written' in output) + + # Test Case 7b - Check size of written file + output = u_boot_console.run_command_list([ + '%ssize host 0:0 /dir1/%s.w7' % (fs_type, MIN_FILE), + 'printenv filesize', + 'setenv filesize']) + assert('filesize=a000' in ''.join(output)) + + # Test Case 7c - Check md5 of file content + output = u_boot_console.run_command_list([ + 'mw.b %x 00 100' % ADDR, + '%sload host 0:0 %x /dir1/%s.w7' % (fs_type, ADDR, MIN_FILE), + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[3] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext8(self, u_boot_console, fs_obj_ext): + """ + Test Case 8 - write at offset beyond the end of file + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 8 - write beyond the end'): + # Test Case 8a - Check if command expectedly failed + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w8 $filesize' + % (fs_type, ADDR, MIN_FILE)]) + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/%s.w8 0x1400 %x' + % (fs_type, ADDR, MIN_FILE, 0x100000 + 0x1400)) + assert('Unable to write file /dir1' in output) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext9(self, u_boot_console, fs_obj_ext): + """ + Test Case 9 - write to a non-existing file at non-zero offset + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 9 - write to non-existing file with non-zero offset'): + # Test Case 9a - Check if command expectedly failed + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE), + '%swrite host 0:0 %x /dir1/%s.w9 0x1400 0x1400' + % (fs_type, ADDR, MIN_FILE)]) + assert('Unable to write file /dir1' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext10(self, u_boot_console, fs_obj_ext): + """ + 'Test Case 10 - create/delete as many directories under root directory + as amount of directory entries goes beyond one cluster size)' + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 10 - create/delete (many)'): + # Test Case 10a - Create many files + # Please note that the size of directory entry is 32 bytes. + # So one typical cluster may holds 64 (2048/32) entries. + output = u_boot_console.run_command( + 'host bind 0 %s' % fs_img) + + for i in range(0, 66): + output = u_boot_console.run_command( + '%swrite host 0:0 %x /FILE0123456789_%02x 100' + % (fs_type, ADDR, i)) + output = u_boot_console.run_command('%sls host 0:0 /' % fs_type) + assert('FILE0123456789_00' in output) + assert('FILE0123456789_41' in output) + + # Test Case 10b - Delete many files + for i in range(0, 66): + output = u_boot_console.run_command( + '%srm host 0:0 /FILE0123456789_%02x' + % (fs_type, i)) + output = u_boot_console.run_command('%sls host 0:0 /' % fs_type) + assert(not 'FILE0123456789_00' in output) + assert(not 'FILE0123456789_41' in output) + + # Test Case 10c - Create many files again + # Please note no.64 and 65 are intentionally re-created + for i in range(64, 128): + output = u_boot_console.run_command( + '%swrite host 0:0 %x /FILE0123456789_%02x 100' + % (fs_type, ADDR, i)) + output = u_boot_console.run_command('%sls host 0:0 /' % fs_type) + assert('FILE0123456789_40' in output) + assert('FILE0123456789_79' in output) + + assert_fs_integrity(fs_type, fs_img) + + def test_fs_ext11(self, u_boot_console, fs_obj_ext): + """ + 'Test Case 11 - create/delete as many directories under non-root + directory as amount of directory entries goes beyond one cluster size)' + """ + fs_type,fs_img,md5val = fs_obj_ext + with u_boot_console.log.section('Test Case 11 - create/delete (many)'): + # Test Case 11a - Create many files + # Please note that the size of directory entry is 32 bytes. + # So one typical cluster may holds 64 (2048/32) entries. + output = u_boot_console.run_command( + 'host bind 0 %s' % fs_img) + + for i in range(0, 66): + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100' + % (fs_type, ADDR, i)) + output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type) + assert('FILE0123456789_00' in output) + assert('FILE0123456789_41' in output) + + # Test Case 11b - Delete many files + for i in range(0, 66): + output = u_boot_console.run_command( + '%srm host 0:0 /dir1/FILE0123456789_%02x' + % (fs_type, i)) + output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type) + assert(not 'FILE0123456789_00' in output) + assert(not 'FILE0123456789_41' in output) + + # Test Case 11c - Create many files again + # Please note no.64 and 65 are intentionally re-created + for i in range(64, 128): + output = u_boot_console.run_command( + '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100' + % (fs_type, ADDR, i)) + output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type) + assert('FILE0123456789_40' in output) + assert('FILE0123456789_79' in output) + + assert_fs_integrity(fs_type, fs_img) diff --git a/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py b/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py new file mode 100644 index 000000000..ba39a5315 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 +# Niel Fourie, DENX Software Engineering, lusus@denx.de + +import pytest + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_dm_compat(u_boot_console): + """Test that `fstypes` prints a result which includes `sandbox`.""" + output = u_boot_console.run_command('fstypes') + assert "Supported filesystems:" in output + assert "sandbox" in output diff --git a/roms/u-boot/test/py/tests/test_fs/test_mkdir.py b/roms/u-boot/test/py/tests/test_fs/test_mkdir.py new file mode 100644 index 000000000..fa9561ec3 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_mkdir.py @@ -0,0 +1,121 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Linaro Limited +# Author: Takahiro Akashi <takahiro.akashi@linaro.org> +# +# U-Boot File System:mkdir Test + +""" +This test verifies mkdir operation on file system. +""" + +import pytest +from fstest_helpers import assert_fs_integrity + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestMkdir(object): + def test_mkdir1(self, u_boot_console, fs_obj_mkdir): + """ + Test Case 1 - create a directory under a root + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 1 - mkdir'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 dir1' % fs_type, + '%sls host 0:0 /' % fs_type]) + assert('dir1/' in ''.join(output)) + + output = u_boot_console.run_command( + '%sls host 0:0 dir1' % fs_type) + assert('./' in output) + assert('../' in output) + assert_fs_integrity(fs_type, fs_img) + + + def test_mkdir2(self, u_boot_console, fs_obj_mkdir): + """ + Test Case 2 - create a directory under a sub-directory + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 2 - mkdir (sub-sub directory)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 dir1/dir2' % fs_type, + '%sls host 0:0 dir1' % fs_type]) + assert('dir2/' in ''.join(output)) + + output = u_boot_console.run_command( + '%sls host 0:0 dir1/dir2' % fs_type) + assert('./' in output) + assert('../' in output) + assert_fs_integrity(fs_type, fs_img) + + def test_mkdir3(self, u_boot_console, fs_obj_mkdir): + """ + Test Case 3 - trying to create a directory with a non-existing + path should fail + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 3 - mkdir (non-existing path)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 none/dir3' % fs_type]) + assert('Unable to create a directory' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_mkdir4(self, u_boot_console, fs_obj_mkdir): + """ + Test Case 4 - trying to create "." should fail + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 4 - mkdir (".")'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 .' % fs_type]) + assert('Unable to create a directory' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_mkdir5(self, u_boot_console, fs_obj_mkdir): + """ + Test Case 5 - trying to create ".." should fail + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 5 - mkdir ("..")'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 ..' % fs_type]) + assert('Unable to create a directory' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_mkdir6(self, u_boot_console, fs_obj_mkdir): + """ + 'Test Case 6 - create as many directories as amount of directory + entries goes beyond a cluster size)' + """ + fs_type,fs_img = fs_obj_mkdir + with u_boot_console.log.section('Test Case 6 - mkdir (create many)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%smkdir host 0:0 dir6' % fs_type, + '%sls host 0:0 /' % fs_type]) + assert('dir6/' in ''.join(output)) + + for i in range(0, 20): + output = u_boot_console.run_command( + '%smkdir host 0:0 dir6/0123456789abcdef%02x' + % (fs_type, i)) + output = u_boot_console.run_command('%sls host 0:0 dir6' % fs_type) + assert('0123456789abcdef00/' in output) + assert('0123456789abcdef13/' in output) + + output = u_boot_console.run_command( + '%sls host 0:0 dir6/0123456789abcdef13/.' % fs_type) + assert('./' in output) + assert('../' in output) + + output = u_boot_console.run_command( + '%sls host 0:0 dir6/0123456789abcdef13/..' % fs_type) + assert('0123456789abcdef00/' in output) + assert('0123456789abcdef13/' in output) + assert_fs_integrity(fs_type, fs_img) diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py new file mode 100644 index 000000000..c96f92c1d --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 Bootlin +# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com> + +import os +import random +import string +import subprocess + +def sqfs_get_random_letters(size): + letters = [] + for i in range(0, size): + letters.append(random.choice(string.ascii_letters)) + + return ''.join(letters) + +def sqfs_generate_file(path, size): + content = sqfs_get_random_letters(size) + file = open(path, "w") + file.write(content) + file.close() + +class Compression: + def __init__(self, name, files, sizes, block_size = 4096): + self.name = name + self.files = files + self.sizes = sizes + self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name + + def add_opt(self, opt): + self.mksquashfs_opts += " " + opt + + def gen_image(self, build_dir): + src = os.path.join(build_dir, "sqfs_src/") + os.mkdir(src) + for (f, s) in zip(self.files, self.sizes): + sqfs_generate_file(src + f, s) + + # the symbolic link always targets the first file + os.symlink(self.files[0], src + "sym") + + sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) + i_o = src + " " + sqfs_img + opts = self.mksquashfs_opts + try: + subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True) + except: + print("mksquashfs error. Compression type: " + self.name) + raise RuntimeError + + def clean_source(self, build_dir): + src = os.path.join(build_dir, "sqfs_src/") + for f in self.files: + os.remove(src + f) + os.remove(src + "sym") + os.rmdir(src) + + def cleanup(self, build_dir): + self.clean_source(build_dir) + sqfs_img = os.path.join(build_dir, "sqfs-" + self.name) + os.remove(sqfs_img) + +files = ["blks_only", "blks_frag", "frag_only"] +sizes = [4096, 5100, 100] +gzip = Compression("gzip", files, sizes) +zstd = Compression("zstd", files, sizes) +lzo = Compression("lzo", files, sizes) + +# use fragment blocks for files larger than block_size +gzip.add_opt("-always-use-fragments") +zstd.add_opt("-always-use-fragments") + +# avoid fragments if lzo is used +lzo.add_opt("-no-fragments") + +comp_opts = [gzip, zstd, lzo] diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py new file mode 100644 index 000000000..9e9006238 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 Bootlin +# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com> + +import os +import pytest +from sqfs_common import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_fs_generic') +@pytest.mark.buildconfigspec('cmd_squashfs') +@pytest.mark.buildconfigspec('fs_squashfs') +@pytest.mark.requiredtool('mksquashfs') +def test_sqfs_load(u_boot_console): + build_dir = u_boot_console.config.build_dir + command = "sqfsload host 0 $kernel_addr_r " + + for opt in comp_opts: + # generate and load the squashfs image + try: + opt.gen_image(build_dir) + except RuntimeError: + opt.clean_source(build_dir) + # skip unsupported compression types + continue + + path = os.path.join(build_dir, "sqfs-" + opt.name) + output = u_boot_console.run_command("host bind 0 " + path) + + output = u_boot_console.run_command(command + "xxx") + assert "File not found." in output + + for (f, s) in zip(opt.files, opt.sizes): + try: + output = u_boot_console.run_command(command + f) + assert str(s) in output + except: + assert False + opt.cleanup(build_dir) + + # test symbolic link + output = u_boot_console.run_command(command + "sym") + assert str(opt.sizes[0]) in output + + # remove generated files + opt.cleanup(build_dir) diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py new file mode 100644 index 000000000..a0dca2e2f --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 Bootlin +# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com> + +import os +import pytest +from sqfs_common import * + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_fs_generic') +@pytest.mark.buildconfigspec('cmd_squashfs') +@pytest.mark.buildconfigspec('fs_squashfs') +@pytest.mark.requiredtool('mksquashfs') +def test_sqfs_ls(u_boot_console): + build_dir = u_boot_console.config.build_dir + for opt in comp_opts: + try: + opt.gen_image(build_dir) + except RuntimeError: + opt.clean_source(build_dir) + # skip unsupported compression types + continue + path = os.path.join(build_dir, "sqfs-" + opt.name) + output = u_boot_console.run_command("host bind 0 " + path) + + try: + # list files in root directory + output = u_boot_console.run_command("sqfsls host 0") + assert str(len(opt.files) + 1) + " file(s), 0 dir(s)" in output + assert "<SYM> sym" in output + output = u_boot_console.run_command("sqfsls host 0 xxx") + assert "** Cannot find directory. **" in output + except: + opt.cleanup(build_dir) + assert False + opt.cleanup(build_dir) diff --git a/roms/u-boot/test/py/tests/test_fs/test_symlink.py b/roms/u-boot/test/py/tests/test_fs/test_symlink.py new file mode 100644 index 000000000..9ced101a2 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_symlink.py @@ -0,0 +1,130 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2019, Texas Instrument +# Author: Jean-Jacques Hiblot <jjhiblot@ti.com> +# +# U-Boot File System:symlink Test + +""" +This test verifies unlink operation (deleting a file or a directory) +on file system. +""" + +import pytest +import re +from fstest_defs import * +from fstest_helpers import assert_fs_integrity + + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestSymlink(object): + def test_symlink1(self, u_boot_console, fs_obj_symlink): + """ + Test Case 1 - create a link. and follow it when reading + """ + fs_type, fs_img, md5val = fs_obj_symlink + with u_boot_console.log.section('Test Case 1 - create link and read'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'ln host 0:0 %s /%s.link ' % (SMALL_FILE, SMALL_FILE), + ]) + assert('' in ''.join(output)) + + output = u_boot_console.run_command_list([ + '%sload host 0:0 %x /%s.link' % (fs_type, ADDR, SMALL_FILE), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 4b - Read full 1MB of small file + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_symlink2(self, u_boot_console, fs_obj_symlink): + """ + Test Case 2 - create chained links + """ + fs_type, fs_img, md5val = fs_obj_symlink + with u_boot_console.log.section('Test Case 2 - create chained links'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'ln host 0:0 %s /%s.link1 ' % (SMALL_FILE, SMALL_FILE), + 'ln host 0:0 /%s.link1 /SUBDIR/%s.link2' % ( + SMALL_FILE, SMALL_FILE), + 'ln host 0:0 SUBDIR/%s.link2 /%s.link3' % ( + SMALL_FILE, SMALL_FILE), + ]) + assert('' in ''.join(output)) + + output = u_boot_console.run_command_list([ + '%sload host 0:0 %x /%s.link3' % (fs_type, ADDR, SMALL_FILE), + 'printenv filesize']) + assert('filesize=100000' in ''.join(output)) + + # Test Case 4b - Read full 1MB of small file + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[0] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_symlink3(self, u_boot_console, fs_obj_symlink): + """ + Test Case 3 - replace file/link with link + """ + fs_type, fs_img, md5val = fs_obj_symlink + with u_boot_console.log.section('Test Case 1 - create link and read'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + 'setenv filesize', + 'ln host 0:0 %s /%s ' % (MEDIUM_FILE, SMALL_FILE), + 'ln host 0:0 %s /%s.link ' % (MEDIUM_FILE, MEDIUM_FILE), + ]) + assert('' in ''.join(output)) + + output = u_boot_console.run_command_list([ + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE), + 'printenv filesize']) + assert('filesize=a00000' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[1] in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'ln host 0:0 %s.link /%s ' % (MEDIUM_FILE, SMALL_FILE), + '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE), + 'printenv filesize']) + assert('filesize=a00000' in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(md5val[1] in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_symlink4(self, u_boot_console, fs_obj_symlink): + """ + Test Case 4 - create a broken link + """ + fs_type, fs_img, md5val = fs_obj_symlink + with u_boot_console.log.section('Test Case 1 - create link and read'): + + output = u_boot_console.run_command_list([ + 'setenv filesize', + 'ln host 0:0 nowhere /link ', + ]) + assert('' in ''.join(output)) + + output = u_boot_console.run_command( + '%sload host 0:0 %x /link' % + (fs_type, ADDR)) + with u_boot_console.disable_check('error_notification'): + output = u_boot_console.run_command('printenv filesize') + assert('"filesize" not defined' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) diff --git a/roms/u-boot/test/py/tests/test_fs/test_unlink.py b/roms/u-boot/test/py/tests/test_fs/test_unlink.py new file mode 100644 index 000000000..97aafc63b --- /dev/null +++ b/roms/u-boot/test/py/tests/test_fs/test_unlink.py @@ -0,0 +1,118 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Linaro Limited +# Author: Takahiro Akashi <takahiro.akashi@linaro.org> +# +# U-Boot File System:unlink Test + +""" +This test verifies unlink operation (deleting a file or a directory) +on file system. +""" + +import pytest +from fstest_helpers import assert_fs_integrity + +@pytest.mark.boardspec('sandbox') +@pytest.mark.slow +class TestUnlink(object): + def test_unlink1(self, u_boot_console, fs_obj_unlink): + """ + Test Case 1 - delete a file + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 1 - unlink (file)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir1/file1' % fs_type, + '%sls host 0:0 dir1/file1' % fs_type]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command( + '%sls host 0:0 dir1/' % fs_type) + assert(not 'file1' in output) + assert('file2' in output) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink2(self, u_boot_console, fs_obj_unlink): + """ + Test Case 2 - delete many files + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 2 - unlink (many)'): + output = u_boot_console.run_command('host bind 0 %s' % fs_img) + + for i in range(0, 20): + output = u_boot_console.run_command_list([ + '%srm host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i), + '%sls host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i)]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command( + '%sls host 0:0 dir2' % fs_type) + assert('0 file(s), 2 dir(s)' in output) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink3(self, u_boot_console, fs_obj_unlink): + """ + Test Case 3 - trying to delete a non-existing file should fail + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 3 - unlink (non-existing)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir1/nofile' % fs_type]) + assert('nofile: doesn\'t exist' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink4(self, u_boot_console, fs_obj_unlink): + """ + Test Case 4 - delete an empty directory + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 4 - unlink (directory)'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir4' % fs_type]) + assert('' == ''.join(output)) + + output = u_boot_console.run_command( + '%sls host 0:0 /' % fs_type) + assert(not 'dir4' in output) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink5(self, u_boot_console, fs_obj_unlink): + """ + Test Case 5 - trying to deleting a non-empty directory ".." + should fail + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 5 - unlink ("non-empty directory")'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir5' % fs_type]) + assert('directory is not empty' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink6(self, u_boot_console, fs_obj_unlink): + """ + Test Case 6 - trying to deleting a "." should fail + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 6 - unlink (".")'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir5/.' % fs_type]) + assert('directory is not empty' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) + + def test_unlink7(self, u_boot_console, fs_obj_unlink): + """ + Test Case 7 - trying to deleting a ".." should fail + """ + fs_type,fs_img = fs_obj_unlink + with u_boot_console.log.section('Test Case 7 - unlink ("..")'): + output = u_boot_console.run_command_list([ + 'host bind 0 %s' % fs_img, + '%srm host 0:0 dir5/..' % fs_type]) + assert('directory is not empty' in ''.join(output)) + assert_fs_integrity(fs_type, fs_img) diff --git a/roms/u-boot/test/py/tests/test_gpio.py b/roms/u-boot/test/py/tests/test_gpio.py new file mode 100644 index 000000000..8c64f686b --- /dev/null +++ b/roms/u-boot/test/py/tests/test_gpio.py @@ -0,0 +1,37 @@ +# SPDX-License-Identifier: GPL-2.0+ + +import pytest + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpio') +def test_gpio_input(u_boot_console): + """Test that gpio input correctly returns the value of a gpio pin.""" + + response = u_boot_console.run_command('gpio input 0; echo rc:$?') + expected_response = 'rc:0' + assert(expected_response in response) + response = u_boot_console.run_command('gpio toggle 0; gpio input 0; echo rc:$?') + expected_response = 'rc:1' + assert(expected_response in response) + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpio') +def test_gpio_exit_statuses(u_boot_console): + """Test that non-input gpio commands correctly return the command + success/failure status.""" + + expected_response = 'rc:0' + response = u_boot_console.run_command('gpio clear 0; echo rc:$?') + assert(expected_response in response) + response = u_boot_console.run_command('gpio set 0; echo rc:$?') + assert(expected_response in response) + response = u_boot_console.run_command('gpio toggle 0; echo rc:$?') + assert(expected_response in response) + response = u_boot_console.run_command('gpio status -a; echo rc:$?') + assert(expected_response in response) + + expected_response = 'rc:1' + response = u_boot_console.run_command('gpio nonexistent-command; echo rc:$?') + assert(expected_response in response) + response = u_boot_console.run_command('gpio input 200; echo rc:$?') + assert(expected_response in response) diff --git a/roms/u-boot/test/py/tests/test_gpt.py b/roms/u-boot/test/py/tests/test_gpt.py new file mode 100644 index 000000000..229d7eb2c --- /dev/null +++ b/roms/u-boot/test/py/tests/test_gpt.py @@ -0,0 +1,178 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2017 Alison Chaiken +# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. + +# Test GPT manipulation commands. + +import os +import pytest +import u_boot_utils + +""" +These tests rely on a 4 MB disk image, which is automatically created by +the test. +""" + +class GptTestDiskImage(object): + """Disk Image used by the GPT tests.""" + + def __init__(self, u_boot_console): + """Initialize a new GptTestDiskImage object. + + Args: + u_boot_console: A U-Boot console. + + Returns: + Nothing. + """ + + filename = 'test_gpt_disk_image.bin' + + persistent = u_boot_console.config.persistent_data_dir + '/' + filename + self.path = u_boot_console.config.result_dir + '/' + filename + + with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent): + if os.path.exists(persistent): + u_boot_console.log.action('Disk image file ' + persistent + + ' already exists') + else: + u_boot_console.log.action('Generating ' + persistent) + fd = os.open(persistent, os.O_RDWR | os.O_CREAT) + os.ftruncate(fd, 4194304) + os.close(fd) + cmd = ('sgdisk', + '--disk-guid=375a56f7-d6c9-4e81-b5f0-09d41ca89efe', + persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + # part1 offset 1MB size 1MB + cmd = ('sgdisk', '--new=1:2048:4095', '--change-name=1:part1', + persistent) + # part2 offset 2MB size 1.5MB + u_boot_utils.run_and_log(u_boot_console, cmd) + cmd = ('sgdisk', '--new=2:4096:7167', '--change-name=2:part2', + persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + cmd = ('sgdisk', '--load-backup=' + persistent) + u_boot_utils.run_and_log(u_boot_console, cmd) + + cmd = ('cp', persistent, self.path) + u_boot_utils.run_and_log(u_boot_console, cmd) + +gtdi = None +@pytest.fixture(scope='function') +def state_disk_image(u_boot_console): + """pytest fixture to provide a GptTestDiskImage object to tests. + This is function-scoped because it uses u_boot_console, which is also + function-scoped. However, we don't need to actually do any function-scope + work, so this simply returns the same object over and over each time.""" + + global gtdi + if not gtdi: + gtdi = GptTestDiskImage(u_boot_console) + return gtdi + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_read(state_disk_image, u_boot_console): + """Test the gpt read command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt read host 0') + assert 'Start 1MiB, size 1MiB' in output + assert 'Block size 512, name part1' in output + assert 'Start 2MiB, size 1MiB' in output + assert 'Block size 512, name part2' in output + output = u_boot_console.run_command('part list host 0') + assert '0x00000800 0x00000fff "part1"' in output + assert '0x00001000 0x00001bff "part2"' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_verify(state_disk_image, u_boot_console): + """Test the gpt verify command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt verify host 0') + assert 'Verify GPT: success!' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_guid(state_disk_image, u_boot_console): + """Test the gpt guid command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt guid host 0') + assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_save_guid(state_disk_image, u_boot_console): + """Test the gpt guid command to save GUID into a string.""" + + if u_boot_console.config.buildconfig.get('config_cmd_gpt', 'n') != 'y': + pytest.skip('gpt command not supported') + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt guid host 0 newguid') + output = u_boot_console.run_command('printenv newguid') + assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_gpt_rename') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_rename_partition(state_disk_image, u_boot_console): + """Test the gpt rename command to write partition names.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + u_boot_console.run_command('gpt rename host 0 1 first') + output = u_boot_console.run_command('gpt read host 0') + assert 'name first' in output + u_boot_console.run_command('gpt rename host 0 2 second') + output = u_boot_console.run_command('gpt read host 0') + assert 'name second' in output + output = u_boot_console.run_command('part list host 0') + assert '0x00000800 0x00000fff "first"' in output + assert '0x00001000 0x00001bff "second"' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_gpt_rename') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_swap_partitions(state_disk_image, u_boot_console): + """Test the gpt swap command to exchange two partition names.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('part list host 0') + assert '0x00000800 0x00000fff "first"' in output + assert '0x00001000 0x00001bff "second"' in output + u_boot_console.run_command('gpt swap host 0 first second') + output = u_boot_console.run_command('part list host 0') + assert '0x00000800 0x00000fff "second"' in output + assert '0x00001000 0x00001bff "first"' in output + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_gpt') +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.requiredtool('sgdisk') +def test_gpt_write(state_disk_image, u_boot_console): + """Test the gpt write command.""" + + u_boot_console.run_command('host bind 0 ' + state_disk_image.path) + output = u_boot_console.run_command('gpt write host 0 "name=all,size=0"') + assert 'Writing GPT: success!' in output + output = u_boot_console.run_command('part list host 0') + assert '0x00000022 0x00001fde "all"' in output + output = u_boot_console.run_command('gpt write host 0 "uuid_disk=375a56f7-d6c9-4e81-b5f0-09d41ca89efe;name=first,start=1M,size=1M;name=second,start=0x200000,size=0x180000;"') + assert 'Writing GPT: success!' in output + output = u_boot_console.run_command('part list host 0') + assert '0x00000800 0x00000fff "first"' in output + assert '0x00001000 0x00001bff "second"' in output + output = u_boot_console.run_command('gpt guid host 0') + assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output diff --git a/roms/u-boot/test/py/tests/test_handoff.py b/roms/u-boot/test/py/tests/test_handoff.py new file mode 100644 index 000000000..038f03064 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_handoff.py @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc + +import pytest + +# Magic number to check that SPL handoff is working +TEST_HANDOFF_MAGIC = 0x14f93c7b + +@pytest.mark.boardspec('sandbox_spl') +@pytest.mark.buildconfigspec('spl') +def test_handoff(u_boot_console): + """Test that of-platdata can be generated and used in sandbox""" + cons = u_boot_console + response = cons.run_command('sb handoff') + assert ('SPL handoff magic %x' % TEST_HANDOFF_MAGIC) in response diff --git a/roms/u-boot/test/py/tests/test_help.py b/roms/u-boot/test/py/tests/test_help.py new file mode 100644 index 000000000..d50295e5b --- /dev/null +++ b/roms/u-boot/test/py/tests/test_help.py @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +def test_help(u_boot_console): + """Test that the "help" command can be executed.""" + + u_boot_console.run_command('help') diff --git a/roms/u-boot/test/py/tests/test_hush_if_test.py b/roms/u-boot/test/py/tests/test_hush_if_test.py new file mode 100644 index 000000000..d117921a6 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_hush_if_test.py @@ -0,0 +1,192 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Test operation of the "if" shell command. + +import os +import os.path +import pytest + +# TODO: These tests should be converted to a C test. +# For more information please take a look at the thread +# https://lists.denx.de/pipermail/u-boot/2019-October/388732.html + +pytestmark = pytest.mark.buildconfigspec('hush_parser') + +# The list of "if test" conditions to test. +subtests = ( + # Base if functionality. + + ('true', True), + ('false', False), + + # Basic operators. + + ('test aaa = aaa', True), + ('test aaa = bbb', False), + + ('test aaa != bbb', True), + ('test aaa != aaa', False), + + ('test aaa < bbb', True), + ('test bbb < aaa', False), + + ('test bbb > aaa', True), + ('test aaa > bbb', False), + + ('test 123 -eq 123', True), + ('test 123 -eq 456', False), + + ('test 123 -ne 456', True), + ('test 123 -ne 123', False), + + ('test 123 -lt 456', True), + ('test 123 -lt 123', False), + ('test 456 -lt 123', False), + + ('test 123 -le 456', True), + ('test 123 -le 123', True), + ('test 456 -le 123', False), + + ('test 456 -gt 123', True), + ('test 123 -gt 123', False), + ('test 123 -gt 456', False), + + ('test 456 -ge 123', True), + ('test 123 -ge 123', True), + ('test 123 -ge 456', False), + + # Octal tests + + ('test 010 -eq 010', True), + ('test 010 -eq 011', False), + + ('test 010 -ne 011', True), + ('test 010 -ne 010', False), + + # Hexadecimal tests + + ('test 0x2000000 -gt 0x2000001', False), + ('test 0x2000000 -gt 0x2000000', False), + ('test 0x2000000 -gt 0x1ffffff', True), + + # Mixed tests + + ('test 010 -eq 10', False), + ('test 010 -ne 10', True), + ('test 0xa -eq 10', True), + ('test 0xa -eq 012', True), + + ('test 2000000 -gt 0x1ffffff', False), + ('test 0x2000000 -gt 1ffffff', True), + ('test 0x2000000 -lt 1ffffff', False), + ('test 0x2000000 -eq 2000000', False), + ('test 0x2000000 -ne 2000000', True), + + ('test -z ""', True), + ('test -z "aaa"', False), + + ('test -n "aaa"', True), + ('test -n ""', False), + + # Inversion of simple tests. + + ('test ! aaa = aaa', False), + ('test ! aaa = bbb', True), + ('test ! ! aaa = aaa', True), + ('test ! ! aaa = bbb', False), + + # Binary operators. + + ('test aaa != aaa -o bbb != bbb', False), + ('test aaa != aaa -o bbb = bbb', True), + ('test aaa = aaa -o bbb != bbb', True), + ('test aaa = aaa -o bbb = bbb', True), + + ('test aaa != aaa -a bbb != bbb', False), + ('test aaa != aaa -a bbb = bbb', False), + ('test aaa = aaa -a bbb != bbb', False), + ('test aaa = aaa -a bbb = bbb', True), + + # Inversion within binary operators. + + ('test ! aaa != aaa -o ! bbb != bbb', True), + ('test ! aaa != aaa -o ! bbb = bbb', True), + ('test ! aaa = aaa -o ! bbb != bbb', True), + ('test ! aaa = aaa -o ! bbb = bbb', False), + + ('test ! ! aaa != aaa -o ! ! bbb != bbb', False), + ('test ! ! aaa != aaa -o ! ! bbb = bbb', True), + ('test ! ! aaa = aaa -o ! ! bbb != bbb', True), + ('test ! ! aaa = aaa -o ! ! bbb = bbb', True), + + # -z operator. + + ('test -z "$ut_var_nonexistent"', True), + ('test -z "$ut_var_exists"', False), +) + +def exec_hush_if(u_boot_console, expr, result): + """Execute a shell "if" command, and validate its result.""" + + config = u_boot_console.config.buildconfig + maxargs = int(config.get('config_sys_maxargs', '0')) + args = len(expr.split(' ')) - 1 + if args > maxargs: + u_boot_console.log.warning('CONFIG_SYS_MAXARGS too low; need ' + + str(args)) + pytest.skip() + + cmd = 'if ' + expr + '; then echo true; else echo false; fi' + response = u_boot_console.run_command(cmd) + assert response.strip() == str(result).lower() + +def test_hush_if_test_setup(u_boot_console): + """Set up environment variables used during the "if" tests.""" + + u_boot_console.run_command('setenv ut_var_nonexistent') + u_boot_console.run_command('setenv ut_var_exists 1') + +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.parametrize('expr,result', subtests) +def test_hush_if_test(u_boot_console, expr, result): + """Test a single "if test" condition.""" + + exec_hush_if(u_boot_console, expr, result) + +def test_hush_if_test_teardown(u_boot_console): + """Clean up environment variables used during the "if" tests.""" + + u_boot_console.run_command('setenv ut_var_exists') + +# We might test this on real filesystems via UMS, DFU, 'save', etc. +# Of those, only UMS currently allows file removal though. +@pytest.mark.buildconfigspec('cmd_echo') +@pytest.mark.boardspec('sandbox') +def test_hush_if_test_host_file_exists(u_boot_console): + """Test the "if test -e" shell command.""" + + test_file = u_boot_console.config.result_dir + \ + '/creating_this_file_breaks_u_boot_tests' + + try: + os.unlink(test_file) + except: + pass + assert not os.path.exists(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, False) + + try: + with open(test_file, 'wb'): + pass + assert os.path.exists(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, True) + finally: + os.unlink(test_file) + + expr = 'test -e hostfs - ' + test_file + exec_hush_if(u_boot_console, expr, False) diff --git a/roms/u-boot/test/py/tests/test_log.py b/roms/u-boot/test/py/tests/test_log.py new file mode 100644 index 000000000..140dcb9aa --- /dev/null +++ b/roms/u-boot/test/py/tests/test_log.py @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016, Google Inc. +# +# U-Boot Verified Boot Test + +""" +This tests U-Boot logging. It uses the 'log test' command with various options +and checks that the output is correct. +""" + +import pytest + +@pytest.mark.buildconfigspec('cmd_log') +def test_log_format(u_boot_console): + """Test the 'log format' and 'log rec' commands""" + def run_with_format(fmt, expected_output): + """Set up the log format and then write a log record + + Args: + fmt: Format to use for 'log format' + expected_output: Expected output from the 'log rec' command + """ + output = cons.run_command('log format %s' % fmt) + assert output == '' + output = cons.run_command('log rec arch notice file.c 123 func msg') + assert output == expected_output + + cons = u_boot_console + with cons.log.section('format'): + run_with_format('all', 'NOTICE.arch,file.c:123-func() msg') + output = cons.run_command('log format') + assert output == 'Log format: clFLfm' + + run_with_format('fm', 'func() msg') + run_with_format('clfm', 'NOTICE.arch,func() msg') + run_with_format('FLfm', 'file.c:123-func() msg') + run_with_format('lm', 'NOTICE. msg') + run_with_format('m', 'msg') + +@pytest.mark.buildconfigspec('debug_uart') +@pytest.mark.boardspec('sandbox') +def test_log_dropped(u_boot_console): + """Test dropped 'log' message when debug_uart is activated""" + + cons = u_boot_console + cons.restart_uboot() + output = cons.get_spawn_output().replace('\r', '') + assert (not 'debug: main' in output) diff --git a/roms/u-boot/test/py/tests/test_lsblk.py b/roms/u-boot/test/py/tests/test_lsblk.py new file mode 100644 index 000000000..40ffe0126 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_lsblk.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (C) 2020 +# Niel Fourie, DENX Software Engineering, lusus@denx.de + +import pytest + +@pytest.mark.buildconfigspec('blk') +@pytest.mark.buildconfigspec('cmd_lsblk') +def test_lsblk(u_boot_console): + """Test that `lsblk` prints a result which includes `host`.""" + output = u_boot_console.run_command('lsblk') + assert "Block Driver" in output + assert "sandbox_host_blk" in output diff --git a/roms/u-boot/test/py/tests/test_md.py b/roms/u-boot/test/py/tests/test_md.py new file mode 100644 index 000000000..83e3c546f --- /dev/null +++ b/roms/u-boot/test/py/tests/test_md.py @@ -0,0 +1,36 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +import pytest +import u_boot_utils + +@pytest.mark.buildconfigspec('cmd_memory') +def test_md(u_boot_console): + """Test that md reads memory as expected, and that memory can be modified + using the mw command.""" + + ram_base = u_boot_utils.find_ram_base(u_boot_console) + addr = '%08x' % ram_base + val = 'a5f09876' + expected_response = addr + ': ' + val + u_boot_console.run_command('mw ' + addr + ' 0 10') + response = u_boot_console.run_command('md ' + addr + ' 10') + assert(not (expected_response in response)) + u_boot_console.run_command('mw ' + addr + ' ' + val) + response = u_boot_console.run_command('md ' + addr + ' 10') + assert(expected_response in response) + +@pytest.mark.buildconfigspec('cmd_memory') +def test_md_repeat(u_boot_console): + """Test command repeat (via executing an empty command) operates correctly + for "md"; the command must repeat and dump an incrementing address.""" + + ram_base = u_boot_utils.find_ram_base(u_boot_console) + addr_base = '%08x' % ram_base + words = 0x10 + addr_repeat = '%08x' % (ram_base + (words * 4)) + u_boot_console.run_command('md %s %x' % (addr_base, words)) + response = u_boot_console.run_command('') + expected_response = addr_repeat + ': ' + assert(expected_response in response) diff --git a/roms/u-boot/test/py/tests/test_mmc_rd.py b/roms/u-boot/test/py/tests/test_mmc_rd.py new file mode 100644 index 000000000..ea652f913 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_mmc_rd.py @@ -0,0 +1,286 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. + +# Test U-Boot's "mmc read" command. The test reads data from the eMMC or SD +# card, and validates the no errors occurred, and that the expected data was +# read if the test configuration contains a CRC of the expected data. + +import pytest +import time +import u_boot_utils + +""" +This test relies on boardenv_* to containing configuration values to define +which MMC devices should be tested. For example: + +# Configuration data for test_mmc_dev, test_mmc_rescan, test_mmc_info; defines +# whole MMC devices that mmc dev/rescan/info commands may operate upon. +env__mmc_dev_configs = ( + { + 'fixture_id': 'emmc-boot0', + 'is_emmc': True, + 'devid': 0, + 'partid': 1, + 'info_device': ???, + 'info_speed': ???, + 'info_mode': ???, + 'info_buswidth': ???. + }, + { + 'fixture_id': 'emmc-boot1', + 'is_emmc': True, + 'devid': 0, + 'partid': 2, + 'info_device': ???, + 'info_speed': ???, + 'info_mode': ???, + 'info_buswidth': ???. + }, + { + 'fixture_id': 'emmc-data', + 'is_emmc': True, + 'devid': 0, + 'partid': 0, + 'info_device': ???, + 'info_speed': ???, + 'info_mode': ???, + 'info_buswidth': ???. + }, + { + 'fixture_id': 'sd', + 'is_emmc': False, + 'devid': 1, + 'partid': None, + 'info_device': ???, + 'info_speed': ???, + 'info_mode': ???, + 'info_buswidth': ???. + }, +) + +# Configuration data for test_mmc_rd; defines regions of the MMC (entire +# devices, or ranges of sectors) which can be read: +env__mmc_rd_configs = ( + { + 'fixture_id': 'emmc-boot0', + 'is_emmc': True, + 'devid': 0, + 'partid': 1, + 'sector': 0x10, + 'count': 1, + }, + { + 'fixture_id': 'emmc-boot1', + 'is_emmc': True, + 'devid': 0, + 'partid': 2, + 'sector': 0x10, + 'count': 1, + }, + { + 'fixture_id': 'emmc-data', + 'is_emmc': True, + 'devid': 0, + 'partid': 0, + 'sector': 0x10, + 'count': 0x1000, + }, + { + 'fixture_id': 'sd-mbr', + 'is_emmc': False, + 'devid': 1, + 'partid': None, + 'sector': 0, + 'count': 1, + 'crc32': '8f6ecf0d', + }, + { + 'fixture_id': 'sd-large', + 'is_emmc': False, + 'devid': 1, + 'partid': None, + 'sector': 0x10, + 'count': 0x1000, + }, +) +""" + +def mmc_dev(u_boot_console, is_emmc, devid, partid): + """Run the "mmc dev" command. + + Args: + u_boot_console: A U-Boot console connection. + is_emmc: Whether the device is eMMC + devid: Device ID + partid: Partition ID + + Returns: + Nothing. + """ + + # Select MMC device + cmd = 'mmc dev %d' % devid + if is_emmc: + cmd += ' %d' % partid + response = u_boot_console.run_command(cmd) + assert 'no card present' not in response + if is_emmc: + partid_response = '(part %d)' % partid + else: + partid_response = '' + good_response = 'mmc%d%s is current device' % (devid, partid_response) + assert good_response in response + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_dev(u_boot_console, env__mmc_dev_config): + """Test the "mmc dev" command. + + Args: + u_boot_console: A U-Boot console connection. + env__mmc_dev_config: The single MMC configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + is_emmc = env__mmc_dev_config['is_emmc'] + devid = env__mmc_dev_config['devid'] + partid = env__mmc_dev_config.get('partid', 0) + + # Select MMC device + mmc_dev(u_boot_console, is_emmc, devid, partid) + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_rescan(u_boot_console, env__mmc_dev_config): + """Test the "mmc rescan" command. + + Args: + u_boot_console: A U-Boot console connection. + env__mmc_dev_config: The single MMC configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + is_emmc = env__mmc_dev_config['is_emmc'] + devid = env__mmc_dev_config['devid'] + partid = env__mmc_dev_config.get('partid', 0) + + # Select MMC device + mmc_dev(u_boot_console, is_emmc, devid, partid) + + # Rescan MMC device + cmd = 'mmc rescan' + response = u_boot_console.run_command(cmd) + assert 'no card present' not in response + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_info(u_boot_console, env__mmc_dev_config): + """Test the "mmc info" command. + + Args: + u_boot_console: A U-Boot console connection. + env__mmc_dev_config: The single MMC configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + is_emmc = env__mmc_dev_config['is_emmc'] + devid = env__mmc_dev_config['devid'] + partid = env__mmc_dev_config.get('partid', 0) + info_device = env__mmc_dev_config['info_device'] + info_speed = env__mmc_dev_config['info_speed'] + info_mode = env__mmc_dev_config['info_mode'] + info_buswidth = env__mmc_dev_config['info_buswidth'] + + # Select MMC device + mmc_dev(u_boot_console, is_emmc, devid, partid) + + # Read MMC device information + cmd = 'mmc info' + response = u_boot_console.run_command(cmd) + good_response = "Device: %s" % info_device + assert good_response in response + good_response = "Bus Speed: %s" % info_speed + assert good_response in response + good_response = "Mode: %s" % info_mode + assert good_response in response + good_response = "Bus Width: %s" % info_buswidth + assert good_response in response + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_rd(u_boot_console, env__mmc_rd_config): + """Test the "mmc read" command. + + Args: + u_boot_console: A U-Boot console connection. + env__mmc_rd_config: The single MMC configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + is_emmc = env__mmc_rd_config['is_emmc'] + devid = env__mmc_rd_config['devid'] + partid = env__mmc_rd_config.get('partid', 0) + sector = env__mmc_rd_config.get('sector', 0) + count_sectors = env__mmc_rd_config.get('count', 1) + expected_crc32 = env__mmc_rd_config.get('crc32', None) + read_duration_max = env__mmc_rd_config.get('read_duration_max', 0) + + count_bytes = count_sectors * 512 + bcfg = u_boot_console.config.buildconfig + has_cmd_memory = bcfg.get('config_cmd_memory', 'n') == 'y' + has_cmd_crc32 = bcfg.get('config_cmd_crc32', 'n') == 'y' + ram_base = u_boot_utils.find_ram_base(u_boot_console) + addr = '0x%08x' % ram_base + + # Select MMC device + mmc_dev(u_boot_console, is_emmc, devid, partid) + + # Clear target RAM + if expected_crc32: + if has_cmd_memory and has_cmd_crc32: + cmd = 'mw.b %s 0 0x%x' % (addr, count_bytes) + u_boot_console.run_command(cmd) + + cmd = 'crc32 %s 0x%x' % (addr, count_bytes) + response = u_boot_console.run_command(cmd) + assert expected_crc32 not in response + else: + u_boot_console.log.warning( + 'CONFIG_CMD_MEMORY or CONFIG_CMD_CRC32 != y: Skipping RAM clear') + + # Read data + cmd = 'mmc read %s %x %x' % (addr, sector, count_sectors) + tstart = time.time() + response = u_boot_console.run_command(cmd) + tend = time.time() + good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % ( + devid, sector, count_sectors, count_sectors) + assert good_response in response + + # Check target RAM + if expected_crc32: + if has_cmd_crc32: + cmd = 'crc32 %s 0x%x' % (addr, count_bytes) + response = u_boot_console.run_command(cmd) + assert expected_crc32 in response + else: + u_boot_console.log.warning('CONFIG_CMD_CRC32 != y: Skipping check') + + # Check if the command did not take too long + if read_duration_max: + elapsed = tend - tstart + u_boot_console.log.info('Reading %d bytes took %f seconds' % + (count_bytes, elapsed)) + assert elapsed <= (read_duration_max - 0.01) diff --git a/roms/u-boot/test/py/tests/test_mmc_wr.py b/roms/u-boot/test/py/tests/test_mmc_wr.py new file mode 100644 index 000000000..05e5c1ee8 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_mmc_wr.py @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2019, Texas Instrument +# Author: Jean-Jacques Hiblot <jjhiblot@ti.com> + +# Test U-Boot's "mmc write" command. The test generates random data, writes it +# to the eMMC or SD card, then reads it back and performs a comparison. + +import pytest +import u_boot_utils + +""" +This test relies on boardenv_* to containing configuration values to define +which MMC devices should be tested. For example: + +env__mmc_wr_configs = ( + { + "fixture_id": "emmc-boot0", + "is_emmc": True, + "devid": 1, + "partid": 1, + "sector": 0x10, + "count": 100, + "test_iterations": 50, + }, + { + "fixture_id": "emmc-boot1", + "is_emmc": True, + "devid": 1, + "partid": 2, + "sector": 0x10, + "count": 100, + "test_iterations": 50, + }, +) + +""" + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_memory') +@pytest.mark.buildconfigspec('cmd_random') +def test_mmc_wr(u_boot_console, env__mmc_wr_config): + """Test the "mmc write" command. + + Args: + u_boot_console: A U-Boot console connection. + env__mmc_wr_config: The single MMC configuration on which + to run the test. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + is_emmc = env__mmc_wr_config['is_emmc'] + devid = env__mmc_wr_config['devid'] + partid = env__mmc_wr_config.get('partid', 0) + sector = env__mmc_wr_config.get('sector', 0) + count_sectors = env__mmc_wr_config.get('count', 1) + test_iterations = env__mmc_wr_config.get('test_iterations', 1) + + + count_bytes = count_sectors * 512 + bcfg = u_boot_console.config.buildconfig + ram_base = u_boot_utils.find_ram_base(u_boot_console) + src_addr = '0x%08x' % ram_base + dst_addr = '0x%08x' % (ram_base + count_bytes) + + + for i in range(test_iterations): + # Generate random data + cmd = 'random %s %x' % (src_addr, count_bytes) + response = u_boot_console.run_command(cmd) + good_response = '%d bytes filled with random data' % (count_bytes) + assert good_response in response + + # Select MMC device + cmd = 'mmc dev %d' % devid + if is_emmc: + cmd += ' %d' % partid + response = u_boot_console.run_command(cmd) + assert 'no card present' not in response + if is_emmc: + partid_response = "(part %d)" % partid + else: + partid_response = "" + good_response = 'mmc%d%s is current device' % (devid, partid_response) + assert good_response in response + + # Write data + cmd = 'mmc write %s %x %x' % (src_addr, sector, count_sectors) + response = u_boot_console.run_command(cmd) + good_response = 'MMC write: dev # %d, block # %d, count %d ... %d blocks written: OK' % (devid, sector, count_sectors, count_sectors) + assert good_response in response + + # Read data + cmd = 'mmc read %s %x %x' % (dst_addr, sector, count_sectors) + response = u_boot_console.run_command(cmd) + good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % (devid, sector, count_sectors, count_sectors) + assert good_response in response + + # Compare src and dst data + cmd = 'cmp.b %s %s %x' % (src_addr, dst_addr, count_bytes) + response = u_boot_console.run_command(cmd) + good_response = 'Total of %d byte(s) were the same' % (count_bytes) + assert good_response in response diff --git a/roms/u-boot/test/py/tests/test_net.py b/roms/u-boot/test/py/tests/test_net.py new file mode 100644 index 000000000..9ca6743af --- /dev/null +++ b/roms/u-boot/test/py/tests/test_net.py @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +# Test various network-related functionality, such as the dhcp, ping, and +# tftpboot commands. + +import pytest +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +which the network environment available for testing. Without this, this test +will be automatically skipped. + +For example: + +# Boolean indicating whether the Ethernet device is attached to USB, and hence +# USB enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_usb = False + +# Boolean indicating whether the Ethernet device is attached to PCI, and hence +# PCI enumeration needs to be performed prior to network tests. +# This variable may be omitted if its value is False. +env__net_uses_pci = True + +# True if a DHCP server is attached to the network, and should be tested. +# If DHCP testing is not possible or desired, this variable may be omitted or +# set to False. +env__net_dhcp_server = True + +# A list of environment variables that should be set in order to configure a +# static IP. If solely relying on DHCP, this variable may be omitted or set to +# an empty list. +env__net_static_env_vars = [ + ('ipaddr', '10.0.0.100'), + ('netmask', '255.255.255.0'), + ('serverip', '10.0.0.1'), +] + +# Details regarding a file that may be read from a TFTP server. This variable +# may be omitted or set to None if TFTP testing is not possible or desired. +env__net_tftp_readable_file = { + 'fn': 'ubtest-readable.bin', + 'addr': 0x10000000, + 'size': 5058624, + 'crc32': 'c2244b26', +} + +# Details regarding a file that may be read from a NFS server. This variable +# may be omitted or set to None if NFS testing is not possible or desired. +env__net_nfs_readable_file = { + 'fn': 'ubtest-readable.bin', + 'addr': 0x10000000, + 'size': 5058624, + 'crc32': 'c2244b26', +} +""" + +net_set_up = False + +def test_net_pre_commands(u_boot_console): + """Execute any commands required to enable network hardware. + + These commands are provided by the boardenv_* file; see the comment at the + beginning of this file. + """ + + init_usb = u_boot_console.config.env.get('env__net_uses_usb', False) + if init_usb: + u_boot_console.run_command('usb start') + + init_pci = u_boot_console.config.env.get('env__net_uses_pci', False) + if init_pci: + u_boot_console.run_command('pci enum') + +@pytest.mark.buildconfigspec('cmd_dhcp') +def test_net_dhcp(u_boot_console): + """Test the dhcp command. + + The boardenv_* file may be used to enable/disable this test; see the + comment at the beginning of this file. + """ + + test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False) + if not test_dhcp: + pytest.skip('No DHCP server available') + + u_boot_console.run_command('setenv autoload no') + output = u_boot_console.run_command('dhcp') + assert 'DHCP client bound to address ' in output + + global net_set_up + net_set_up = True + +@pytest.mark.buildconfigspec('net') +def test_net_setup_static(u_boot_console): + """Set up a static IP configuration. + + The configuration is provided by the boardenv_* file; see the comment at + the beginning of this file. + """ + + env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None) + if not env_vars: + pytest.skip('No static network configuration is defined') + + for (var, val) in env_vars: + u_boot_console.run_command('setenv %s %s' % (var, val)) + + global net_set_up + net_set_up = True + +@pytest.mark.buildconfigspec('cmd_ping') +def test_net_ping(u_boot_console): + """Test the ping command. + + The $serverip (as set up by either test_net_dhcp or test_net_setup_static) + is pinged. The test validates that the host is alive, as reported by the + ping command's output. + """ + + if not net_set_up: + pytest.skip('Network not initialized') + + output = u_boot_console.run_command('ping $serverip') + assert 'is alive' in output + +@pytest.mark.buildconfigspec('cmd_net') +def test_net_tftpboot(u_boot_console): + """Test the tftpboot command. + + A file is downloaded from the TFTP server, its size and optionally its + CRC32 are validated. + + The details of the file to download are provided by the boardenv_* file; + see the comment at the beginning of this file. + """ + + if not net_set_up: + pytest.skip('Network not initialized') + + f = u_boot_console.config.env.get('env__net_tftp_readable_file', None) + if not f: + pytest.skip('No TFTP readable file to read') + + addr = f.get('addr', None) + + fn = f['fn'] + if not addr: + output = u_boot_console.run_command('tftpboot %s' % (fn)) + else: + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output + + expected_crc = f.get('crc32', None) + if not expected_crc: + return + + if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return + + output = u_boot_console.run_command('crc32 $fileaddr $filesize') + assert expected_crc in output + +@pytest.mark.buildconfigspec('cmd_nfs') +def test_net_nfs(u_boot_console): + """Test the nfs command. + + A file is downloaded from the NFS server, its size and optionally its + CRC32 are validated. + + The details of the file to download are provided by the boardenv_* file; + see the comment at the beginning of this file. + """ + + if not net_set_up: + pytest.skip('Network not initialized') + + f = u_boot_console.config.env.get('env__net_nfs_readable_file', None) + if not f: + pytest.skip('No NFS readable file to read') + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + fn = f['fn'] + output = u_boot_console.run_command('nfs %x %s' % (addr, fn)) + expected_text = 'Bytes transferred = ' + sz = f.get('size', None) + if sz: + expected_text += '%d' % sz + assert expected_text in output + + expected_crc = f.get('crc32', None) + if not expected_crc: + return + + if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y': + return + + output = u_boot_console.run_command('crc32 %x $filesize' % addr) + assert expected_crc in output diff --git a/roms/u-boot/test/py/tests/test_ofplatdata.py b/roms/u-boot/test/py/tests/test_ofplatdata.py new file mode 100644 index 000000000..e9cce4daf --- /dev/null +++ b/roms/u-boot/test/py/tests/test_ofplatdata.py @@ -0,0 +1,23 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016 Google, Inc + +import pytest +import u_boot_utils as util + +@pytest.mark.boardspec('sandbox_spl') +@pytest.mark.buildconfigspec('spl_of_platdata') +def test_spl_devicetree(u_boot_console): + """Test content of spl device-tree""" + cons = u_boot_console + dtb = cons.config.build_dir + '/spl/u-boot-spl.dtb' + fdtgrep = cons.config.build_dir + '/tools/fdtgrep' + output = util.run_and_log(cons, [fdtgrep, '-l', dtb]) + + assert "u-boot,dm-pre-reloc" not in output + assert "u-boot,dm-pre-proper" not in output + assert "u-boot,dm-spl" not in output + assert "u-boot,dm-tpl" not in output + + assert "spl-test5" not in output + assert "spl-test6" not in output + assert "spl-test7" in output diff --git a/roms/u-boot/test/py/tests/test_part.py b/roms/u-boot/test/py/tests/test_part.py new file mode 100644 index 000000000..cba980451 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_part.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (C) 2020 +# Niel Fourie, DENX Software Engineering, lusus@denx.de + +import pytest + +@pytest.mark.buildconfigspec('cmd_part') +@pytest.mark.buildconfigspec('partitions') +@pytest.mark.buildconfigspec('efi_partition') +def test_dm_compat(u_boot_console): + """Test that `part types` prints a result which includes `EFI`.""" + output = u_boot_console.run_command('part types') + assert "Supported partition tables:" in output + assert "EFI" in output diff --git a/roms/u-boot/test/py/tests/test_pinmux.py b/roms/u-boot/test/py/tests/test_pinmux.py new file mode 100644 index 000000000..0cbbae000 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_pinmux.py @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: GPL-2.0 + +import pytest +import u_boot_utils + +@pytest.mark.buildconfigspec('cmd_pinmux') +def test_pinmux_usage_1(u_boot_console): + """Test that 'pinmux' command without parameters displays + pinmux usage.""" + output = u_boot_console.run_command('pinmux') + assert 'Usage:' in output + +@pytest.mark.buildconfigspec('cmd_pinmux') +def test_pinmux_usage_2(u_boot_console): + """Test that 'pinmux status' executed without previous "pinmux dev" + command displays pinmux usage.""" + output = u_boot_console.run_command('pinmux status') + assert 'Usage:' in output + +@pytest.mark.buildconfigspec('cmd_pinmux') +@pytest.mark.boardspec('sandbox') +def test_pinmux_status_all(u_boot_console): + """Test that 'pinmux status -a' displays pin's muxing.""" + output = u_boot_console.run_command('pinmux status -a') + + assert ('pinctrl-gpio:' in output) + assert ('a5 : gpio output .' in output) + assert ('a6 : gpio output .' in output) + + assert ('pinctrl:' in output) + assert ('P0 : UART TX.' in output) + assert ('P1 : UART RX.' in output) + assert ('P2 : I2S SCK.' in output) + assert ('P3 : I2S SD.' in output) + assert ('P4 : I2S WS.' in output) + assert ('P5 : GPIO0 bias-pull-up input-disable.' in output) + assert ('P6 : GPIO1 drive-open-drain.' in output) + assert ('P7 : GPIO2 bias-pull-down input-enable.' in output) + assert ('P8 : GPIO3 bias-disable.' in output) + +@pytest.mark.buildconfigspec('cmd_pinmux') +@pytest.mark.boardspec('sandbox') +def test_pinmux_list(u_boot_console): + """Test that 'pinmux list' returns the pin-controller list.""" + output = u_boot_console.run_command('pinmux list') + assert 'sandbox_pinctrl' in output + +@pytest.mark.buildconfigspec('cmd_pinmux') +def test_pinmux_dev_bad(u_boot_console): + """Test that 'pinmux dev' returns an error when trying to select a + wrong pin controller.""" + pincontroller = 'bad_pin_controller_name' + output = u_boot_console.run_command('pinmux dev ' + pincontroller) + expected_output = 'Can\'t get the pin-controller: ' + pincontroller + '!' + assert (expected_output in output) + +@pytest.mark.buildconfigspec('cmd_pinmux') +@pytest.mark.boardspec('sandbox') +def test_pinmux_dev(u_boot_console): + """Test that 'pinmux dev' select the wanted pin controller.""" + pincontroller = 'pinctrl' + output = u_boot_console.run_command('pinmux dev ' + pincontroller) + expected_output = 'dev: ' + pincontroller + assert (expected_output in output) + +@pytest.mark.buildconfigspec('cmd_pinmux') +@pytest.mark.boardspec('sandbox') +def test_pinmux_status(u_boot_console): + """Test that 'pinmux status' displays selected pincontroller's pin + muxing descriptions.""" + output = u_boot_console.run_command('pinmux status') + + assert (not 'pinctrl-gpio:' in output) + assert (not 'pinctrl:' in output) + + assert ('P0 : UART TX.' in output) + assert ('P1 : UART RX.' in output) + assert ('P2 : I2S SCK.' in output) + assert ('P3 : I2S SD.' in output) + assert ('P4 : I2S WS.' in output) + assert ('P5 : GPIO0 bias-pull-up input-disable.' in output) + assert ('P6 : GPIO1 drive-open-drain.' in output) + assert ('P7 : GPIO2 bias-pull-down input-enable.' in output) + assert ('P8 : GPIO3 bias-disable.' in output) diff --git a/roms/u-boot/test/py/tests/test_pstore.py b/roms/u-boot/test/py/tests/test_pstore.py new file mode 100644 index 000000000..5a35724f6 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_pstore.py @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2020, Collabora +# Author: Frédéric Danis <frederic.danis@collabora.com> + +import pytest +import u_boot_utils +import os +import tempfile +import shutil + +PSTORE_ADDR=0x3000000 +PSTORE_LENGTH=0x100000 +PSTORE_PANIC1='test/py/tests/test_pstore_data_panic1.hex' +PSTORE_PANIC2='test/py/tests/test_pstore_data_panic2.hex' +PSTORE_CONSOLE='test/py/tests/test_pstore_data_console.hex' +ADDR=0x01000008 + +def load_pstore(u_boot_console): + """Load PStore records from sample files""" + + output = u_boot_console.run_command_list([ + 'host load hostfs - 0x%x %s' % (PSTORE_ADDR, + os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC1)), + 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 4096, + os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC2)), + 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 253 * 4096, + os.path.join(u_boot_console.config.source_dir, PSTORE_CONSOLE)), + 'pstore set 0x%x 0x%x' % (PSTORE_ADDR, PSTORE_LENGTH)]) + +def checkfile(u_boot_console, path, filesize, checksum): + """Check file against MD5 checksum""" + + output = u_boot_console.run_command_list([ + 'load hostfs - %x %s' % (ADDR, path), + 'printenv filesize']) + assert('filesize=%x' % (filesize) in ''.join(output)) + + output = u_boot_console.run_command_list([ + 'md5sum %x $filesize' % ADDR, + 'setenv filesize']) + assert(checksum in ''.join(output)) + +@pytest.mark.buildconfigspec('cmd_pstore') +def test_pstore_display_all_records(u_boot_console): + """Test that pstore displays all records.""" + + u_boot_console.run_command('') + load_pstore(u_boot_console) + response = u_boot_console.run_command('pstore display') + assert('**** Dump' in response) + assert('**** Console' in response) + +@pytest.mark.buildconfigspec('cmd_pstore') +def test_pstore_display_one_record(u_boot_console): + """Test that pstore displays only one record.""" + + u_boot_console.run_command('') + load_pstore(u_boot_console) + response = u_boot_console.run_command('pstore display dump 1') + assert('Panic#2 Part1' in response) + assert('**** Console' not in response) + +@pytest.mark.buildconfigspec('cmd_pstore') +def test_pstore_save_records(u_boot_console): + """Test that pstore saves all records.""" + + outdir = tempfile.mkdtemp() + + u_boot_console.run_command('') + load_pstore(u_boot_console) + u_boot_console.run_command('pstore save hostfs - %s' % (outdir)) + + checkfile(u_boot_console, '%s/dmesg-ramoops-0' % (outdir), 3798, '8059335ab4cfa62c77324c491659c503') + checkfile(u_boot_console, '%s/dmesg-ramoops-1' % (outdir), 4035, '3ff30df3429d81939c75d0070b5187b9') + checkfile(u_boot_console, '%s/console-ramoops-0' % (outdir), 4084, 'bb44de4a9b8ebd9b17ae98003287325b') + + shutil.rmtree(outdir) diff --git a/roms/u-boot/test/py/tests/test_pstore_data_console.hex b/roms/u-boot/test/py/tests/test_pstore_data_console.hex Binary files differnew file mode 100644 index 000000000..e7f426e89 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_pstore_data_console.hex diff --git a/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex b/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex Binary files differnew file mode 100644 index 000000000..988929d12 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex diff --git a/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex b/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex Binary files differnew file mode 100644 index 000000000..8f9d56cbe --- /dev/null +++ b/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex diff --git a/roms/u-boot/test/py/tests/test_qfw.py b/roms/u-boot/test/py/tests/test_qfw.py new file mode 100644 index 000000000..8b668c972 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_qfw.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2021, Asherah Connor <ashe@kivikakk.ee> + +# Test qfw command implementation + +import pytest + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_cpus(u_boot_console): + "Test QEMU firmware config reports the CPU count." + + output = u_boot_console.run_command('qfw cpus') + # The actual number varies depending on the board under test, so only + # assert a non-zero output. + assert 'cpu(s) online' in output + assert '0 cpu(s) online' not in output + +@pytest.mark.buildconfigspec('cmd_qfw') +def test_qfw_list(u_boot_console): + "Test QEMU firmware config lists devices." + + output = u_boot_console.run_command('qfw list') + # Assert either: + # 1) 'test-one', from the sandbox driver, or + # 2) 'bootorder', found in every real QEMU implementation. + assert ("bootorder" in output) or ("test-one" in output) diff --git a/roms/u-boot/test/py/tests/test_sandbox_exit.py b/roms/u-boot/test/py/tests/test_sandbox_exit.py new file mode 100644 index 000000000..706f5fa35 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_sandbox_exit.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +import pytest +import signal + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('sysreset_cmd_poweroff') +def test_poweroff(u_boot_console): + """Test that the "poweroff" command exits sandbox process.""" + + u_boot_console.run_command('poweroff', wait_for_prompt=False) + assert(u_boot_console.validate_exited()) + +@pytest.mark.boardspec('sandbox') +def test_ctrl_c(u_boot_console): + """Test that sending SIGINT to sandbox causes it to exit.""" + + u_boot_console.kill(signal.SIGINT) + assert(u_boot_console.validate_exited()) + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_exception') +@pytest.mark.buildconfigspec('sandbox_crash_reset') +def test_exception_reset(u_boot_console): + """Test that SIGILL causes a reset.""" + + u_boot_console.run_command('exception undefined', wait_for_prompt=False) + m = u_boot_console.p.expect(['resetting ...', 'U-Boot']) + if m != 0: + raise Exception('SIGILL did not lead to reset') + m = u_boot_console.p.expect(['U-Boot', '=>']) + if m != 0: + raise Exception('SIGILL did not lead to reset') + u_boot_console.restart_uboot() + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('cmd_exception') +@pytest.mark.notbuildconfigspec('sandbox_crash_reset') +def test_exception_exit(u_boot_console): + """Test that SIGILL causes a reset.""" + + u_boot_console.run_command('exception undefined', wait_for_prompt=False) + assert(u_boot_console.validate_exited()) diff --git a/roms/u-boot/test/py/tests/test_scp03.py b/roms/u-boot/test/py/tests/test_scp03.py new file mode 100644 index 000000000..1f689252d --- /dev/null +++ b/roms/u-boot/test/py/tests/test_scp03.py @@ -0,0 +1,27 @@ +# Copyright (c) 2021 Foundries.io Ltd +# +# SPDX-License-Identifier: GPL-2.0+ +# +# SCP03 command test + +""" +This tests SCP03 command in U-boot. + +For additional details check doc/usage/scp03.rst +""" + +import pytest +import u_boot_utils as util + +@pytest.mark.buildconfigspec('cmd_scp03') +def test_scp03(u_boot_console): + """Enable and provision keys with SCP03 + """ + + success_str1 = "SCP03 is enabled" + success_str2 = "SCP03 is provisioned" + + response = u_boot_console.run_command('scp03 enable') + assert success_str1 in response + response = u_boot_console.run_command('scp03 provision') + assert success_str2 in response diff --git a/roms/u-boot/test/py/tests/test_sf.py b/roms/u-boot/test/py/tests/test_sf.py new file mode 100644 index 000000000..adf8b7dc8 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_sf.py @@ -0,0 +1,217 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, Xilinx Inc. Michal Simek +# Copyright (c) 2017, Xiphos Systems Corp. All rights reserved. + +import re +import pytest +import random +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +which SPI Flash areas are available for testing. Without this, this test will +be automatically skipped. +For example: + +# A list of sections of Flash memory to be tested. +env__sf_configs = ( + { + # Where in SPI Flash should the test operate. + 'offset': 0x00000000, + # This value is optional. + # If present, specifies the [[bus:]cs] argument used in `sf probe` + # If missing, defaults to 0. + 'id': '0:1', + # This value is optional. + # If set as a number, specifies the speed of the SPI Flash. + # If set as an array of 2, specifies a range for a random speed. + # If missing, defaults to 0. + 'speed': 1000000, + # This value is optional. + # If present, specifies the size to use for read/write operations. + # If missing, the SPI Flash page size is used as a default (based on + # the `sf probe` output). + 'len': 0x10000, + # This value is optional. + # If present, specifies if the test can write to Flash offset + # If missing, defaults to False. + 'writeable': False, + # This value is optional. + # If present, specifies the expected CRC32 value of the flash area. + # If missing, extra check is ignored. + 'crc32': 0xCAFECAFE, + }, +) +""" + +def sf_prepare(u_boot_console, env__sf_config): + """Check global state of the SPI Flash before running any test. + + Args: + u_boot_console: A U-Boot console connection. + env__sf_config: The single SPI Flash device configuration on which to + run the tests. + + Returns: + sf_params: a dictionary of SPI Flash parameters. + """ + + sf_params = {} + sf_params['ram_base'] = u_boot_utils.find_ram_base(u_boot_console) + + probe_id = env__sf_config.get('id', 0) + speed = env__sf_config.get('speed', 0) + if isinstance(speed, int): + sf_params['speed'] = speed + else: + assert len(speed) == 2, "If speed is a list, it must have 2 entries" + sf_params['speed'] = random.randint(speed[0], speed[1]) + + cmd = 'sf probe %d %d' % (probe_id, sf_params['speed']) + + output = u_boot_console.run_command(cmd) + assert 'SF: Detected' in output, 'No Flash device available' + + m = re.search('page size (.+?) Bytes', output) + assert m, 'SPI Flash page size not recognized' + sf_params['page_size'] = int(m.group(1)) + + m = re.search('erase size (.+?) KiB', output) + assert m, 'SPI Flash erase size not recognized' + sf_params['erase_size'] = int(m.group(1)) + sf_params['erase_size'] *= 1024 + + m = re.search('total (.+?) MiB', output) + assert m, 'SPI Flash total size not recognized' + sf_params['total_size'] = int(m.group(1)) + sf_params['total_size'] *= 1024 * 1024 + + assert 'offset' in env__sf_config, \ + '\'offset\' is required for this test.' + sf_params['len'] = env__sf_config.get('len', sf_params['erase_size']) + + assert not env__sf_config['offset'] % sf_params['erase_size'], \ + 'offset not multiple of erase size.' + assert not sf_params['len'] % sf_params['erase_size'], \ + 'erase length not multiple of erase size.' + + assert not (env__sf_config.get('writeable', False) and + 'crc32' in env__sf_config), \ + 'Cannot check crc32 on writeable sections' + + return sf_params + +def sf_read(u_boot_console, env__sf_config, sf_params): + """Helper function used to read and compute the CRC32 value of a section of + SPI Flash memory. + + Args: + u_boot_console: A U-Boot console connection. + env__sf_config: The single SPI Flash device configuration on which to + run the tests. + sf_params: SPI Flash parameters. + + Returns: + CRC32 value of SPI Flash section + """ + + addr = sf_params['ram_base'] + offset = env__sf_config['offset'] + count = sf_params['len'] + pattern = random.randint(0, 0xFF) + crc_expected = env__sf_config.get('crc32', None) + + cmd = 'mw.b %08x %02x %x' % (addr, pattern, count) + u_boot_console.run_command(cmd) + crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count) + if crc_expected: + assert crc_pattern != crc_expected + + cmd = 'sf read %08x %08x %x' % (addr, offset, count) + response = u_boot_console.run_command(cmd) + assert 'Read: OK' in response, 'Read operation failed' + crc_readback = u_boot_utils.crc32(u_boot_console, addr, count) + assert crc_pattern != crc_readback, 'sf read did not update RAM content.' + if crc_expected: + assert crc_readback == crc_expected + + return crc_readback + +def sf_update(u_boot_console, env__sf_config, sf_params): + """Helper function used to update a section of SPI Flash memory. + + Args: + u_boot_console: A U-Boot console connection. + env__sf_config: The single SPI Flash device configuration on which to + run the tests. + + Returns: + CRC32 value of SPI Flash section + """ + + addr = sf_params['ram_base'] + offset = env__sf_config['offset'] + count = sf_params['len'] + pattern = int(random.random() * 0xFF) + + cmd = 'mw.b %08x %02x %x' % (addr, pattern, count) + u_boot_console.run_command(cmd) + crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count) + + cmd = 'sf update %08x %08x %x' % (addr, offset, count) + u_boot_console.run_command(cmd) + crc_readback = sf_read(u_boot_console, env__sf_config, sf_params) + + assert crc_readback == crc_pattern + +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_crc32') +@pytest.mark.buildconfigspec('cmd_memory') +def test_sf_read(u_boot_console, env__sf_config): + sf_params = sf_prepare(u_boot_console, env__sf_config) + sf_read(u_boot_console, env__sf_config, sf_params) + +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_crc32') +@pytest.mark.buildconfigspec('cmd_memory') +def test_sf_read_twice(u_boot_console, env__sf_config): + sf_params = sf_prepare(u_boot_console, env__sf_config) + + crc1 = sf_read(u_boot_console, env__sf_config, sf_params) + sf_params['ram_base'] += 0x100 + crc2 = sf_read(u_boot_console, env__sf_config, sf_params) + + assert crc1 == crc2, 'CRC32 of two successive read operation do not match' + +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_crc32') +@pytest.mark.buildconfigspec('cmd_memory') +def test_sf_erase(u_boot_console, env__sf_config): + if not env__sf_config.get('writeable', False): + pytest.skip('Flash config is tagged as not writeable') + + sf_params = sf_prepare(u_boot_console, env__sf_config) + addr = sf_params['ram_base'] + offset = env__sf_config['offset'] + count = sf_params['len'] + + cmd = 'sf erase %08x %x' % (offset, count) + output = u_boot_console.run_command(cmd) + assert 'Erased: OK' in output, 'Erase operation failed' + + cmd = 'mw.b %08x ff %x' % (addr, count) + u_boot_console.run_command(cmd) + crc_ffs = u_boot_utils.crc32(u_boot_console, addr, count) + + crc_read = sf_read(u_boot_console, env__sf_config, sf_params) + assert crc_ffs == crc_read, 'Unexpected CRC32 after erase operation.' + +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_crc32') +@pytest.mark.buildconfigspec('cmd_memory') +def test_sf_update(u_boot_console, env__sf_config): + if not env__sf_config.get('writeable', False): + pytest.skip('Flash config is tagged as not writeable') + + sf_params = sf_prepare(u_boot_console, env__sf_config) + sf_update(u_boot_console, env__sf_config, sf_params) diff --git a/roms/u-boot/test/py/tests/test_shell_basics.py b/roms/u-boot/test/py/tests/test_shell_basics.py new file mode 100644 index 000000000..68a3f892f --- /dev/null +++ b/roms/u-boot/test/py/tests/test_shell_basics.py @@ -0,0 +1,45 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Test basic shell functionality, such as commands separate by semi-colons. + +import pytest + +pytestmark = pytest.mark.buildconfigspec('cmd_echo') + +def test_shell_execute(u_boot_console): + """Test any shell command.""" + + response = u_boot_console.run_command('echo hello') + assert response.strip() == 'hello' + +def test_shell_semicolon_two(u_boot_console): + """Test two shell commands separate by a semi-colon.""" + + cmd = 'echo hello; echo world' + response = u_boot_console.run_command(cmd) + # This validation method ignores the exact whitespace between the strings + assert response.index('hello') < response.index('world') + +def test_shell_semicolon_three(u_boot_console): + """Test three shell commands separate by a semi-colon, with variable + expansion dependencies between them.""" + + cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \ + 'echo ${list}' + response = u_boot_console.run_command(cmd) + assert response.strip() == '123' + u_boot_console.run_command('setenv list') + +def test_shell_run(u_boot_console): + """Test the "run" shell command.""" + + u_boot_console.run_command('setenv foo \'setenv monty 1; setenv python 2\'') + u_boot_console.run_command('run foo') + response = u_boot_console.run_command('echo ${monty}') + assert response.strip() == '1' + response = u_boot_console.run_command('echo ${python}') + assert response.strip() == '2' + u_boot_console.run_command('setenv foo') + u_boot_console.run_command('setenv monty') + u_boot_console.run_command('setenv python') diff --git a/roms/u-boot/test/py/tests/test_sleep.py b/roms/u-boot/test/py/tests/test_sleep.py new file mode 100644 index 000000000..392af29db --- /dev/null +++ b/roms/u-boot/test/py/tests/test_sleep.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +import pytest +import time + +""" +Note: This test doesn't rely on boardenv_* configuration values but they can +change test behavior. + +# Setup env__sleep_accurate to False if time is not accurate on your platform +env__sleep_accurate = False + +# Setup env__sleep_time time in seconds board is set to sleep +env__sleep_time = 3 + +# Setup env__sleep_margin set a margin for any system overhead +env__sleep_margin = 0.25 + +""" + +def test_sleep(u_boot_console): + """Test the sleep command, and validate that it sleeps for approximately + the correct amount of time.""" + + sleep_skip = u_boot_console.config.env.get('env__sleep_accurate', True) + if not sleep_skip: + pytest.skip('sleep is not accurate') + + if u_boot_console.config.buildconfig.get('config_cmd_misc', 'n') != 'y': + pytest.skip('sleep command not supported') + + # 3s isn't too long, but is enough to cross a few second boundaries. + sleep_time = u_boot_console.config.env.get('env__sleep_time', 3) + sleep_margin = u_boot_console.config.env.get('env__sleep_margin', 0.25) + tstart = time.time() + u_boot_console.run_command('sleep %d' % sleep_time) + tend = time.time() + elapsed = tend - tstart + assert elapsed >= (sleep_time - 0.01) + if not u_boot_console.config.gdbserver: + # margin is hopefully enough to account for any system overhead. + assert elapsed < (sleep_time + sleep_margin) diff --git a/roms/u-boot/test/py/tests/test_spl.py b/roms/u-boot/test/py/tests/test_spl.py new file mode 100644 index 000000000..bd273dad8 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_spl.py @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright 2020 Google LLC +# Written by Simon Glass <sjg@chromium.org> + +import os.path +import pytest + +def test_spl(u_boot_console, ut_spl_subtest): + """Execute a "ut" subtest. + + The subtests are collected in function generate_ut_subtest() from linker + generated lists by applying a regular expression to the lines of file + spl/u-boot-spl.sym. The list entries are created using the C macro + UNIT_TEST(). + + Strict naming conventions have to be followed to match the regular + expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in + test suite foo that can be executed via command 'ut foo bar' and is + implemented in C function foo_test_bar(). + + Args: + u_boot_console (ConsoleBase): U-Boot console + ut_subtest (str): SPL test to be executed (e.g. 'dm platdata_phandle') + """ + try: + cons = u_boot_console + cons.restart_uboot_with_flags(['-u', '-k', ut_spl_subtest.split()[1]]) + output = cons.get_spawn_output().replace('\r', '') + assert 'Failures: 0' in output + finally: + # Restart afterward in case a non-SPL test is run next. This should not + # happen since SPL tests are run in their own invocation of test.py, but + # the cost of doing this is not too great at present. + u_boot_console.restart_uboot() diff --git a/roms/u-boot/test/py/tests/test_stackprotector.py b/roms/u-boot/test/py/tests/test_stackprotector.py new file mode 100644 index 000000000..b009437e5 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_stackprotector.py @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2021 Broadcom + +import pytest +import signal + +@pytest.mark.buildconfigspec('cmd_stackprotector_test') +def test_stackprotector(u_boot_console): + """Test that the stackprotector function works.""" + + u_boot_console.run_command('stackprot_test',wait_for_prompt=False) + expected_response = 'Stack smashing detected' + u_boot_console.wait_for(expected_response) + u_boot_console.restart_uboot() diff --git a/roms/u-boot/test/py/tests/test_tpm2.py b/roms/u-boot/test/py/tests/test_tpm2.py new file mode 100644 index 000000000..70f906da5 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_tpm2.py @@ -0,0 +1,233 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2018, Bootlin +# Author: Miquel Raynal <miquel.raynal@bootlin.com> + +import os.path +import pytest +import u_boot_utils +import re +import time + +""" +Test the TPMv2.x related commands. You must have a working hardware setup in +order to do these tests. + +Notes: +* These tests will prove the password mechanism. The TPM chip must be cleared of +any password. +* Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented +here because they would fail the tests in most cases (TPMs do not implement them +and return an error). +""" + +updates = 0 + +def force_init(u_boot_console, force=False): + """When a test fails, U-Boot is reset. Because TPM stack must be initialized + after each reboot, we must ensure these lines are always executed before + trying any command or they will fail with no reason. Executing 'tpm init' + twice will spawn an error used to detect that the TPM was not reset and no + initialization code should be run. + """ + output = u_boot_console.run_command('tpm2 init') + if force or not 'Error' in output: + u_boot_console.run_command('echo --- start of init ---') + u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR') + u_boot_console.run_command('tpm2 self_test full') + u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT') + output = u_boot_console.run_command('echo $?') + if not output.endswith('0'): + u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') + u_boot_console.run_command('echo --- end of init ---') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_init(u_boot_console): + """Init the software stack to use TPMv2 commands.""" + + u_boot_console.run_command('tpm2 init') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_startup(u_boot_console): + """Execute a TPM2_Startup command. + + Initiate the TPM internal state machine. + """ + + u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_self_test_full(u_boot_console): + """Execute a TPM2_SelfTest (full) command. + + Ask the TPM to perform all self tests to also enable full capabilities. + """ + + u_boot_console.run_command('tpm2 self_test full') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_continue_self_test(u_boot_console): + """Execute a TPM2_SelfTest (continued) command. + + Ask the TPM to finish its self tests (alternative to the full test) in order + to enter a fully operational state. + """ + + u_boot_console.run_command('tpm2 self_test continue') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_clear(u_boot_console): + """Execute a TPM2_Clear command. + + Ask the TPM to reset entirely its internal state (including internal + configuration, passwords, counters and DAM parameters). This is half of the + TAKE_OWNERSHIP command from TPMv1. + + Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must + not have a password set, otherwise this test will fail. ENDORSEMENT and + PLATFORM hierarchies are also available. + """ + + u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_change_auth(u_boot_console): + """Execute a TPM2_HierarchyChangeAuth command. + + Ask the TPM to change the owner, ie. set a new password: 'unicorn' + + Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are + also available. + """ + + force_init(u_boot_console) + + u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn') + output = u_boot_console.run_command('echo $?') + u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_get_capability(u_boot_console): + """Execute a TPM_GetCapability command. + + Display one capability. In our test case, let's display the default DAM + lockout counter that should be 0 since the CLEAR: + - TPM_CAP_TPM_PROPERTIES = 0x6 + - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14 + + There is no expected default values because it would depend on the chip + used. We can still save them in order to check they have changed later. + """ + + force_init(u_boot_console) + ram = u_boot_utils.find_ram_base(u_boot_console) + + read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + assert 'Property 0x0000020e: 0x00000000' in read_cap + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_dam_parameters(u_boot_console): + """Execute a TPM2_DictionaryAttackParameters command. + + Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change: + - Max number of failed authentication before lockout: 3 + - Time before the failure counter is automatically decremented: 10 sec + - Time after a lockout failure before it can be attempted again: 0 sec + + For an unknown reason, the DAM parameters must be changed before changing + the authentication, otherwise the lockout will be engaged after the first + failed authentication attempt. + """ + + force_init(u_boot_console) + ram = u_boot_utils.find_ram_base(u_boot_console) + + # Set the DAM parameters to known values + u_boot_console.run_command('tpm2 dam_parameters 3 10 0') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + # Check the values have been saved + read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + assert 'Property 0x0000020f: 0x00000003' in read_cap + assert 'Property 0x00000210: 0x0000000a' in read_cap + assert 'Property 0x00000211: 0x00000000' in read_cap + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_pcr_read(u_boot_console): + """Execute a TPM2_PCR_Read command. + + Perform a PCR read of the 0th PCR. Must be zero. + """ + + force_init(u_boot_console) + ram = u_boot_utils.find_ram_base(u_boot_console) + + read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + # Save the number of PCR updates + str = re.findall(r'\d+ known updates', read_pcr)[0] + global updates + updates = int(re.findall(r'\d+', str)[0]) + + # Check the output value + assert 'PCR #0 content' in read_pcr + assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_pcr_extend(u_boot_console): + """Execute a TPM2_PCR_Extend command. + + Perform a PCR extension with a known hash in memory (zeroed since the board + must have been rebooted). + + No authentication mechanism is used here, not protecting against packet + replay, yet. + """ + + force_init(u_boot_console) + ram = u_boot_utils.find_ram_base(u_boot_console) + + u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr + assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr + + str = re.findall(r'\d+ known updates', read_pcr)[0] + new_updates = int(re.findall(r'\d+', str)[0]) + assert (updates + 1) == new_updates + +@pytest.mark.buildconfigspec('cmd_tpm_v2') +def test_tpm2_cleanup(u_boot_console): + """Ensure the TPM is cleared from password or test related configuration.""" + + force_init(u_boot_console, True) diff --git a/roms/u-boot/test/py/tests/test_ums.py b/roms/u-boot/test/py/tests/test_ums.py new file mode 100644 index 000000000..749b16062 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_ums.py @@ -0,0 +1,236 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB +# device enumeration on the host, reads a small block of data from the UMS +# block device, optionally mounts a partition and performs filesystem-based +# read/write tests, and finally aborts the "ums" command in U-Boot. + +import os +import os.path +import pytest +import re +import time +import u_boot_utils + +""" +Note: This test relies on: + +a) boardenv_* to contain configuration values to define which USB ports are +available for testing. Without this, this test will be automatically skipped. +For example: + +# Leave this list empty if you have no block_devs below with writable +# partitions defined. +env__mount_points = ( + '/mnt/ubtest-mnt-p2371-2180-na', +) + +env__usb_dev_ports = ( + { + 'fixture_id': 'micro_b', + 'tgt_usb_ctlr': '0', + 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0', + }, +) + +env__block_devs = ( + # eMMC; always present + { + 'fixture_id': 'emmc', + 'type': 'mmc', + 'id': '0', + # The following two properties are optional. + # If present, the partition will be mounted and a file written-to and + # read-from it. If missing, only a simple block read test will be + # performed. + 'writable_fs_partition': 1, + 'writable_fs_subdir': 'tmp/', + }, + # SD card; present since I plugged one in + { + 'fixture_id': 'sd', + 'type': 'mmc', + 'id': '1' + }, +) + +b) udev rules to set permissions on devices nodes, so that sudo is not +required. For example: + +ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666" + +(You may wish to change the group ID instead of setting the permissions wide +open. All that matters is that the user ID running the test can access the +device.) + +c) /etc/fstab entries to allow the block device to be mounted without requiring +root permissions. For example: + +/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev + +This entry is only needed if any block_devs above contain a +writable_fs_partition value. +""" + +@pytest.mark.buildconfigspec('cmd_usb_mass_storage') +def test_ums(u_boot_console, env__usb_dev_port, env__block_devs): + """Test the "ums" command; the host system must be able to enumerate a UMS + device when "ums" is running, block and optionally file I/O are tested, + and this device must disappear when "ums" is aborted. + + Args: + u_boot_console: A U-Boot console connection. + env__usb_dev_port: The single USB device-mode port specification on + which to run the test. See the file-level comment above for + details of the format. + env__block_devs: The list of block devices that the target U-Boot + device has attached. See the file-level comment above for details + of the format. + + Returns: + Nothing. + """ + + have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0] + if not have_writable_fs_partition: + # If 'writable_fs_subdir' is missing, we'll skip all parts of the + # testing which mount filesystems. + u_boot_console.log.warning( + 'boardenv missing "writable_fs_partition"; ' + + 'UMS testing will be limited.') + + tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr'] + host_ums_dev_node = env__usb_dev_port['host_ums_dev_node'] + + # We're interested in testing USB device mode on each port, not the cross- + # product of that with each device. So, just pick the first entry in the + # device list here. We'll test each block device somewhere else. + tgt_dev_type = env__block_devs[0]['type'] + tgt_dev_id = env__block_devs[0]['id'] + if have_writable_fs_partition: + mount_point = u_boot_console.config.env['env__mount_points'][0] + mount_subdir = env__block_devs[0]['writable_fs_subdir'] + part_num = env__block_devs[0]['writable_fs_partition'] + host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num) + else: + host_ums_part_node = host_ums_dev_node + + test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin', + 1024 * 1024); + if have_writable_fs_partition: + mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn + + def start_ums(): + """Start U-Boot's ums shell command. + + This also waits for the host-side USB enumeration process to complete. + + Args: + None. + + Returns: + Nothing. + """ + + u_boot_console.log.action( + 'Starting long-running U-Boot ums shell command') + cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id) + u_boot_console.run_command(cmd, wait_for_prompt=False) + u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]')) + fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node) + u_boot_console.log.action('Reading raw data from UMS device') + fh.read(4096) + fh.close() + + def mount(): + """Mount the block device that U-Boot exports. + + Args: + None. + + Returns: + Nothing. + """ + + u_boot_console.log.action('Mounting exported UMS device') + cmd = ('/bin/mount', host_ums_part_node) + u_boot_utils.run_and_log(u_boot_console, cmd) + + def umount(ignore_errors): + """Unmount the block device that U-Boot exports. + + Args: + ignore_errors: Ignore any errors. This is useful if an error has + already been detected, and the code is performing best-effort + cleanup. In this case, we do not want to mask the original + error by "honoring" any new errors. + + Returns: + Nothing. + """ + + u_boot_console.log.action('Unmounting UMS device') + cmd = ('/bin/umount', host_ums_part_node) + u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors) + + def stop_ums(ignore_errors): + """Stop U-Boot's ums shell command from executing. + + This also waits for the host-side USB de-enumeration process to + complete. + + Args: + ignore_errors: Ignore any errors. This is useful if an error has + already been detected, and the code is performing best-effort + cleanup. In this case, we do not want to mask the original + error by "honoring" any new errors. + + Returns: + Nothing. + """ + + u_boot_console.log.action( + 'Stopping long-running U-Boot ums shell command') + u_boot_console.ctrlc() + u_boot_utils.wait_until_file_open_fails(host_ums_part_node, + ignore_errors) + + ignore_cleanup_errors = True + try: + start_ums() + if not have_writable_fs_partition: + # Skip filesystem-based testing if not configured + return + try: + mount() + u_boot_console.log.action('Writing test file via UMS') + cmd = ('rm', '-f', mounted_test_fn) + u_boot_utils.run_and_log(u_boot_console, cmd) + if os.path.exists(mounted_test_fn): + raise Exception('Could not rm target UMS test file') + cmd = ('cp', test_f.abs_fn, mounted_test_fn) + u_boot_utils.run_and_log(u_boot_console, cmd) + ignore_cleanup_errors = False + finally: + umount(ignore_errors=ignore_cleanup_errors) + finally: + stop_ums(ignore_errors=ignore_cleanup_errors) + + ignore_cleanup_errors = True + try: + start_ums() + try: + mount() + u_boot_console.log.action('Reading test file back via UMS') + read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn) + cmd = ('rm', '-f', mounted_test_fn) + u_boot_utils.run_and_log(u_boot_console, cmd) + ignore_cleanup_errors = False + finally: + umount(ignore_errors=ignore_cleanup_errors) + finally: + stop_ums(ignore_errors=ignore_cleanup_errors) + + written_hash = test_f.content_hash + assert(written_hash == read_back_hash) diff --git a/roms/u-boot/test/py/tests/test_unknown_cmd.py b/roms/u-boot/test/py/tests/test_unknown_cmd.py new file mode 100644 index 000000000..8fc284a92 --- /dev/null +++ b/roms/u-boot/test/py/tests/test_unknown_cmd.py @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +def test_unknown_command(u_boot_console): + """Test that executing an unknown command causes U-Boot to print an + error.""" + + # The "unknown command" error is actively expected here, + # so error detection for it is disabled. + with u_boot_console.disable_check('unknown_command'): + response = u_boot_console.run_command('non_existent_cmd') + assert('Unknown command \'non_existent_cmd\' - try \'help\'' in response) diff --git a/roms/u-boot/test/py/tests/test_ut.py b/roms/u-boot/test/py/tests/test_ut.py new file mode 100644 index 000000000..01c2b3ffa --- /dev/null +++ b/roms/u-boot/test/py/tests/test_ut.py @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +import os.path +import pytest + +@pytest.mark.buildconfigspec('ut_dm') +def test_ut_dm_init(u_boot_console): + """Initialize data for ut dm tests.""" + + fn = u_boot_console.config.source_dir + '/testflash.bin' + if not os.path.exists(fn): + data = b'this is a test' + data += b'\x00' * ((4 * 1024 * 1024) - len(data)) + with open(fn, 'wb') as fh: + fh.write(data) + + fn = u_boot_console.config.source_dir + '/spi.bin' + if not os.path.exists(fn): + data = b'\x00' * (2 * 1024 * 1024) + with open(fn, 'wb') as fh: + fh.write(data) + +def test_ut(u_boot_console, ut_subtest): + """Execute a "ut" subtest. + + The subtests are collected in function generate_ut_subtest() from linker + generated lists by applying a regular expression to the lines of file + u-boot.sym. The list entries are created using the C macro UNIT_TEST(). + + Strict naming conventions have to be followed to match the regular + expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in + test suite foo that can be executed via command 'ut foo bar' and is + implemented in C function foo_test_bar(). + + Args: + u_boot_console (ConsoleBase): U-Boot console + ut_subtest (str): test to be executed via command ut, e.g 'foo bar' to + execute command 'ut foo bar' + """ + + output = u_boot_console.run_command('ut ' + ut_subtest) + assert output.endswith('Failures: 0') diff --git a/roms/u-boot/test/py/tests/test_vboot.py b/roms/u-boot/test/py/tests/test_vboot.py new file mode 100644 index 000000000..6dff6779d --- /dev/null +++ b/roms/u-boot/test/py/tests/test_vboot.py @@ -0,0 +1,401 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2016, Google Inc. +# +# U-Boot Verified Boot Test + +""" +This tests verified boot in the following ways: + +For image verification: +- Create FIT (unsigned) with mkimage +- Check that verification shows that no keys are verified +- Sign image +- Check that verification shows that a key is now verified + +For configuration verification: +- Corrupt signature and check for failure +- Create FIT (with unsigned configuration) with mkimage +- Check that image verification works +- Sign the FIT and mark the key as 'required' for verification +- Check that image verification works +- Corrupt the signature +- Check that image verification no-longer works + +Tests run with both SHA1 and SHA256 hashing. +""" + +import shutil +import struct +import pytest +import u_boot_utils as util +import vboot_forge +import vboot_evil + +# Only run the full suite on a few combinations, since it doesn't add any more +# test coverage. +TESTDATA = [ + ['sha1', '', None, False, True], + ['sha1', '', '-E -p 0x10000', False, False], + ['sha1', '-pss', None, False, False], + ['sha1', '-pss', '-E -p 0x10000', False, False], + ['sha256', '', None, False, False], + ['sha256', '', '-E -p 0x10000', False, False], + ['sha256', '-pss', None, False, False], + ['sha256', '-pss', '-E -p 0x10000', False, False], + ['sha256', '-pss', None, True, False], + ['sha256', '-pss', '-E -p 0x10000', True, True], +] + +@pytest.mark.boardspec('sandbox') +@pytest.mark.buildconfigspec('fit_signature') +@pytest.mark.requiredtool('dtc') +@pytest.mark.requiredtool('fdtget') +@pytest.mark.requiredtool('fdtput') +@pytest.mark.requiredtool('openssl') +@pytest.mark.parametrize("sha_algo,padding,sign_options,required,full_test", + TESTDATA) +def test_vboot(u_boot_console, sha_algo, padding, sign_options, required, + full_test): + """Test verified boot signing with mkimage and verification with 'bootm'. + + This works using sandbox only as it needs to update the device tree used + by U-Boot to hold public keys from the signing process. + + The SHA1 and SHA256 tests are combined into a single test since the + key-generation process is quite slow and we want to avoid doing it twice. + """ + def dtc(dts): + """Run the device tree compiler to compile a .dts file + + The output file will be the same as the input file but with a .dtb + extension. + + Args: + dts: Device tree file to compile. + """ + dtb = dts.replace('.dts', '.dtb') + util.run_and_log(cons, 'dtc %s %s%s -O dtb ' + '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb)) + + def run_bootm(sha_algo, test_type, expect_string, boots, fit=None): + """Run a 'bootm' command U-Boot. + + This always starts a fresh U-Boot instance since the device tree may + contain a new public key. + + Args: + test_type: A string identifying the test type. + expect_string: A string which is expected in the output. + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + boots: A boolean that is True if Linux should boot and False if + we are expected to not boot + fit: FIT filename to load and verify + """ + if not fit: + fit = '%stest.fit' % tmpdir + cons.restart_uboot() + with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)): + output = cons.run_command_list( + ['host load hostfs - 100 %s' % fit, + 'fdt addr 100', + 'bootm 100']) + assert expect_string in ''.join(output) + if boots: + assert 'sandbox: continuing, as we cannot run' in ''.join(output) + else: + assert('sandbox: continuing, as we cannot run' + not in ''.join(output)) + + def make_fit(its): + """Make a new FIT from the .its source file. + + This runs 'mkimage -f' to create a new FIT. + + Args: + its: Filename containing .its source. + """ + util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', + '%s%s' % (datadir, its), fit]) + + def sign_fit(sha_algo, options): + """Sign the FIT + + Signs the FIT and writes the signature into it. It also writes the + public key into the dtb. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + options: Options to provide to mkimage. + """ + args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit] + if options: + args += options.split(' ') + cons.log.action('%s: Sign images' % sha_algo) + util.run_and_log(cons, args) + + def sign_fit_norequire(sha_algo, options): + """Sign the FIT + + Signs the FIT and writes the signature into it. It also writes the + public key into the dtb. It does not mark key as 'required' in dtb. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + options: Options to provide to mkimage. + """ + args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit] + if options: + args += options.split(' ') + cons.log.action('%s: Sign images' % sha_algo) + util.run_and_log(cons, args) + + def replace_fit_totalsize(size): + """Replace FIT header's totalsize with something greater. + + The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE. + If the size is greater, the signature verification should return false. + + Args: + size: The new totalsize of the header + + Returns: + prev_size: The previous totalsize read from the header + """ + total_size = 0 + with open(fit, 'r+b') as handle: + handle.seek(4) + total_size = handle.read(4) + handle.seek(4) + handle.write(struct.pack(">I", size)) + return struct.unpack(">I", total_size)[0] + + def create_rsa_pair(name): + """Generate a new RSA key paid and certificate + + Args: + name: Name of of the key (e.g. 'dev') + """ + public_exponent = 65537 + util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key ' + '-pkeyopt rsa_keygen_bits:2048 ' + '-pkeyopt rsa_keygen_pubexp:%d' % + (tmpdir, name, public_exponent)) + + # Create a certificate containing the public key + util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key ' + '-out %s%s.crt' % (tmpdir, name, tmpdir, name)) + + def test_with_algo(sha_algo, padding, sign_options): + """Test verified boot with the given hash algorithm. + + This is the main part of the test code. The same procedure is followed + for both hashing algorithms. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to + use. + padding: Either '' or '-pss', to select the padding to use for the + rsa signature algorithm. + sign_options: Options to mkimage when signing a fit image. + """ + # Compile our device tree files for kernel and U-Boot. These are + # regenerated here since mkimage will modify them (by adding a + # public key) below. + dtc('sandbox-kernel.dts') + dtc('sandbox-u-boot.dts') + + # Build the FIT, but don't sign anything yet + cons.log.action('%s: Test FIT with signed images' % sha_algo) + make_fit('sign-images-%s%s.its' % (sha_algo, padding)) + run_bootm(sha_algo, 'unsigned images', 'dev-', True) + + # Sign images with our dev keys + sign_fit(sha_algo, sign_options) + run_bootm(sha_algo, 'signed images', 'dev+', True) + + # Create a fresh .dtb without the public keys + dtc('sandbox-u-boot.dts') + + cons.log.action('%s: Test FIT with signed configuration' % sha_algo) + make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + run_bootm(sha_algo, 'unsigned config', '%s+ OK' % sha_algo, True) + + # Sign images with our dev keys + sign_fit(sha_algo, sign_options) + run_bootm(sha_algo, 'signed config', 'dev+', True) + + cons.log.action('%s: Check signed config on the host' % sha_algo) + + util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) + + if full_test: + # Make sure that U-Boot checks that the config is in the list of + # hashed nodes. If it isn't, a security bypass is possible. + ffit = '%stest.forged.fit' % tmpdir + shutil.copyfile(fit, ffit) + with open(ffit, 'rb') as fd: + root, strblock = vboot_forge.read_fdt(fd) + root, strblock = vboot_forge.manipulate(root, strblock) + with open(ffit, 'w+b') as fd: + vboot_forge.write_fdt(root, strblock, fd) + util.run_and_log_expect_exception( + cons, [fit_check_sign, '-f', ffit, '-k', dtb], + 1, 'Failed to verify required signature') + + run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit) + + # Try adding an evil root node. This should be detected. + efit = '%stest.evilf.fit' % tmpdir + shutil.copyfile(fit, efit) + vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot') + + util.run_and_log_expect_exception( + cons, [fit_check_sign, '-f', efit, '-k', dtb], + 1, 'Failed to verify required signature') + run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format', + False, efit) + + # Try adding an @ to the kernel node name. This should be detected. + efit = '%stest.evilk.fit' % tmpdir + shutil.copyfile(fit, efit) + vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@') + + msg = 'Signature checking prevents use of unit addresses (@) in nodes' + util.run_and_log_expect_exception( + cons, [fit_check_sign, '-f', efit, '-k', dtb], + 1, msg) + run_bootm(sha_algo, 'evil kernel@', msg, False, efit) + + # Create a new properly signed fit and replace header bytes + make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + sign_fit(sha_algo, sign_options) + bcfg = u_boot_console.config.buildconfig + max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0) + existing_size = replace_fit_totalsize(max_size + 1) + run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', + False) + cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo) + + # Replace with existing header bytes + replace_fit_totalsize(existing_size) + run_bootm(sha_algo, 'signed config', 'dev+', True) + cons.log.action('%s: Check default FIT header totalsize' % sha_algo) + + # Increment the first byte of the signature, which should cause failure + sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' % + (fit, sig_node)) + byte_list = sig.split() + byte = int(byte_list[0], 16) + byte_list[0] = '%x' % (byte + 1) + sig = ' '.join(byte_list) + util.run_and_log(cons, 'fdtput -t bx %s %s value %s' % + (fit, sig_node, sig)) + + run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash', + False) + + cons.log.action('%s: Check bad config on the host' % sha_algo) + util.run_and_log_expect_exception( + cons, [fit_check_sign, '-f', fit, '-k', dtb], + 1, 'Failed to verify required signature') + + def test_required_key(sha_algo, padding, sign_options): + """Test verified boot with the given hash algorithm. + + This function tests if U-Boot rejects an image when a required key isn't + used to sign a FIT. + + Args: + sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use + padding: Either '' or '-pss', to select the padding to use for the + rsa signature algorithm. + sign_options: Options to mkimage when signing a fit image. + """ + # Compile our device tree files for kernel and U-Boot. These are + # regenerated here since mkimage will modify them (by adding a + # public key) below. + dtc('sandbox-kernel.dts') + dtc('sandbox-u-boot.dts') + + cons.log.action('%s: Test FIT with configs images' % sha_algo) + + # Build the FIT with prod key (keys required) and sign it. This puts the + # signature into sandbox-u-boot.dtb, marked 'required' + make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding)) + sign_fit(sha_algo, sign_options) + + # Build the FIT with dev key (keys NOT required). This adds the + # signature into sandbox-u-boot.dtb, NOT marked 'required'. + make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + sign_fit_norequire(sha_algo, sign_options) + + # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. + # Only the prod key is set as 'required'. But FIT we just built has + # a dev signature only (sign_fit_norequire() overwrites the FIT). + # Try to boot the FIT with dev key. This FIT should not be accepted by + # U-Boot because the prod key is required. + run_bootm(sha_algo, 'required key', '', False) + + # Build the FIT with dev key (keys required) and sign it. This puts the + # signature into sandbox-u-boot.dtb, marked 'required'. + make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) + sign_fit(sha_algo, sign_options) + + # Set the required-mode policy to "any". + # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. + # Both the dev and prod key are set as 'required'. But FIT we just built has + # a dev signature only (sign_fit() overwrites the FIT). + # Try to boot the FIT with dev key. This FIT should be accepted by + # U-Boot because the dev key is required and policy is "any" required key. + util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' % + (dtb)) + run_bootm(sha_algo, 'multi required key', 'dev+', True) + + # Set the required-mode policy to "all". + # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys. + # Both the dev and prod key are set as 'required'. But FIT we just built has + # a dev signature only (sign_fit() overwrites the FIT). + # Try to boot the FIT with dev key. This FIT should not be accepted by + # U-Boot because the prod key is required and policy is "all" required key + util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' % + (dtb)) + run_bootm(sha_algo, 'multi required key', '', False) + + cons = u_boot_console + tmpdir = cons.config.result_dir + '/' + datadir = cons.config.source_dir + '/test/py/tests/vboot/' + fit = '%stest.fit' % tmpdir + mkimage = cons.config.build_dir + '/tools/mkimage' + fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign' + dtc_args = '-I dts -O dtb -i %s' % tmpdir + dtb = '%ssandbox-u-boot.dtb' % tmpdir + sig_node = '/configurations/conf-1/signature' + + create_rsa_pair('dev') + create_rsa_pair('prod') + + # Create a number kernel image with zeroes + with open('%stest-kernel.bin' % tmpdir, 'wb') as fd: + fd.write(500 * b'\0') + + # Create a second kernel image with ones + evil_kernel = '%stest-kernel1.bin' % tmpdir + with open(evil_kernel, 'wb') as fd: + fd.write(500 * b'\x01') + + try: + # We need to use our own device tree file. Remember to restore it + # afterwards. + old_dtb = cons.config.dtb + cons.config.dtb = dtb + if required: + test_required_key(sha_algo, padding, sign_options) + else: + test_with_algo(sha_algo, padding, sign_options) + finally: + # Go back to the original U-Boot with the correct dtb. + cons.config.dtb = old_dtb + cons.restart_uboot() diff --git a/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts b/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts new file mode 100644 index 000000000..a1e853c9c --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts @@ -0,0 +1,7 @@ +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + +}; diff --git a/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts b/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts new file mode 100644 index 000000000..63f8f401d --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts @@ -0,0 +1,10 @@ +/dts-v1/; + +/ { + model = "Sandbox Verified Boot Test"; + compatible = "sandbox"; + + reset@0 { + compatible = "sandbox,reset"; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its new file mode 100644 index 000000000..72a5637e3 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its @@ -0,0 +1,46 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-1 { + algo = "sha1"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature { + algo = "sha1,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its new file mode 100644 index 000000000..d8bc1fa09 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its @@ -0,0 +1,45 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-1 { + algo = "sha1"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-1 { + algo = "sha1"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its new file mode 100644 index 000000000..aac732e30 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its @@ -0,0 +1,46 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature { + algo = "sha256,rsa2048"; + padding = "pss"; + key-name-hint = "prod"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its new file mode 100644 index 000000000..7bdcc7e28 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its @@ -0,0 +1,46 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature { + algo = "sha256,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its new file mode 100644 index 000000000..f5591aad3 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its @@ -0,0 +1,45 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + hash-1 { + algo = "sha256"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + signature { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + sign-images = "fdt", "kernel"; + }; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its new file mode 100644 index 000000000..ded7ae4f5 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its @@ -0,0 +1,44 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature { + algo = "sha1,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature { + algo = "sha1,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its new file mode 100644 index 000000000..18c759e9e --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its @@ -0,0 +1,42 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature { + algo = "sha1,rsa2048"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its new file mode 100644 index 000000000..34850cc6c --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its @@ -0,0 +1,44 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature { + algo = "sha256,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature { + algo = "sha256,rsa2048"; + padding = "pss"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its new file mode 100644 index 000000000..bb0f8ee8a --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its @@ -0,0 +1,42 @@ +/dts-v1/; + +/ { + description = "Chrome OS kernel image with one or more FDT blobs"; + #address-cells = <1>; + + images { + kernel { + data = /incbin/("test-kernel.bin"); + type = "kernel_noload"; + arch = "sandbox"; + os = "linux"; + compression = "none"; + load = <0x4>; + entry = <0x8>; + kernel-version = <1>; + signature { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + }; + }; + fdt-1 { + description = "snow"; + data = /incbin/("sandbox-kernel.dtb"); + type = "flat_dt"; + arch = "sandbox"; + compression = "none"; + fdt-version = <1>; + signature { + algo = "sha256,rsa2048"; + key-name-hint = "dev"; + }; + }; + }; + configurations { + default = "conf-1"; + conf-1 { + kernel = "kernel"; + fdt = "fdt-1"; + }; + }; +}; diff --git a/roms/u-boot/test/py/tests/vboot_evil.py b/roms/u-boot/test/py/tests/vboot_evil.py new file mode 100644 index 000000000..9825c2171 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot_evil.py @@ -0,0 +1,485 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2020, Intel Corporation + +"""Modifies a devicetree to add a fake root node, for testing purposes""" + +import hashlib +import struct +import sys + +FDT_PROP = 0x3 +FDT_BEGIN_NODE = 0x1 +FDT_END_NODE = 0x2 +FDT_END = 0x9 + +FAKE_ROOT_ATTACK = 0 +KERNEL_AT = 1 + +MAGIC = 0xd00dfeed + +EVIL_KERNEL_NAME = b'evil_kernel' +FAKE_ROOT_NAME = b'f@keroot' + + +def getstr(dt_strings, off): + """Get a string from the devicetree string table + + Args: + dt_strings (bytes): Devicetree strings section + off (int): Offset of string to read + + Returns: + str: String read from the table + """ + output = '' + while dt_strings[off]: + output += chr(dt_strings[off]) + off += 1 + + return output + + +def align(offset): + """Align an offset to a multiple of 4 + + Args: + offset (int): Offset to align + + Returns: + int: Resulting aligned offset (rounds up to nearest multiple) + """ + return (offset + 3) & ~3 + + +def determine_offset(dt_struct, dt_strings, searched_node_name): + """Determines the offset of an element, either a node or a property + + Args: + dt_struct (bytes): Devicetree struct section + dt_strings (bytes): Devicetree strings section + searched_node_name (str): element path, ex: /images/kernel@1/data + + Returns: + tuple: (node start offset, node end offset) + if element is not found, returns (None, None) + """ + offset = 0 + depth = -1 + + path = '/' + + object_start_offset = None + object_end_offset = None + object_depth = None + + while offset < len(dt_struct): + (tag,) = struct.unpack('>I', dt_struct[offset:offset + 4]) + + if tag == FDT_BEGIN_NODE: + depth += 1 + + begin_node_offset = offset + offset += 4 + + node_name = getstr(dt_struct, offset) + offset += len(node_name) + 1 + offset = align(offset) + + if path[-1] != '/': + path += '/' + + path += str(node_name) + + if path == searched_node_name: + object_start_offset = begin_node_offset + object_depth = depth + + elif tag == FDT_PROP: + begin_prop_offset = offset + + offset += 4 + len_tag, nameoff = struct.unpack('>II', + dt_struct[offset:offset + 8]) + offset += 8 + prop_name = getstr(dt_strings, nameoff) + + len_tag = align(len_tag) + + offset += len_tag + + node_path = path + '/' + str(prop_name) + + if node_path == searched_node_name: + object_start_offset = begin_prop_offset + + elif tag == FDT_END_NODE: + offset += 4 + + path = path[:path.rfind('/')] + if not path: + path = '/' + + if depth == object_depth: + object_end_offset = offset + break + depth -= 1 + elif tag == FDT_END: + break + + else: + print('unknown tag=0x%x, offset=0x%x found!' % (tag, offset)) + break + + return object_start_offset, object_end_offset + + +def modify_node_name(dt_struct, node_offset, replcd_name): + """Change the name of a node + + Args: + dt_struct (bytes): Devicetree struct section + node_offset (int): Offset of node + replcd_name (str): New name for node + + Returns: + bytes: New dt_struct contents + """ + + # skip 4 bytes for the FDT_BEGIN_NODE + node_offset += 4 + + node_name = getstr(dt_struct, node_offset) + node_name_len = len(node_name) + 1 + + node_name_len = align(node_name_len) + + replcd_name += b'\0' + + # align on 4 bytes + while len(replcd_name) % 4: + replcd_name += b'\0' + + dt_struct = (dt_struct[:node_offset] + replcd_name + + dt_struct[node_offset + node_name_len:]) + + return dt_struct + + +def modify_prop_content(dt_struct, prop_offset, content): + """Overwrite the value of a property + + Args: + dt_struct (bytes): Devicetree struct section + prop_offset (int): Offset of property (FDT_PROP tag) + content (bytes): New content for the property + + Returns: + bytes: New dt_struct contents + """ + # skip FDT_PROP + prop_offset += 4 + (len_tag, nameoff) = struct.unpack('>II', + dt_struct[prop_offset:prop_offset + 8]) + + # compute padded original node length + original_node_len = len_tag + 8 # content length + prop meta data len + + original_node_len = align(original_node_len) + + added_data = struct.pack('>II', len(content), nameoff) + added_data += content + while len(added_data) % 4: + added_data += b'\0' + + dt_struct = (dt_struct[:prop_offset] + added_data + + dt_struct[prop_offset + original_node_len:]) + + return dt_struct + + +def change_property_value(dt_struct, dt_strings, prop_path, prop_value, + required=True): + """Change a given property value + + Args: + dt_struct (bytes): Devicetree struct section + dt_strings (bytes): Devicetree strings section + prop_path (str): full path of the target property + prop_value (bytes): new property name + required (bool): raise an exception if property not found + + Returns: + bytes: New dt_struct contents + + Raises: + ValueError: if the property is not found + """ + (rt_node_start, _) = determine_offset(dt_struct, dt_strings, prop_path) + if rt_node_start is None: + if not required: + return dt_struct + raise ValueError('Fatal error, unable to find prop %s' % prop_path) + + dt_struct = modify_prop_content(dt_struct, rt_node_start, prop_value) + + return dt_struct + +def change_node_name(dt_struct, dt_strings, node_path, node_name): + """Change a given node name + + Args: + dt_struct (bytes): Devicetree struct section + dt_strings (bytes): Devicetree strings section + node_path (str): full path of the target node + node_name (str): new node name, just node name not full path + + Returns: + bytes: New dt_struct contents + + Raises: + ValueError: if the node is not found + """ + (rt_node_start, rt_node_end) = ( + determine_offset(dt_struct, dt_strings, node_path)) + if rt_node_start is None or rt_node_end is None: + raise ValueError('Fatal error, unable to find root node') + + dt_struct = modify_node_name(dt_struct, rt_node_start, node_name) + + return dt_struct + +def get_prop_value(dt_struct, dt_strings, prop_path): + """Get the content of a property based on its path + + Args: + dt_struct (bytes): Devicetree struct section + dt_strings (bytes): Devicetree strings section + prop_path (str): full path of the target property + + Returns: + bytes: Property value + + Raises: + ValueError: if the property is not found + """ + (offset, _) = determine_offset(dt_struct, dt_strings, prop_path) + if offset is None: + raise ValueError('Fatal error, unable to find prop') + + offset += 4 + (len_tag,) = struct.unpack('>I', dt_struct[offset:offset + 4]) + + offset += 8 + tag_data = dt_struct[offset:offset + len_tag] + + return tag_data + + +def kernel_at_attack(dt_struct, dt_strings, kernel_content, kernel_hash): + """Conduct the kernel@ attack + + It fetches from /configurations/default the name of the kernel being loaded. + Then, if the kernel name does not contain any @sign, duplicates the kernel + in /images node and appends '@evil' to its name. + It inserts a new kernel content and updates its images digest. + + Inputs: + - FIT dt_struct + - FIT dt_strings + - kernel content blob + - kernel hash blob + + Important note: it assumes the U-Boot loading method is 'kernel' and the + loaded kernel hash's subnode name is 'hash-1' + """ + + # retrieve the default configuration name + default_conf_name = get_prop_value( + dt_struct, dt_strings, '/configurations/default') + default_conf_name = str(default_conf_name[:-1], 'utf-8') + + conf_path = '/configurations/' + default_conf_name + + # fetch the loaded kernel name from the default configuration + loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel') + + loaded_kernel = str(loaded_kernel[:-1], 'utf-8') + + if loaded_kernel.find('@') != -1: + print('kernel@ attack does not work on nodes already containing an @ sign!') + sys.exit() + + # determine boundaries of the loaded kernel + (krn_node_start, krn_node_end) = (determine_offset( + dt_struct, dt_strings, '/images/' + loaded_kernel)) + if krn_node_start is None and krn_node_end is None: + print('Fatal error, unable to find root node') + sys.exit() + + # copy the loaded kernel + loaded_kernel_copy = dt_struct[krn_node_start:krn_node_end] + + # insert the copy inside the tree + dt_struct = dt_struct[:krn_node_start] + \ + loaded_kernel_copy + dt_struct[krn_node_start:] + + evil_kernel_name = loaded_kernel+'@evil' + + # change the inserted kernel name + dt_struct = change_node_name( + dt_struct, dt_strings, '/images/' + loaded_kernel, bytes(evil_kernel_name, 'utf-8')) + + # change the content of the kernel being loaded + dt_struct = change_property_value( + dt_struct, dt_strings, '/images/' + evil_kernel_name + '/data', kernel_content) + + # change the content of the kernel being loaded + dt_struct = change_property_value( + dt_struct, dt_strings, '/images/' + evil_kernel_name + '/hash-1/value', kernel_hash) + + return dt_struct + + +def fake_root_node_attack(dt_struct, dt_strings, kernel_content, kernel_digest): + """Conduct the fakenode attack + + It duplicates the original root node at the beginning of the tree. + Then it modifies within this duplicated tree: + - The loaded kernel name + - The loaded kernel data + + Important note: it assumes the UBoot loading method is 'kernel' and the loaded kernel + hash's subnode name is hash@1 + """ + + # retrieve the default configuration name + default_conf_name = get_prop_value( + dt_struct, dt_strings, '/configurations/default') + default_conf_name = str(default_conf_name[:-1], 'utf-8') + + conf_path = '/configurations/'+default_conf_name + + # fetch the loaded kernel name from the default configuration + loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel') + + loaded_kernel = str(loaded_kernel[:-1], 'utf-8') + + # determine root node start and end: + (rt_node_start, rt_node_end) = (determine_offset(dt_struct, dt_strings, '/')) + if (rt_node_start is None) or (rt_node_end is None): + print('Fatal error, unable to find root node') + sys.exit() + + # duplicate the whole tree + duplicated_node = dt_struct[rt_node_start:rt_node_end] + + # dchange root name (empty name) to fake root name + new_dup = change_node_name(duplicated_node, dt_strings, '/', FAKE_ROOT_NAME) + + dt_struct = new_dup + dt_struct + + # change the value of /<fake_root_name>/configs/<default_config_name>/kernel + # so our modified kernel will be loaded + base = '/' + str(FAKE_ROOT_NAME, 'utf-8') + value_path = base + conf_path+'/kernel' + dt_struct = change_property_value(dt_struct, dt_strings, value_path, + EVIL_KERNEL_NAME + b'\0') + + # change the node of the /<fake_root_name>/images/<original_kernel_name> + images_path = base + '/images/' + node_path = images_path + loaded_kernel + dt_struct = change_node_name(dt_struct, dt_strings, node_path, + EVIL_KERNEL_NAME) + + # change the content of the kernel being loaded + data_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/data' + dt_struct = change_property_value(dt_struct, dt_strings, data_path, + kernel_content, required=False) + + # update the digest value + hash_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/hash-1/value' + dt_struct = change_property_value(dt_struct, dt_strings, hash_path, + kernel_digest) + + return dt_struct + +def add_evil_node(in_fname, out_fname, kernel_fname, attack): + """Add an evil node to the devicetree + + Args: + in_fname (str): Filename of input devicetree + out_fname (str): Filename to write modified devicetree to + kernel_fname (str): Filename of kernel data to add to evil node + attack (str): Attack type ('fakeroot' or 'kernel@') + + Raises: + ValueError: Unknown attack name + """ + if attack == 'fakeroot': + attack = FAKE_ROOT_ATTACK + elif attack == 'kernel@': + attack = KERNEL_AT + else: + raise ValueError('Unknown attack name!') + + with open(in_fname, 'rb') as fin: + input_data = fin.read() + + hdr = input_data[0:0x28] + + offset = 0 + magic = struct.unpack('>I', hdr[offset:offset + 4])[0] + if magic != MAGIC: + raise ValueError('Wrong magic!') + + offset += 4 + (totalsize, off_dt_struct, off_dt_strings, off_mem_rsvmap, version, + last_comp_version, boot_cpuid_phys, size_dt_strings, + size_dt_struct) = struct.unpack('>IIIIIIIII', hdr[offset:offset + 36]) + + rsv_map = input_data[off_mem_rsvmap:off_dt_struct] + dt_struct = input_data[off_dt_struct:off_dt_struct + size_dt_struct] + dt_strings = input_data[off_dt_strings:off_dt_strings + size_dt_strings] + + with open(kernel_fname, 'rb') as kernel_file: + kernel_content = kernel_file.read() + + # computing inserted kernel hash + val = hashlib.sha1() + val.update(kernel_content) + hash_digest = val.digest() + + if attack == FAKE_ROOT_ATTACK: + dt_struct = fake_root_node_attack(dt_struct, dt_strings, kernel_content, + hash_digest) + elif attack == KERNEL_AT: + dt_struct = kernel_at_attack(dt_struct, dt_strings, kernel_content, + hash_digest) + + # now rebuild the new file + size_dt_strings = len(dt_strings) + size_dt_struct = len(dt_struct) + totalsize = 0x28 + len(rsv_map) + size_dt_struct + size_dt_strings + off_mem_rsvmap = 0x28 + off_dt_struct = off_mem_rsvmap + len(rsv_map) + off_dt_strings = off_dt_struct + len(dt_struct) + + header = struct.pack('>IIIIIIIIII', MAGIC, totalsize, off_dt_struct, + off_dt_strings, off_mem_rsvmap, version, + last_comp_version, boot_cpuid_phys, size_dt_strings, + size_dt_struct) + + with open(out_fname, 'wb') as output_file: + output_file.write(header) + output_file.write(rsv_map) + output_file.write(dt_struct) + output_file.write(dt_strings) + +if __name__ == '__main__': + if len(sys.argv) != 5: + print('usage: %s <input_filename> <output_filename> <kernel_binary> <attack_name>' % + sys.argv[0]) + print('valid attack names: [fakeroot, kernel@]') + sys.exit(1) + + add_evil_node(sys.argv[1:]) diff --git a/roms/u-boot/test/py/tests/vboot_forge.py b/roms/u-boot/test/py/tests/vboot_forge.py new file mode 100644 index 000000000..b41105bd0 --- /dev/null +++ b/roms/u-boot/test/py/tests/vboot_forge.py @@ -0,0 +1,423 @@ +#!/usr/bin/python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2020, F-Secure Corporation, https://foundry.f-secure.com +# +# pylint: disable=E1101,W0201,C0103 + +""" +Verified boot image forgery tools and utilities + +This module provides services to both take apart and regenerate FIT images +in a way that preserves all existing verified boot signatures, unless you +manipulate nodes in the process. +""" + +import struct +import binascii +from io import BytesIO + +# +# struct parsing helpers +# + +class BetterStructMeta(type): + """ + Preprocesses field definitions and creates a struct.Struct instance from them + """ + def __new__(cls, clsname, superclasses, attributedict): + if clsname != 'BetterStruct': + fields = attributedict['__fields__'] + field_types = [_[0] for _ in fields] + field_names = [_[1] for _ in fields if _[1] is not None] + attributedict['__names__'] = field_names + s = struct.Struct(attributedict.get('__endian__', '') + ''.join(field_types)) + attributedict['__struct__'] = s + attributedict['size'] = s.size + return type.__new__(cls, clsname, superclasses, attributedict) + +class BetterStruct(metaclass=BetterStructMeta): + """ + Base class for better structures + """ + def __init__(self): + for t, n in self.__fields__: + if 's' in t: + setattr(self, n, '') + elif t in ('Q', 'I', 'H', 'B'): + setattr(self, n, 0) + + @classmethod + def unpack_from(cls, buffer, offset=0): + """ + Unpack structure instance from a buffer + """ + fields = cls.__struct__.unpack_from(buffer, offset) + instance = cls() + for n, v in zip(cls.__names__, fields): + setattr(instance, n, v) + return instance + + def pack(self): + """ + Pack structure instance into bytes + """ + return self.__struct__.pack(*[getattr(self, n) for n in self.__names__]) + + def __str__(self): + items = ["'%s': %s" % (n, repr(getattr(self, n))) for n in self.__names__ if n is not None] + return '(' + ', '.join(items) + ')' + +# +# some defs for flat DT data +# + +class HeaderV17(BetterStruct): + __endian__ = '>' + __fields__ = [ + ('I', 'magic'), + ('I', 'totalsize'), + ('I', 'off_dt_struct'), + ('I', 'off_dt_strings'), + ('I', 'off_mem_rsvmap'), + ('I', 'version'), + ('I', 'last_comp_version'), + ('I', 'boot_cpuid_phys'), + ('I', 'size_dt_strings'), + ('I', 'size_dt_struct'), + ] + +class RRHeader(BetterStruct): + __endian__ = '>' + __fields__ = [ + ('Q', 'address'), + ('Q', 'size'), + ] + +class PropHeader(BetterStruct): + __endian__ = '>' + __fields__ = [ + ('I', 'value_size'), + ('I', 'name_offset'), + ] + +# magical constants for DTB format +OF_DT_HEADER = 0xd00dfeed +OF_DT_BEGIN_NODE = 1 +OF_DT_END_NODE = 2 +OF_DT_PROP = 3 +OF_DT_END = 9 + +class StringsBlock: + """ + Represents a parsed device tree string block + """ + def __init__(self, values=None): + if values is None: + self.values = [] + else: + self.values = values + + def __getitem__(self, at): + if isinstance(at, str): + offset = 0 + for value in self.values: + if value == at: + break + offset += len(value) + 1 + else: + self.values.append(at) + return offset + + if isinstance(at, int): + offset = 0 + for value in self.values: + if offset == at: + return value + offset += len(value) + 1 + raise IndexError('no string found corresponding to the given offset') + + raise TypeError('only strings and integers are accepted') + +class Prop: + """ + Represents a parsed device tree property + """ + def __init__(self, name=None, value=None): + self.name = name + self.value = value + + def clone(self): + return Prop(self.name, self.value) + + def __repr__(self): + return "<Prop(name='%s', value=%s>" % (self.name, repr(self.value)) + +class Node: + """ + Represents a parsed device tree node + """ + def __init__(self, name=None): + self.name = name + self.props = [] + self.children = [] + + def clone(self): + o = Node(self.name) + o.props = [x.clone() for x in self.props] + o.children = [x.clone() for x in self.children] + return o + + def __getitem__(self, index): + return self.children[index] + + def __repr__(self): + return "<Node('%s'), %s, %s>" % (self.name, repr(self.props), repr(self.children)) + +# +# flat DT to memory +# + +def parse_strings(strings): + """ + Converts the bytes into a StringsBlock instance so it is convenient to work with + """ + strings = strings.split(b'\x00') + return StringsBlock(strings) + +def parse_struct(stream): + """ + Parses DTB structure(s) into a Node or Prop instance + """ + tag = bytearray(stream.read(4))[3] + if tag == OF_DT_BEGIN_NODE: + name = b'' + while b'\x00' not in name: + name += stream.read(4) + name = name.rstrip(b'\x00') + node = Node(name) + + item = parse_struct(stream) + while item is not None: + if isinstance(item, Node): + node.children.append(item) + elif isinstance(item, Prop): + node.props.append(item) + item = parse_struct(stream) + + return node + + if tag == OF_DT_PROP: + h = PropHeader.unpack_from(stream.read(PropHeader.size)) + length = (h.value_size + 3) & (~3) + value = stream.read(length)[:h.value_size] + prop = Prop(h.name_offset, value) + return prop + + if tag in (OF_DT_END_NODE, OF_DT_END): + return None + + raise ValueError('unexpected tag value') + +def read_fdt(fp): + """ + Reads and parses the flattened device tree (or derivatives like FIT) + """ + header = HeaderV17.unpack_from(fp.read(HeaderV17.size)) + if header.magic != OF_DT_HEADER: + raise ValueError('invalid magic value %08x; expected %08x' % (header.magic, OF_DT_HEADER)) + # TODO: read/parse reserved regions + fp.seek(header.off_dt_struct) + structs = fp.read(header.size_dt_struct) + fp.seek(header.off_dt_strings) + strings = fp.read(header.size_dt_strings) + strblock = parse_strings(strings) + root = parse_struct(BytesIO(structs)) + + return root, strblock + +# +# memory to flat DT +# + +def compose_structs_r(item): + """ + Recursive part of composing Nodes and Props into a bytearray + """ + t = bytearray() + + if isinstance(item, Node): + t.extend(struct.pack('>I', OF_DT_BEGIN_NODE)) + if isinstance(item.name, str): + item.name = bytes(item.name, 'utf-8') + name = item.name + b'\x00' + if len(name) & 3: + name += b'\x00' * (4 - (len(name) & 3)) + t.extend(name) + for p in item.props: + t.extend(compose_structs_r(p)) + for c in item.children: + t.extend(compose_structs_r(c)) + t.extend(struct.pack('>I', OF_DT_END_NODE)) + + elif isinstance(item, Prop): + t.extend(struct.pack('>I', OF_DT_PROP)) + value = item.value + h = PropHeader() + h.name_offset = item.name + if value: + h.value_size = len(value) + t.extend(h.pack()) + if len(value) & 3: + value += b'\x00' * (4 - (len(value) & 3)) + t.extend(value) + else: + h.value_size = 0 + t.extend(h.pack()) + + return t + +def compose_structs(root): + """ + Composes the parsed Nodes into a flat bytearray instance + """ + t = compose_structs_r(root) + t.extend(struct.pack('>I', OF_DT_END)) + return t + +def compose_strings(strblock): + """ + Composes the StringsBlock instance back into a bytearray instance + """ + b = bytearray() + for s in strblock.values: + b.extend(s) + b.append(0) + return bytes(b) + +def write_fdt(root, strblock, fp): + """ + Writes out a complete flattened device tree (or FIT) + """ + header = HeaderV17() + header.magic = OF_DT_HEADER + header.version = 17 + header.last_comp_version = 16 + fp.write(header.pack()) + + header.off_mem_rsvmap = fp.tell() + fp.write(RRHeader().pack()) + + structs = compose_structs(root) + header.off_dt_struct = fp.tell() + header.size_dt_struct = len(structs) + fp.write(structs) + + strings = compose_strings(strblock) + header.off_dt_strings = fp.tell() + header.size_dt_strings = len(strings) + fp.write(strings) + + header.totalsize = fp.tell() + + fp.seek(0) + fp.write(header.pack()) + +# +# pretty printing / converting to DT source +# + +def as_bytes(value): + return ' '.join(["%02X" % x for x in value]) + +def prety_print_value(value): + """ + Formats a property value as appropriate depending on the guessed data type + """ + if not value: + return '""' + if value[-1] == b'\x00': + printable = True + for x in value[:-1]: + x = ord(x) + if x != 0 and (x < 0x20 or x > 0x7F): + printable = False + break + if printable: + value = value[:-1] + return ', '.join('"' + x + '"' for x in value.split(b'\x00')) + if len(value) > 0x80: + return '[' + as_bytes(value[:0x80]) + ' ... ]' + return '[' + as_bytes(value) + ']' + +def pretty_print_r(node, strblock, indent=0): + """ + Prints out a single node, recursing further for each of its children + """ + spaces = ' ' * indent + print((spaces + '%s {' % (node.name.decode('utf-8') if node.name else '/'))) + for p in node.props: + print((spaces + ' %s = %s;' % (strblock[p.name].decode('utf-8'), prety_print_value(p.value)))) + for c in node.children: + pretty_print_r(c, strblock, indent+1) + print((spaces + '};')) + +def pretty_print(node, strblock): + """ + Generates an almost-DTS formatted printout of the parsed device tree + """ + print('/dts-v1/;') + pretty_print_r(node, strblock, 0) + +# +# manipulating the DT structure +# + +def manipulate(root, strblock): + """ + Maliciously manipulates the structure to create a crafted FIT file + """ + # locate /images/kernel-1 (frankly, it just expects it to be the first one) + kernel_node = root[0][0] + # clone it to save time filling all the properties + fake_kernel = kernel_node.clone() + # rename the node + fake_kernel.name = b'kernel-2' + # get rid of signatures/hashes + fake_kernel.children = [] + # NOTE: this simply replaces the first prop... either description or data + # should be good for testing purposes + fake_kernel.props[0].value = b'Super 1337 kernel\x00' + # insert the new kernel node under /images + root[0].children.append(fake_kernel) + + # modify the default configuration + root[1].props[0].value = b'conf-2\x00' + # clone the first (only?) configuration + fake_conf = root[1][0].clone() + # rename and change kernel and fdt properties to select the crafted kernel + fake_conf.name = b'conf-2' + fake_conf.props[0].value = b'kernel-2\x00' + fake_conf.props[1].value = b'fdt-1\x00' + # insert the new configuration under /configurations + root[1].children.append(fake_conf) + + return root, strblock + +def main(argv): + with open(argv[1], 'rb') as fp: + root, strblock = read_fdt(fp) + + print("Before:") + pretty_print(root, strblock) + + root, strblock = manipulate(root, strblock) + print("After:") + pretty_print(root, strblock) + + with open('blah', 'w+b') as fp: + write_fdt(root, strblock, fp) + +if __name__ == '__main__': + import sys + main(sys.argv) +# EOF diff --git a/roms/u-boot/test/py/u_boot_console_base.py b/roms/u-boot/test/py/u_boot_console_base.py new file mode 100644 index 000000000..1db5da4c1 --- /dev/null +++ b/roms/u-boot/test/py/u_boot_console_base.py @@ -0,0 +1,478 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Common logic to interact with U-Boot via the console. This class provides +# the interface that tests use to execute U-Boot shell commands and wait for +# their results. Sub-classes exist to perform board-type-specific setup +# operations, such as spawning a sub-process for Sandbox, or attaching to the +# serial console of real hardware. + +import multiplexed_log +import os +import pytest +import re +import sys +import u_boot_spawn + +# Regexes for text we expect U-Boot to send to the console. +pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))') +pattern_u_boot_spl2_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))') +pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))') +pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ') +pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'') +pattern_error_notification = re.compile('## Error: ') +pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###') + +PAT_ID = 0 +PAT_RE = 1 + +bad_pattern_defs = ( + ('spl_signon', pattern_u_boot_spl_signon), + ('spl2_signon', pattern_u_boot_spl2_signon), + ('main_signon', pattern_u_boot_main_signon), + ('stop_autoboot_prompt', pattern_stop_autoboot_prompt), + ('unknown_command', pattern_unknown_command), + ('error_notification', pattern_error_notification), + ('error_please_reset', pattern_error_please_reset), +) + +class ConsoleDisableCheck(object): + """Context manager (for Python's with statement) that temporarily disables + the specified console output error check. This is useful when deliberately + executing a command that is known to trigger one of the error checks, in + order to test that the error condition is actually raised. This class is + used internally by ConsoleBase::disable_check(); it is not intended for + direct usage.""" + + def __init__(self, console, check_type): + self.console = console + self.check_type = check_type + + def __enter__(self): + self.console.disable_check_count[self.check_type] += 1 + self.console.eval_bad_patterns() + + def __exit__(self, extype, value, traceback): + self.console.disable_check_count[self.check_type] -= 1 + self.console.eval_bad_patterns() + +class ConsoleSetupTimeout(object): + """Context manager (for Python's with statement) that temporarily sets up + timeout for specific command. This is useful when execution time is greater + then default 30s.""" + + def __init__(self, console, timeout): + self.p = console.p + self.orig_timeout = self.p.timeout + self.p.timeout = timeout + + def __enter__(self): + return self + + def __exit__(self, extype, value, traceback): + self.p.timeout = self.orig_timeout + +class ConsoleBase(object): + """The interface through which test functions interact with the U-Boot + console. This primarily involves executing shell commands, capturing their + results, and checking for common error conditions. Some common utilities + are also provided too.""" + + def __init__(self, log, config, max_fifo_fill): + """Initialize a U-Boot console connection. + + Can only usefully be called by sub-classes. + + Args: + log: A mulptiplex_log.Logfile object, to which the U-Boot output + will be logged. + config: A configuration data structure, as built by conftest.py. + max_fifo_fill: The maximum number of characters to send to U-Boot + command-line before waiting for U-Boot to echo the characters + back. For UART-based HW without HW flow control, this value + should be set less than the UART RX FIFO size to avoid + overflow, assuming that U-Boot can't keep up with full-rate + traffic at the baud rate. + + Returns: + Nothing. + """ + + self.log = log + self.config = config + self.max_fifo_fill = max_fifo_fill + + self.logstream = self.log.get_stream('console', sys.stdout) + + # Array slice removes leading/trailing quotes + self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1] + self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE) + self.p = None + self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs} + self.eval_bad_patterns() + + self.at_prompt = False + self.at_prompt_logevt = None + + def eval_bad_patterns(self): + self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \ + if self.disable_check_count[pat[PAT_ID]] == 0] + self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \ + if self.disable_check_count[pat[PAT_ID]] == 0] + + def close(self): + """Terminate the connection to the U-Boot console. + + This function is only useful once all interaction with U-Boot is + complete. Once this function is called, data cannot be sent to or + received from U-Boot. + + Args: + None. + + Returns: + Nothing. + """ + + if self.p: + self.p.close() + self.logstream.close() + + def run_command(self, cmd, wait_for_echo=True, send_nl=True, + wait_for_prompt=True): + """Execute a command via the U-Boot console. + + The command is always sent to U-Boot. + + U-Boot echoes any command back to its output, and this function + typically waits for that to occur. The wait can be disabled by setting + wait_for_echo=False, which is useful e.g. when sending CTRL-C to + interrupt a long-running command such as "ums". + + Command execution is typically triggered by sending a newline + character. This can be disabled by setting send_nl=False, which is + also useful when sending CTRL-C. + + This function typically waits for the command to finish executing, and + returns the console output that it generated. This can be disabled by + setting wait_for_prompt=False, which is useful when invoking a long- + running command such as "ums". + + Args: + cmd: The command to send. + wait_for_echo: Boolean indicating whether to wait for U-Boot to + echo the command text back to its output. + send_nl: Boolean indicating whether to send a newline character + after the command string. + wait_for_prompt: Boolean indicating whether to wait for the + command prompt to be sent by U-Boot. This typically occurs + immediately after the command has been executed. + + Returns: + If wait_for_prompt == False: + Nothing. + Else: + The output from U-Boot during command execution. In other + words, the text U-Boot emitted between the point it echod the + command string and emitted the subsequent command prompts. + """ + + if self.at_prompt and \ + self.at_prompt_logevt != self.logstream.logfile.cur_evt: + self.logstream.write(self.prompt, implicit=True) + + try: + self.at_prompt = False + if send_nl: + cmd += '\n' + while cmd: + # Limit max outstanding data, so UART FIFOs don't overflow + chunk = cmd[:self.max_fifo_fill] + cmd = cmd[self.max_fifo_fill:] + self.p.send(chunk) + if not wait_for_echo: + continue + chunk = re.escape(chunk) + chunk = chunk.replace('\\\n', '[\r\n]') + m = self.p.expect([chunk] + self.bad_patterns) + if m != 0: + self.at_prompt = False + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) + if not wait_for_prompt: + return + m = self.p.expect([self.prompt_compiled] + self.bad_patterns) + if m != 0: + self.at_prompt = False + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) + self.at_prompt = True + self.at_prompt_logevt = self.logstream.logfile.cur_evt + # Only strip \r\n; space/TAB might be significant if testing + # indentation. + return self.p.before.strip('\r\n') + except Exception as ex: + self.log.error(str(ex)) + self.cleanup_spawn() + raise + finally: + self.log.timestamp() + + def run_command_list(self, cmds): + """Run a list of commands. + + This is a helper function to call run_command() with default arguments + for each command in a list. + + Args: + cmd: List of commands (each a string). + Returns: + A list of output strings from each command, one element for each + command. + """ + output = [] + for cmd in cmds: + output.append(self.run_command(cmd)) + return output + + def ctrlc(self): + """Send a CTRL-C character to U-Boot. + + This is useful in order to stop execution of long-running synchronous + commands such as "ums". + + Args: + None. + + Returns: + Nothing. + """ + + self.log.action('Sending Ctrl-C') + self.run_command(chr(3), wait_for_echo=False, send_nl=False) + + def wait_for(self, text): + """Wait for a pattern to be emitted by U-Boot. + + This is useful when a long-running command such as "dfu" is executing, + and it periodically emits some text that should show up at a specific + location in the log file. + + Args: + text: The text to wait for; either a string (containing raw text, + not a regular expression) or an re object. + + Returns: + Nothing. + """ + + if type(text) == type(''): + text = re.escape(text) + m = self.p.expect([text] + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) + + def drain_console(self): + """Read from and log the U-Boot console for a short time. + + U-Boot's console output is only logged when the test code actively + waits for U-Boot to emit specific data. There are cases where tests + can fail without doing this. For example, if a test asks U-Boot to + enable USB device mode, then polls until a host-side device node + exists. In such a case, it is useful to log U-Boot's console output + in case U-Boot printed clues as to why the host-side even did not + occur. This function will do that. + + Args: + None. + + Returns: + Nothing. + """ + + # If we are already not connected to U-Boot, there's nothing to drain. + # This should only happen when a previous call to run_command() or + # wait_for() failed (and hence the output has already been logged), or + # the system is shutting down. + if not self.p: + return + + orig_timeout = self.p.timeout + try: + # Drain the log for a relatively short time. + self.p.timeout = 1000 + # Wait for something U-Boot will likely never send. This will + # cause the console output to be read and logged. + self.p.expect(['This should never match U-Boot output']) + except: + # We expect a timeout, since U-Boot won't print what we waited + # for. Squash it when it happens. + # + # Squash any other exception too. This function is only used to + # drain (and log) the U-Boot console output after a failed test. + # The U-Boot process will be restarted, or target board reset, once + # this function returns. So, we don't care about detecting any + # additional errors, so they're squashed so that the rest of the + # post-test-failure cleanup code can continue operation, and + # correctly terminate any log sections, etc. + pass + finally: + self.p.timeout = orig_timeout + + def ensure_spawned(self): + """Ensure a connection to a correctly running U-Boot instance. + + This may require spawning a new Sandbox process or resetting target + hardware, as defined by the implementation sub-class. + + This is an internal function and should not be called directly. + + Args: + None. + + Returns: + Nothing. + """ + + if self.p: + return + try: + self.log.start_section('Starting U-Boot') + self.at_prompt = False + self.p = self.get_spawn() + # Real targets can take a long time to scroll large amounts of + # text if LCD is enabled. This value may need tweaking in the + # future, possibly per-test to be optimal. This works for 'help' + # on board 'seaboard'. + if not self.config.gdbserver: + self.p.timeout = 30000 + self.p.logfile_read = self.logstream + bcfg = self.config.buildconfig + config_spl = bcfg.get('config_spl', 'n') == 'y' + config_spl_serial_support = bcfg.get('config_spl_serial_support', + 'n') == 'y' + env_spl_skipped = self.config.env.get('env__spl_skipped', + False) + env_spl2_skipped = self.config.env.get('env__spl2_skipped', + True) + if config_spl and config_spl_serial_support and not env_spl_skipped: + m = self.p.expect([pattern_u_boot_spl_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL console: ' + + self.bad_pattern_ids[m - 1]) + if not env_spl2_skipped: + m = self.p.expect([pattern_u_boot_spl2_signon] + + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on SPL2 console: ' + + self.bad_pattern_ids[m - 1]) + m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns) + if m != 0: + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 1]) + self.u_boot_version_string = self.p.after + while True: + m = self.p.expect([self.prompt_compiled, + pattern_stop_autoboot_prompt] + self.bad_patterns) + if m == 0: + break + if m == 1: + self.p.send(' ') + continue + raise Exception('Bad pattern found on console: ' + + self.bad_pattern_ids[m - 2]) + self.at_prompt = True + self.at_prompt_logevt = self.logstream.logfile.cur_evt + except Exception as ex: + self.log.error(str(ex)) + self.cleanup_spawn() + raise + finally: + self.log.timestamp() + self.log.end_section('Starting U-Boot') + + def cleanup_spawn(self): + """Shut down all interaction with the U-Boot instance. + + This is used when an error is detected prior to re-establishing a + connection with a fresh U-Boot instance. + + This is an internal function and should not be called directly. + + Args: + None. + + Returns: + Nothing. + """ + + try: + if self.p: + self.p.close() + except: + pass + self.p = None + + def restart_uboot(self): + """Shut down and restart U-Boot.""" + self.cleanup_spawn() + self.ensure_spawned() + + def get_spawn_output(self): + """Return the start-up output from U-Boot + + Returns: + The output produced by ensure_spawed(), as a string. + """ + if self.p: + return self.p.get_expect_output() + return None + + def validate_version_string_in_text(self, text): + """Assert that a command's output includes the U-Boot signon message. + + This is primarily useful for validating the "version" command without + duplicating the signon text regex in a test function. + + Args: + text: The command output text to check. + + Returns: + Nothing. An exception is raised if the validation fails. + """ + + assert(self.u_boot_version_string in text) + + def disable_check(self, check_type): + """Temporarily disable an error check of U-Boot's output. + + Create a new context manager (for use with the "with" statement) which + temporarily disables a particular console output error check. + + Args: + check_type: The type of error-check to disable. Valid values may + be found in self.disable_check_count above. + + Returns: + A context manager object. + """ + + return ConsoleDisableCheck(self, check_type) + + def temporary_timeout(self, timeout): + """Temporarily set up different timeout for commands. + + Create a new context manager (for use with the "with" statement) which + temporarily change timeout. + + Args: + timeout: Time in milliseconds. + + Returns: + A context manager object. + """ + + return ConsoleSetupTimeout(self, timeout) diff --git a/roms/u-boot/test/py/u_boot_console_exec_attach.py b/roms/u-boot/test/py/u_boot_console_exec_attach.py new file mode 100644 index 000000000..27834b55c --- /dev/null +++ b/roms/u-boot/test/py/u_boot_console_exec_attach.py @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Logic to interact with U-Boot running on real hardware, typically via a +# physical serial port. + +import sys +from u_boot_spawn import Spawn +from u_boot_console_base import ConsoleBase + +class ConsoleExecAttach(ConsoleBase): + """Represents a physical connection to a U-Boot console, typically via a + serial port. This implementation executes a sub-process to attach to the + console, expecting that the stdin/out of the sub-process will be forwarded + to/from the physical hardware. This approach isolates the test infra- + structure from the user-/installation-specific details of how to + communicate with, and the identity of, serial ports etc.""" + + def __init__(self, log, config): + """Initialize a U-Boot console connection. + + Args: + log: A multiplexed_log.Logfile instance. + config: A "configuration" object as defined in conftest.py. + + Returns: + Nothing. + """ + + # The max_fifo_fill value might need tweaking per-board/-SoC? + # 1 would be safe anywhere, but is very slow (a pexpect issue?). + # 16 is a common FIFO size. + # HW flow control would mean this could be infinite. + super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16) + + with self.log.section('flash'): + self.log.action('Flashing U-Boot') + cmd = ['u-boot-test-flash', config.board_type, config.board_identity] + runner = self.log.get_runner(cmd[0], sys.stdout) + runner.run(cmd) + runner.close() + self.log.status_pass('OK') + + def get_spawn(self): + """Connect to a fresh U-Boot instance. + + The target board is reset, so that U-Boot begins running from scratch. + + Args: + None. + + Returns: + A u_boot_spawn.Spawn object that is attached to U-Boot. + """ + + args = [self.config.board_type, self.config.board_identity] + s = Spawn(['u-boot-test-console'] + args) + + try: + self.log.action('Resetting board') + cmd = ['u-boot-test-reset'] + args + runner = self.log.get_runner(cmd[0], sys.stdout) + runner.run(cmd) + runner.close() + except: + s.close() + raise + + return s diff --git a/roms/u-boot/test/py/u_boot_console_sandbox.py b/roms/u-boot/test/py/u_boot_console_sandbox.py new file mode 100644 index 000000000..836f5a9e2 --- /dev/null +++ b/roms/u-boot/test/py/u_boot_console_sandbox.py @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015 Stephen Warren +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Logic to interact with the sandbox port of U-Boot, running as a sub-process. + +import time +from u_boot_spawn import Spawn +from u_boot_console_base import ConsoleBase + +class ConsoleSandbox(ConsoleBase): + """Represents a connection to a sandbox U-Boot console, executed as a sub- + process.""" + + def __init__(self, log, config): + """Initialize a U-Boot console connection. + + Args: + log: A multiplexed_log.Logfile instance. + config: A "configuration" object as defined in conftest.py. + + Returns: + Nothing. + """ + + super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024) + self.sandbox_flags = [] + + def get_spawn(self): + """Connect to a fresh U-Boot instance. + + A new sandbox process is created, so that U-Boot begins running from + scratch. + + Args: + None. + + Returns: + A u_boot_spawn.Spawn object that is attached to U-Boot. + """ + + bcfg = self.config.buildconfig + config_spl = bcfg.get('config_spl', 'n') == 'y' + fname = '/spl/u-boot-spl' if config_spl else '/u-boot' + print(fname) + cmd = [] + if self.config.gdbserver: + cmd += ['gdbserver', self.config.gdbserver] + cmd += [ + self.config.build_dir + fname, + '-v', + '-d', + self.config.dtb + ] + cmd += self.sandbox_flags + return Spawn(cmd, cwd=self.config.source_dir) + + def restart_uboot_with_flags(self, flags): + """Run U-Boot with the given command-line flags + + Args: + flags: List of flags to pass, each a string + + Returns: + A u_boot_spawn.Spawn object that is attached to U-Boot. + """ + + try: + self.sandbox_flags = flags + return self.restart_uboot() + finally: + self.sandbox_flags = [] + + def kill(self, sig): + """Send a specific Unix signal to the sandbox process. + + Args: + sig: The Unix signal to send to the process. + + Returns: + Nothing. + """ + + self.log.action('kill %d' % sig) + self.p.kill(sig) + + def validate_exited(self): + """Determine whether the sandbox process has exited. + + If required, this function waits a reasonable time for the process to + exit. + + Args: + None. + + Returns: + Boolean indicating whether the process has exited. + """ + + p = self.p + self.p = None + for i in range(100): + ret = not p.isalive() + if ret: + break + time.sleep(0.1) + p.close() + return ret diff --git a/roms/u-boot/test/py/u_boot_spawn.py b/roms/u-boot/test/py/u_boot_spawn.py new file mode 100644 index 000000000..6991b78cc --- /dev/null +++ b/roms/u-boot/test/py/u_boot_spawn.py @@ -0,0 +1,209 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. + +# Logic to spawn a sub-process and interact with its stdio. + +import os +import re +import pty +import signal +import select +import time + +class Timeout(Exception): + """An exception sub-class that indicates that a timeout occurred.""" + pass + +class Spawn(object): + """Represents the stdio of a freshly created sub-process. Commands may be + sent to the process, and responses waited for. + + Members: + output: accumulated output from expect() + """ + + def __init__(self, args, cwd=None): + """Spawn (fork/exec) the sub-process. + + Args: + args: array of processs arguments. argv[0] is the command to + execute. + cwd: the directory to run the process in, or None for no change. + + Returns: + Nothing. + """ + + self.waited = False + self.buf = '' + self.output = '' + self.logfile_read = None + self.before = '' + self.after = '' + self.timeout = None + # http://stackoverflow.com/questions/7857352/python-regex-to-match-vt100-escape-sequences + self.re_vt100 = re.compile(r'(\x1b\[|\x9b)[^@-_]*[@-_]|\x1b[@-_]', re.I) + + (self.pid, self.fd) = pty.fork() + if self.pid == 0: + try: + # For some reason, SIGHUP is set to SIG_IGN at this point when + # run under "go" (www.go.cd). Perhaps this happens under any + # background (non-interactive) system? + signal.signal(signal.SIGHUP, signal.SIG_DFL) + if cwd: + os.chdir(cwd) + os.execvp(args[0], args) + except: + print('CHILD EXECEPTION:') + import traceback + traceback.print_exc() + finally: + os._exit(255) + + try: + self.poll = select.poll() + self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR | select.POLLHUP | select.POLLNVAL) + except: + self.close() + raise + + def kill(self, sig): + """Send unix signal "sig" to the child process. + + Args: + sig: The signal number to send. + + Returns: + Nothing. + """ + + os.kill(self.pid, sig) + + def isalive(self): + """Determine whether the child process is still running. + + Args: + None. + + Returns: + Boolean indicating whether process is alive. + """ + + if self.waited: + return False + + w = os.waitpid(self.pid, os.WNOHANG) + if w[0] == 0: + return True + + self.waited = True + return False + + def send(self, data): + """Send data to the sub-process's stdin. + + Args: + data: The data to send to the process. + + Returns: + Nothing. + """ + + os.write(self.fd, data.encode(errors='replace')) + + def expect(self, patterns): + """Wait for the sub-process to emit specific data. + + This function waits for the process to emit one pattern from the + supplied list of patterns, or for a timeout to occur. + + Args: + patterns: A list of strings or regex objects that we expect to + see in the sub-process' stdout. + + Returns: + The index within the patterns array of the pattern the process + emitted. + + Notable exceptions: + Timeout, if the process did not emit any of the patterns within + the expected time. + """ + + for pi in range(len(patterns)): + if type(patterns[pi]) == type(''): + patterns[pi] = re.compile(patterns[pi]) + + tstart_s = time.time() + try: + while True: + earliest_m = None + earliest_pi = None + for pi in range(len(patterns)): + pattern = patterns[pi] + m = pattern.search(self.buf) + if not m: + continue + if earliest_m and m.start() >= earliest_m.start(): + continue + earliest_m = m + earliest_pi = pi + if earliest_m: + pos = earliest_m.start() + posafter = earliest_m.end() + self.before = self.buf[:pos] + self.after = self.buf[pos:posafter] + self.output += self.buf[:posafter] + self.buf = self.buf[posafter:] + return earliest_pi + tnow_s = time.time() + if self.timeout: + tdelta_ms = (tnow_s - tstart_s) * 1000 + poll_maxwait = self.timeout - tdelta_ms + if tdelta_ms > self.timeout: + raise Timeout() + else: + poll_maxwait = None + events = self.poll.poll(poll_maxwait) + if not events: + raise Timeout() + c = os.read(self.fd, 1024).decode(errors='replace') + if not c: + raise EOFError() + if self.logfile_read: + self.logfile_read.write(c) + self.buf += c + # count=0 is supposed to be the default, which indicates + # unlimited substitutions, but in practice the version of + # Python in Ubuntu 14.04 appears to default to count=2! + self.buf = self.re_vt100.sub('', self.buf, count=1000000) + finally: + if self.logfile_read: + self.logfile_read.flush() + + def close(self): + """Close the stdio connection to the sub-process. + + This also waits a reasonable time for the sub-process to stop running. + + Args: + None. + + Returns: + Nothing. + """ + + os.close(self.fd) + for i in range(100): + if not self.isalive(): + break + time.sleep(0.1) + + def get_expect_output(self): + """Return the output read by expect() + + Returns: + The output processed by expect(), as a string. + """ + return self.output diff --git a/roms/u-boot/test/py/u_boot_utils.py b/roms/u-boot/test/py/u_boot_utils.py new file mode 100644 index 000000000..939d82eec --- /dev/null +++ b/roms/u-boot/test/py/u_boot_utils.py @@ -0,0 +1,341 @@ +# SPDX-License-Identifier: GPL-2.0 +# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. + +# Utility code shared across multiple tests. + +import hashlib +import inspect +import os +import os.path +import pytest +import sys +import time +import re + +def md5sum_data(data): + """Calculate the MD5 hash of some data. + + Args: + data: The data to hash. + + Returns: + The hash of the data, as a binary string. + """ + + h = hashlib.md5() + h.update(data) + return h.digest() + +def md5sum_file(fn, max_length=None): + """Calculate the MD5 hash of the contents of a file. + + Args: + fn: The filename of the file to hash. + max_length: The number of bytes to hash. If the file has more + bytes than this, they will be ignored. If None or omitted, the + entire file will be hashed. + + Returns: + The hash of the file content, as a binary string. + """ + + with open(fn, 'rb') as fh: + if max_length: + params = [max_length] + else: + params = [] + data = fh.read(*params) + return md5sum_data(data) + +class PersistentRandomFile(object): + """Generate and store information about a persistent file containing + random data.""" + + def __init__(self, u_boot_console, fn, size): + """Create or process the persistent file. + + If the file does not exist, it is generated. + + If the file does exist, its content is hashed for later comparison. + + These files are always located in the "persistent data directory" of + the current test run. + + Args: + u_boot_console: A console connection to U-Boot. + fn: The filename (without path) to create. + size: The desired size of the file in bytes. + + Returns: + Nothing. + """ + + self.fn = fn + + self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn + + if os.path.exists(self.abs_fn): + u_boot_console.log.action('Persistent data file ' + self.abs_fn + + ' already exists') + self.content_hash = md5sum_file(self.abs_fn) + else: + u_boot_console.log.action('Generating ' + self.abs_fn + + ' (random, persistent, %d bytes)' % size) + data = os.urandom(size) + with open(self.abs_fn, 'wb') as fh: + fh.write(data) + self.content_hash = md5sum_data(data) + +def attempt_to_open_file(fn): + """Attempt to open a file, without throwing exceptions. + + Any errors (exceptions) that occur during the attempt to open the file + are ignored. This is useful in order to test whether a file (in + particular, a device node) exists and can be successfully opened, in order + to poll for e.g. USB enumeration completion. + + Args: + fn: The filename to attempt to open. + + Returns: + An open file handle to the file, or None if the file could not be + opened. + """ + + try: + return open(fn, 'rb') + except: + return None + +def wait_until_open_succeeds(fn): + """Poll until a file can be opened, or a timeout occurs. + + Continually attempt to open a file, and return when this succeeds, or + raise an exception after a timeout. + + Args: + fn: The filename to attempt to open. + + Returns: + An open file handle to the file. + """ + + for i in range(100): + fh = attempt_to_open_file(fn) + if fh: + return fh + time.sleep(0.1) + raise Exception('File could not be opened') + +def wait_until_file_open_fails(fn, ignore_errors): + """Poll until a file cannot be opened, or a timeout occurs. + + Continually attempt to open a file, and return when this fails, or + raise an exception after a timeout. + + Args: + fn: The filename to attempt to open. + ignore_errors: Indicate whether to ignore timeout errors. If True, the + function will simply return if a timeout occurs, otherwise an + exception will be raised. + + Returns: + Nothing. + """ + + for i in range(100): + fh = attempt_to_open_file(fn) + if not fh: + return + fh.close() + time.sleep(0.1) + if ignore_errors: + return + raise Exception('File can still be opened') + +def run_and_log(u_boot_console, cmd, ignore_errors=False): + """Run a command and log its output. + + Args: + u_boot_console: A console connection to U-Boot. + cmd: The command to run, as an array of argv[], or a string. + If a string, note that it is split up so that quoted spaces + will not be preserved. E.g. "fred and" becomes ['"fred', 'and"'] + ignore_errors: Indicate whether to ignore errors. If True, the function + will simply return if the command cannot be executed or exits with + an error code, otherwise an exception will be raised if such + problems occur. + + Returns: + The output as a string. + """ + if isinstance(cmd, str): + cmd = cmd.split() + runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) + output = runner.run(cmd, ignore_errors=ignore_errors) + runner.close() + return output + +def run_and_log_expect_exception(u_boot_console, cmd, retcode, msg): + """Run a command that is expected to fail. + + This runs a command and checks that it fails with the expected return code + and exception method. If not, an exception is raised. + + Args: + u_boot_console: A console connection to U-Boot. + cmd: The command to run, as an array of argv[]. + retcode: Expected non-zero return code from the command. + msg: String that should be contained within the command's output. + """ + try: + runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) + runner.run(cmd) + except Exception as e: + assert(retcode == runner.exit_status) + assert(msg in runner.output) + else: + raise Exception("Expected an exception with retcode %d message '%s'," + "but it was not raised" % (retcode, msg)) + finally: + runner.close() + +ram_base = None +def find_ram_base(u_boot_console): + """Find the running U-Boot's RAM location. + + Probe the running U-Boot to determine the address of the first bank + of RAM. This is useful for tests that test reading/writing RAM, or + load/save files that aren't associated with some standard address + typically represented in an environment variable such as + ${kernel_addr_r}. The value is cached so that it only needs to be + actively read once. + + Args: + u_boot_console: A console connection to U-Boot. + + Returns: + The address of U-Boot's first RAM bank, as an integer. + """ + + global ram_base + if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': + pytest.skip('bdinfo command not supported') + if ram_base == -1: + pytest.skip('Previously failed to find RAM bank start') + if ram_base is not None: + return ram_base + + with u_boot_console.log.section('find_ram_base'): + response = u_boot_console.run_command('bdinfo') + for l in response.split('\n'): + if '-> start' in l or 'memstart =' in l: + ram_base = int(l.split('=')[1].strip(), 16) + break + if ram_base is None: + ram_base = -1 + raise Exception('Failed to find RAM bank start in `bdinfo`') + + # We don't want ram_base to be zero as some functions test if the given + # address is NULL (0). Besides, on some RISC-V targets the low memory + # is protected that prevents S-mode U-Boot from access. + # Let's add 2MiB then (size of an ARM LPAE/v8 section). + + ram_base += 1024 * 1024 * 2 + + return ram_base + +class PersistentFileHelperCtxMgr(object): + """A context manager for Python's "with" statement, which ensures that any + generated file is deleted (and hence regenerated) if its mtime is older + than the mtime of the Python module which generated it, and gets an mtime + newer than the mtime of the Python module which generated after it is + generated. Objects of this type should be created by factory function + persistent_file_helper rather than directly.""" + + def __init__(self, log, filename): + """Initialize a new object. + + Args: + log: The Logfile object to log to. + filename: The filename of the generated file. + + Returns: + Nothing. + """ + + self.log = log + self.filename = filename + + def __enter__(self): + frame = inspect.stack()[1] + module = inspect.getmodule(frame[0]) + self.module_filename = module.__file__ + self.module_timestamp = os.path.getmtime(self.module_filename) + + if os.path.exists(self.filename): + filename_timestamp = os.path.getmtime(self.filename) + if filename_timestamp < self.module_timestamp: + self.log.action('Removing stale generated file ' + + self.filename) + os.unlink(self.filename) + + def __exit__(self, extype, value, traceback): + if extype: + try: + os.path.unlink(self.filename) + except: + pass + return + logged = False + for i in range(20): + filename_timestamp = os.path.getmtime(self.filename) + if filename_timestamp > self.module_timestamp: + break + if not logged: + self.log.action( + 'Waiting for generated file timestamp to increase') + logged = True + os.utime(self.filename) + time.sleep(0.1) + +def persistent_file_helper(u_boot_log, filename): + """Manage the timestamps and regeneration of a persistent generated + file. This function creates a context manager for Python's "with" + statement + + Usage: + with persistent_file_helper(u_boot_console.log, filename): + code to generate the file, if it's missing. + + Args: + u_boot_log: u_boot_console.log. + filename: The filename of the generated file. + + Returns: + A context manager object. + """ + + return PersistentFileHelperCtxMgr(u_boot_log, filename) + +def crc32(u_boot_console, address, count): + """Helper function used to compute the CRC32 value of a section of RAM. + + Args: + u_boot_console: A U-Boot console connection. + address: Address where data starts. + count: Amount of data to use for calculation. + + Returns: + CRC32 value + """ + + bcfg = u_boot_console.config.buildconfig + has_cmd_crc32 = bcfg.get('config_cmd_crc32', 'n') == 'y' + assert has_cmd_crc32, 'Cannot compute crc32 without CONFIG_CMD_CRC32.' + output = u_boot_console.run_command('crc32 %08x %x' % (address, count)) + + m = re.search('==> ([0-9a-fA-F]{8})$', output) + assert m, 'CRC32 operation failed.' + + return m.group(1) diff --git a/roms/u-boot/test/run b/roms/u-boot/test/run new file mode 100755 index 000000000..869406cd8 --- /dev/null +++ b/roms/u-boot/test/run @@ -0,0 +1,74 @@ +#!/bin/bash + +# Script to run all U-Boot tests that use sandbox. +# $1: tests to run (empty for all, 'quick' for quick ones only) + +# Runs a test and checks the exit code to decide if it passed +# $1: Test name +# $2 onwards: command line to run +run_test() { + echo -n "$1: " + shift + "$@" + [ $? -ne 0 ] && failures=$((failures+1)) +} + +# SKip slow tests if requested +[ "$1" == "quick" ] && mark_expr="not slow" +[ "$1" == "quick" ] && skip=--skip-net-tests +[ "$1" == "tools" ] && tools_only=y + +failures=0 + +if [ -z "$tools_only" ]; then + # Run all tests that the standard sandbox build can support + run_test "sandbox" ./test/py/test.py --bd sandbox --build \ + -m "${mark_expr}" +fi + +# Run tests which require sandbox_spl +run_test "sandbox_spl" ./test/py/test.py --bd sandbox_spl --build \ + -k 'test_ofplatdata or test_handoff or test_spl' + +# Run the sane tests with sandbox_noinst (i.e. without OF_PLATDATA_INST) +run_test "sandbox_spl" ./test/py/test.py --bd sandbox_noinst --build \ + -k 'test_ofplatdata or test_handoff or test_spl' + +if [ -z "$tools_only" ]; then + # Run tests for the flat-device-tree version of sandbox. This is a special + # build which does not enable CONFIG_OF_LIVE for the live device tree, so we can + # check that functionality is the same. The standard sandbox build (above) uses + # CONFIG_OF_LIVE. + run_test "sandbox_flattree" ./test/py/test.py --bd sandbox_flattree \ + --build -k test_ut +fi + +# Set up a path to dtc (device-tree compiler) and libfdt.py, a library it +# provides and which is built by the sandbox_spl config. Also set up the path +# to tools build by the build. +DTC_DIR=build-sandbox_spl/scripts/dtc +export PYTHONPATH=${DTC_DIR}/pylibfdt +export DTC=${DTC_DIR}/dtc +TOOLS_DIR=build-sandbox_spl/tools + +run_test "binman" ./tools/binman/binman --toolpath ${TOOLS_DIR} test +run_test "patman" ./tools/patman/patman test + +run_test "buildman" ./tools/buildman/buildman -t ${skip} +run_test "fdt" ./tools/dtoc/test_fdt -t +run_test "dtoc" ./tools/dtoc/dtoc -t + +# This needs you to set up Python test coverage tools. +# To enable Python test coverage on Debian-type distributions (e.g. Ubuntu): +# $ sudo apt-get install python-pytest python-coverage +export PATH=$PATH:${TOOLS_DIR} +run_test "binman code coverage" ./tools/binman/binman test -T +run_test "dtoc code coverage" ./tools/dtoc/dtoc -T +run_test "fdt code coverage" ./tools/dtoc/test_fdt -T + +if [ $failures == 0 ]; then + echo "Tests passed!" +else + echo "Tests FAILED" + exit 1 +fi diff --git a/roms/u-boot/test/stdint/int-types.c b/roms/u-boot/test/stdint/int-types.c new file mode 100644 index 000000000..f6d09e864 --- /dev/null +++ b/roms/u-boot/test/stdint/int-types.c @@ -0,0 +1,12 @@ +#include <common.h> + +int test_types(void) +{ + uintptr_t uintptr = 0; + uint64_t uint64 = 0; + u64 u64_val = 0; + + printf("uintptr = %lu\n", uintptr); + printf("uint64 = %llu\n", uint64); + printf("u64 = %llu\n", u64_val); +} diff --git a/roms/u-boot/test/stdint/test-includes.sh b/roms/u-boot/test/stdint/test-includes.sh new file mode 100755 index 000000000..1db8515e8 --- /dev/null +++ b/roms/u-boot/test/stdint/test-includes.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +# Test script to check uintptr_t and 64-bit types for warnings +# +# It builds a few boards with different toolchains. If there are no warnings +# then all is well. +# +# Usage: +# +# Make sure that your toolchains are correct at the bottom of this file +# +# Then: +# ./test/stdint/test-includes.sh + +out=/tmp/test-includes.tmp + +try_test() { + local board=$1 + local arch=$2 + local soc=$3 + local gcc=$4 + local flags="$5" + + echo $@ + if ! which ${gcc} >/dev/null 2>&1; then + echo "Not found: ${gcc}" + return + fi + + rm -rf ${out} + mkdir -p ${out} + touch ${out}/config.h + mkdir -p ${out}/generated + touch ${out}/generated/generic-asm-offsets.h + mkdir -p ${out}/include/asm + ln -s $(pwd)/arch/${arch}/include/asm/arch-${soc} \ + ${out}/include/asm/arch + + cmd="${gcc} -c -D__KERNEL__ ${flags} \ + -fno-builtin -ffreestanding \ + -Iarch/${arch}/include \ + -Iinclude \ + -I${out} -I${out}/include \ + -include configs/${board}.h test/stdint/int-types.c \ + -o /dev/null" + $cmd +} + +try_both() { + try_test $@ +} + +# board arch soc path-to-gcc +try_both sandbox sandbox - gcc +try_both coreboot x86 - x86_64-linux-gnu-gcc +try_both seaboard arm tegra20 /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux/bin/arm-linux-gnueabihf-gcc diff --git a/roms/u-boot/test/str_ut.c b/roms/u-boot/test/str_ut.c new file mode 100644 index 000000000..359d7d4ea --- /dev/null +++ b/roms/u-boot/test/str_ut.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2020 Google LLC + */ + +#include <common.h> +#include <vsprintf.h> +#include <test/suites.h> +#include <test/test.h> +#include <test/ut.h> + +/* This is large enough for any of the test strings */ +#define TEST_STR_SIZE 200 + +static const char str1[] = "I'm sorry I'm late."; +static const char str2[] = "1099abNo, don't bother apologising."; +static const char str3[] = "0xbI'm sorry you're alive."; + +/* Declare a new str test */ +#define STR_TEST(_name, _flags) UNIT_TEST(_name, _flags, str_test) + +static int str_upper(struct unit_test_state *uts) +{ + char out[TEST_STR_SIZE]; + + /* Make sure it adds a terminator */ + out[strlen(str1)] = 'a'; + str_to_upper(str1, out, SIZE_MAX); + ut_asserteq_str("I'M SORRY I'M LATE.", out); + + /* In-place operation */ + strcpy(out, str2); + str_to_upper(out, out, SIZE_MAX); + ut_asserteq_str("1099ABNO, DON'T BOTHER APOLOGISING.", out); + + /* Limited length */ + str_to_upper(str1, out, 7); + ut_asserteq_str("I'M SORO, DON'T BOTHER APOLOGISING.", out); + + /* In-place with limited length */ + strcpy(out, str2); + str_to_upper(out, out, 7); + ut_asserteq_str("1099ABNo, don't bother apologising.", out); + + /* Copy an empty string to a buffer with space*/ + out[1] = 0x7f; + str_to_upper("", out, SIZE_MAX); + ut_asserteq('\0', *out); + ut_asserteq(0x7f, out[1]); + + /* Copy an empty string to a buffer with no space*/ + out[0] = 0x7f; + str_to_upper("", out, 0); + ut_asserteq(0x7f, out[0]); + + return 0; +} +STR_TEST(str_upper, 0); + +static int run_strtoul(struct unit_test_state *uts, const char *str, int base, + ulong expect_val, int expect_endp_offset, bool upper) +{ + char out[TEST_STR_SIZE]; + char *endp; + ulong val; + + strcpy(out, str); + if (upper) + str_to_upper(out, out, -1); + + val = simple_strtoul(out, &endp, base); + ut_asserteq(expect_val, val); + ut_asserteq(expect_endp_offset, endp - out); + + return 0; +} + +static int str_simple_strtoul(struct unit_test_state *uts) +{ + int upper; + + /* Check that it is case-insentive */ + for (upper = 0; upper < 2; upper++) { + /* Base 10 and base 16 */ + ut_assertok(run_strtoul(uts, str2, 10, 1099, 4, upper)); + ut_assertok(run_strtoul(uts, str2, 16, 0x1099ab, 6, upper)); + + /* Invalid string */ + ut_assertok(run_strtoul(uts, str1, 10, 0, 0, upper)); + + /* Base 0 */ + ut_assertok(run_strtoul(uts, str1, 0, 0, 0, upper)); + ut_assertok(run_strtoul(uts, str2, 0, 1099, 4, upper)); + ut_assertok(run_strtoul(uts, str3, 0, 0xb, 3, upper)); + + /* Base 2 */ + ut_assertok(run_strtoul(uts, str1, 2, 0, 0, upper)); + ut_assertok(run_strtoul(uts, str2, 2, 2, 2, upper)); + } + + /* Check endp being NULL */ + ut_asserteq(1099, simple_strtoul(str2, NULL, 0)); + + return 0; +} +STR_TEST(str_simple_strtoul, 0); + +int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(str_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(str_test); + + return cmd_ut_category("str", "str_", tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/test-main.c b/roms/u-boot/test/test-main.c new file mode 100644 index 000000000..7afe8741c --- /dev/null +++ b/roms/u-boot/test/test-main.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <dm/root.h> +#include <dm/test.h> +#include <dm/uclass-internal.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* This is valid when a test is running, NULL otherwise */ +static struct unit_test_state *cur_test_state; + +struct unit_test_state *test_get_state(void) +{ + return cur_test_state; +} + +void test_set_state(struct unit_test_state *uts) +{ + cur_test_state = uts; +} + +/** + * dm_test_pre_run() - Get ready to run a driver model test + * + * This clears out the driver model data structures. For sandbox it resets the + * state structure + * + * @uts: Test state + */ +static int dm_test_pre_run(struct unit_test_state *uts) +{ + bool of_live = uts->of_live; + + uts->root = NULL; + uts->testdev = NULL; + uts->force_fail_alloc = false; + uts->skip_post_probe = false; + gd->dm_root = NULL; + if (IS_ENABLED(CONFIG_UT_DM) && !CONFIG_IS_ENABLED(OF_PLATDATA)) + memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count)); + arch_reset_for_test(); + + /* Determine whether to make the live tree available */ + gd_set_of_root(of_live ? uts->of_root : NULL); + ut_assertok(dm_init(of_live)); + uts->root = dm_root(); + + return 0; +} + +static int dm_test_post_run(struct unit_test_state *uts) +{ + int id; + + /* + * With of-platdata-inst the uclasses are created at build time. If we + * destroy them we cannot get them back since uclass_add() is not + * supported. So skip this. + */ + if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) { + for (id = 0; id < UCLASS_COUNT; id++) { + struct uclass *uc; + + /* + * If the uclass doesn't exist we don't want to create + * it. So check that here before we call + * uclass_find_device(). + */ + uc = uclass_find(id); + if (!uc) + continue; + ut_assertok(uclass_destroy(uc)); + } + } + + return 0; +} + +/* Ensure all the test devices are probed */ +static int do_autoprobe(struct unit_test_state *uts) +{ + struct udevice *dev; + int ret; + + /* Scanning the uclass is enough to probe all the devices */ + for (ret = uclass_first_device(UCLASS_TEST, &dev); + dev; + ret = uclass_next_device(&dev)) + ; + + return ret; +} + +/* + * ut_test_run_on_flattree() - Check if we should run a test with flat DT + * + * This skips long/slow tests where there is not much value in running a flat + * DT test in addition to a live DT test. + * + * @return true to run the given test on the flat device tree + */ +static bool ut_test_run_on_flattree(struct unit_test *test) +{ + const char *fname = strrchr(test->file, '/') + 1; + + if (!(test->flags & UT_TESTF_DM)) + return false; + + return !strstr(fname, "video") || strstr(test->name, "video_base"); +} + +/** + * test_matches() - Check if a test should be run + * + * This checks if the a test should be run. In the normal case of running all + * tests, @select_name is NULL. + * + * @prefix: String prefix for the tests. Any tests that have this prefix will be + * printed without the prefix, so that it is easier to see the unique part + * of the test name. If NULL, any suite name (xxx_test) is considered to be + * a prefix. + * @test_name: Name of current test + * @select_name: Name of test to run (or NULL for all) + * @return true to run this test, false to skip it + */ +static bool test_matches(const char *prefix, const char *test_name, + const char *select_name) +{ + size_t len; + + if (!select_name) + return true; + + /* Allow glob expansion in the test name */ + len = select_name[strlen(select_name) - 1] == '*' ? strlen(select_name) : 0; + if (len-- == 1) + return true; + + if (!strncmp(test_name, select_name, len)) + return true; + + if (prefix) { + /* All tests have this prefix */ + if (!strncmp(test_name, prefix, strlen(prefix))) + test_name += strlen(prefix); + } else { + const char *p = strstr(test_name, "_test_"); + + /* convert xxx_test_yyy to yyy, i.e. remove the suite name */ + if (p) + test_name = p + strlen("_test_"); + } + + if (!strncmp(test_name, select_name, len)) + return true; + + return false; +} + +/** + * ut_list_has_dm_tests() - Check if a list of tests has driver model ones + * + * @tests: List of tests to run + * @count: Number of tests to ru + * @return true if any of the tests have the UT_TESTF_DM flag + */ +static bool ut_list_has_dm_tests(struct unit_test *tests, int count) +{ + struct unit_test *test; + + for (test = tests; test < tests + count; test++) { + if (test->flags & UT_TESTF_DM) + return true; + } + + return false; +} + +/** + * dm_test_restore() Put things back to normal so sandbox works as expected + * + * @of_root: Value to set for of_root + * @return 0 if OK, -ve on error + */ +static int dm_test_restore(struct device_node *of_root) +{ + int ret; + + gd_set_of_root(of_root); + gd->dm_root = NULL; + ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE)); + if (ret) + return ret; + dm_scan_plat(false); + if (!CONFIG_IS_ENABLED(OF_PLATDATA)) + dm_scan_fdt(false); + + return 0; +} + +/** + * test_pre_run() - Handle any preparation needed to run a test + * + * @uts: Test state + * @test: Test to prepare for + * @return 0 if OK, -EAGAIN to skip this test since some required feature is not + * available, other -ve on error (meaning that testing cannot likely + * continue) + */ +static int test_pre_run(struct unit_test_state *uts, struct unit_test *test) +{ + if (test->flags & UT_TESTF_DM) + ut_assertok(dm_test_pre_run(uts)); + + ut_set_skip_delays(uts, false); + + uts->start = mallinfo(); + + if (test->flags & UT_TESTF_SCAN_PDATA) + ut_assertok(dm_scan_plat(false)); + + if (test->flags & UT_TESTF_PROBE_TEST) + ut_assertok(do_autoprobe(uts)); + + if (!CONFIG_IS_ENABLED(OF_PLATDATA) && + (test->flags & UT_TESTF_SCAN_FDT)) + ut_assertok(dm_extended_scan(false)); + + if (test->flags & UT_TESTF_CONSOLE_REC) { + int ret = console_record_reset_enable(); + + if (ret) { + printf("Skipping: Console recording disabled\n"); + return -EAGAIN; + } + } + ut_silence_console(uts); + + return 0; +} + +/** + * test_post_run() - Handle cleaning up after a test + * + * @uts: Test state + * @test: Test to clean up after + * @return 0 if OK, -ve on error (meaning that testing cannot likely continue) + */ +static int test_post_run(struct unit_test_state *uts, struct unit_test *test) +{ + ut_unsilence_console(uts); + if (test->flags & UT_TESTF_DM) + ut_assertok(dm_test_post_run(uts)); + + return 0; +} + +/** + * ut_run_test() - Run a single test + * + * This runs the test, handling any preparation and clean-up needed. It prints + * the name of each test before running it. + * + * @uts: Test state to update. The caller should ensure that this is zeroed for + * the first call to this function. On exit, @uts->fail_count is + * incremented by the number of failures (0, one hopes) + * @test_name: Test to run + * @name: Name of test, possibly skipping a prefix that should not be displayed + * @return 0 if all tests passed, -EAGAIN if the test should be skipped, -1 if + * any failed + */ +static int ut_run_test(struct unit_test_state *uts, struct unit_test *test, + const char *test_name) +{ + const char *fname = strrchr(test->file, '/') + 1; + const char *note = ""; + int ret; + + if ((test->flags & UT_TESTF_DM) && !uts->of_live) + note = " (flat tree)"; + printf("Test: %s: %s%s\n", test_name, fname, note); + + /* Allow access to test state from drivers */ + test_set_state(uts); + + ret = test_pre_run(uts, test); + if (ret == -EAGAIN) + return -EAGAIN; + if (ret) + return ret; + + test->func(uts); + + ret = test_post_run(uts, test); + if (ret) + return ret; + + test_set_state( NULL); + + return 0; +} + +/** + * ut_run_test_live_flat() - Run a test with both live and flat tree + * + * This calls ut_run_test() with livetree enabled, which is the standard setup + * for runnig tests. Then, for driver model test, it calls it again with + * livetree disabled. This allows checking of flattree being used when OF_LIVE + * is enabled, as is the case in U-Boot proper before relocation, as well as in + * SPL. + * + * @uts: Test state to update. The caller should ensure that this is zeroed for + * the first call to this function. On exit, @uts->fail_count is + * incremented by the number of failures (0, one hopes) + * @test: Test to run + * @name: Name of test, possibly skipping a prefix that should not be displayed + * @return 0 if all tests passed, -EAGAIN if the test should be skipped, -1 if + * any failed + */ +static int ut_run_test_live_flat(struct unit_test_state *uts, + struct unit_test *test, const char *name) +{ + int runs; + + /* Run with the live tree if possible */ + runs = 0; + if (CONFIG_IS_ENABLED(OF_LIVE)) { + if (!(test->flags & UT_TESTF_FLAT_TREE)) { + uts->of_live = true; + ut_assertok(ut_run_test(uts, test, test->name)); + runs++; + } + } + + /* + * Run with the flat tree if we couldn't run it with live tree, + * or it is a core test. + */ + if (!(test->flags & UT_TESTF_LIVE_TREE) && + (!runs || ut_test_run_on_flattree(test))) { + uts->of_live = false; + ut_assertok(ut_run_test(uts, test, test->name)); + runs++; + } + + return 0; +} + +/** + * ut_run_tests() - Run a set of tests + * + * This runs the tests, handling any preparation and clean-up needed. It prints + * the name of each test before running it. + * + * @uts: Test state to update. The caller should ensure that this is zeroed for + * the first call to this function. On exit, @uts->fail_count is + * incremented by the number of failures (0, one hopes) + * @prefix: String prefix for the tests. Any tests that have this prefix will be + * printed without the prefix, so that it is easier to see the unique part + * of the test name. If NULL, no prefix processing is done + * @tests: List of tests to run + * @count: Number of tests to run + * @select_name: Name of a single test to run (from the list provided). If NULL + * then all tests are run + * @return 0 if all tests passed, -ENOENT if test @select_name was not found, + * -EBADF if any failed + */ +static int ut_run_tests(struct unit_test_state *uts, const char *prefix, + struct unit_test *tests, int count, + const char *select_name) +{ + struct unit_test *test; + int found = 0; + + for (test = tests; test < tests + count; test++) { + const char *test_name = test->name; + int ret; + + if (!test_matches(prefix, test_name, select_name)) + continue; + ret = ut_run_test_live_flat(uts, test, select_name); + found++; + if (ret == -EAGAIN) + continue; + if (ret) + return ret; + } + if (select_name && !found) + return -ENOENT; + + return uts->fail_count ? -EBADF : 0; +} + +int ut_run_list(const char *category, const char *prefix, + struct unit_test *tests, int count, const char *select_name) +{ + struct unit_test_state uts = { .fail_count = 0 }; + bool has_dm_tests = false; + int ret; + + if (!CONFIG_IS_ENABLED(OF_PLATDATA) && + ut_list_has_dm_tests(tests, count)) { + has_dm_tests = true; + /* + * If we have no device tree, or it only has a root node, then + * these * tests clearly aren't going to work... + */ + if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) { + puts("Please run with test device tree:\n" + " ./u-boot -d arch/sandbox/dts/test.dtb\n"); + return CMD_RET_FAILURE; + } + } + + if (!select_name) + printf("Running %d %s tests\n", count, category); + + uts.of_root = gd_of_root(); + ret = ut_run_tests(&uts, prefix, tests, count, select_name); + + if (ret == -ENOENT) + printf("Test '%s' not found\n", select_name); + else + printf("Failures: %d\n", uts.fail_count); + + /* Best efforts only...ignore errors */ + if (has_dm_tests) + dm_test_restore(uts.of_root); + + return ret; +} diff --git a/roms/u-boot/test/time_ut.c b/roms/u-boot/test/time_ut.c new file mode 100644 index 000000000..80b82dbfd --- /dev/null +++ b/roms/u-boot/test/time_ut.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + * Written by Simon Glass <sjg@chromium.org> + */ + +#include <common.h> +#include <command.h> +#include <errno.h> +#include <time.h> +#include <linux/delay.h> + +static int test_get_timer(void) +{ + ulong base, start, next, diff; + int iter; + + base = get_timer(0); + start = get_timer(0); + for (iter = 0; iter < 10; iter++) { + do { + next = get_timer(0); + } while (start == next); + + if (start + 1 != next) { + printf("%s: iter=%d, start=%lu, next=%lu, expected a difference of 1\n", + __func__, iter, start, next); + return -EINVAL; + } + start++; + } + + /* + * Check that get_timer(base) matches our elapsed time, allowing that + * an extra millisecond may have passed. + */ + diff = get_timer(base); + if (diff != iter && diff != iter + 1) { + printf("%s: expected get_timer(base) to match elapsed time: diff=%lu, expected=%d\n", + __func__, diff, iter); + return -EINVAL; + } + + return 0; +} + +static int test_timer_get_us(void) +{ + ulong prev, next, min = 1000000; + long delta; + int iter; + + /* Find the minimum delta we can measure, in microseconds */ + prev = timer_get_us(); + for (iter = 0; iter < 100; ) { + next = timer_get_us(); + if (next != prev) { + delta = next - prev; + if (delta < 0) { + printf("%s: timer_get_us() went backwards from %lu to %lu\n", + __func__, prev, next); + return -EINVAL; + } else if (delta != 0) { + if (delta < min) + min = delta; + prev = next; + iter++; + } + } + } + + if (min != 1) { + printf("%s: Minimum microsecond delta should be 1 but is %lu\n", + __func__, min); + return -EINVAL; + } + + return 0; +} + +static int test_time_comparison(void) +{ + ulong start_us, end_us, delta_us; + long error; + ulong start; + + start = get_timer(0); + start_us = timer_get_us(); + while (get_timer(start) < 1000) + ; + end_us = timer_get_us(); + delta_us = end_us - start_us; + error = delta_us - 1000000; + printf("%s: Microsecond time for 1 second: %lu, error = %ld\n", + __func__, delta_us, error); + if (abs(error) > 1000) + return -EINVAL; + + return 0; +} + +static int test_udelay(void) +{ + long error; + ulong start, delta; + int iter; + + start = get_timer(0); + for (iter = 0; iter < 1000; iter++) + udelay(1000); + delta = get_timer(start); + error = delta - 1000; + printf("%s: Delay time for 1000 udelay(1000): %lu ms, error = %ld\n", + __func__, delta, error); + if (abs(error) > 100) + return -EINVAL; + + return 0; +} + +int do_ut_time(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + int ret = 0; + + ret |= test_get_timer(); + ret |= test_timer_get_us(); + ret |= test_time_comparison(); + ret |= test_udelay(); + + printf("Test %s\n", ret ? "failed" : "passed"); + + return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS; +} diff --git a/roms/u-boot/test/trace/test-trace.sh b/roms/u-boot/test/trace/test-trace.sh new file mode 100755 index 000000000..5130b2bf0 --- /dev/null +++ b/roms/u-boot/test/trace/test-trace.sh @@ -0,0 +1,64 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0+ +# Copyright (c) 2013 The Chromium OS Authors. +# + +# Simple test script for tracing with sandbox + +TRACE_OPT="FTRACE=1" + +BASE="$(dirname $0)/.." +. $BASE/common.sh + +run_trace() { + echo "Run trace" + ./${OUTPUT_DIR}/u-boot <<END +trace stats +hash sha256 0 10000 +trace pause +trace stats +hash sha256 0 10000 +trace stats +trace resume +hash sha256 0 10000 +trace pause +trace stats +reset +END +} + +check_results() { + echo "Check results" + + # Expect sha256 to run 3 times, so we see the string 6 times + if [ $(grep -c sha256 ${tmp}) -ne 6 ]; then + fail "sha256 error" + fi + + # 4 sets of results (output of 'trace stats') + if [ $(grep -c "traced function calls" ${tmp}) -ne 4 ]; then + fail "trace output error" + fi + + # Check trace counts. We expect to see an increase in the number of + # traced function calls between each 'trace stats' command, except + # between calls 2 and 3, where tracing is paused. + # This code gets the sign of the difference between each number and + # its predecessor. + counts="$(tr -d ',\r' <${tmp} | awk \ + '/traced function calls/ { diff = $1 - upto; upto = $1; \ + printf "%d ", diff < 0 ? -1 : (diff > 0 ? 1 : 0)}')" + + if [ "${counts}" != "1 1 0 1 " ]; then + fail "trace collection error: ${counts}" + fi +} + +echo "Simple trace test / sanity check using sandbox" +echo +tmp="$(tempfile)" +build_uboot "${TRACE_OPT}" +run_trace >${tmp} +check_results ${tmp} +rm ${tmp} +echo "Test passed" diff --git a/roms/u-boot/test/unicode_ut.c b/roms/u-boot/test/unicode_ut.c new file mode 100644 index 000000000..617eed8cf --- /dev/null +++ b/roms/u-boot/test/unicode_ut.c @@ -0,0 +1,737 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Unit tests for Unicode functions + * + * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de> + */ + +#include <common.h> +#include <charset.h> +#include <command.h> +#include <efi_loader.h> +#include <errno.h> +#include <log.h> +#include <malloc.h> +#include <test/test.h> +#include <test/suites.h> +#include <test/ut.h> + +/* Linker list entry for a Unicode test */ +#define UNICODE_TEST(_name) UNIT_TEST(_name, 0, unicode_test) + +/* Constants c1-c4 and d1-d4 encode the same letters */ + +/* Six characters translating to one utf-8 byte each. */ +static const u16 c1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00}; +/* One character translating to two utf-8 bytes */ +static const u16 c2[] = {0x6b, 0x61, 0x66, 0x62, 0xe1, 0x74, 0x75, 0x72, 0x00}; +/* Three characters translating to three utf-8 bytes each */ +static const u16 c3[] = {0x6f5c, 0x6c34, 0x8266, 0x00}; +/* Three letters translating to four utf-8 bytes each */ +static const u16 c4[] = {0xd801, 0xdc8d, 0xd801, 0xdc96, 0xd801, 0xdc87, + 0x0000}; + +/* Illegal utf-16 strings */ +static const u16 i1[] = {0x69, 0x31, 0xdc87, 0x6c, 0x00}; +static const u16 i2[] = {0x69, 0x32, 0xd801, 0xd801, 0x6c, 0x00}; +static const u16 i3[] = {0x69, 0x33, 0xd801, 0x00}; + +/* Six characters translating to one utf-16 word each. */ +static const char d1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00}; +/* Eight characters translating to one utf-16 word each */ +static const char d2[] = {0x6b, 0x61, 0x66, 0x62, 0xc3, 0xa1, 0x74, 0x75, + 0x72, 0x00}; +/* Three characters translating to one utf-16 word each */ +static const char d3[] = {0xe6, 0xbd, 0x9c, 0xe6, 0xb0, 0xb4, 0xe8, 0x89, + 0xa6, 0x00}; +/* Three letters translating to two utf-16 word each */ +static const char d4[] = {0xf0, 0x90, 0x92, 0x8d, 0xf0, 0x90, 0x92, 0x96, + 0xf0, 0x90, 0x92, 0x87, 0x00}; +/* Letter not in code page 437 */ +static const char d5[] = {0xCE, 0x92, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, + 0x74, 0x20, 0x42, 0x00}; + +/* Illegal utf-8 strings */ +static const char j1[] = {0x6a, 0x31, 0xa1, 0x6c, 0x00}; +static const char j2[] = {0x6a, 0x32, 0xc3, 0xc3, 0x6c, 0x00}; +static const char j3[] = {0x6a, 0x33, 0xf0, 0x90, 0xf0, 0x00}; +static const char j4[] = {0xa1, 0x00}; + +static int unicode_test_u16_strlen(struct unit_test_state *uts) +{ + ut_asserteq(6, u16_strlen(c1)); + ut_asserteq(8, u16_strlen(c2)); + ut_asserteq(3, u16_strlen(c3)); + ut_asserteq(6, u16_strlen(c4)); + return 0; +} +UNICODE_TEST(unicode_test_u16_strlen); + +static int unicode_test_u16_strdup(struct unit_test_state *uts) +{ + u16 *copy = u16_strdup(c4); + + ut_assert(copy != c4); + ut_asserteq_mem(copy, c4, sizeof(c4)); + free(copy); + + return 0; +} +UNICODE_TEST(unicode_test_u16_strdup); + +static int unicode_test_u16_strcpy(struct unit_test_state *uts) +{ + u16 *r; + u16 copy[10]; + + r = u16_strcpy(copy, c1); + ut_assert(r == copy); + ut_asserteq_mem(copy, c1, sizeof(c1)); + + return 0; +} +UNICODE_TEST(unicode_test_u16_strcpy); + +/* U-Boot uses UTF-16 strings in the EFI context only. */ +#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD) +static int unicode_test_string16(struct unit_test_state *uts) +{ + char buf[20]; + + /* Test length and precision */ + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%8.6ls", c2); + ut_asserteq(' ', buf[1]); + ut_assert(!strncmp(&buf[2], d2, 7)); + ut_assert(!buf[9]); + + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%8.6ls", c4); + ut_asserteq(' ', buf[4]); + ut_assert(!strncmp(&buf[5], d4, 12)); + ut_assert(!buf[17]); + + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%-8.2ls", c4); + ut_asserteq(' ', buf[8]); + ut_assert(!strncmp(buf, d4, 8)); + ut_assert(!buf[14]); + + /* Test handling of illegal utf-16 sequences */ + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%ls", i1); + ut_asserteq_str("i1?l", buf); + + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%ls", i2); + ut_asserteq_str("i2?l", buf); + + memset(buf, 0xff, sizeof(buf)); + sprintf(buf, "%ls", i3); + ut_asserteq_str("i3?", buf); + + return 0; +} +UNICODE_TEST(unicode_test_string16); +#endif + +static int unicode_test_utf8_get(struct unit_test_state *uts) +{ + const char *s; + s32 code; + int i; + + /* Check characters less than 0x800 */ + s = d2; + for (i = 0; i < 8; ++i) { + code = utf8_get((const char **)&s); + /* c2 is the utf-8 encoding of d2 */ + ut_asserteq(c2[i], code); + if (!code) + break; + } + ut_asserteq_ptr(s, d2 + 9) + + /* Check characters less than 0x10000 */ + s = d3; + for (i = 0; i < 4; ++i) { + code = utf8_get((const char **)&s); + /* c3 is the utf-8 encoding of d3 */ + ut_asserteq(c3[i], code); + if (!code) + break; + } + ut_asserteq_ptr(s, d3 + 9) + + /* Check character greater 0xffff */ + s = d4; + code = utf8_get((const char **)&s); + ut_asserteq(0x0001048d, code); + ut_asserteq_ptr(s, d4 + 4); + + /* Check illegal character */ + s = j4; + code = utf8_get((const char **)&s); + ut_asserteq(-1, code); + ut_asserteq_ptr(j4 + 1, s); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_get); + +static int unicode_test_utf8_put(struct unit_test_state *uts) +{ + char buffer[8] = { 0, }; + char *pos; + + /* Commercial at, translates to one character */ + pos = buffer; + ut_assert(!utf8_put('@', &pos)) + ut_asserteq(1, pos - buffer); + ut_asserteq('@', buffer[0]); + ut_assert(!buffer[1]); + + /* Latin letter G with acute, translates to two charactes */ + pos = buffer; + ut_assert(!utf8_put(0x1f4, &pos)); + ut_asserteq(2, pos - buffer); + ut_asserteq_str("\xc7\xb4", buffer); + + /* Tagalog letter i, translates to three characters */ + pos = buffer; + ut_assert(!utf8_put(0x1701, &pos)); + ut_asserteq(3, pos - buffer); + ut_asserteq_str("\xe1\x9c\x81", buffer); + + /* Hamster face, translates to four characters */ + pos = buffer; + ut_assert(!utf8_put(0x1f439, &pos)); + ut_asserteq(4, pos - buffer); + ut_asserteq_str("\xf0\x9f\x90\xb9", buffer); + + /* Illegal code */ + pos = buffer; + ut_asserteq(-1, utf8_put(0xd888, &pos)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_put); + +static int unicode_test_utf8_utf16_strlen(struct unit_test_state *uts) +{ + ut_asserteq(6, utf8_utf16_strlen(d1)); + ut_asserteq(8, utf8_utf16_strlen(d2)); + ut_asserteq(3, utf8_utf16_strlen(d3)); + ut_asserteq(6, utf8_utf16_strlen(d4)); + + /* illegal utf-8 sequences */ + ut_asserteq(4, utf8_utf16_strlen(j1)); + ut_asserteq(4, utf8_utf16_strlen(j2)); + ut_asserteq(3, utf8_utf16_strlen(j3)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_utf16_strlen); + +static int unicode_test_utf8_utf16_strnlen(struct unit_test_state *uts) +{ + ut_asserteq(3, utf8_utf16_strnlen(d1, 3)); + ut_asserteq(6, utf8_utf16_strnlen(d1, 13)); + ut_asserteq(6, utf8_utf16_strnlen(d2, 6)); + ut_asserteq(2, utf8_utf16_strnlen(d3, 2)); + ut_asserteq(4, utf8_utf16_strnlen(d4, 2)); + ut_asserteq(6, utf8_utf16_strnlen(d4, 3)); + + /* illegal utf-8 sequences */ + ut_asserteq(4, utf8_utf16_strnlen(j1, 16)); + ut_asserteq(4, utf8_utf16_strnlen(j2, 16)); + ut_asserteq(3, utf8_utf16_strnlen(j3, 16)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_utf16_strnlen); + +/** + * ut_u16_strcmp() - Compare to u16 strings. + * + * @a1: first string + * @a2: second string + * @count: number of u16 to compare + * Return: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2 + */ +static int unicode_test_u16_strcmp(const u16 *a1, const u16 *a2, size_t count) +{ + for (; (*a1 || *a2) && count; ++a1, ++a2, --count) { + if (*a1 < *a2) + return -1; + if (*a1 > *a2) + return 1; + } + return 0; +} + +static int unicode_test_utf8_utf16_strcpy(struct unit_test_state *uts) +{ + u16 buf[16]; + u16 *pos; + + pos = buf; + utf8_utf16_strcpy(&pos, d1); + ut_asserteq(6, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, c1, SIZE_MAX)); + + pos = buf; + utf8_utf16_strcpy(&pos, d2); + ut_asserteq(8, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX)); + + pos = buf; + utf8_utf16_strcpy(&pos, d3); + ut_asserteq(3, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, c3, SIZE_MAX)); + + pos = buf; + utf8_utf16_strcpy(&pos, d4); + ut_asserteq(6, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX)); + + /* Illegal utf-8 strings */ + pos = buf; + utf8_utf16_strcpy(&pos, j1); + ut_asserteq(4, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, L"j1?l", SIZE_MAX)); + + pos = buf; + utf8_utf16_strcpy(&pos, j2); + ut_asserteq(4, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, L"j2?l", SIZE_MAX)); + + pos = buf; + utf8_utf16_strcpy(&pos, j3); + ut_asserteq(3, pos - buf); + ut_assert(!unicode_test_u16_strcmp(buf, L"j3?", SIZE_MAX)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_utf16_strcpy); + +static int unicode_test_utf8_utf16_strncpy(struct unit_test_state *uts) +{ + u16 buf[16]; + u16 *pos; + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf8_utf16_strncpy(&pos, d1, 4); + ut_asserteq(4, pos - buf); + ut_assert(!buf[4]); + ut_assert(!unicode_test_u16_strcmp(buf, c1, 4)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf8_utf16_strncpy(&pos, d2, 10); + ut_asserteq(8, pos - buf); + ut_assert(buf[4]); + ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf8_utf16_strncpy(&pos, d3, 2); + ut_asserteq(2, pos - buf); + ut_assert(!buf[2]); + ut_assert(!unicode_test_u16_strcmp(buf, c3, 2)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf8_utf16_strncpy(&pos, d4, 2); + ut_asserteq(4, pos - buf); + ut_assert(!buf[4]); + ut_assert(!unicode_test_u16_strcmp(buf, c4, 4)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf8_utf16_strncpy(&pos, d4, 10); + ut_asserteq(6, pos - buf); + ut_assert(buf[5]); + ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_utf16_strncpy); + +static int unicode_test_utf16_get(struct unit_test_state *uts) +{ + const u16 *s; + s32 code; + int i; + + /* Check characters less than 0x10000 */ + s = c2; + for (i = 0; i < 9; ++i) { + code = utf16_get((const u16 **)&s); + ut_asserteq(c2[i], code); + if (!code) + break; + } + ut_asserteq_ptr(c2 + 8, s); + + /* Check character greater 0xffff */ + s = c4; + code = utf16_get((const u16 **)&s); + ut_asserteq(0x0001048d, code); + ut_asserteq_ptr(c4 + 2, s); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_get); + +static int unicode_test_utf16_put(struct unit_test_state *uts) +{ + u16 buffer[4] = { 0, }; + u16 *pos; + + /* Commercial at, translates to one word */ + pos = buffer; + ut_assert(!utf16_put('@', &pos)); + ut_asserteq(1, pos - buffer); + ut_asserteq((u16)'@', buffer[0]); + ut_assert(!buffer[1]); + + /* Hamster face, translates to two words */ + pos = buffer; + ut_assert(!utf16_put(0x1f439, &pos)); + ut_asserteq(2, pos - buffer); + ut_asserteq((u16)0xd83d, buffer[0]); + ut_asserteq((u16)0xdc39, buffer[1]); + ut_assert(!buffer[2]); + + /* Illegal code */ + pos = buffer; + ut_asserteq(-1, utf16_put(0xd888, &pos)); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_put); + +static int unicode_test_utf16_strnlen(struct unit_test_state *uts) +{ + ut_asserteq(3, utf16_strnlen(c1, 3)); + ut_asserteq(6, utf16_strnlen(c1, 13)); + ut_asserteq(6, utf16_strnlen(c2, 6)); + ut_asserteq(2, utf16_strnlen(c3, 2)); + ut_asserteq(2, utf16_strnlen(c4, 2)); + ut_asserteq(3, utf16_strnlen(c4, 3)); + + /* illegal utf-16 word sequences */ + ut_asserteq(4, utf16_strnlen(i1, 16)); + ut_asserteq(4, utf16_strnlen(i2, 16)); + ut_asserteq(3, utf16_strnlen(i3, 16)); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_strnlen); + +static int unicode_test_utf16_utf8_strlen(struct unit_test_state *uts) +{ + ut_asserteq(6, utf16_utf8_strlen(c1)); + ut_asserteq(9, utf16_utf8_strlen(c2)); + ut_asserteq(9, utf16_utf8_strlen(c3)); + ut_asserteq(12, utf16_utf8_strlen(c4)); + + /* illegal utf-16 word sequences */ + ut_asserteq(4, utf16_utf8_strlen(i1)); + ut_asserteq(4, utf16_utf8_strlen(i2)); + ut_asserteq(3, utf16_utf8_strlen(i3)); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_utf8_strlen); + +static int unicode_test_utf16_utf8_strnlen(struct unit_test_state *uts) +{ + ut_asserteq(3, utf16_utf8_strnlen(c1, 3)); + ut_asserteq(6, utf16_utf8_strnlen(c1, 13)); + ut_asserteq(7, utf16_utf8_strnlen(c2, 6)); + ut_asserteq(6, utf16_utf8_strnlen(c3, 2)); + ut_asserteq(8, utf16_utf8_strnlen(c4, 2)); + ut_asserteq(12, utf16_utf8_strnlen(c4, 3)); + return 0; +} +UNICODE_TEST(unicode_test_utf16_utf8_strnlen); + +static int unicode_test_utf16_utf8_strcpy(struct unit_test_state *uts) +{ + char buf[16]; + char *pos; + + pos = buf; + utf16_utf8_strcpy(&pos, c1); + ut_asserteq(6, pos - buf); + ut_asserteq_str(d1, buf); + + pos = buf; + utf16_utf8_strcpy(&pos, c2); + ut_asserteq(9, pos - buf); + ut_asserteq_str(d2, buf); + + pos = buf; + utf16_utf8_strcpy(&pos, c3); + ut_asserteq(9, pos - buf); + ut_asserteq_str(d3, buf); + + pos = buf; + utf16_utf8_strcpy(&pos, c4); + ut_asserteq(12, pos - buf); + ut_asserteq_str(d4, buf); + + /* Illegal utf-16 strings */ + pos = buf; + utf16_utf8_strcpy(&pos, i1); + ut_asserteq(4, pos - buf); + ut_asserteq_str("i1?l", buf); + + pos = buf; + utf16_utf8_strcpy(&pos, i2); + ut_asserteq(4, pos - buf); + ut_asserteq_str("i2?l", buf); + + pos = buf; + utf16_utf8_strcpy(&pos, i3); + ut_asserteq(3, pos - buf); + ut_asserteq_str("i3?", buf); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_utf8_strcpy); + +static int unicode_test_utf16_utf8_strncpy(struct unit_test_state *uts) +{ + char buf[16]; + char *pos; + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf16_utf8_strncpy(&pos, c1, 4); + ut_asserteq(4, pos - buf); + ut_assert(!buf[4]); + ut_assert(!strncmp(buf, d1, 4)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf16_utf8_strncpy(&pos, c2, 10); + ut_asserteq(9, pos - buf); + ut_assert(buf[4]); + ut_assert(!strncmp(buf, d2, SIZE_MAX)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf16_utf8_strncpy(&pos, c3, 2); + ut_asserteq(6, pos - buf); + ut_assert(!buf[6]); + ut_assert(!strncmp(buf, d3, 6)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf16_utf8_strncpy(&pos, c4, 2); + ut_asserteq(8, pos - buf); + ut_assert(!buf[8]); + ut_assert(!strncmp(buf, d4, 8)); + + pos = buf; + memset(buf, 0, sizeof(buf)); + utf16_utf8_strncpy(&pos, c4, 10); + ut_asserteq(12, pos - buf); + ut_assert(buf[5]); + ut_assert(!strncmp(buf, d4, SIZE_MAX)); + + return 0; +} +UNICODE_TEST(unicode_test_utf16_utf8_strncpy); + +static int unicode_test_utf_to_lower(struct unit_test_state *uts) +{ + ut_asserteq('@', utf_to_lower('@')); + ut_asserteq('a', utf_to_lower('A')); + ut_asserteq('z', utf_to_lower('Z')); + ut_asserteq('[', utf_to_lower('[')); + ut_asserteq('m', utf_to_lower('m')); + /* Latin letter O with diaresis (umlaut) */ + ut_asserteq(0x00f6, utf_to_lower(0x00d6)); +#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION + /* Cyrillic letter I*/ + ut_asserteq(0x0438, utf_to_lower(0x0418)); +#endif + return 0; +} +UNICODE_TEST(unicode_test_utf_to_lower); + +static int unicode_test_utf_to_upper(struct unit_test_state *uts) +{ + ut_asserteq('`', utf_to_upper('`')); + ut_asserteq('A', utf_to_upper('a')); + ut_asserteq('Z', utf_to_upper('z')); + ut_asserteq('{', utf_to_upper('{')); + ut_asserteq('M', utf_to_upper('M')); + /* Latin letter O with diaresis (umlaut) */ + ut_asserteq(0x00d6, utf_to_upper(0x00f6)); +#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION + /* Cyrillic letter I */ + ut_asserteq(0x0418, utf_to_upper(0x0438)); +#endif + return 0; +} +UNICODE_TEST(unicode_test_utf_to_upper); + +static int unicode_test_u16_strncmp(struct unit_test_state *uts) +{ + ut_assert(u16_strncmp(L"abc", L"abc", 3) == 0); + ut_assert(u16_strncmp(L"abcdef", L"abcghi", 3) == 0); + ut_assert(u16_strncmp(L"abcdef", L"abcghi", 6) < 0); + ut_assert(u16_strncmp(L"abcghi", L"abcdef", 6) > 0); + ut_assert(u16_strcmp(L"abc", L"abc") == 0); + ut_assert(u16_strcmp(L"abcdef", L"deghi") < 0); + ut_assert(u16_strcmp(L"deghi", L"abcdef") > 0); + return 0; +} +UNICODE_TEST(unicode_test_u16_strncmp); + +static int unicode_test_u16_strsize(struct unit_test_state *uts) +{ + ut_asserteq_64(u16_strsize(c1), 14); + ut_asserteq_64(u16_strsize(c2), 18); + ut_asserteq_64(u16_strsize(c3), 8); + ut_asserteq_64(u16_strsize(c4), 14); + return 0; +} +UNICODE_TEST(unicode_test_u16_strsize); + +static int unicode_test_utf_to_cp(struct unit_test_state *uts) +{ + int ret; + s32 c; + + c = '\n'; + ret = utf_to_cp(&c, codepage_437); + ut_asserteq(0, ret); + ut_asserteq('\n', c); + + c = 'a'; + ret = utf_to_cp(&c, codepage_437); + ut_asserteq(0, ret); + ut_asserteq('a', c); + + c = 0x03c4; /* Greek small letter tau */ + ret = utf_to_cp(&c, codepage_437); + ut_asserteq(0, ret); + ut_asserteq(0xe7, c); + + c = 0x03a4; /* Greek capital letter tau */ + ret = utf_to_cp(&c, codepage_437); + ut_asserteq(-ENOENT, ret); + ut_asserteq('?', c); + + return 0; +} +UNICODE_TEST(unicode_test_utf_to_cp); + +static void utf8_to_cp437_stream_helper(const char *in, char *out) +{ + char buffer[5]; + int ret; + + *buffer = 0; + for (; *in; ++in) { + ret = utf8_to_cp437_stream(*in, buffer); + if (ret) + *out++ = ret; + } + *out = 0; +} + +static int unicode_test_utf8_to_cp437_stream(struct unit_test_state *uts) +{ + char buf[16]; + + utf8_to_cp437_stream_helper(d1, buf); + ut_asserteq_str("U-Boot", buf); + utf8_to_cp437_stream_helper(d2, buf); + ut_asserteq_str("kafb\xa0tur", buf); + utf8_to_cp437_stream_helper(d5, buf); + ut_asserteq_str("? is not B", buf); + utf8_to_cp437_stream_helper(j2, buf); + ut_asserteq_str("j2l", buf); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_to_cp437_stream); + +static void utf8_to_utf32_stream_helper(const char *in, s32 *out) +{ + char buffer[5]; + int ret; + + *buffer = 0; + for (; *in; ++in) { + ret = utf8_to_utf32_stream(*in, buffer); + if (ret) + *out++ = ret; + } + *out = 0; +} + +static int unicode_test_utf8_to_utf32_stream(struct unit_test_state *uts) +{ + s32 buf[16]; + + const u32 u1[] = {0x55, 0x2D, 0x42, 0x6F, 0x6F, 0x74, 0x0000}; + const u32 u2[] = {0x6B, 0x61, 0x66, 0x62, 0xE1, 0x74, 0x75, 0x72, 0x00}; + const u32 u3[] = {0x0392, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, 0x74, + 0x20, 0x42, 0x00}; + const u32 u4[] = {0x6A, 0x32, 0x6C, 0x00}; + + memset(buf, 0, sizeof(buf)); + utf8_to_utf32_stream_helper(d1, buf); + ut_asserteq_mem(u1, buf, sizeof(u1)); + + memset(buf, 0, sizeof(buf)); + utf8_to_utf32_stream_helper(d2, buf); + ut_asserteq_mem(u2, buf, sizeof(u2)); + + memset(buf, 0, sizeof(buf)); + utf8_to_utf32_stream_helper(d5, buf); + ut_asserteq_mem(u3, buf, sizeof(u3)); + + memset(buf, 0, sizeof(buf)); + utf8_to_utf32_stream_helper(j2, buf); + ut_asserteq_mem(u4, buf, sizeof(u4)); + + return 0; +} +UNICODE_TEST(unicode_test_utf8_to_utf32_stream); + +#ifdef CONFIG_EFI_LOADER +static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts) +{ + u16 buf[16]; + u16 const expected[] = L"Capsule0AF9"; + u16 *pos; + + memset(buf, 0xeb, sizeof(buf)); + pos = efi_create_indexed_name(buf, sizeof(buf), "Capsule", 0x0af9); + + ut_asserteq_mem(expected, buf, sizeof(expected)); + ut_asserteq(pos - buf, u16_strnlen(buf, SIZE_MAX)); + + return 0; +} +UNICODE_TEST(unicode_test_efi_create_indexed_name); +#endif + +int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + struct unit_test *tests = UNIT_TEST_SUITE_START(unicode_test); + const int n_ents = UNIT_TEST_SUITE_COUNT(unicode_test); + + return cmd_ut_category("Unicode", "unicode_test_", + tests, n_ents, argc, argv); +} diff --git a/roms/u-boot/test/ut.c b/roms/u-boot/test/ut.c new file mode 100644 index 000000000..ea0af153e --- /dev/null +++ b/roms/u-boot/test/ut.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Simple unit test library + * + * Copyright (c) 2013 Google, Inc + */ + +#include <common.h> +#include <console.h> +#include <malloc.h> +#ifdef CONFIG_SANDBOX +#include <asm/state.h> +#endif +#include <asm/global_data.h> +#include <test/test.h> +#include <test/ut.h> + +DECLARE_GLOBAL_DATA_PTR; + +void ut_fail(struct unit_test_state *uts, const char *fname, int line, + const char *func, const char *cond) +{ + gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD); + printf("%s:%d, %s(): %s\n", fname, line, func, cond); + uts->fail_count++; +} + +void ut_failf(struct unit_test_state *uts, const char *fname, int line, + const char *func, const char *cond, const char *fmt, ...) +{ + va_list args; + + gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD); + printf("%s:%d, %s(): %s: ", fname, line, func, cond); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + putc('\n'); + uts->fail_count++; +} + +ulong ut_check_free(void) +{ + struct mallinfo info = mallinfo(); + + return info.uordblks; +} + +long ut_check_delta(ulong last) +{ + return ut_check_free() - last; +} + +int ut_check_console_line(struct unit_test_state *uts, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args); + va_end(args); + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + + return strcmp(uts->expect_str, uts->actual_str); +} + +int ut_check_console_linen(struct unit_test_state *uts, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args); + va_end(args); + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + + return strncmp(uts->expect_str, uts->actual_str, + strlen(uts->expect_str)); +} + +int ut_check_skipline(struct unit_test_state *uts) +{ + if (!console_record_avail()) + return -ENFILE; + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + + return 0; +} + +int ut_check_console_end(struct unit_test_state *uts) +{ + if (!console_record_avail()) + return 0; + + console_record_readline(uts->actual_str, sizeof(uts->actual_str)); + + return 1; +} + +int ut_check_console_dump(struct unit_test_state *uts, int total_bytes) +{ + char *str = uts->actual_str; + int upto; + + /* Handle empty dump */ + if (!total_bytes) + return 0; + + for (upto = 0; upto < total_bytes;) { + int len; + int bytes; + + len = console_record_readline(str, sizeof(uts->actual_str)); + if (str[8] != ':' || str[9] != ' ') + return 1; + + bytes = len - 8 - 2 - 3 * 16 - 4; + upto += bytes; + } + + return upto == total_bytes ? 0 : 1; +} + +void ut_silence_console(struct unit_test_state *uts) +{ +#ifdef CONFIG_SANDBOX + struct sandbox_state *state = state_get_current(); + + if (!state->show_test_output) + gd->flags |= GD_FLG_SILENT; +#endif +} + +void ut_unsilence_console(struct unit_test_state *uts) +{ + gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD); +} + +void ut_set_skip_delays(struct unit_test_state *uts, bool skip_delays) +{ +#ifdef CONFIG_SANDBOX + state_set_skip_delays(skip_delays); +#endif +} |