From 6ec5178c0ef7c336ac41335aedc63f7f080a11a6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 12 Jul 2023 09:04:30 -0600 Subject: test: Skip flat-tree tests if devicetree is not used Many tests don't actually use the devicetree at all so there is no point in running the tests both with livetree and flat tree. Check for this and skip the flat tree test in that case. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- test/test-main.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/test/test-main.c b/test/test-main.c index 2a3b2ba364a..778bf0a18a0 100644 --- a/test/test-main.c +++ b/test/test-main.c @@ -476,7 +476,8 @@ static int ut_run_test_live_flat(struct unit_test_state *uts, * (for sandbox we handle this by copying the tree, but not for other * boards) */ - if (!(test->flags & UT_TESTF_LIVE_TREE) && + if ((test->flags & UT_TESTF_SCAN_FDT) && + !(test->flags & UT_TESTF_LIVE_TREE) && (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) || !(test->flags & UT_TESTF_OTHER_FDT)) && (!runs || ut_test_run_on_flattree(test)) && -- cgit v1.3.1 From f4a91655c36a1a5fad2ea879ff3ab9217cd21337 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 12 Jul 2023 09:04:34 -0600 Subject: bootstd: Allow storing the OS command line in the bootflow Some operating systems have a command line which can be adjusted before booting. Store this in the bootflow so it can be controlled within U-Boot. Fix up the example output while we are here, since there are a few new items. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- cmd/bootflow.c | 6 ++++++ doc/usage/cmd/bootflow.rst | 5 ++++- include/bootflow.h | 2 ++ test/boot/bootflow.c | 1 + 4 files changed, 13 insertions(+), 1 deletion(-) (limited to 'test') diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 5c61286a2a7..ddb168c7039 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -324,6 +324,12 @@ static int do_bootflow_info(struct cmd_tbl *cmdtp, int flag, int argc, printf("Buffer: %lx\n", (ulong)map_to_sysmem(bflow->buf)); printf("Size: %x (%d bytes)\n", bflow->size, bflow->size); printf("OS: %s\n", bflow->os_name ? bflow->os_name : "(none)"); + printf("Cmdline: "); + if (bflow->cmdline) + puts(bflow->cmdline); + else + puts("(none)"); + putc('\n'); printf("Logo: %s\n", bflow->logo ? simple_xtoa((ulong)map_to_sysmem(bflow->logo)) : "(none)"); if (bflow->logo) { diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index 8590efca218..907d44ad9f9 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -258,7 +258,6 @@ displayed and booted:: Name: mmc@7e202000.bootdev.part_2 Device: mmc@7e202000.bootdev Block dev: mmc@7e202000.blk - Sequence: 1 Method: distro State: ready Partition: 2 @@ -266,6 +265,10 @@ displayed and booted:: Filename: extlinux/extlinux.conf Buffer: 3db7ae88 Size: 232 (562 bytes) + OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) + Cmdline: (none) + Logo: (none) + FDT: Error: 0 U-Boot> bootflow boot ** Booting bootflow 'smsc95xx_eth.bootdev.0' diff --git a/include/bootflow.h b/include/bootflow.h index 018d021b810..a1c16ab93b7 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -81,6 +81,7 @@ enum bootflow_flags_t { * @fdt_size: Size of FDT file * @fdt_addr: Address of loaded fdt * @flags: Flags for the bootflow (see enum bootflow_flags_t) + * @cmdline: OS command line, or NULL if not known (allocated) */ struct bootflow { struct list_head bm_node; @@ -104,6 +105,7 @@ struct bootflow { int fdt_size; ulong fdt_addr; int flags; + char *cmdline; }; /** diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 2b5f87d7f09..f7ebf9d7f27 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -226,6 +226,7 @@ static int bootflow_cmd_info(struct unit_test_state *uts) ut_assert_nextlinen("Buffer: "); ut_assert_nextline("Size: 253 (595 bytes)"); ut_assert_nextline("OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)"); + ut_assert_nextline("Cmdline: (none)"); ut_assert_nextline("Logo: (none)"); ut_assert_nextline("FDT: "); ut_assert_nextline("Error: 0"); -- cgit v1.3.1 From 347a845aec18561d36299b0735c47c2b3f45bc71 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 12 Jul 2023 09:04:37 -0600 Subject: bdinfo: Show information about the serial port It is useful to see the detailed setting of the serial port, e.g. to allow setting up earlycon or console for Linux. Add this output to the 'bdinfo' command. Signed-off-by: Simon Glass Reviewed-by: Bin Meng [bmeng: squashed in 20230716033929.253357-2-sjg@chromium.org] Signed-off-by: Bin Meng --- cmd/bdinfo.c | 21 +++++++++++++++++++++ test/cmd/bdinfo.c | 14 ++++++++++++++ 2 files changed, 35 insertions(+) (limited to 'test') diff --git a/cmd/bdinfo.c b/cmd/bdinfo.c index 365357ca545..dab73f1d93c 100644 --- a/cmd/bdinfo.c +++ b/cmd/bdinfo.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -113,6 +114,25 @@ static void show_video_info(void) } } +static void print_serial(struct udevice *dev) +{ + struct serial_device_info info; + int ret; + + if (!dev || !IS_ENABLED(CONFIG_DM_SERIAL)) + return; + + ret = serial_getinfo(dev, &info); + if (ret) + return; + + bdinfo_print_num_l("serial addr", info.addr); + bdinfo_print_num_l(" width", info.reg_width); + bdinfo_print_num_l(" shift", info.reg_shift); + bdinfo_print_num_l(" offset", info.reg_offset); + bdinfo_print_num_l(" clock", info.clock); +} + int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct bd_info *bd = gd->bd; @@ -151,6 +171,7 @@ int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) if (IS_ENABLED(CONFIG_OF_REAL)) printf("devicetree = %s\n", fdtdec_get_srcname()); } + print_serial(gd->cur_serial_dev); arch_print_bdinfo(); diff --git a/test/cmd/bdinfo.c b/test/cmd/bdinfo.c index cddf1a46d49..6480393fd5d 100644 --- a/test/cmd/bdinfo.c +++ b/test/cmd/bdinfo.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -191,6 +192,19 @@ static int bdinfo_test_move(struct unit_test_state *uts) ut_assert_nextline("devicetree = %s", fdtdec_get_srcname()); } + if (IS_ENABLED(CONFIG_DM_SERIAL)) { + struct serial_device_info info; + + ut_assertnonnull(gd->cur_serial_dev); + ut_assertok(serial_getinfo(gd->cur_serial_dev, &info)); + + ut_assertok(test_num_l(uts, "serial addr", info.addr)); + ut_assertok(test_num_l(uts, " width", info.reg_width)); + ut_assertok(test_num_l(uts, " shift", info.reg_shift)); + ut_assertok(test_num_l(uts, " offset", info.reg_offset)); + ut_assertok(test_num_l(uts, " clock", info.clock)); + } + ut_assertok(ut_check_console_end(uts)); return 0; -- cgit v1.3.1 From d07861cc7aa605a64c83bc7fbe1340bc31b2c2cf Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 12 Jul 2023 09:04:38 -0600 Subject: bootstd: Add a function to update a command line The Linux command line consists of a number of words with optional values. At present U-Boot allows this to be changed using the bootargs environment variable. But this is quite painful, since the command line can be very long. Add a function which can adjust a single field in the command line, so that it is easier to make changes before booting. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- boot/bootflow.c | 191 +++++++++++++++++++++++++++++++++++++++++++++++++++ include/bootflow.h | 40 +++++++++++ test/boot/bootflow.c | 154 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 385 insertions(+) (limited to 'test') diff --git a/boot/bootflow.c b/boot/bootflow.c index b820678d41a..26f26aee38a 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -610,3 +610,194 @@ static int on_bootargs(const char *name, const char *value, enum env_op op, } U_BOOT_ENV_CALLBACK(bootargs, on_bootargs); #endif + +/** + * copy_in() - Copy a string into a cmdline buffer + * + * @buf: Buffer to copy into + * @end: End of buffer (pointer to char after the end) + * @arg: String to copy from + * @len: Number of chars to copy from @arg (note that this is not usually the + * sane as strlen(arg) since the string may contain following arguments) + * @new_val: Value to put after arg, or BOOTFLOWCL_EMPTY to use an empty value + * with no '=' sign + * Returns: Number of chars written to @buf + */ +static int copy_in(char *buf, char *end, const char *arg, int len, + const char *new_val) +{ + char *to = buf; + + /* copy the arg name */ + if (to + len >= end) + return -E2BIG; + memcpy(to, arg, len); + to += len; + + if (new_val == BOOTFLOWCL_EMPTY) { + /* no value */ + } else { + bool need_quote = strchr(new_val, ' '); + len = strlen(new_val); + + /* need space for value, equals sign and maybe two quotes */ + if (to + 1 + (need_quote ? 2 : 0) + len >= end) + return -E2BIG; + *to++ = '='; + if (need_quote) + *to++ = '"'; + memcpy(to, new_val, len); + to += len; + if (need_quote) + *to++ = '"'; + } + + return to - buf; +} + +int cmdline_set_arg(char *buf, int maxlen, const char *cmdline, + const char *set_arg, const char *new_val, int *posp) +{ + bool found_arg = false; + const char *from; + char *to, *end; + int set_arg_len; + char empty = '\0'; + int ret; + + from = cmdline ?: ∅ + + /* check if the value has quotes inside */ + if (new_val && new_val != BOOTFLOWCL_EMPTY && strchr(new_val, '"')) + return -EBADF; + + set_arg_len = strlen(set_arg); + for (to = buf, end = buf + maxlen; *from;) { + const char *val, *arg_end, *val_end, *p; + bool in_quote; + + if (to >= end) + return -E2BIG; + while (*from == ' ') + from++; + if (!*from) + break; + + /* find the end of this arg */ + val = NULL; + arg_end = NULL; + val_end = NULL; + in_quote = false; + for (p = from;; p++) { + if (in_quote) { + if (!*p) + return -EINVAL; + if (*p == '"') + in_quote = false; + continue; + } + if (*p == '=') { + arg_end = p; + val = p + 1; + } else if (*p == '"') { + in_quote = true; + } else if (!*p || *p == ' ') { + val_end = p; + if (!arg_end) + arg_end = p; + break; + } + } + /* + * At this point val_end points to the end of the value, or the + * last char after the arg name, if there is no label. + * arg_end is the char after the arg name + * val points to the value, or NULL if there is none + * char after the value. + * + * fred=1234 + * ^ ^^ ^ + * from || | + * / \ \ + * arg_end val val_end + */ + log_debug("from %s arg_end %ld val %ld val_end %ld\n", from, + (long)(arg_end - from), (long)(val - from), + (long)(val_end - from)); + + if (to != buf) { + if (to >= end) + return -E2BIG; + *to++ = ' '; + } + + /* if this is the target arg, update it */ + if (!strncmp(from, set_arg, arg_end - from)) { + if (!buf) { + bool has_quote = val_end[-1] == '"'; + + /* + * exclude any start/end quotes from + * calculations + */ + if (!val) + val = val_end; + *posp = val - cmdline + has_quote; + return val_end - val - 2 * has_quote; + } + found_arg = true; + if (!new_val) { + /* delete this arg */ + from = val_end + (*val_end == ' '); + log_debug("delete from: %s\n", from); + if (to != buf) + to--; /* drop the space we added */ + continue; + } + + ret = copy_in(to, end, from, arg_end - from, new_val); + if (ret < 0) + return ret; + to += ret; + + /* if not the target arg, copy it unchanged */ + } else if (to) { + int len; + + len = val_end - from; + if (to + len >= end) + return -E2BIG; + memcpy(to, from, len); + to += len; + } + from = val_end; + } + + /* If we didn't find the arg, add it */ + if (!found_arg) { + /* trying to delete something that is not there */ + if (!new_val || !buf) + return -ENOENT; + if (to >= end) + return -E2BIG; + + /* add a space to separate it from the previous arg */ + if (to != buf && to[-1] != ' ') + *to++ = ' '; + ret = copy_in(to, end, set_arg, set_arg_len, new_val); + log_debug("ret=%d, to: %s buf: %s\n", ret, to, buf); + if (ret < 0) + return ret; + to += ret; + } + + /* delete any trailing space */ + if (to > buf && to[-1] == ' ') + to--; + + if (to >= end) + return -E2BIG; + *to++ = '\0'; + + return to - buf; +} diff --git a/include/bootflow.h b/include/bootflow.h index f263173c4da..8db875adf61 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -444,4 +444,44 @@ int bootflow_menu_apply_theme(struct expo *exp, ofnode node); int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, struct bootflow **bflowp); +#define BOOTFLOWCL_EMPTY ((void *)1) + +/** + * cmdline_set_arg() - Update or read an argument in a cmdline string + * + * Handles updating a single arg in a cmdline string, returning it in a supplied + * buffer; also reading an arg from a cmdline string + * + * When updating, consecutive spaces are squashed as are spaces at the start and + * end. + * + * @buf: Working buffer to use (initial contents are ignored). Use NULL when + * reading + * @maxlen: Length of working buffer. Use 0 when reading + * @cmdline: Command line to update, in the form: + * + * fred mary= jane=123 john="has spaces" + * + * @set_arg: Argument to set or read (may or may not exist) + * @new_val: Value for the new argument. May not include quotes (") but may + * include embedded spaces, in which case it will be quoted when added to the + * command line. Use NULL to delete the argument from @cmdline, BOOTFLOWCL_EMPTY + * to set it to an empty value (no '=' sign after arg), "" to add an '=' sign + * but with an empty value. Use NULL when reading. + * @posp: Ignored when setting an argument; when getting an argument, returns + * the start position of its value in @cmdline, after the first quote, if any + * + * Return: + * For updating: + * length of new buffer (including \0 terminator) on success, -ENOENT if + * @new_val is NULL and @set_arg does not exist in @from, -EINVAL if a + * quoted arg-value in @from is not terminated with a quote, -EBADF if + * @new_val has spaces but does not start and end with quotes (or it has + * quotes in the middle of the string), -E2BIG if @maxlen is too small + * For reading: + * length of arg value (excluding quotes), -ENOENT if not found + */ +int cmdline_set_arg(char *buf, int maxlen, const char *cmdline, + const char *set_arg, const char *new_val, int *posp); + #endif diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index f7ebf9d7f27..ead71172e12 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -683,3 +683,157 @@ static int bootflow_menu_theme(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_menu_theme, UT_TESTF_DM | UT_TESTF_SCAN_FDT); + +/** + * check_arg() - Check both the normal case and the buffer-overflow case + * + * @uts: Unit-test state + * @expect_ret: Expected return value (i.e. buffer length) + * @expect_str: String expected to be returned + * @buf: Buffer to use + * @from: Original cmdline to update + * @arg: Argument to update (e.g. "console") + * @val: Value to set (e.g. "ttyS2") or NULL to delete the argument if present, + * "" to set it to an empty value (e.g. "console=") and BOOTFLOWCL_EMPTY to add + * it without any value ("initrd") + */ +static int check_arg(struct unit_test_state *uts, int expect_ret, + const char *expect_str, char *buf, const char *from, + const char *arg, const char *val) +{ + /* check for writing outside the reported bounds */ + buf[expect_ret] = '['; + ut_asserteq(expect_ret, + cmdline_set_arg(buf, expect_ret, from, arg, val, NULL)); + ut_asserteq_str(expect_str, buf); + ut_asserteq('[', buf[expect_ret]); + + /* do the test again but with one less byte in the buffer */ + ut_asserteq(-E2BIG, cmdline_set_arg(buf, expect_ret - 1, from, arg, + val, NULL)); + + return 0; +} + +/* Test of bootflow_cmdline_set_arg() */ +static int test_bootflow_cmdline_set(struct unit_test_state *uts) +{ + char buf[50]; + const int size = sizeof(buf); + + /* + * note that buffer-overflow tests are immediately each test case, just + * top keep the code together + */ + + /* add an arg that doesn't already exist, starting from empty */ + ut_asserteq(-ENOENT, cmdline_set_arg(buf, size, NULL, "me", NULL, + NULL)); + + ut_assertok(check_arg(uts, 3, "me", buf, NULL, "me", BOOTFLOWCL_EMPTY)); + ut_assertok(check_arg(uts, 4, "me=", buf, NULL, "me", "")); + ut_assertok(check_arg(uts, 8, "me=fred", buf, NULL, "me", "fred")); + + /* add an arg that doesn't already exist, starting from non-empty */ + ut_assertok(check_arg(uts, 11, "arg=123 me", buf, "arg=123", "me", + BOOTFLOWCL_EMPTY)); + ut_assertok(check_arg(uts, 12, "arg=123 me=", buf, "arg=123", "me", + "")); + ut_assertok(check_arg(uts, 16, "arg=123 me=fred", buf, "arg=123", "me", + "fred")); + + /* update an arg at the start */ + ut_assertok(check_arg(uts, 1, "", buf, "arg=123", "arg", NULL)); + ut_assertok(check_arg(uts, 4, "arg", buf, "arg=123", "arg", + BOOTFLOWCL_EMPTY)); + ut_assertok(check_arg(uts, 5, "arg=", buf, "arg=123", "arg", "")); + ut_assertok(check_arg(uts, 6, "arg=1", buf, "arg=123", "arg", "1")); + ut_assertok(check_arg(uts, 9, "arg=1234", buf, "arg=123", "arg", + "1234")); + + /* update an arg at the end */ + ut_assertok(check_arg(uts, 5, "mary", buf, "mary arg=123", "arg", + NULL)); + ut_assertok(check_arg(uts, 9, "mary arg", buf, "mary arg=123", "arg", + BOOTFLOWCL_EMPTY)); + ut_assertok(check_arg(uts, 10, "mary arg=", buf, "mary arg=123", "arg", + "")); + ut_assertok(check_arg(uts, 11, "mary arg=1", buf, "mary arg=123", "arg", + "1")); + ut_assertok(check_arg(uts, 14, "mary arg=1234", buf, "mary arg=123", + "arg", "1234")); + + /* update an arg in the middle */ + ut_assertok(check_arg(uts, 16, "mary=abc john=2", buf, + "mary=abc arg=123 john=2", "arg", NULL)); + ut_assertok(check_arg(uts, 20, "mary=abc arg john=2", buf, + "mary=abc arg=123 john=2", "arg", + BOOTFLOWCL_EMPTY)); + ut_assertok(check_arg(uts, 21, "mary=abc arg= john=2", buf, + "mary=abc arg=123 john=2", "arg", "")); + ut_assertok(check_arg(uts, 22, "mary=abc arg=1 john=2", buf, + "mary=abc arg=123 john=2", "arg", "1")); + ut_assertok(check_arg(uts, 25, "mary=abc arg=1234 john=2", buf, + "mary=abc arg=123 john=2", "arg", "1234")); + + /* handle existing args with quotes */ + ut_assertok(check_arg(uts, 16, "mary=\"abc\" john", buf, + "mary=\"abc\" arg=123 john", "arg", NULL)); + + /* handle existing args with quoted spaces */ + ut_assertok(check_arg(uts, 20, "mary=\"abc def\" john", buf, + "mary=\"abc def\" arg=123 john", "arg", NULL)); + + ut_assertok(check_arg(uts, 34, "mary=\"abc def\" arg=123 john def=4", + buf, "mary=\"abc def\" arg=123 john", "def", + "4")); + + /* quote at the start */ + ut_asserteq(-EBADF, cmdline_set_arg(buf, size, + "mary=\"abc def\" arg=\"123 456\"", + "arg", "\"4 5 6", NULL)); + + /* quote at the end */ + ut_asserteq(-EBADF, cmdline_set_arg(buf, size, + "mary=\"abc def\" arg=\"123 456\"", + "arg", "4 5 6\"", NULL)); + + /* quote in the middle */ + ut_asserteq(-EBADF, cmdline_set_arg(buf, size, + "mary=\"abc def\" arg=\"123 456\"", + "arg", "\"4 \"5 6\"", NULL)); + + /* handle updating a quoted arg */ + ut_assertok(check_arg(uts, 27, "mary=\"abc def\" arg=\"4 5 6\"", buf, + "mary=\"abc def\" arg=\"123 456\"", "arg", + "4 5 6")); + + /* changing a quoted arg to a non-quoted arg */ + ut_assertok(check_arg(uts, 23, "mary=\"abc def\" arg=789", buf, + "mary=\"abc def\" arg=\"123 456\"", "arg", + "789")); + + /* changing a non-quoted arg to a quoted arg */ + ut_assertok(check_arg(uts, 29, "mary=\"abc def\" arg=\"456 789\"", buf, + "mary=\"abc def\" arg=123", "arg", "456 789")); + + /* handling of spaces */ + ut_assertok(check_arg(uts, 8, "arg=123", buf, " ", "arg", "123")); + ut_assertok(check_arg(uts, 8, "arg=123", buf, " ", "arg", "123")); + ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john ", "arg", + "123")); + ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john arg=123 ", + "arg", "123")); + ut_assertok(check_arg(uts, 18, "john arg=123 mary", buf, + " john arg=123 mary ", "arg", "123")); + + /* unchanged arg */ + ut_assertok(check_arg(uts, 3, "me", buf, "me", "me", BOOTFLOWCL_EMPTY)); + + /* arg which starts with the same name */ + ut_assertok(check_arg(uts, 28, "mary=abc johnathon=2 john=3", buf, + "mary=abc johnathon=2 john=1", "john", "3")); + + return 0; +} +BOOTSTD_TEST(test_bootflow_cmdline_set, 0); -- cgit v1.3.1 From 82c0938f1d3befdd7dd1a1bda3b0a02b219abb5d Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Wed, 12 Jul 2023 09:04:39 -0600 Subject: bootstd: Add support for updating elements of the cmdline Add a bootflow command to update the command line more easily. This allows changing a particular parameter rather than editing a very long strings. It is also easier to handle with scripting. The new 'bootflow cmdline' command allows getting and setting single parameters. Fix up the example output while we are here, since there are a few new items. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- boot/bootflow.c | 53 ++++++++++++++++++++++ cmd/bootflow.c | 70 ++++++++++++++++++++++++++++- doc/usage/cmd/bootflow.rst | 22 ++++++++- include/bootflow.h | 42 ++++++++++++++++++ test/boot/bootflow.c | 108 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 293 insertions(+), 2 deletions(-) (limited to 'test') diff --git a/boot/bootflow.c b/boot/bootflow.c index 26f26aee38a..8c649e8e66c 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -801,3 +801,56 @@ int cmdline_set_arg(char *buf, int maxlen, const char *cmdline, return to - buf; } + +int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *set_arg, + const char *new_val, bool set_env) +{ + char buf[2048]; + char *cmd = NULL; + int ret; + + ret = cmdline_set_arg(buf, sizeof(buf), bflow->cmdline, set_arg, + new_val, NULL); + if (ret < 0) + return ret; + + ret = bootflow_cmdline_set(bflow, buf); + if (*buf) { + cmd = strdup(buf); + if (!cmd) + return -ENOMEM; + } + free(bflow->cmdline); + bflow->cmdline = cmd; + + if (set_env) { + ret = env_set("bootargs", bflow->cmdline); + if (ret) + return ret; + } + + return 0; +} + +int cmdline_get_arg(const char *cmdline, const char *arg, int *posp) +{ + int ret; + + ret = cmdline_set_arg(NULL, 1, cmdline, arg, NULL, posp); + + return ret; +} + +int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg, + const char **val) +{ + int ret; + int pos; + + ret = cmdline_get_arg(bflow->cmdline, arg, &pos); + if (ret < 0) + return ret; + *val = bflow->cmdline + pos; + + return ret; +} diff --git a/cmd/bootflow.c b/cmd/bootflow.c index bf30087c7c4..ab00e4a19e1 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -431,6 +431,72 @@ static int do_bootflow_menu(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } + +static int do_bootflow_cmdline(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct bootstd_priv *std; + struct bootflow *bflow; + const char *op, *arg, *val = NULL; + int ret; + + if (argc < 3) + return CMD_RET_USAGE; + + ret = bootstd_get_priv(&std); + if (ret) + return CMD_RET_FAILURE; + + bflow = std->cur_bootflow; + if (!bflow) { + printf("No bootflow selected\n"); + return CMD_RET_FAILURE; + } + + op = argv[1]; + arg = argv[2]; + if (*op == 's') { + if (argc < 4) + return CMD_RET_USAGE; + val = argv[3]; + } + + switch (*op) { + case 'c': /* clear */ + val = ""; + fallthrough; + case 's': /* set */ + case 'd': /* delete */ + ret = bootflow_cmdline_set_arg(bflow, arg, val, true); + break; + case 'g': /* get */ + ret = bootflow_cmdline_get_arg(bflow, arg, &val); + if (ret >= 0) + printf("%.*s\n", ret, val); + break; + } + switch (ret) { + case -E2BIG: + printf("Argument too long\n"); + break; + case -ENOENT: + printf("Argument not found\n"); + break; + case -EINVAL: + printf("Mismatched quotes\n"); + break; + case -EBADF: + printf("Value must be quoted\n"); + break; + default: + if (ret < 0) + printf("Unknown error: %dE\n", ret); + } + if (ret < 0) + return CMD_RET_FAILURE; + + return 0; +} #endif /* CONFIG_CMD_BOOTFLOW_FULL */ #ifdef CONFIG_SYS_LONGHELP @@ -441,7 +507,8 @@ static char bootflow_help_text[] = "bootflow select [|] - select a bootflow\n" "bootflow info [-d] - show info on current bootflow (-d dump bootflow)\n" "bootflow boot - boot current bootflow (or first available if none selected)\n" - "bootflow menu [-t] - show a menu of available bootflows"; + "bootflow menu [-t] - show a menu of available bootflows\n" + "bootflow cmdline [set|get|clear|delete] [] - update cmdline"; #else "scan - boot first available bootflow\n"; #endif @@ -455,5 +522,6 @@ U_BOOT_CMD_WITH_SUBCMDS(bootflow, "Boot flows", bootflow_help_text, U_BOOT_SUBCMD_MKENT(info, 2, 1, do_bootflow_info), U_BOOT_SUBCMD_MKENT(boot, 1, 1, do_bootflow_boot), U_BOOT_SUBCMD_MKENT(menu, 2, 1, do_bootflow_menu), + U_BOOT_SUBCMD_MKENT(cmdline, 4, 1, do_bootflow_cmdline), #endif ); diff --git a/doc/usage/cmd/bootflow.rst b/doc/usage/cmd/bootflow.rst index 907d44ad9f9..07af789e670 100644 --- a/doc/usage/cmd/bootflow.rst +++ b/doc/usage/cmd/bootflow.rst @@ -13,7 +13,7 @@ Synopis bootflow select [] bootflow info [-d] bootflow boot - + bootflow cmdline [set|get|clear|delete] [] Description ----------- @@ -198,6 +198,26 @@ bootflow boot This boots the current bootflow. +bootflow cmdline +~~~~~~~~~~~~~~~~ + +Some bootmeths can obtain the OS command line since it is stored with the OS. +In that case, you can use `bootflow cmdline` to adjust this. The command line +is assumed to be in the format used by Linux, i.e. a space-separated set of +parameters with optional values, e.g. "noinitrd console=/dev/tty0". + +To change or add a parameter, use:: + + bootflow cmdline set + +To clear a parameter value to empty you can use "" for the value, or use:: + + bootflow cmdline clear + +To delete a parameter entirely, use:: + + bootflow cmdline delete + Example ------- diff --git a/include/bootflow.h b/include/bootflow.h index 8db875adf61..7a8595f3dfc 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -484,4 +484,46 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, int cmdline_set_arg(char *buf, int maxlen, const char *cmdline, const char *set_arg, const char *new_val, int *posp); +/** + * bootflow_cmdline_set_arg() - Set a single argument for a bootflow + * + * Update the allocated cmdline and set the bootargs variable + * + * @bflow: Bootflow to update + * @arg: Argument to update (e.g. "console") + * @val: Value to set (e.g. "ttyS2") or NULL to delete the argument if present, + * "" to set it to an empty value (e.g. "console=") and BOOTFLOWCL_EMPTY to add + * it without any value ("initrd") + * @set_env: true to set the "bootargs" environment variable too + * + * Return: 0 if OK, -ENOMEM if out of memory + */ +int bootflow_cmdline_set_arg(struct bootflow *bflow, const char *arg, + const char *val, bool set_env); + +/** + * cmdline_get_arg() - Read an argument from a cmdline + * + * @cmdline: Command line to read, in the form: + * + * fred mary= jane=123 john="has spaces" + * @arg: Argument to read (may or may not exist) + * @posp: Returns position of argument (after any leading quote) if present + * Return: Length of argument value excluding quotes if found, -ENOENT if not + * found + */ +int cmdline_get_arg(const char *cmdline, const char *arg, int *posp); + +/** + * bootflow_cmdline_get_arg() - Read an argument from a cmdline + * + * @bootflow: Bootflow to read from + * @arg: Argument to read (may or may not exist) + * @valp: Returns a pointer to the argument (after any leading quote) if present + * Return: Length of argument value excluding quotes if found, -ENOENT if not + * found + */ +int bootflow_cmdline_get_arg(struct bootflow *bflow, const char *arg, + const char **val); + #endif diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index ead71172e12..8a4e090e9bc 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -837,3 +837,111 @@ static int test_bootflow_cmdline_set(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(test_bootflow_cmdline_set, 0); + +/* Test of bootflow_cmdline_set_arg() */ +static int bootflow_set_arg(struct unit_test_state *uts) +{ + struct bootflow s_bflow, *bflow = &s_bflow; + ulong mem_start; + + ut_assertok(env_set("bootargs", NULL)); + + mem_start = ut_check_delta(0); + + /* Do a simple sanity check. Rely on bootflow_cmdline() for the rest */ + bflow->cmdline = NULL; + ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", "123", false)); + ut_asserteq_str(bflow->cmdline, "fred=123"); + + ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "and here", false)); + ut_asserteq_str(bflow->cmdline, "fred=123 mary=\"and here\""); + + ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", NULL, false)); + ut_asserteq_str(bflow->cmdline, "fred=123"); + ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", NULL, false)); + ut_asserteq_ptr(bflow->cmdline, NULL); + + ut_asserteq(0, ut_check_delta(mem_start)); + + ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "here", true)); + ut_asserteq_str("mary=here", env_get("bootargs")); + ut_assertok(env_set("bootargs", NULL)); + + return 0; +} +BOOTSTD_TEST(bootflow_set_arg, 0); + +/* Test of bootflow_cmdline_get_arg() */ +static int bootflow_cmdline_get(struct unit_test_state *uts) +{ + int pos; + + /* empty string */ + ut_asserteq(-ENOENT, cmdline_get_arg("", "fred", &pos)); + + /* arg with empty value */ + ut_asserteq(0, cmdline_get_arg("fred= mary", "fred", &pos)); + ut_asserteq(5, pos); + + /* arg with a value */ + ut_asserteq(2, cmdline_get_arg("fred=23", "fred", &pos)); + ut_asserteq(5, pos); + + /* arg with a value */ + ut_asserteq(3, cmdline_get_arg("mary=1 fred=234", "fred", &pos)); + ut_asserteq(12, pos); + + /* arg with a value, after quoted arg */ + ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=234", "fred", &pos)); + ut_asserteq(16, pos); + + /* arg in the middle */ + ut_asserteq(0, cmdline_get_arg("mary=\"1 2\" fred john=23", "fred", + &pos)); + ut_asserteq(15, pos); + + /* quoted arg */ + ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=\"3 4\" john=23", + "fred", &pos)); + ut_asserteq(17, pos); + + /* args starting with the same prefix */ + ut_asserteq(1, cmdline_get_arg("mary=abc johnathon=3 john=1", "john", + &pos)); + ut_asserteq(26, pos); + + return 0; +} +BOOTSTD_TEST(bootflow_cmdline_get, 0); + +static int bootflow_cmdline(struct unit_test_state *uts) +{ + ut_assertok(run_command("bootflow scan mmc", 0)); + ut_assertok(run_command("bootflow sel 0", 0)); + console_record_reset_enable(); + + ut_asserteq(1, run_command("bootflow cmdline get fred", 0)); + ut_assert_nextline("Argument not found"); + ut_assert_console_end(); + + ut_asserteq(0, run_command("bootflow cmdline set fred 123", 0)); + ut_asserteq(0, run_command("bootflow cmdline get fred", 0)); + ut_assert_nextline("123"); + + ut_asserteq(0, run_command("bootflow cmdline set mary abc", 0)); + ut_asserteq(0, run_command("bootflow cmdline get mary", 0)); + ut_assert_nextline("abc"); + + ut_asserteq(0, run_command("bootflow cmdline delete fred", 0)); + ut_asserteq(1, run_command("bootflow cmdline get fred", 0)); + ut_assert_nextline("Argument not found"); + + ut_asserteq(0, run_command("bootflow cmdline clear mary", 0)); + ut_asserteq(0, run_command("bootflow cmdline get mary", 0)); + ut_assert_nextline_empty(); + + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootflow_cmdline, 0); -- cgit v1.3.1 From 297184143ab788b7e591604475760c1794532c99 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 15 Jul 2023 21:38:45 -0600 Subject: acpi: Add a comment to set the acpi tables Sometimes a previous bootloader has written ACPI tables. It is useful to be able to find and list these. Add an 'acpi set' command to set the address for these tables. Signed-off-by: Simon Glass Reviewed-by: Bin Meng --- cmd/acpi.c | 24 +++++++++++++++++++++--- doc/usage/cmd/acpi.rst | 29 +++++++++++++++++++++++++++-- test/dm/acpi.c | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 5 deletions(-) (limited to 'test') diff --git a/cmd/acpi.c b/cmd/acpi.c index e70913e40bf..ede9c8c7dcb 100644 --- a/cmd/acpi.c +++ b/cmd/acpi.c @@ -118,6 +118,22 @@ static int do_acpi_list(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_acpi_set(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + ulong val; + + if (argc < 2) { + printf("ACPI pointer: %lx\n", gd_acpi_start()); + } else { + val = hextoul(argv[1], NULL); + printf("Setting ACPI pointer to %lx\n", val); + gd_set_acpi_start(val); + } + + return 0; +} + static int do_acpi_items(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -157,12 +173,14 @@ static int do_acpi_dump(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LONGHELP static char acpi_help_text[] = - "list - list ACPI tables\n" - "acpi items [-d] - List/dump each piece of ACPI data from devices\n" - "acpi dump - Dump ACPI table"; + "list - list ACPI tables\n" + "acpi items [-d] - List/dump each piece of ACPI data from devices\n" + "acpi set [] - Set or show address of ACPI tables\n" + "acpi dump - Dump ACPI table"; #endif U_BOOT_CMD_WITH_SUBCMDS(acpi, "ACPI tables", acpi_help_text, U_BOOT_SUBCMD_MKENT(list, 1, 1, do_acpi_list), U_BOOT_SUBCMD_MKENT(items, 2, 1, do_acpi_items), + U_BOOT_SUBCMD_MKENT(set, 2, 1, do_acpi_set), U_BOOT_SUBCMD_MKENT(dump, 2, 1, do_acpi_dump)); diff --git a/doc/usage/cmd/acpi.rst b/doc/usage/cmd/acpi.rst index 14bafc8e352..6b9b8949f32 100644 --- a/doc/usage/cmd/acpi.rst +++ b/doc/usage/cmd/acpi.rst @@ -11,12 +11,14 @@ Synopis acpi list acpi items [-d] acpi dump + acpi set
Description ----------- -The *acpi* command is used to dump the ACPI tables generated by U-Boot for passing -to the operating systems. +The *acpi* command is used to dump the ACPI tables generated by U-Boot for +passing to the operating systems. It allows manually setting the address to take +a look at existing ACPI tables. ACPI tables can be generated by various output functions and even devices can output material to include in the Differentiated System Description Table (DSDT) @@ -231,5 +233,28 @@ Example 00000000: 44 53 44 54 ea 32 00 00 02 eb 55 2d 42 4f 4f 54 DSDT.2....U-BOOT 00000010: 55 2d 42 4f 4f 54 42 4c 25 07 11 20 49 4e 54 4c U-BOOTBL%.. INTL +This shows searching for tables in a known area of memory, then setting the +pointer:: + + => acpi list + No ACPI tables present + => ms.s bff00000 80000 "RSD PTR" + bff75000: 52 53 44 20 50 54 52 20 cf 42 4f 43 48 53 20 00 RSD PTR .BOCHS . + 1 match + => acpi set bff75000 + Setting ACPI pointer to bff75000 + => acpi list + Name Base Size Detail + ---- -------- ----- ------ + RSDP bff75000 0 v00 BOCHS + RSDT bff76a63 38 v01 BOCHS BXPC 1 BXPC 1 + FACP bff768ff 74 v01 BOCHS BXPC 1 BXPC 1 + DSDT bff75080 187f v01 BOCHS BXPC 1 BXPC 1 + FACS bff75040 40 + APIC bff76973 90 v01 BOCHS BXPC 1 BXPC 1 + HPET bff76a03 38 v01 BOCHS BXPC 1 BXPC 1 + WAET bff76a3b 28 v01 BOCHS BXPC 1 BXPC 1 + SSDT bff95040 c5 v02 COREv4 COREBOOT 2a CORE 20221020 + .. _`ACPI specification`: https://uefi.org/sites/default/files/resources/ACPI_6_3_final_Jan30.pdf diff --git a/test/dm/acpi.c b/test/dm/acpi.c index 818f71572c7..77eb524b59f 100644 --- a/test/dm/acpi.c +++ b/test/dm/acpi.c @@ -609,3 +609,41 @@ static int dm_test_acpi_cmd_items(struct unit_test_state *uts) return 0; } DM_TEST(dm_test_acpi_cmd_items, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); + +/* Test 'acpi set' command */ +static int dm_test_acpi_cmd_set(struct unit_test_state *uts) +{ + struct acpi_ctx ctx; + ulong addr; + void *buf; + + gd_set_acpi_start(0); + + console_record_reset(); + ut_asserteq(0, gd_acpi_start()); + ut_assertok(run_command("acpi set", 0)); + ut_assert_nextline("ACPI pointer: 0"); + + buf = memalign(16, BUF_SIZE); + ut_assertnonnull(buf); + addr = map_to_sysmem(buf); + ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr)); + + ut_assertok(acpi_write_dev_tables(&ctx)); + + ut_assertok(run_command("acpi set", 0)); + ut_assert_nextline("ACPI pointer: %lx", addr); + + ut_assertok(run_command("acpi set 0", 0)); + ut_assert_nextline("Setting ACPI pointer to 0"); + ut_asserteq(0, gd_acpi_start()); + + ut_assertok(run_commandf("acpi set %lx", addr)); + ut_assert_nextline("Setting ACPI pointer to %lx", addr); + ut_asserteq(addr, gd_acpi_start()); + + ut_assert_console_end(); + + return 0; +} +DM_TEST(dm_test_acpi_cmd_set, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT); -- cgit v1.3.1 From b279f5170a807a87a5726bcbeb0bc98937102eee Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Sat, 15 Jul 2023 21:38:46 -0600 Subject: bdinfo: Show the RAM top and approximate stack pointer These are useful pieces of information when debugging. The RAM top shows where U-Boot started allocating memory from, before it relocated. The stack pointer can be checked to ensure it is in the correct region. Signed-off-by: Simon Glass Reviewed-by: Nikhil M Jain Reviewed-by: Bin Meng Tested-by: Nikhil M Jain --- cmd/Kconfig | 8 ++++++++ cmd/bdinfo.c | 5 +++++ test/cmd/bdinfo.c | 7 +++++++ 3 files changed, 20 insertions(+) (limited to 'test') diff --git a/cmd/Kconfig b/cmd/Kconfig index fd76972eaa3..ecfd5752377 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -135,6 +135,14 @@ config CMD_BDI help Print board info +config CMD_BDINFO_EXTRA + bool "bdinfo extra features" + default y if SANDBOX || X86 + help + Show additional information about the board. This uses a little more + code space but provides more options, particularly those useful for + bringup, development and debugging. + config CMD_CONFIG bool "config" default SANDBOX diff --git a/cmd/bdinfo.c b/cmd/bdinfo.c index dab73f1d93c..44e6d6a972e 100644 --- a/cmd/bdinfo.c +++ b/cmd/bdinfo.c @@ -173,6 +173,11 @@ int do_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) } print_serial(gd->cur_serial_dev); + if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) { + bdinfo_print_num_ll("stack ptr", (ulong)&bd); + bdinfo_print_num_ll("ram_top ptr", (ulong)gd->ram_top); + } + arch_print_bdinfo(); return 0; diff --git a/test/cmd/bdinfo.c b/test/cmd/bdinfo.c index 6480393fd5d..8c09281cac0 100644 --- a/test/cmd/bdinfo.c +++ b/test/cmd/bdinfo.c @@ -205,6 +205,13 @@ static int bdinfo_test_move(struct unit_test_state *uts) ut_assertok(test_num_l(uts, " clock", info.clock)); } + if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) { + ut_assert_nextlinen("stack ptr"); + ut_assertok(test_num_ll(uts, "ram_top ptr", + (unsigned long long)gd->ram_top)); + ut_assertok(test_num_l(uts, "malloc base", gd_malloc_start())); + } + ut_assertok(ut_check_console_end(uts)); return 0; -- cgit v1.3.1