From fa847bb409d6a07bbd923e7889b485e943d75689 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 10 Mar 2023 04:33:13 +0100 Subject: [PATCH 01/16] test: Wrap assert macros in ({ ... }) and fix missing semicolons Wrap the assert macros in ({ ... }) so they can be safely used both as right side argument as well as in conditionals without curly brackets around them. In the process, find a bunch of missing semicolons, fix them. Reviewed-by: Simon Glass Signed-off-by: Marek Vasut --- include/test/ut.h | 152 +++++++++++++++++++++++++++-------------- test/cmd/pwm.c | 4 +- test/dm/acpigen.c | 2 +- test/dm/misc.c | 4 +- test/dm/phy.c | 8 +-- test/dm/scmi.c | 4 +- test/lib/kconfig.c | 10 +-- test/lib/kconfig_spl.c | 6 +- test/unicode_ut.c | 6 +- 9 files changed, 124 insertions(+), 72 deletions(-) diff --git a/include/test/ut.h b/include/test/ut.h index 2b0dab32f68..dddf9ad241f 100644 --- a/include/test/ut.h +++ b/include/test/ut.h @@ -125,36 +125,47 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); fmt, ##args) /* Assert that a condition is non-zero */ -#define ut_assert(cond) \ +#define ut_assert(cond) ({ \ + int __ret = 0; \ + \ if (!(cond)) { \ ut_fail(uts, __FILE__, __LINE__, __func__, #cond); \ - return CMD_RET_FAILURE; \ - } + __ret = CMD_RET_FAILURE; \ + } \ + __ret; \ +}) /* Assert that a condition is non-zero, with printf() string */ -#define ut_assertf(cond, fmt, args...) \ +#define ut_assertf(cond, fmt, args...) ({ \ + int __ret = 0; \ + \ if (!(cond)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, #cond, \ fmt, ##args); \ - return CMD_RET_FAILURE; \ - } + __ret = CMD_RET_FAILURE; \ + } \ + __ret; \ +}) /* Assert that two int expressions are equal */ -#define ut_asserteq(expr1, expr2) { \ +#define ut_asserteq(expr1, expr2) ({ \ unsigned int _val1 = (expr1), _val2 = (expr2); \ + int __ret = 0; \ \ if (_val1 != _val2) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " == " #expr2, \ "Expected %#x (%d), got %#x (%d)", \ _val1, _val1, _val2, _val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that two 64 int expressions are equal */ -#define ut_asserteq_64(expr1, expr2) { \ +#define ut_asserteq_64(expr1, expr2) ({ \ u64 _val1 = (expr1), _val2 = (expr2); \ + int __ret = 0; \ \ if (_val1 != _val2) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ @@ -164,43 +175,49 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); (unsigned long long)_val1, \ (unsigned long long)_val2, \ (unsigned long long)_val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that two string expressions are equal */ -#define ut_asserteq_str(expr1, expr2) { \ +#define ut_asserteq_str(expr1, expr2) ({ \ const char *_val1 = (expr1), *_val2 = (expr2); \ + int __ret = 0; \ \ if (strcmp(_val1, _val2)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected \"%s\", got \"%s\"", _val1, _val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* * Assert that two string expressions are equal, up to length of the * first */ -#define ut_asserteq_strn(expr1, expr2) { \ +#define ut_asserteq_strn(expr1, expr2) ({ \ const char *_val1 = (expr1), *_val2 = (expr2); \ int _len = strlen(_val1); \ + int __ret = 0; \ \ if (memcmp(_val1, _val2, _len)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected \"%.*s\", got \"%.*s\"", \ _len, _val1, _len, _val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that two memory areas are equal */ -#define ut_asserteq_mem(expr1, expr2, len) { \ +#define ut_asserteq_mem(expr1, expr2, len) ({ \ const u8 *_val1 = (u8 *)(expr1), *_val2 = (u8 *)(expr2); \ const uint __len = len; \ + int __ret = 0; \ \ if (memcmp(_val1, _val2, __len)) { \ char __buf1[64 + 1] = "\0"; \ @@ -211,128 +228,163 @@ int ut_check_console_dump(struct unit_test_state *uts, int total_bytes); #expr1 " = " #expr2, \ "Expected \"%s\", got \"%s\"", \ __buf1, __buf2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that two pointers are equal */ -#define ut_asserteq_ptr(expr1, expr2) { \ +#define ut_asserteq_ptr(expr1, expr2) ({ \ const void *_val1 = (expr1), *_val2 = (expr2); \ + int __ret = 0; \ \ if (_val1 != _val2) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected %p, got %p", _val1, _val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that two addresses (converted from pointers) are equal */ -#define ut_asserteq_addr(expr1, expr2) { \ +#define ut_asserteq_addr(expr1, expr2) ({ \ ulong _val1 = map_to_sysmem(expr1); \ ulong _val2 = map_to_sysmem(expr2); \ + int __ret = 0; \ \ if (_val1 != _val2) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr1 " = " #expr2, \ "Expected %lx, got %lx", _val1, _val2); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that a pointer is NULL */ -#define ut_assertnull(expr) { \ +#define ut_assertnull(expr) ({ \ const void *_val = (expr); \ + int __ret = 0; \ \ - if (_val) { \ + if (_val) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr " != NULL", \ "Expected NULL, got %p", _val); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that a pointer is not NULL */ -#define ut_assertnonnull(expr) { \ +#define ut_assertnonnull(expr) ({ \ const void *_val = (expr); \ + int __ret = 0; \ \ - if (!_val) { \ + if (!_val) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr " = NULL", \ "Expected non-null, got NULL"); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that a pointer is not an error pointer */ -#define ut_assertok_ptr(expr) { \ +#define ut_assertok_ptr(expr) ({ \ const void *_val = (expr); \ + int __ret = 0; \ \ if (IS_ERR(_val)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ #expr " = NULL", \ "Expected pointer, got error %ld", \ PTR_ERR(_val)); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ -} + __ret; \ +}) /* Assert that an operation succeeds (returns 0) */ #define ut_assertok(cond) ut_asserteq(0, cond) /* Assert that the next console output line matches */ -#define ut_assert_nextline(fmt, args...) \ +#define ut_assert_nextline(fmt, args...) ({ \ + int __ret = 0; \ + \ if (ut_check_console_line(uts, fmt, ##args)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got '%s'", \ uts->expect_str, uts->actual_str); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that the next console output line matches up to the length */ -#define ut_assert_nextlinen(fmt, args...) \ +#define ut_assert_nextlinen(fmt, args...) ({ \ + int __ret = 0; \ + \ if (ut_check_console_linen(uts, fmt, ##args)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got '%s'", \ uts->expect_str, uts->actual_str); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that there is a 'next' console output line, and skip it */ -#define ut_assert_skipline() \ +#define ut_assert_skipline() ({ \ + int __ret = 0; \ + \ if (ut_check_skipline(uts)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected a line, got end"); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that a following console output line matches */ -#define ut_assert_skip_to_line(fmt, args...) \ +#define ut_assert_skip_to_line(fmt, args...) ({ \ + int __ret = 0; \ + \ if (ut_check_skip_to_line(uts, fmt, ##args)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "\nExpected '%s',\n got to '%s'", \ uts->expect_str, uts->actual_str); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that there is no more console output */ -#define ut_assert_console_end() \ +#define ut_assert_console_end() ({ \ + int __ret = 0; \ + \ if (ut_check_console_end(uts)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", "Expected no more output, got '%s'",\ uts->actual_str); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that the next lines are print_buffer() dump at an address */ -#define ut_assert_nextlines_are_dump(total_bytes) \ +#define ut_assert_nextlines_are_dump(total_bytes) ({ \ + int __ret = 0; \ + \ if (ut_check_console_dump(uts, total_bytes)) { \ ut_failf(uts, __FILE__, __LINE__, __func__, \ "console", \ "Expected dump of length %x bytes, got '%s'", \ total_bytes, uts->actual_str); \ - return CMD_RET_FAILURE; \ + __ret = CMD_RET_FAILURE; \ } \ + __ret; \ +}) /* Assert that the next console output line is empty */ #define ut_assert_nextline_empty() \ diff --git a/test/cmd/pwm.c b/test/cmd/pwm.c index 2fc0b5e4070..cf7ee0e0e65 100644 --- a/test/cmd/pwm.c +++ b/test/cmd/pwm.c @@ -27,11 +27,11 @@ static int dm_test_pwm_cmd(struct unit_test_state *uts) /* pwm */ /* cros-ec-pwm doesn't support invert */ ut_asserteq(1, run_command("pwm invert 0 0 1", 0)); - ut_assert_nextline("error(-38)") + ut_assert_nextline("error(-38)"); ut_assert_console_end(); ut_asserteq(1, run_command("pwm invert 0 0 0", 0)); - ut_assert_nextline("error(-38)") + ut_assert_nextline("error(-38)"); ut_assert_console_end(); /* pwm */ diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c index 3ec2743af9f..15b2b6f64a0 100644 --- a/test/dm/acpigen.c +++ b/test/dm/acpigen.c @@ -1083,7 +1083,7 @@ static int dm_test_acpi_write_name(struct unit_test_state *uts) ut_asserteq(NAME_OP, *ptr++); ptr += 10; ut_asserteq(STRING_PREFIX, *ptr++); - ut_asserteq_str("baldrick", (char *)ptr) + ut_asserteq_str("baldrick", (char *)ptr); ptr += 9; ut_asserteq(NAME_OP, *ptr++); diff --git a/test/dm/misc.c b/test/dm/misc.c index 1506fdefe32..8bdd8c64bca 100644 --- a/test/dm/misc.c +++ b/test/dm/misc.c @@ -51,13 +51,13 @@ static int dm_test_misc(struct unit_test_state *uts) /* Read back last issued ioctl */ ut_assertok(misc_call(dev, 2, NULL, 0, &last_ioctl, sizeof(last_ioctl))); - ut_asserteq(6, 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) + ut_asserteq(23, last_ioctl); /* Enable / disable tests */ diff --git a/test/dm/phy.c b/test/dm/phy.c index df4c73fc701..4d4a083dd0f 100644 --- a/test/dm/phy.c +++ b/test/dm/phy.c @@ -28,22 +28,22 @@ static int dm_test_phy_base(struct unit_test_state *uts) /* * 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_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_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_assertok(generic_phy_get_by_name(parent, "phy3", &phy3)); ut_assert(phy2.dev != phy3.dev); /* Try to get a non-existing phy */ diff --git a/test/dm/scmi.c b/test/dm/scmi.c index 93c7d08f43f..d87e2731ce4 100644 --- a/test/dm/scmi.c +++ b/test/dm/scmi.c @@ -187,10 +187,10 @@ static int dm_test_scmi_resets(struct unit_test_state *uts) ut_assertnonnull(agent); /* Test SCMI resect controller manipulation */ - ut_assert(!agent->reset[0].asserted) + ut_assert(!agent->reset[0].asserted); ut_assertok(reset_assert(&scmi_devices->reset[0])); - ut_assert(agent->reset[0].asserted) + ut_assert(agent->reset[0].asserted); ut_assertok(reset_deassert(&scmi_devices->reset[0])); ut_assert(!agent->reset[0].asserted); diff --git a/test/lib/kconfig.c b/test/lib/kconfig.c index 472d2c57280..76225ba8ffa 100644 --- a/test/lib/kconfig.c +++ b/test/lib/kconfig.c @@ -15,12 +15,12 @@ static int lib_test_is_enabled(struct unit_test_state *uts) { ulong val; - ut_asserteq(1, IS_ENABLED(CONFIG_CMDLINE)) - ut_asserteq(0, IS_ENABLED(CONFIG__UNDEFINED)) + ut_asserteq(1, IS_ENABLED(CONFIG_CMDLINE)); + ut_asserteq(0, IS_ENABLED(CONFIG__UNDEFINED)); - ut_asserteq(1, CONFIG_IS_ENABLED(CMDLINE)) - ut_asserteq(0, CONFIG_IS_ENABLED(OF_PLATDATA)) - ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED)) + ut_asserteq(1, CONFIG_IS_ENABLED(CMDLINE)); + ut_asserteq(0, CONFIG_IS_ENABLED(OF_PLATDATA)); + ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED)); ut_asserteq(0xc000, IF_ENABLED_INT(CONFIG_BLOBLIST_FIXED, CONFIG_BLOBLIST_ADDR)); diff --git a/test/lib/kconfig_spl.c b/test/lib/kconfig_spl.c index c89ceaec66f..8f8a3411b14 100644 --- a/test/lib/kconfig_spl.c +++ b/test/lib/kconfig_spl.c @@ -15,9 +15,9 @@ static int lib_test_spl_is_enabled(struct unit_test_state *uts) { ulong val; - ut_asserteq(0, CONFIG_IS_ENABLED(CMDLINE)) - ut_asserteq(1, CONFIG_IS_ENABLED(OF_PLATDATA)) - ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED)) + ut_asserteq(0, CONFIG_IS_ENABLED(CMDLINE)); + ut_asserteq(1, CONFIG_IS_ENABLED(OF_PLATDATA)); + ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED)); /* * This fails if CONFIG_TEST_KCONFIG_ENABLE is not enabled, since the diff --git a/test/unicode_ut.c b/test/unicode_ut.c index 382b7965161..b27d7116b9e 100644 --- a/test/unicode_ut.c +++ b/test/unicode_ut.c @@ -192,7 +192,7 @@ static int unicode_test_utf8_get(struct unit_test_state *uts) if (!code) break; } - ut_asserteq_ptr(s, d2 + 9) + ut_asserteq_ptr(s, d2 + 9); /* Check characters less than 0x10000 */ s = d3; @@ -203,7 +203,7 @@ static int unicode_test_utf8_get(struct unit_test_state *uts) if (!code) break; } - ut_asserteq_ptr(s, d3 + 9) + ut_asserteq_ptr(s, d3 + 9); /* Check character greater 0xffff */ s = d4; @@ -228,7 +228,7 @@ static int unicode_test_utf8_put(struct unit_test_state *uts) /* Commercial at, translates to one character */ pos = buffer; - ut_assert(!utf8_put('@', &pos)) + ut_assert(!utf8_put('@', &pos)); ut_asserteq(1, pos - buffer); ut_asserteq('@', buffer[0]); ut_assert(!buffer[1]); From a96dea25694de42ca596c333f650f8636de09ed9 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Fri, 10 Mar 2023 04:33:14 +0100 Subject: [PATCH 02/16] test: cmd: fdt: Drop new unneeded curly brackets Drop no longer needed { } around ut_assert*() functions in FDT test. No functional change. Reviewed-by: Simon Glass Signed-off-by: Marek Vasut --- test/cmd/fdt.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index 8ae8a52896e..a03aff2663c 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -653,23 +653,21 @@ static int fdt_test_set_single(struct unit_test_state *uts, * => fdt set /path property */ ut_assertok(console_record_reset_enable()); - if (sval) { + if (sval) ut_assertok(run_commandf("fdt set %s %s %s", path, prop, sval)); - } else if (integer) { + else if (integer) ut_assertok(run_commandf("fdt set %s %s <%d>", path, prop, ival)); - } else { + else ut_assertok(run_commandf("fdt set %s %s", path, prop)); - } /* Validate the property is present and has correct value. */ ut_assertok(run_commandf("fdt get value svar %s %s", path, prop)); - if (sval) { + if (sval) ut_asserteq_str(sval, env_get("svar")); - } else if (integer) { + else if (integer) ut_asserteq(ival, env_get_hex("svar", 0x1234)); - } else { + else ut_assertnull(env_get("svar")); - } ut_assertok(ut_check_console_end(uts)); return 0; From 97d6d7e3606a352843bae6547e972fbaca3ccf7b Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 27 Feb 2023 20:55:39 +0100 Subject: [PATCH 03/16] console: Use only 0x00 as line separator for console recording In case character 0x20 (space) is used as line separator, character 0x9 (tab) is treated end of line. Commands which output a lot of tabs, i.e. various tree printing commands like 'fdt print' then end up generating a lot of newlines in the recorded output, and the recorded output is corrupted. Use character 0x00 (NUL) as separator instead to treat the tabs as valid part of recorded line. Suggested-by: Simon Glass Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- common/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/console.c b/common/console.c index e4301a49322..71ad8efd6f4 100644 --- a/common/console.c +++ b/common/console.c @@ -842,7 +842,7 @@ int console_record_readline(char *str, int maxlen) return -ENOSPC; return membuff_readline((struct membuff *)&gd->console_out, str, - maxlen, ' '); + maxlen, '\0'); } int console_record_avail(void) From ed4dcb1f9b12e84721f70cc0a9bf838059581967 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:04:40 +0100 Subject: [PATCH 04/16] cmd: fdt: Drop the 0x prefix The 'fdt get addr' is always assumed to be hex value, drop the prefix. Since this might break existing users who depend on the existing behavior with 0x prefix, this is a separate patch. Revert if this breaks anything. Signed-off-by: Marek Vasut --- cmd/fdt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/fdt.c b/cmd/fdt.c index f38fe909c3e..04b664e652c 100644 --- a/cmd/fdt.c +++ b/cmd/fdt.c @@ -478,7 +478,7 @@ static int do_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) /* Get address */ char buf[19]; - snprintf(buf, sizeof(buf), "0x%lx", + snprintf(buf, sizeof(buf), "%lx", (ulong)map_to_sysmem(nodep)); env_set(var, buf); } else if (subcmd[0] == 's') { From b0cd7ccebd3d0220cbfd8d09720977746d1d1e2a Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:32 +0100 Subject: [PATCH 05/16] test: cmd: fdt: Test fdt print and list Add 'fdt print' and 'fdt list' test which works as follows: - Create fuller FDT, map it to sysmem - Print the entire FDT, parts of the FDT and select properties - Compare output from the print or list The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- test/cmd/fdt.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index a03aff2663c..12f5fe60da7 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -303,6 +303,149 @@ static int fdt_test_resize(struct unit_test_state *uts) } FDT_TEST(fdt_test_resize, UT_TESTF_CONSOLE_REC); +static int fdt_test_print_list_common(struct unit_test_state *uts, + const char *opc, const char *node) +{ + /* + * Test printing/listing the working FDT + * subnode $node/subnode + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s/subnode", opc, node)); + ut_assert_nextline("subnode {"); + ut_assert_nextline("\t#address-cells = <0x00000000>;"); + ut_assert_nextline("\t#size-cells = <0x00000000>;"); + ut_assert_nextline("\tcompatible = \"u-boot,fdt-subnode-test-device\";"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path / string property model + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s / model", opc)); + ut_assert_nextline("model = \"U-Boot FDT test\""); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path $node string property compatible + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s compatible", opc, node)); + ut_assert_nextline("compatible = \"u-boot,fdt-test-device1\""); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path $node stringlist property clock-names + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s clock-names", opc, node)); + ut_assert_nextline("clock-names = \"fixed\", \"i2c\", \"spi\", \"uart2\", \"uart1\""); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path $node u32 property clock-frequency + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s clock-frequency", opc, node)); + ut_assert_nextline("clock-frequency = <0x00fde800>"); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path $node empty property u-boot,empty-property + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s u-boot,empty-property", opc, node)); + /* + * This is the only 'fdt print' / 'fdt list' incantation which + * prefixes the property with node path. This has been in U-Boot + * since the beginning of the command 'fdt', keep it. + */ + ut_assert_nextline("%s u-boot,empty-property", node); + ut_assertok(ut_check_console_end(uts)); + + /* + * Test printing/listing the working FDT + * path $node prop-encoded array property regs + */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s %s regs", opc, node)); + ut_assert_nextline("regs = <0x00001234 0x00001000>"); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} + +static int fdt_test_print_list(struct unit_test_state *uts, bool print) +{ + const char *opc = print ? "print" : "list"; + char fdt[4096]; + ulong addr; + int ret; + + /* Original source DT */ + ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt))); + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Test printing/listing the working FDT -- node / */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt %s", opc)); + ut_assert_nextline("/ {"); + ut_assert_nextline("\t#address-cells = <0x00000001>;"); + ut_assert_nextline("\t#size-cells = <0x00000001>;"); + ut_assert_nextline("\tcompatible = \"u-boot,fdt-test\";"); + ut_assert_nextline("\tmodel = \"U-Boot FDT test\";"); + ut_assert_nextline("\taliases {"); + if (print) { + ut_assert_nextline("\t\tbadalias = \"/bad/alias\";"); + ut_assert_nextline("\t\tsubnodealias = \"/test-node@1234/subnode\";"); + ut_assert_nextline("\t\ttestnodealias = \"/test-node@1234\";"); + } + ut_assert_nextline("\t};"); + ut_assert_nextline("\ttest-node@1234 {"); + if (print) { + ut_assert_nextline("\t\t#address-cells = <0x00000000>;"); + ut_assert_nextline("\t\t#size-cells = <0x00000000>;"); + ut_assert_nextline("\t\tcompatible = \"u-boot,fdt-test-device1\";"); + ut_assert_nextline("\t\tclock-names = \"fixed\", \"i2c\", \"spi\", \"uart2\", \"uart1\";"); + ut_assert_nextline("\t\tu-boot,empty-property;"); + ut_assert_nextline("\t\tclock-frequency = <0x00fde800>;"); + ut_assert_nextline("\t\tregs = <0x00001234 0x00001000>;"); + ut_assert_nextline("\t\tsubnode {"); + ut_assert_nextline("\t\t\t#address-cells = <0x00000000>;"); + ut_assert_nextline("\t\t\t#size-cells = <0x00000000>;"); + ut_assert_nextline("\t\t\tcompatible = \"u-boot,fdt-subnode-test-device\";"); + ut_assert_nextline("\t\t};"); + } + ut_assert_nextline("\t};"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + ret = fdt_test_print_list_common(uts, opc, "/test-node@1234"); + if (!ret) + ret = fdt_test_print_list_common(uts, opc, "testnodealias"); + + return 0; +} + +static int fdt_test_print(struct unit_test_state *uts) +{ + return fdt_test_print_list(uts, true); +} +FDT_TEST(fdt_test_print, UT_TESTF_CONSOLE_REC); + +static int fdt_test_list(struct unit_test_state *uts) +{ + return fdt_test_print_list(uts, false); +} +FDT_TEST(fdt_test_list, UT_TESTF_CONSOLE_REC); + /* Test 'fdt get value' reading an fdt */ static int fdt_test_get_value_string(struct unit_test_state *uts, const char *node, const char *prop, From 927e03b4f695ea6aa17ef8cf5e1ff593fac1d84f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:39 +0100 Subject: [PATCH 06/16] test: cmd: fdt: Test fdt header Add 'fdt header' test which works as follows: - Create basic FDT, map it to sysmem - Print the FDT header - Get all members of the FDT header into variable and verify the variables contain correct data The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass Merged in test: cmd: fdt: Drop unused fdt_test_header_get() fdt parameter: Signed-off-by: Simon Glass --- test/cmd/fdt.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index 12f5fe60da7..5a6827e1a44 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -1112,6 +1112,65 @@ static int fdt_test_bootcpu(struct unit_test_state *uts) } FDT_TEST(fdt_test_bootcpu, UT_TESTF_CONSOLE_REC); +static int fdt_test_header_get(struct unit_test_state *uts, + const char *field, const unsigned long val) +{ + /* Test getting valid header entry */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt header get fvar %s", field)); + ut_asserteq(val, env_get_hex("fvar", 0x1234)); + ut_assertok(ut_check_console_end(uts)); + + /* Test getting malformed header entry */ + ut_assertok(console_record_reset_enable()); + ut_asserteq(1, run_commandf("fdt header get fvar typo%stypo", field)); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} + +static int fdt_test_header(struct unit_test_state *uts) +{ + char fdt[256]; + ulong addr; + + ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt))); + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Test header print */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt header")); + ut_assert_nextline("magic:\t\t\t0x%x", fdt_magic(fdt)); + ut_assert_nextline("totalsize:\t\t0x%x (%d)", fdt_totalsize(fdt), fdt_totalsize(fdt)); + ut_assert_nextline("off_dt_struct:\t\t0x%x", fdt_off_dt_struct(fdt)); + ut_assert_nextline("off_dt_strings:\t\t0x%x", fdt_off_dt_strings(fdt)); + ut_assert_nextline("off_mem_rsvmap:\t\t0x%x", fdt_off_mem_rsvmap(fdt)); + ut_assert_nextline("version:\t\t%d", fdt_version(fdt)); + ut_assert_nextline("last_comp_version:\t%d", fdt_last_comp_version(fdt)); + ut_assert_nextline("boot_cpuid_phys:\t0x%x", fdt_boot_cpuid_phys(fdt)); + ut_assert_nextline("size_dt_strings:\t0x%x", fdt_size_dt_strings(fdt)); + ut_assert_nextline("size_dt_struct:\t\t0x%x", fdt_size_dt_struct(fdt)); + ut_assert_nextline("number mem_rsv:\t\t0x%x", fdt_num_mem_rsv(fdt)); + ut_assert_nextline_empty(); + ut_assertok(ut_check_console_end(uts)); + + /* Test header get */ + fdt_test_header_get(uts, "magic", fdt_magic(fdt)); + fdt_test_header_get(uts, "totalsize", fdt_totalsize(fdt)); + fdt_test_header_get(uts, "off_dt_struct", fdt_off_dt_struct(fdt)); + fdt_test_header_get(uts, "off_dt_strings", fdt_off_dt_strings(fdt)); + fdt_test_header_get(uts, "off_mem_rsvmap", fdt_off_mem_rsvmap(fdt)); + fdt_test_header_get(uts, "version", fdt_version(fdt)); + fdt_test_header_get(uts, "last_comp_version", fdt_last_comp_version(fdt)); + fdt_test_header_get(uts, "boot_cpuid_phys", fdt_boot_cpuid_phys(fdt)); + fdt_test_header_get(uts, "size_dt_strings", fdt_size_dt_strings(fdt)); + fdt_test_header_get(uts, "size_dt_struct", fdt_size_dt_struct(fdt)); + + return 0; +} +FDT_TEST(fdt_test_header, UT_TESTF_CONSOLE_REC); + int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test); From d9b4c9fc941ca097d77b75a29231fe795ca0ab2f Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:41 +0100 Subject: [PATCH 07/16] test: cmd: fdt: Test fdt memory Add 'fdt memory' test which works as follows: - Create custom FDT with /memory node, with select #*cells, map it to sysmem - Perform memory fixup - Read back the /memory node and validate its content The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- test/cmd/fdt.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index 5a6827e1a44..a73dc1eab0e 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -1171,6 +1171,89 @@ static int fdt_test_header(struct unit_test_state *uts) } FDT_TEST(fdt_test_header, UT_TESTF_CONSOLE_REC); +static int fdt_test_memory_cells(struct unit_test_state *uts, + const unsigned int cells) +{ + unsigned char *pada, *pads; + unsigned char *seta, *sets; + char fdt[8192]; + const int size = sizeof(fdt); + fdt32_t *regs; + ulong addr; + char *spc; + int i; + + /* Create DT with node /memory { regs = <0x100 0x200>; } and #*cells */ + ut_assertnonnull(regs = calloc(2 * cells, sizeof(*regs))); + ut_assertnonnull(pada = calloc(12, cells)); + ut_assertnonnull(pads = calloc(12, cells)); + ut_assertnonnull(seta = calloc(12, cells)); + ut_assertnonnull(sets = calloc(12, cells)); + for (i = cells; i >= 1; i--) { + regs[cells - 1] = cpu_to_fdt32(i * 0x10000); + regs[(cells * 2) - 1] = cpu_to_fdt32(~i); + snprintf(seta + (8 * (cells - i)), 9, "%08x", i * 0x10000); + snprintf(sets + (8 * (cells - i)), 9, "%08x", ~i); + spc = (i != 1) ? " " : ""; + snprintf(pada + (11 * (cells - i)), 12, "0x%08x%s", i * 0x10000, spc); + snprintf(pads + (11 * (cells - i)), 12, "0x%08x%s", ~i, spc); + } + + ut_assertok(fdt_create(fdt, size)); + ut_assertok(fdt_finish_reservemap(fdt)); + ut_assert(fdt_begin_node(fdt, "") >= 0); + ut_assertok(fdt_property_u32(fdt, "#address-cells", cells)); + ut_assertok(fdt_property_u32(fdt, "#size-cells", cells)); + ut_assert(fdt_begin_node(fdt, "memory") >= 0); + ut_assertok(fdt_property_string(fdt, "device_type", "memory")); + ut_assertok(fdt_property(fdt, "reg", ®s, cells * 2)); + ut_assertok(fdt_end_node(fdt)); + ut_assertok(fdt_end_node(fdt)); + ut_assertok(fdt_finish(fdt)); + fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */ + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Test updating the memory node */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt memory 0x%s 0x%s", seta, sets)); + ut_assertok(run_commandf("fdt print /memory")); + ut_assert_nextline("memory {"); + ut_assert_nextline("\tdevice_type = \"memory\";"); + ut_assert_nextline("\treg = <%s %s>;", pada, pads); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + free(sets); + free(seta); + free(pads); + free(pada); + free(regs); + + return 0; +} + +static int fdt_test_memory(struct unit_test_state *uts) +{ + /* + * Test memory fixup for 32 and 64 bit systems, anything bigger is + * so far unsupported and fails because of simple_stroull() being + * 64bit tops in the 'fdt memory' command implementation. + */ + fdt_test_memory_cells(uts, 1); + fdt_test_memory_cells(uts, 2); + + /* + * The 'fdt memory' command is limited to /memory node, it does + * not support any other valid DT memory node format, which is + * either one or multiple /memory@adresss nodes. Therefore, this + * DT variant is not tested here. + */ + + return 0; +} +FDT_TEST(fdt_test_memory, UT_TESTF_CONSOLE_REC); + int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test); From 50daa2e615a51ee470aaeac7f9a3d32ba48b2f82 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:42 +0100 Subject: [PATCH 08/16] test: cmd: fdt: Test fdt rsvmem Add 'fdt rsvmem' test which works as follows: - Create custom FDT with single reserved memory (rsvmem) entry, map it to sysmem - Add new rsvmem entry - Delete existing older rsvmem entry - Add new rsvmem entry again - Always print the rsvmem list and validate it The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- test/cmd/fdt.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index a73dc1eab0e..3b48694a004 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -1254,6 +1254,69 @@ static int fdt_test_memory(struct unit_test_state *uts) } FDT_TEST(fdt_test_memory, UT_TESTF_CONSOLE_REC); +static int fdt_test_rsvmem(struct unit_test_state *uts) +{ + char fdt[8192]; + ulong addr; + + ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt))); + fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */ + fdt_add_mem_rsv(fdt, 0x42, 0x1701); + fdt_add_mem_rsv(fdt, 0x74656, 0x9); + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Test default reserved memory node presence */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt rsvmem print")); + ut_assert_nextline("index\t\t start\t\t size"); + ut_assert_nextline("------------------------------------------------"); + ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x42, 0x1701); + ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x74656, 0x9); + ut_assertok(ut_check_console_end(uts)); + + /* Test add new reserved memory node */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt rsvmem add 0x1234 0x5678")); + ut_assertok(run_commandf("fdt rsvmem print")); + ut_assert_nextline("index\t\t start\t\t size"); + ut_assert_nextline("------------------------------------------------"); + ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x42, 0x1701); + ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x74656, 0x9); + ut_assert_nextline(" %x\t%016x\t%016x", 2, 0x1234, 0x5678); + ut_assertok(ut_check_console_end(uts)); + + /* Test delete reserved memory node */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt rsvmem delete 0")); + ut_assertok(run_commandf("fdt rsvmem print")); + ut_assert_nextline("index\t\t start\t\t size"); + ut_assert_nextline("------------------------------------------------"); + ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x74656, 0x9); + ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x1234, 0x5678); + ut_assertok(ut_check_console_end(uts)); + + /* Test re-add new reserved memory node */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt rsvmem add 0x42 0x1701")); + ut_assertok(run_commandf("fdt rsvmem print")); + ut_assert_nextline("index\t\t start\t\t size"); + ut_assert_nextline("------------------------------------------------"); + ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x74656, 0x9); + ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x1234, 0x5678); + ut_assert_nextline(" %x\t%016x\t%016x", 2, 0x42, 0x1701); + ut_assertok(ut_check_console_end(uts)); + + /* Test delete nonexistent reserved memory node */ + ut_assertok(console_record_reset_enable()); + ut_asserteq(1, run_commandf("fdt rsvmem delete 10")); + ut_assert_nextline("libfdt fdt_del_mem_rsv(): FDT_ERR_NOTFOUND"); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} +FDT_TEST(fdt_test_rsvmem, UT_TESTF_CONSOLE_REC); + int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test); From 77291e6e90e9c84b9c963acb15d0cac381cf8b44 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:43 +0100 Subject: [PATCH 09/16] test: cmd: fdt: Test fdt chosen Add 'fdt chosen' test which works as follows: - Create basic DT, map it to sysmem - Print /chosen node, verify it is nonexistent - Create chosen node - Print /chosen node, verify it contains only version - Create /chosen node with initrd entries - Print /chosen node, verify it contains version and initrd entries The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- test/cmd/fdt.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index 3b48694a004..d3c4760c931 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -1317,6 +1317,54 @@ static int fdt_test_rsvmem(struct unit_test_state *uts) } FDT_TEST(fdt_test_rsvmem, UT_TESTF_CONSOLE_REC); +static int fdt_test_chosen(struct unit_test_state *uts) +{ + const char *env_bootargs = env_get("bootargs"); + char fdt[8192]; + ulong addr; + + ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt))); + fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */ + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Test default chosen node presence, fail as there is no /chosen node */ + ut_assertok(console_record_reset_enable()); + ut_asserteq(1, run_commandf("fdt print /chosen")); + ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND"); + ut_assertok(ut_check_console_end(uts)); + + /* Test add new chosen node without initrd */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt chosen")); + ut_assertok(run_commandf("fdt print /chosen")); + ut_assert_nextline("chosen {"); + ut_assert_nextlinen("\tu-boot,version = "); /* Ignore the version string */ + if (env_bootargs) + ut_assert_nextline("\tbootargs = \"%s\";", env_bootargs); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + /* Test add new chosen node with initrd */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt chosen 0x1234 0x5678")); + ut_assertok(run_commandf("fdt print /chosen")); + ut_assert_nextline("chosen {"); + ut_assert_nextline("\tlinux,initrd-end = <0x%08x 0x%08x>;", + upper_32_bits(0x1234 + 0x5678 - 1), + lower_32_bits(0x1234 + 0x5678 - 1)); + ut_assert_nextline("\tlinux,initrd-start = <0x%08x 0x%08x>;", + upper_32_bits(0x1234), lower_32_bits(0x1234)); + ut_assert_nextlinen("\tu-boot,version = "); /* Ignore the version string */ + if (env_bootargs) + ut_assert_nextline("\tbootargs = \"%s\";", env_bootargs); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} +FDT_TEST(fdt_test_chosen, UT_TESTF_CONSOLE_REC); + int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test); From c5fe73ecb41e04c82e6ad612e646f203355a0a99 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Thu, 2 Mar 2023 04:08:44 +0100 Subject: [PATCH 10/16] test: cmd: fdt: Test fdt apply Add 'fdt chosen' test which works as follows: - Create basic DT, map it to sysmem - Apply DTO which adds single property via fragment (without address spec) - Apply DTO which adds more properties (string, u32, empty) and a subnode, with phandle via frament@0 and thus tests /__symbols__ node - Apply DTO which modifies property of the previous DTO via phandle and thus tests the /__fixups__ node - Print modified DT, verify it contains updates from DTOs The test case can be triggered using: " ./u-boot -Dc 'ut fdt' " To dump the full output from commands used during test, add '-v' flag. Signed-off-by: Marek Vasut Reviewed-by: Simon Glass --- test/cmd/fdt.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c index d3c4760c931..22e8c7e3d26 100644 --- a/test/cmd/fdt.c +++ b/test/cmd/fdt.c @@ -1365,6 +1365,158 @@ static int fdt_test_chosen(struct unit_test_state *uts) } FDT_TEST(fdt_test_chosen, UT_TESTF_CONSOLE_REC); +static int fdt_test_apply(struct unit_test_state *uts) +{ + char fdt[8192], fdto[8192]; + ulong addr, addro; + + /* Create base DT with __symbols__ node */ + ut_assertok(fdt_create(fdt, sizeof(fdt))); + ut_assertok(fdt_finish_reservemap(fdt)); + ut_assert(fdt_begin_node(fdt, "") >= 0); + ut_assert(fdt_begin_node(fdt, "__symbols__") >= 0); + ut_assertok(fdt_end_node(fdt)); + ut_assertok(fdt_end_node(fdt)); + ut_assertok(fdt_finish(fdt)); + fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */ + addr = map_to_sysmem(fdt); + set_working_fdt_addr(addr); + + /* Create DTO which adds single property to root node / */ + ut_assertok(fdt_create(fdto, sizeof(fdto))); + ut_assertok(fdt_finish_reservemap(fdto)); + ut_assert(fdt_begin_node(fdto, "") >= 0); + ut_assert(fdt_begin_node(fdto, "fragment") >= 0); + ut_assertok(fdt_property_string(fdto, "target-path", "/")); + ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0); + ut_assertok(fdt_property_string(fdto, "newstring", "newvalue")); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_finish(fdto)); + addro = map_to_sysmem(fdto); + + /* Test default DT print */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt print /")); + ut_assert_nextline("/ {"); + ut_assert_nextline("\t__symbols__ {"); + ut_assert_nextline("\t};"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + /* Test simple DTO application */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt apply 0x%08x", addro)); + ut_assertok(run_commandf("fdt print /")); + ut_assert_nextline("/ {"); + ut_assert_nextline("\tnewstring = \"newvalue\";"); + ut_assert_nextline("\t__symbols__ {"); + ut_assert_nextline("\t};"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + /* + * Create complex DTO which: + * - modifies newstring property in root node / + * - adds new properties to root node / + * - adds new subnode with properties to root node / + * - adds phandle to the subnode and therefore __symbols__ node + */ + ut_assertok(fdt_create(fdto, sizeof(fdto))); + ut_assertok(fdt_finish_reservemap(fdto)); + ut_assert(fdt_begin_node(fdto, "") >= 0); + ut_assertok(fdt_property_cell(fdto, "#address-cells", 1)); + ut_assertok(fdt_property_cell(fdto, "#size-cells", 0)); + + ut_assert(fdt_begin_node(fdto, "fragment@0") >= 0); + ut_assertok(fdt_property_string(fdto, "target-path", "/")); + ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0); + ut_assertok(fdt_property_string(fdto, "newstring", "newervalue")); + ut_assertok(fdt_property_u32(fdto, "newu32", 0x12345678)); + ut_assertok(fdt_property(fdto, "empty-property", NULL, 0)); + ut_assert(fdt_begin_node(fdto, "subnode") >= 0); + ut_assertok(fdt_property_string(fdto, "subnewstring", "newervalue")); + ut_assertok(fdt_property_u32(fdto, "subnewu32", 0x12345678)); + ut_assertok(fdt_property(fdto, "subempty-property", NULL, 0)); + ut_assertok(fdt_property_u32(fdto, "phandle", 0x01)); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_end_node(fdto)); + + ut_assert(fdt_begin_node(fdto, "__symbols__") >= 0); + ut_assertok(fdt_property_string(fdto, "subnodephandle", "/fragment@0/__overlay__/subnode")); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_finish(fdto)); + addro = map_to_sysmem(fdto); + + /* Test complex DTO application */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt apply 0x%08x", addro)); + ut_assertok(run_commandf("fdt print /")); + ut_assert_nextline("/ {"); + ut_assert_nextline("\tempty-property;"); + ut_assert_nextline("\tnewu32 = <0x12345678>;"); + ut_assert_nextline("\tnewstring = \"newervalue\";"); + ut_assert_nextline("\tsubnode {"); + ut_assert_nextline("\t\tphandle = <0x00000001>;"); + ut_assert_nextline("\t\tsubempty-property;"); + ut_assert_nextline("\t\tsubnewu32 = <0x12345678>;"); + ut_assert_nextline("\t\tsubnewstring = \"newervalue\";"); + ut_assert_nextline("\t};"); + ut_assert_nextline("\t__symbols__ {"); + ut_assert_nextline("\t\tsubnodephandle = \"/subnode\";"); + ut_assert_nextline("\t};"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + /* + * Create complex DTO which: + * - modifies subnewu32 property in subnode via phandle and uses __fixups__ node + */ + ut_assertok(fdt_create(fdto, sizeof(fdto))); + ut_assertok(fdt_finish_reservemap(fdto)); + ut_assert(fdt_begin_node(fdto, "") >= 0); + ut_assertok(fdt_property_cell(fdto, "#address-cells", 1)); + ut_assertok(fdt_property_cell(fdto, "#size-cells", 0)); + + ut_assert(fdt_begin_node(fdto, "fragment@0") >= 0); + ut_assertok(fdt_property_u32(fdto, "target", 0xffffffff)); + ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0); + ut_assertok(fdt_property_u32(fdto, "subnewu32", 0xabcdef01)); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_end_node(fdto)); + + ut_assert(fdt_begin_node(fdto, "__fixups__") >= 0); + ut_assertok(fdt_property_string(fdto, "subnodephandle", "/fragment@0:target:0")); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_end_node(fdto)); + ut_assertok(fdt_finish(fdto)); + addro = map_to_sysmem(fdto); + + /* Test complex DTO application */ + ut_assertok(console_record_reset_enable()); + ut_assertok(run_commandf("fdt apply 0x%08x", addro)); + ut_assertok(run_commandf("fdt print /")); + ut_assert_nextline("/ {"); + ut_assert_nextline("\tempty-property;"); + ut_assert_nextline("\tnewu32 = <0x12345678>;"); + ut_assert_nextline("\tnewstring = \"newervalue\";"); + ut_assert_nextline("\tsubnode {"); + ut_assert_nextline("\t\tphandle = <0x00000001>;"); + ut_assert_nextline("\t\tsubempty-property;"); + ut_assert_nextline("\t\tsubnewu32 = <0xabcdef01>;"); + ut_assert_nextline("\t\tsubnewstring = \"newervalue\";"); + ut_assert_nextline("\t};"); + ut_assert_nextline("\t__symbols__ {"); + ut_assert_nextline("\t\tsubnodephandle = \"/subnode\";"); + ut_assert_nextline("\t};"); + ut_assert_nextline("};"); + ut_assertok(ut_check_console_end(uts)); + + return 0; +} +FDT_TEST(fdt_test_apply, UT_TESTF_CONSOLE_REC); + int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test); From 0f40e23fd2282809f62d2be6ea4eb8c1d995a09b Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Wed, 8 Mar 2023 01:13:38 +0000 Subject: [PATCH 11/16] binman: add documentation for binman sign option Add the documentation about binman sign option and providing an example. Signed-off-by: Ivan Mikhaylov Add a section about 'binman sign' at the bottom: Signed-off-by: Simon Glass --- tools/binman/binman.rst | 47 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tools/binman/binman.rst b/tools/binman/binman.rst index e65fbff783e..fac1cd3bb8b 100644 --- a/tools/binman/binman.rst +++ b/tools/binman/binman.rst @@ -1366,6 +1366,24 @@ when it was created. .. _`BinmanLogging`: +Signing FIT container with private key in an image +-------------------------------------------------- + +You can sign FIT container with private key in your image. +For example:: + + $ binman sign -i image.bin -k privatekey -a sha256,rsa4096 fit + +binman will extract FIT container, sign and replace it immediately. + +If you want to sign and replace FIT container in place:: + + $ binman sign -i image.bin -k privatekey -a sha256,rsa4096 -f fit.fit fit + +which will sign FIT container with private key and replace it immediately +inside your image. + + Logging ------- @@ -1751,6 +1769,35 @@ Options: output directory if a single test is run (pass test name at the end of the command line +binman sign +----------- + +Usage:: + + binman sign [-h] -a ALGO [-f FILE] -i IMAGE -k KEY [paths ...] + +positional arguments: + +paths + Paths within file to sign (wildcard) + +options: + +-h, --help + show this help message and exit + +-a ALGO, --algo ALGO + Hash algorithm e.g. sha256,rsa4096 + +-f FILE, --file FILE + Input filename to sign + +-i IMAGE, --image IMAGE + Image filename to update + +-k KEY, --key KEY + Private key file for signing + binman tool ----------- From 4023dc9c95ccb5bcb3719c1c10e3d4dce967e0a2 Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Wed, 8 Mar 2023 01:13:39 +0000 Subject: [PATCH 12/16] binman: add sign option for binman Introduce proof of concept for binman's new option which provides sign and replace FIT containers in binary images. Usage as example: from: mkimage -G privateky -r -o sha256,rsa4096 -F fit binman replace -i flash.bin -f fit.fit fit to: binman sign -i flash.bin -k privatekey -a sha256,rsa4096 -f fit.fit fit and to this one if it's need to be extracted, signed with key and put it back in image: binman sign -i flash.bin -k privatekey -a sha256,rsa4096 fit Signed-off-by: Ivan Mikhaylov --- common/main.c | 1 + tools/binman/cmdline.py | 13 +++++++++++++ tools/binman/control.py | 28 +++++++++++++++++++++++++++- tools/binman/etype/fit.py | 18 ++++++++++++++++++ tools/binman/etype/section.py | 3 +++ 5 files changed, 62 insertions(+), 1 deletion(-) diff --git a/common/main.c b/common/main.c index 682f3359ea3..7c70de2e59a 100644 --- a/common/main.c +++ b/common/main.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include diff --git a/tools/binman/cmdline.py b/tools/binman/cmdline.py index 1b7bbe80cda..4b875a9dcda 100644 --- a/tools/binman/cmdline.py +++ b/tools/binman/cmdline.py @@ -176,6 +176,19 @@ controlled by a description in the board device tree.''' replace_parser.add_argument('paths', type=str, nargs='*', help='Paths within file to replace (wildcard)') + sign_parser = subparsers.add_parser('sign', + help='Sign entries in image') + sign_parser.add_argument('-a', '--algo', type=str, required=True, + help='Hash algorithm e.g. sha256,rsa4096') + sign_parser.add_argument('-f', '--file', type=str, required=False, + help='Input filename to sign') + sign_parser.add_argument('-i', '--image', type=str, required=True, + help='Image filename to update') + sign_parser.add_argument('-k', '--key', type=str, required=True, + help='Private key file for signing') + sign_parser.add_argument('paths', type=str, nargs='*', + help='Paths within file to sign (wildcard)') + if HAS_TESTS: test_parser = subparsers.add_parser('test', help='Run tests') test_parser.add_argument('-P', '--processes', type=int, diff --git a/tools/binman/control.py b/tools/binman/control.py index 2f2b4893b7e..cf2c91f622a 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -448,6 +448,29 @@ def ReplaceEntries(image_fname, input_fname, indir, entry_paths, AfterReplace(image, allow_resize=allow_resize, write_map=write_map) return image +def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, + write_map=False): + """Sign and replace the data from one or more entries from input files + + Args: + image_fname: Image filename to process + input_fname: Single input filename to use if replacing one file, None + otherwise + algo: Hashing algorithm + entry_paths: List of entry paths to sign + privatekey_fname: Private key filename + write_map (bool): True to write the map file + """ + image_fname = os.path.abspath(image_fname) + image = Image.FromFile(image_fname) + + BeforeReplace(image, allow_resize=True) + + for entry_path in entry_paths: + entry = image.FindEntryPath(entry_path) + entry.UpdateSignatures(privatekey_fname, algo, input_fname) + + AfterReplace(image, allow_resize=True, write_map=write_map) def PrepareImagesAndDtbs(dtb_fname, select_images, update_fdt, use_expanded): """Prepare the images to be processed and select the device tree @@ -660,7 +683,7 @@ def Binman(args): tools.set_tool_paths(tool_paths or None) bintool.Bintool.set_tool_dir(args.tooldir) - if args.cmd in ['ls', 'extract', 'replace', 'tool']: + if args.cmd in ['ls', 'extract', 'replace', 'tool', 'sign']: try: tout.init(args.verbosity) if args.cmd == 'replace': @@ -679,6 +702,9 @@ def Binman(args): do_compress=not args.compressed, allow_resize=not args.fix_size, write_map=args.map) + if args.cmd == 'sign': + SignEntries(args.image, args.file, args.key, args.algo, args.paths) + if args.cmd == 'tool': if args.list: bintool.Bintool.list_all() diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 03fe88e7a6c..3aea9865bf4 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -835,3 +835,21 @@ class Entry_fit(Entry_section): def CheckEntries(self): pass + + def UpdateSignatures(self, privatekey_fname, algo, input_fname): + uniq = self.GetUniqueName() + args = [ '-G', privatekey_fname, '-r', '-o', algo, '-F' ] + if input_fname: + fname = input_fname + else: + fname = tools.get_output_filename('%s.fit' % uniq) + tools.write_file(fname, self.GetData()) + args.append(fname) + + if self.mkimage.run_cmd(*args) is None: + # Bintool is missing; just use empty data as the output + self.record_missing_bintool(self.mkimage) + return + + data = tools.read_file(fname) + self.WriteData(data) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index c36edd13508..e87009d6ce3 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -1014,3 +1014,6 @@ class Entry_section(Entry): for entry in entries.values(): return entry.read_elf_segments() return None + + def UpdateSignatures(self, privatekey_fname, algo, input_fname): + self.Raise('Updating signatures is not supported with this entry type') From 5b34efe865887060e626fe4e78859dab591fc24a Mon Sep 17 00:00:00 2001 From: Ivan Mikhaylov Date: Wed, 8 Mar 2023 01:13:40 +0000 Subject: [PATCH 13/16] binman: add tests for sign option Add the test which provides sequence of actions: 1. create the image from binman dts 2. create public and private keys 3. add public key into dtb with fdt_add_pubkey 4. 1. sign FIT container with new sign option with extracting from image 2. sign exact FIT container with replacing of it in image 5. check with fit_check_sign Signed-off-by: Ivan Mikhaylov Renumber test file from 277 to 280; Move UpdateSignatures() to Entry base class; Don't allow missing mkimage as it doesn't make sense; Propagate --toolpath for CI; Call mark_build_done() to avoid regenerating FIT: Signed-off-by: Simon Glass --- tools/binman/control.py | 2 + tools/binman/entry.py | 3 + tools/binman/etype/fit.py | 4 +- tools/binman/etype/section.py | 3 - tools/binman/ftest.py | 93 ++++++++++++++++++++++++++ tools/binman/test/280_fit_sign.dts | 63 +++++++++++++++++ tools/binman/test/281_sign_non_fit.dts | 65 ++++++++++++++++++ 7 files changed, 227 insertions(+), 6 deletions(-) create mode 100644 tools/binman/test/280_fit_sign.dts create mode 100644 tools/binman/test/281_sign_non_fit.dts diff --git a/tools/binman/control.py b/tools/binman/control.py index cf2c91f622a..0febcb79a60 100644 --- a/tools/binman/control.py +++ b/tools/binman/control.py @@ -464,6 +464,8 @@ def SignEntries(image_fname, input_fname, privatekey_fname, algo, entry_paths, image_fname = os.path.abspath(image_fname) image = Image.FromFile(image_fname) + image.mark_build_done() + BeforeReplace(image, allow_resize=True) for entry_path in entry_paths: diff --git a/tools/binman/entry.py b/tools/binman/entry.py index b10a43333ef..39456906a47 100644 --- a/tools/binman/entry.py +++ b/tools/binman/entry.py @@ -1378,3 +1378,6 @@ features to produce new behaviours. if entries: for entry in entries.values(): entry.mark_build_done() + + def UpdateSignatures(self, privatekey_fname, algo, input_fname): + self.Raise('Updating signatures is not supported with this entry type') diff --git a/tools/binman/etype/fit.py b/tools/binman/etype/fit.py index 3aea9865bf4..c395706ece5 100644 --- a/tools/binman/etype/fit.py +++ b/tools/binman/etype/fit.py @@ -847,9 +847,7 @@ class Entry_fit(Entry_section): args.append(fname) if self.mkimage.run_cmd(*args) is None: - # Bintool is missing; just use empty data as the output - self.record_missing_bintool(self.mkimage) - return + self.Raise("Missing tool: 'mkimage'") data = tools.read_file(fname) self.WriteData(data) diff --git a/tools/binman/etype/section.py b/tools/binman/etype/section.py index e87009d6ce3..c36edd13508 100644 --- a/tools/binman/etype/section.py +++ b/tools/binman/etype/section.py @@ -1014,6 +1014,3 @@ class Entry_section(Entry): for entry in entries.values(): return entry.read_elf_segments() return None - - def UpdateSignatures(self, privatekey_fname, algo, input_fname): - self.Raise('Updating signatures is not supported with this entry type') diff --git a/tools/binman/ftest.py b/tools/binman/ftest.py index f1e14c6b3dc..9862e234386 100644 --- a/tools/binman/ftest.py +++ b/tools/binman/ftest.py @@ -707,6 +707,14 @@ class TestFunctional(unittest.TestCase): AddNode(dtb.GetRoot(), '') return tree + def _CheckSign(self, fit, key): + try: + tools.run('fit_check_sign', '-k', key, '-f', fit) + except: + self.fail('Expected signed FIT container') + return False + return True + def testRun(self): """Test a basic run with valid args""" result = self._RunBinman('-h') @@ -6565,6 +6573,91 @@ fdt fdtmap Extract the devicetree blob from the fdtmap err = stderr.getvalue() self.assertRegex(err, "Image 'image'.*missing bintools.*: openssl") + def _PrepareSignEnv(self, dts='280_fit_sign.dts'): + """Prepare sign environment + + Create private and public keys, add pubkey into dtb. + + Returns: + Tuple: + FIT container + Image name + Private key + DTB + """ + + data = self._DoReadFileRealDtb(dts) + updated_fname = tools.get_output_filename('image-updated.bin') + tools.write_file(updated_fname, data) + dtb = tools.get_output_filename('source.dtb') + private_key = tools.get_output_filename('test_key.key') + public_key = tools.get_output_filename('test_key.crt') + fit = tools.get_output_filename('fit.fit') + key_dir = tools.get_output_dir() + + tools.run('openssl', 'req', '-batch' , '-newkey', 'rsa:4096', + '-sha256', '-new', '-nodes', '-x509', '-keyout', + private_key, '-out', public_key) + tools.run('fdt_add_pubkey', '-a', 'sha256,rsa4096', '-k', key_dir, + '-n', 'test_key', '-r', 'conf', dtb) + + return fit, updated_fname, private_key, dtb + + def testSignSimple(self): + """Test that a FIT container can be signed in image""" + is_signed = False + fit, fname, private_key, dtb = self._PrepareSignEnv() + + # do sign with private key + control.SignEntries(fname, None, private_key, 'sha256,rsa4096', + ['fit']) + is_signed = self._CheckSign(fit, dtb) + + self.assertEqual(is_signed, True) + + def testSignExactFIT(self): + """Test that a FIT container can be signed and replaced in image""" + is_signed = False + fit, fname, private_key, dtb = self._PrepareSignEnv() + + # Make sure we propagate the toolpath, since mkimage may not be on PATH + args = [] + if self.toolpath: + for path in self.toolpath: + args += ['--toolpath', path] + + # do sign with private key + self._DoBinman(*args, 'sign', '-i', fname, '-k', private_key, '-a', + 'sha256,rsa4096', '-f', fit, 'fit') + is_signed = self._CheckSign(fit, dtb) + + self.assertEqual(is_signed, True) + + def testSignNonFit(self): + """Test a non-FIT entry cannot be signed""" + is_signed = False + fit, fname, private_key, _ = self._PrepareSignEnv( + '281_sign_non_fit.dts') + + # do sign with private key + with self.assertRaises(ValueError) as e: + self._DoBinman('sign', '-i', fname, '-k', private_key, '-a', + 'sha256,rsa4096', '-f', fit, 'u-boot') + self.assertIn( + "Node '/u-boot': Updating signatures is not supported with this entry type", + str(e.exception)) + + def testSignMissingMkimage(self): + """Test that FIT signing handles a missing mkimage tool""" + fit, fname, private_key, _ = self._PrepareSignEnv() + + # try to sign with a missing mkimage tool + bintool.Bintool.set_missing_list(['mkimage']) + with self.assertRaises(ValueError) as e: + control.SignEntries(fname, None, private_key, 'sha256,rsa4096', + ['fit']) + self.assertIn("Node '/fit': Missing tool: 'mkimage'", str(e.exception)) + if __name__ == "__main__": unittest.main() diff --git a/tools/binman/test/280_fit_sign.dts b/tools/binman/test/280_fit_sign.dts new file mode 100644 index 00000000000..b9f17dc5c0b --- /dev/null +++ b/tools/binman/test/280_fit_sign.dts @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x100000>; + allow-repack; + + fit { + description = "U-Boot"; + offset = <0x10000>; + images { + u-boot-1 { + description = "U-Boot"; + type = "standalone"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot { + }; + }; + + fdt-1 { + description = "test.dtb"; + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot-spl-dtb { + }; + }; + + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "u-boot with fdt"; + firmware = "u-boot-1"; + fdt = "fdt-1"; + signature-1 { + algo = "sha256,rsa4096"; + key-name-hint = "test_key"; + sign-images = "firmware", "fdt"; + }; + + }; + }; + }; + + fdtmap { + }; + }; +}; diff --git a/tools/binman/test/281_sign_non_fit.dts b/tools/binman/test/281_sign_non_fit.dts new file mode 100644 index 00000000000..e16c954246d --- /dev/null +++ b/tools/binman/test/281_sign_non_fit.dts @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + binman { + size = <0x100000>; + allow-repack; + + u-boot { + }; + fit { + description = "U-Boot"; + offset = <0x10000>; + images { + u-boot-1 { + description = "U-Boot"; + type = "standalone"; + arch = "arm64"; + os = "u-boot"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot { + }; + }; + + fdt-1 { + description = "test.dtb"; + type = "flat_dt"; + arch = "arm64"; + compression = "none"; + hash-1 { + algo = "sha256"; + }; + u-boot-spl-dtb { + }; + }; + + }; + + configurations { + default = "conf-1"; + conf-1 { + description = "u-boot with fdt"; + firmware = "u-boot-1"; + fdt = "fdt-1"; + signature-1 { + algo = "sha256,rsa4096"; + key-name-hint = "test_key"; + sign-images = "firmware", "fdt"; + }; + + }; + }; + }; + + fdtmap { + }; + }; +}; From 30238e99619c0d58fd5d10eb4fd52e05561f4fd4 Mon Sep 17 00:00:00 2001 From: Roman Kopytin Date: Wed, 8 Mar 2023 01:13:41 +0000 Subject: [PATCH 14/16] tools: add fdt_add_pubkey Having to use the -K option to mkimage to populate U-Boot's .dtb with the public key while signing the kernel FIT image is often a little awkward. In particular, when using a meta-build system such as bitbake/Yocto, having the tasks of the kernel and U-Boot recipes intertwined, modifying deployed artifacts and rebuilding U-Boot with an updated .dtb is quite cumbersome. Also, in some scenarios one may wish to build U-Boot complete with the public key(s) embedded in the .dtb without the corresponding private keys being present on the same build host. So this adds a simple tool that allows one to disentangle the kernel and U-Boot builds, by simply copy-pasting just enough of the mkimage code to allow one to add a public key to a .dtb. When using mkimage, some of the information is taken from the .its used to build the kernel (algorithm and key name), so that of course needs to be supplied on the command line. Signed-off-by: Roman Kopytin Signed-off-by: Ivan Mikhaylov Signed-off-by: Jan Kiszka Cc: Rasmus Villemoes --- tools/.gitignore | 1 + tools/Makefile | 3 + tools/fdt_add_pubkey.c | 138 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 tools/fdt_add_pubkey.c diff --git a/tools/.gitignore b/tools/.gitignore index 788ea260a07..cda3ea628c3 100644 --- a/tools/.gitignore +++ b/tools/.gitignore @@ -6,6 +6,7 @@ /dumpimage /easylogo/easylogo /envcrc +/fdt_add_pubkey /fdtgrep /file2include /fit_check_sign diff --git a/tools/Makefile b/tools/Makefile index e13effbb66a..38699b069d6 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -65,6 +65,7 @@ mkenvimage-objs := mkenvimage.o os_support.o lib/crc32.o hostprogs-y += dumpimage mkimage hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fit_info fit_check_sign +hostprogs-$(CONFIG_TOOLS_LIBCRYPTO) += fdt_add_pubkey ifneq ($(CONFIG_CMD_BOOTEFI_SELFTEST)$(CONFIG_FWU_MDATA_GPT_BLK),) hostprogs-y += file2include @@ -150,6 +151,7 @@ dumpimage-objs := $(dumpimage-mkimage-objs) dumpimage.o mkimage-objs := $(dumpimage-mkimage-objs) mkimage.o fit_info-objs := $(dumpimage-mkimage-objs) fit_info.o fit_check_sign-objs := $(dumpimage-mkimage-objs) fit_check_sign.o +fdt_add_pubkey-objs := $(dumpimage-mkimage-objs) fdt_add_pubkey.o file2include-objs := file2include.o ifneq ($(CONFIG_MX23)$(CONFIG_MX28)$(CONFIG_TOOLS_LIBCRYPTO),) @@ -187,6 +189,7 @@ HOSTCFLAGS_fit_image.o += -DMKIMAGE_DTC=\"$(CONFIG_MKIMAGE_DTC_PATH)\" HOSTLDLIBS_dumpimage := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_info := $(HOSTLDLIBS_mkimage) HOSTLDLIBS_fit_check_sign := $(HOSTLDLIBS_mkimage) +HOSTLDLIBS_fdt_add_pubkey := $(HOSTLDLIBS_mkimage) hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl diff --git a/tools/fdt_add_pubkey.c b/tools/fdt_add_pubkey.c new file mode 100644 index 00000000000..999f5a7e83b --- /dev/null +++ b/tools/fdt_add_pubkey.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include +#include "fit_common.h" + +static const char *cmdname; + +static const char *algo_name = "sha1,rsa2048"; /* -a */ +static const char *keydir = "."; /* -k */ +static const char *keyname = "key"; /* -n */ +static const char *require_keys; /* -r */ +static const char *keydest; /* argv[n] */ + +static void print_usage(const char *msg) +{ + fprintf(stderr, "Error: %s\n", msg); + fprintf(stderr, "Usage: %s [-a ] [-k ] [-n ] [-r ]" + " \n", cmdname); + fprintf(stderr, "Help information: %s [-h]\n", cmdname); + exit(EXIT_FAILURE); +} + +static void print_help(void) +{ + fprintf(stderr, "Options:\n" + "\t-a Cryptographic algorithm. Optional parameter, default value: sha1,rsa2048\n" + "\t-k Directory with public key. Optional parameter, default value: .\n" + "\t-n Public key name. Optional parameter, default value: key\n" + "\t-r Required: If present this indicates that the key must be verified for the image / configuration to be considered valid.\n" + "\t FDT blob file for adding of the public key. Required parameter.\n"); + exit(EXIT_FAILURE); +} + +static void process_args(int argc, char *argv[]) +{ + int opt; + + while ((opt = getopt(argc, argv, "a:k:n:r:h")) != -1) { + switch (opt) { + case 'k': + keydir = optarg; + break; + case 'a': + algo_name = optarg; + break; + case 'n': + keyname = optarg; + break; + case 'r': + require_keys = optarg; + break; + case 'h': + print_help(); + default: + print_usage("Invalid option"); + } + } + /* The last parameter is expected to be the .dtb to add the public key to */ + if (optind < argc) + keydest = argv[optind]; + + if (!keydest) + print_usage("Missing dtb file to update"); +} + +static void reset_info(struct image_sign_info *info) +{ + if (!info) + fprintf(stderr, "Error: info is NULL in %s\n", __func__); + + memset(info, 0, sizeof(struct image_sign_info)); + + info->keydir = keydir; + info->keyname = keyname; + info->name = algo_name; + info->require_keys = require_keys; + info->crypto = image_get_crypto_algo(algo_name); + + if (!info->crypto) { + fprintf(stderr, "Unsupported signature algorithm '%s'\n", + algo_name); + exit(EXIT_FAILURE); + } +} + +static int add_pubkey(struct image_sign_info *info) +{ + int destfd = -1, ret; + void *dest_blob = NULL; + struct stat dest_sbuf; + size_t size_inc = 0; + + if (!info) + fprintf(stderr, "Error: info is NULL in %s\n", __func__); + + do { + if (destfd >= 0) { + munmap(dest_blob, dest_sbuf.st_size); + close(destfd); + + fprintf(stderr, ".dtb too small, increasing size by 1024 bytes\n"); + size_inc = 1024; + } + + destfd = mmap_fdt(cmdname, keydest, size_inc, &dest_blob, + &dest_sbuf, false, false); + if (destfd < 0) + exit(EXIT_FAILURE); + + ret = info->crypto->add_verify_data(info, dest_blob); + if (ret == -ENOSPC) + continue; + else if (ret < 0) + break; + } while (ret == -ENOSPC); + + return ret; +} + +int main(int argc, char *argv[]) +{ + struct image_sign_info info; + int ret; + + cmdname = argv[0]; + + process_args(argc, argv); + reset_info(&info); + ret = add_pubkey(&info); + + if (ret < 0) { + fprintf(stderr, "%s: Cannot add public key to FIT blob: %s\n", + cmdname, strerror(ret)); + exit(EXIT_FAILURE); + } + + exit(EXIT_SUCCESS); +} + From 2dbfcf439a3d4223feed5b3be03f1ddaff2384be Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 10 Mar 2023 12:48:50 -0800 Subject: [PATCH 15/16] Revert "buildman: Correct CROSS_COMPILE output for sandbox" This reverts commit bd0a548ad4a155fec29473d4cc8e135832926973. Signed-off-by: Simon Glass --- tools/buildman/toolchain.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/buildman/toolchain.py b/tools/buildman/toolchain.py index 8f9130bdcdf..688f2e26872 100644 --- a/tools/buildman/toolchain.py +++ b/tools/buildman/toolchain.py @@ -156,8 +156,9 @@ class Toolchain: Returns: Value of that environment variable or arguments """ + wrapper = self.GetWrapper() if which == VAR_CROSS_COMPILE: - return self.GetWrapper() + self.cross + return wrapper + os.path.join(self.path, self.cross) elif which == VAR_PATH: return self.path elif which == VAR_ARCH: From c3cea95fd21937ce82be3dbd1062dde8fb0e6114 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Fri, 10 Mar 2023 12:48:51 -0800 Subject: [PATCH 16/16] buildman: Fix CROSS_COMPILE output for sandbox The previous attempt at fixing this broke the normal usage of the -A flag. At present, 'buildman -A sandbox' adds the path containing the toolchain. We can assume that this is in the path and we don't want to set CROSS_COMPILE=/bin/ Change this to align with what MakeEnvironment() does, but only for sandbox boards. Signed-off-by: Simon Glass --- tools/buildman/toolchain.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tools/buildman/toolchain.py b/tools/buildman/toolchain.py index 688f2e26872..241e8e69307 100644 --- a/tools/buildman/toolchain.py +++ b/tools/buildman/toolchain.py @@ -156,9 +156,10 @@ class Toolchain: Returns: Value of that environment variable or arguments """ - wrapper = self.GetWrapper() if which == VAR_CROSS_COMPILE: - return wrapper + os.path.join(self.path, self.cross) + wrapper = self.GetWrapper() + base = '' if self.arch == 'sandbox' else self.path + return wrapper + os.path.join(base, self.cross) elif which == VAR_PATH: return self.path elif which == VAR_ARCH: