From f2eb6ad50a3e610897d6386bb3192c049dc6fd12 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:23 -0600 Subject: expo: Provide a way to iterate through all scene objects For some operations it is necessary to process all objects in an expo. Provide an iterator to handle this. Signed-off-by: Simon Glass --- test/boot/expo.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'test/boot') diff --git a/test/boot/expo.c b/test/boot/expo.c index 3898f853a75..458e332440c 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -289,6 +289,33 @@ static int expo_object_attr(struct unit_test_state *uts) } BOOTSTD_TEST(expo_object_attr, UT_TESTF_DM | UT_TESTF_SCAN_FDT); +/** + * struct test_iter_priv - private data for expo-iterator test + * + * @count: number of scene objects + * @menu_count: number of menus + * @fail_at: item ID at which to return an error + */ +struct test_iter_priv { + int count; + int menu_count; + int fail_at; +}; + +int h_test_iter(struct scene_obj *obj, void *vpriv) +{ + struct test_iter_priv *priv = vpriv; + + if (priv->fail_at == obj->id) + return -EINVAL; + + priv->count++; + if (obj->type == SCENEOBJT_MENU) + priv->menu_count++; + + return 0; +} + /* Check creating a scene with a menu */ static int expo_object_menu(struct unit_test_state *uts) { @@ -296,6 +323,7 @@ static int expo_object_menu(struct unit_test_state *uts) struct scene_menitem *item; int id, label_id, desc_id, key_id, pointer_id, preview_id; struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1; + struct test_iter_priv priv; struct scene *scn; struct expo *exp; ulong start_mem; @@ -382,6 +410,23 @@ static int expo_object_menu(struct unit_test_state *uts) ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y); ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE); + /* check iterating through scene items */ + memset(&priv, '\0', sizeof(priv)); + ut_assertok(expo_iter_scene_objs(exp, h_test_iter, &priv)); + ut_asserteq(7, priv.count); + ut_asserteq(1, priv.menu_count); + + /* check the iterator failing part way through iteration */ + memset(&priv, '\0', sizeof(priv)); + priv.fail_at = key_id; + ut_asserteq(-EINVAL, expo_iter_scene_objs(exp, h_test_iter, &priv)); + + /* 2 items (preview_id and the menuitem) are after key_id, 7 - 2 = 5 */ + ut_asserteq(5, priv.count); + + /* menu is first, so is still processed */ + ut_asserteq(1, priv.menu_count); + expo_destroy(exp); ut_assertok(ut_check_delta(start_mem)); -- cgit v1.3.1 From f80ebb2fe10b8e31226424d46adbedd86768a9df Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:26 -0600 Subject: expo: Move cedit test into its own file and tidy Move this test out so it can have its own file. Rename the test to use a cedit_ prefix. This allows us to drop the check for CONFIG_CMD_CEDIT in the test. Also we don't need driver model objects for this test, so drop them. Signed-off-by: Simon Glass --- test/boot/Makefile | 1 + test/boot/cedit.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ test/boot/expo.c | 43 ------------------------------------------- 3 files changed, 54 insertions(+), 43 deletions(-) create mode 100644 test/boot/cedit.c (limited to 'test/boot') diff --git a/test/boot/Makefile b/test/boot/Makefile index 22ed61c8fa0..52947580ae6 100644 --- a/test/boot/Makefile +++ b/test/boot/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o obj-$(CONFIG_FIT) += image.o obj-$(CONFIG_EXPO) += expo.o +obj-$(CONFIG_CEDIT) += cedit.o ifdef CONFIG_OF_LIVE obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o diff --git a/test/boot/cedit.c b/test/boot/cedit.c new file mode 100644 index 00000000000..f3411f734fa --- /dev/null +++ b/test/boot/cedit.c @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2023 Google LLC + * Written by Simon Glass + */ + +#include +#include +#include +#include +#include "bootstd_common.h" +#include +#include "../../boot/scene_internal.h" + +/* Check the cedit command */ +static int cedit_base(struct unit_test_state *uts) +{ + extern struct expo *cur_exp; + struct scene_obj_menu *menu; + struct scene_obj_txt *txt; + struct expo *exp; + struct scene *scn; + + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + console_record_reset_enable(); + + /* + * ^N Move down to second menu + * ^M Open menu + * ^N Move down to second item + * ^M Select item + * \e Quit + */ + console_in_puts("\x0e\x0d\x0e\x0d\e"); + ut_assertok(run_command("cedit run", 0)); + + exp = cur_exp; + scn = expo_lookup_scene_id(exp, exp->scene_id); + ut_assertnonnull(scn); + + menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE); + ut_assertnonnull(menu); + + txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); + ut_assertnonnull(txt); + ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id)); + + ut_asserteq(ID_AC_ON, menu->cur_item_id); + + return 0; +} +BOOTSTD_TEST(cedit_base, 0); diff --git a/test/boot/expo.c b/test/boot/expo.c index 458e332440c..90027409c81 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -714,46 +714,3 @@ static int expo_test_build(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(expo_test_build, UT_TESTF_DM); - -/* Check the cedit command */ -static int expo_cedit(struct unit_test_state *uts) -{ - extern struct expo *cur_exp; - struct scene_obj_menu *menu; - struct scene_obj_txt *txt; - struct expo *exp; - struct scene *scn; - - if (!IS_ENABLED(CONFIG_CMD_CEDIT)) - return -EAGAIN; - - ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); - - console_record_reset_enable(); - - /* - * ^N Move down to second menu - * ^M Open menu - * ^N Move down to second item - * ^M Select item - * \e Quit - */ - console_in_puts("\x0e\x0d\x0e\x0d\e"); - ut_assertok(run_command("cedit run", 0)); - - exp = cur_exp; - scn = expo_lookup_scene_id(exp, exp->scene_id); - ut_assertnonnull(scn); - - menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE); - ut_assertnonnull(menu); - - txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); - ut_assertnonnull(txt); - ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id)); - - ut_asserteq(ID_AC_ON, menu->cur_item_id); - - return 0; -} -BOOTSTD_TEST(expo_cedit, UT_TESTF_DM | UT_TESTF_SCAN_FDT); -- cgit v1.3.1 From d5737b3f6a0239caf2dd5578a4bc8ebfccfdee3b Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:28 -0600 Subject: expo: Tidy up the expo.py tool and usage Tidy up this tool a little: - define which arguments are needed - split the enum values out into a header file - warn if no enum values are found - display the dtc error if something goes wrong - avoid a Python traceback on error Signed-off-by: Simon Glass --- doc/develop/expo.rst | 37 +++++++++++++++++++++---------------- test/boot/files/expo_ids.h | 25 +++++++++++++++++++++++++ test/boot/files/expo_layout.dts | 23 +---------------------- test/py/tests/test_ut.py | 4 +++- tools/expo.py | 22 +++++++++++++++------- 5 files changed, 65 insertions(+), 46 deletions(-) create mode 100644 test/boot/files/expo_ids.h (limited to 'test/boot') diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index 2ac4af232da..0643283ae48 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -367,22 +367,27 @@ strings are provided inline in the nodes where they are used. :: - #define ID_PROMPT 1 - #define ID_SCENE1 2 - #define ID_SCENE1_TITLE 3 - - #define ID_CPU_SPEED 4 - #define ID_CPU_SPEED_TITLE 5 - #define ID_CPU_SPEED_1 6 - #define ID_CPU_SPEED_2 7 - #define ID_CPU_SPEED_3 8 - - #define ID_POWER_LOSS 9 - #define ID_AC_OFF 10 - #define ID_AC_ON 11 - #define ID_AC_MEMORY 12 - - #define ID_DYNAMIC_START 13 + /* this comment is parsed by the expo.py tool to insert the values below + + enum { + ZERO, + ID_PROMPT, + ID_SCENE1, + ID_SCENE1_TITLE, + + ID_CPU_SPEED, + ID_CPU_SPEED_TITLE, + ID_CPU_SPEED_1, + ID_CPU_SPEED_2, + ID_CPU_SPEED_3, + + ID_POWER_LOSS, + ID_AC_OFF, + ID_AC_ON, + ID_AC_MEMORY, + + ID_DYNAMIC_START, + */ &cedit { dynamic-start = ; diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h new file mode 100644 index 00000000000..027d44bf38c --- /dev/null +++ b/test/boot/files/expo_ids.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Sample expo screen layout (ID numbers) + */ + +enum { + ZERO, + ID_PROMPT, + + ID_SCENE1, + ID_SCENE1_TITLE, + + ID_CPU_SPEED, + ID_CPU_SPEED_TITLE, + ID_CPU_SPEED_1, + ID_CPU_SPEED_2, + ID_CPU_SPEED_3, + + ID_POWER_LOSS, + ID_AC_OFF, + ID_AC_ON, + ID_AC_MEMORY, + + ID_DYNAMIC_START, +}; diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts index 55d5c910dd5..913140bace9 100644 --- a/test/boot/files/expo_layout.dts +++ b/test/boot/files/expo_layout.dts @@ -5,28 +5,7 @@ /dts-v1/; -/* -enum { - ZERO, - ID_PROMPT, - - ID_SCENE1, - ID_SCENE1_TITLE, - - ID_CPU_SPEED, - ID_CPU_SPEED_TITLE, - ID_CPU_SPEED_1, - ID_CPU_SPEED_2, - ID_CPU_SPEED_3, - - ID_POWER_LOSS, - ID_AC_OFF, - ID_AC_ON, - ID_AC_MEMORY, - - ID_DYNAMIC_START, -}; -*/ +/* see expo_ids.h for the IDs */ / { dynamic-start = ; diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index aa1d477cd56..6a59c306322 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -285,10 +285,12 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) def setup_cedit_file(cons): infname = os.path.join(cons.config.source_dir, 'test/boot/files/expo_layout.dts') + inhname = os.path.join(cons.config.source_dir, + 'test/boot/files/expo_ids.h') expo_tool = os.path.join(cons.config.source_dir, 'tools/expo.py') outfname = 'cedit.dtb' u_boot_utils.run_and_log( - cons, f'{expo_tool} -e {infname} -l {infname} -o {outfname}') + cons, f'{expo_tool} -e {inhname} -l {infname} -o {outfname}') @pytest.mark.buildconfigspec('ut_dm') diff --git a/tools/expo.py b/tools/expo.py index c6eb87aec73..ea80c70f04e 100755 --- a/tools/expo.py +++ b/tools/expo.py @@ -69,7 +69,10 @@ def calc_ids(fname): def run_expo(args): """Run the expo program""" - ids = calc_ids(args.enum_fname) + fname = args.enum_fname or args.layout + ids = calc_ids(fname) + if not ids: + print(f"Warning: No enum ID values found in file '{fname}'") indata = tools.read_file(args.layout) @@ -88,10 +91,10 @@ def run_expo(args): with open('/tmp/asc', 'wb') as outf: outf.write(data) - proc = subprocess.run('dtc', input=data, capture_output=True, check=True) + proc = subprocess.run('dtc', input=data, capture_output=True) edtb = proc.stdout if proc.stderr: - print(proc.stderr) + print(f"Devicetree compiler error:\n{proc.stderr.decode('utf-8')}") return 1 tools.write_file(args.outfile, edtb) return 0 @@ -109,11 +112,13 @@ def parse_args(argv): args is a list of string arguments """ parser = argparse.ArgumentParser() + parser.add_argument('-D', '--debug', action='store_true', + help='Enable full debug traceback') parser.add_argument('-e', '--enum-fname', type=str, - help='C file containing enum declaration for expo items') - parser.add_argument('-l', '--layout', type=str, - help='Devicetree file source .dts for expo layout') - parser.add_argument('-o', '--outfile', type=str, + help='.dts or C file containing enum declaration for expo items') + parser.add_argument('-l', '--layout', type=str, required=True, + help='Devicetree file source .dts for expo layout (and perhaps enums)') + parser.add_argument('-o', '--outfile', type=str, required=True, help='Filename to write expo layout dtb') return parser.parse_args(argv) @@ -122,6 +127,9 @@ def start_expo(): """Start the expo program""" args = parse_args(sys.argv[1:]) + if not args.debug: + sys.tracebacklimit = 0 + ret_code = run_expo(args) sys.exit(ret_code) -- cgit v1.3.1 From 2dee81fe5f4a6427ba48fe17ff017930ddf3b4e4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:33 -0600 Subject: expo: cedit: Support writing settings to a file Support writing settings from an expo into a file in FDT format. It consists of a single node with a two properties for each sceneitem, one with tag ID chosen by the user and another for its text value. Signed-off-by: Simon Glass --- boot/cedit.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 51 ++++++++++++++++-- doc/usage/cmd/cedit.rst | 19 +++++++ include/cedit.h | 22 ++++++++ test/boot/cedit.c | 45 ++++++++++++++++ 5 files changed, 270 insertions(+), 3 deletions(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index 6c10b211454..4dd79a2263d 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -9,6 +9,7 @@ #define LOG_CATEGORY LOGC_EXPO #include +#include #include #include #include @@ -18,6 +19,15 @@ #include #include "scene_internal.h" +/** + * struct cedit_iter_priv - private data for cedit operations + * + * @buf: Buffer to use when writing settings to the devicetree + */ +struct cedit_iter_priv { + struct abuf *buf; +}; + int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) { struct scene_obj_txt *txt; @@ -182,3 +192,129 @@ int cedit_run(struct expo *exp) return 0; } + +static int check_space(int ret, struct abuf *buf) +{ + if (ret == -FDT_ERR_NOSPACE) { + if (!abuf_realloc_inc(buf, CEDIT_SIZE_INC)) + return log_msg_ret("spc", -ENOMEM); + ret = fdt_resize(abuf_data(buf), abuf_data(buf), + abuf_size(buf)); + if (ret) + return log_msg_ret("res", -EFAULT); + } + + return 0; +} + +static int h_write_settings(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + struct abuf *buf = priv->buf; + + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { + const struct scene_obj_menu *menu; + const struct scene_obj_txt *txt; + struct scene *scn = obj->scene; + const struct scene_menitem *mi; + const char *str; + char name[80]; + int ret, i; + + menu = (struct scene_obj_menu *)obj; + ret = -EAGAIN; + for (i = 0; ret && i < 2; i++) { + ret = fdt_property_u32(abuf_data(buf), obj->name, + menu->cur_item_id); + if (!i) { + ret = check_space(ret, buf); + if (ret) + return log_msg_ret("res", -ENOMEM); + } + } + /* this should not happen */ + if (ret) + return log_msg_ret("wrt", -EFAULT); + + mi = scene_menuitem_find(menu, menu->cur_item_id); + if (!mi) + return log_msg_ret("mi", -ENOENT); + + txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); + if (!txt) + return log_msg_ret("txt", -ENOENT); + + str = expo_get_str(scn->expo, txt->str_id); + if (!str) + return log_msg_ret("str", -ENOENT); + + snprintf(name, sizeof(name), "%s-str", obj->name); + ret = -EAGAIN; + for (i = 0; ret && i < 2; i++) { + ret = fdt_property_string(abuf_data(buf), name, str); + if (!i) { + ret = check_space(ret, buf); + if (ret) + return log_msg_ret("rs2", -ENOMEM); + } + } + + /* this should not happen */ + if (ret) + return log_msg_ret("wr2", -EFAULT); + + break; + } + } + + return 0; +} + +int cedit_write_settings(struct expo *exp, struct abuf *buf) +{ + struct cedit_iter_priv priv; + void *fdt; + int ret; + + abuf_init(buf); + if (!abuf_realloc(buf, CEDIT_SIZE_INC)) + return log_msg_ret("buf", -ENOMEM); + + fdt = abuf_data(buf); + ret = fdt_create(fdt, abuf_size(buf)); + if (!ret) + ret = fdt_finish_reservemap(fdt); + if (!ret) + ret = fdt_begin_node(fdt, ""); + if (!ret) + ret = fdt_begin_node(fdt, CEDIT_NODE_NAME); + if (ret) { + log_debug("Failed to start FDT (err=%d)\n", ret); + return log_msg_ret("sta", -EINVAL); + } + + /* write out the items */ + priv.buf = buf; + ret = expo_iter_scene_objs(exp, h_write_settings, &priv); + if (ret) { + log_debug("Failed to write settings (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + ret = fdt_end_node(fdt); + if (!ret) + ret = fdt_end_node(fdt); + if (!ret) + ret = fdt_finish(fdt); + if (ret) { + log_debug("Failed to finish FDT (err=%d)\n", ret); + return log_msg_ret("fin", -EINVAL); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index e98121b067b..18cc8ba191b 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -7,15 +7,28 @@ */ #include +#include #include #include #include #include +#include +#include #include #include struct expo *cur_exp; +static int check_cur_expo(void) +{ + if (!cur_exp) { + printf("No expo loaded\n"); + return -ENOENT; + } + + return 0; +} + static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -54,16 +67,46 @@ static int do_cedit_load(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *fname; + struct abuf buf; + loff_t bytes; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + fname = argv[3]; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + ret = cedit_write_settings(cur_exp, &buf); + if (ret) { + printf("Failed to write settings: %dE\n", ret); + return CMD_RET_FAILURE; + } + + if (fs_set_blk_dev(argv[1], argv[2], FS_TYPE_ANY)) + return CMD_RET_FAILURE; + + ret = fs_write(fname, map_to_sysmem(abuf_data(&buf)), 0, + abuf_size(&buf), &bytes); + if (ret) + return CMD_RET_FAILURE; + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { ofnode node; int ret; - if (!cur_exp) { - printf("No expo loaded\n"); + if (check_cur_expo()) return CMD_RET_FAILURE; - } node = ofnode_path("/bootstd/cedit-theme"); if (ofnode_valid(node)) { @@ -85,10 +128,12 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LONGHELP static char cedit_help_text[] = "load - load config editor\n" + "cedit write_fdt - write settings\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), + U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index d34a220797e..0581594831f 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -10,6 +10,7 @@ Synopis cedit load cedit run + cedit write_fdt Description ----------- @@ -38,6 +39,12 @@ Runs the default configuration-editor event loop. This is very simple, just accepting character input and moving through the objects under user control. The implementation is at `cedit_run()`. +cedit write_fdt +~~~~~~~~~~~~~~~ + +Writes the current user settings to a devicetree file. For each menu item the +selected ID and its text string are written. + Example ------- @@ -46,3 +53,15 @@ Example => cedit load hostfs - fred.dtb => cedit run + => cedit write_fdt hostfs - settings.dtb + +That results in:: + + / { + cedit-values { + cpu-speed = <0x00000006>; + cpu-speed-str = "2 GHz"; + power-loss = <0x0000000a>; + power-loss-str = "Always Off"; + }; + } diff --git a/include/cedit.h b/include/cedit.h index 851d8e83564..6086e302006 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -7,10 +7,21 @@ #ifndef __CEDIT_H #define __CEDIT_H +struct abuf; struct expo; struct scene; struct video_priv; +enum { + /* size increment for writing FDT */ + CEDIT_SIZE_INC = 1024, +}; + +/* Name of the cedit node in the devicetree */ +#define CEDIT_NODE_NAME "cedit-values" + +extern struct expo *cur_exp; + /** * cedit_arange() - Arrange objects in a configuration-editor scene * @@ -45,4 +56,15 @@ int cedit_run(struct expo *exp); int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, struct scene **scnp); +/** + * cedit_write_settings() - Write settings in FDT format + * + * Sets up an FDT with the settings + * + * @exp: Expo to write settings from + * @buf: Returns abuf containing the settings FDT (inited by this function) + * Return: 0 if OK, -ve on error + */ +int cedit_write_settings(struct expo *exp, struct abuf *buf); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index f3411f734fa..1dd78c64158 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include #include "bootstd_common.h" #include @@ -51,3 +53,46 @@ static int cedit_base(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_base, 0); + +/* Check the cedit write_fdt commands */ +static int cedit_fdt(struct unit_test_state *uts) +{ + struct video_priv *vid_priv; + extern struct expo *cur_exp; + ulong addr = 0x1000; + struct ofprop prop; + struct scene *scn; + oftree tree; + ofnode node; + void *fdt; + int i; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0)); + ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr)); + ut_assert_nextlinen("1024 bytes read"); + + fdt = map_sysmem(addr, 1024); + tree = oftree_from_fdt(fdt); + node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME); + + ut_asserteq(ID_CPU_SPEED_1, + ofnode_read_u32_default(node, "cpu-speed", 0)); + ut_asserteq_str("2 GHz", ofnode_read_string(node, "cpu-speed-str")); + ut_assert(ofnode_valid(node)); + + /* There should only be 4 properties */ + for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop); + i++, ofnode_next_property(&prop)) + ; + ut_asserteq(4, i); + + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(cedit_fdt, 0); -- cgit v1.3.1 From 472317cb12e534f56b631365987934960dfb0a3f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:34 -0600 Subject: expo: cedit: Support reading settings from a file Add a command to read cedit settings from a devicetree file. Signed-off-by: Simon Glass --- boot/cedit.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 39 +++++++++++++++++++++++++++++++++++++ doc/usage/cmd/cedit.rst | 8 ++++++++ include/cedit.h | 13 +++++++++++++ test/boot/cedit.c | 22 ++++++++++++++++++--- 5 files changed, 131 insertions(+), 3 deletions(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index 4dd79a2263d..6a74a380989 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -23,9 +23,11 @@ * struct cedit_iter_priv - private data for cedit operations * * @buf: Buffer to use when writing settings to the devicetree + * @node: Node to read from when reading settings from devicetree */ struct cedit_iter_priv { struct abuf *buf; + ofnode node; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -318,3 +320,53 @@ int cedit_write_settings(struct expo *exp, struct abuf *buf) return 0; } + +static int h_read_settings(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + ofnode node = priv->node; + + switch (obj->type) { + case SCENEOBJT_NONE: + case SCENEOBJT_IMAGE: + case SCENEOBJT_TEXT: + break; + case SCENEOBJT_MENU: { + struct scene_obj_menu *menu; + uint val; + + if (ofnode_read_u32(node, obj->name, &val)) + return log_msg_ret("rd", -ENOENT); + menu = (struct scene_obj_menu *)obj; + menu->cur_item_id = val; + + break; + } + } + + return 0; +} + +int cedit_read_settings(struct expo *exp, oftree tree) +{ + struct cedit_iter_priv priv; + ofnode root, node; + int ret; + + root = oftree_root(tree); + if (!ofnode_valid(root)) + return log_msg_ret("roo", -ENOENT); + node = ofnode_find_subnode(root, CEDIT_NODE_NAME); + if (!ofnode_valid(node)) + return log_msg_ret("pat", -ENOENT); + + /* read in the items */ + priv.node = node; + ret = expo_iter_scene_objs(exp, h_read_settings, &priv); + if (ret) { + log_debug("Failed to read settings (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index 18cc8ba191b..a155e080b1f 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -99,6 +99,43 @@ static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + const char *fname; + void *buf; + oftree tree; + ulong size; + int ret; + + if (argc < 4) + return CMD_RET_USAGE; + fname = argv[3]; + + ret = fs_load_alloc(argv[1], argv[2], argv[3], SZ_1M, 0, &buf, &size); + if (ret) { + printf("File not found\n"); + return CMD_RET_FAILURE; + } + + tree = oftree_from_fdt(buf); + if (!oftree_valid(tree)) { + free(buf); + printf("Cannot create oftree\n"); + return CMD_RET_FAILURE; + } + + ret = cedit_read_settings(cur_exp, tree); + oftree_dispose(tree); + free(buf); + if (ret) { + printf("Failed to read settings: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -128,12 +165,14 @@ static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, #ifdef CONFIG_SYS_LONGHELP static char cedit_help_text[] = "load - load config editor\n" + "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), + U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 0581594831f..0a9f620b59b 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -11,6 +11,7 @@ Synopis cedit load cedit run cedit write_fdt + cedit read_fdt Description ----------- @@ -45,6 +46,11 @@ cedit write_fdt Writes the current user settings to a devicetree file. For each menu item the selected ID and its text string are written. +cedit read_fdt +~~~~~~~~~~~~~~ + +Reads the user settings from a devicetree file and updates the cedit with those +settings. Example ------- @@ -65,3 +71,5 @@ That results in:: power-loss-str = "Always Off"; }; } + + => cedit read_fdt hostfs - settings.dtb diff --git a/include/cedit.h b/include/cedit.h index 6086e302006..bb6e87d4af7 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -7,6 +7,8 @@ #ifndef __CEDIT_H #define __CEDIT_H +#include + struct abuf; struct expo; struct scene; @@ -67,4 +69,15 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, */ int cedit_write_settings(struct expo *exp, struct abuf *buf); +/** + * cedit_read_settings() - Read settings in FDT format + * + * Read an FDT with the settings + * + * @exp: Expo to read settings into + * @tree: Tree to read from + * Return: 0 if OK, -ve on error + */ +int cedit_read_settings(struct expo *exp, oftree tree); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 1dd78c64158..659c47ed35f 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -54,11 +54,12 @@ static int cedit_base(struct unit_test_state *uts) } BOOTSTD_TEST(cedit_base, 0); -/* Check the cedit write_fdt commands */ +/* Check the cedit write_fdt and read_fdt commands */ static int cedit_fdt(struct unit_test_state *uts) { struct video_priv *vid_priv; extern struct expo *cur_exp; + struct scene_obj_menu *menu; ulong addr = 0x1000; struct ofprop prop; struct scene *scn; @@ -72,6 +73,11 @@ static int cedit_fdt(struct unit_test_state *uts) ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + /* get a menu to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0)); ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr)); ut_assert_nextlinen("1024 bytes read"); @@ -80,9 +86,9 @@ static int cedit_fdt(struct unit_test_state *uts) tree = oftree_from_fdt(fdt); node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME); - ut_asserteq(ID_CPU_SPEED_1, + ut_asserteq(ID_CPU_SPEED_2, ofnode_read_u32_default(node, "cpu-speed", 0)); - ut_asserteq_str("2 GHz", ofnode_read_string(node, "cpu-speed-str")); + ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str")); ut_assert(ofnode_valid(node)); /* There should only be 4 properties */ @@ -93,6 +99,16 @@ static int cedit_fdt(struct unit_test_state *uts) ut_assert_console_end(); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + + /* load in the settings and make sure they update */ + ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0)); + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + + ut_assertnonnull(menu); + ut_assert_console_end(); + return 0; } BOOTSTD_TEST(cedit_fdt, 0); -- cgit v1.3.1 From fc9c0e0771cad76b24f73bb64c105b6ea39721ca Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:35 -0600 Subject: expo: cedit: Support writing settings to environment vars Add a command to write cedit settings to environment variables so that they can be stored with 'saveenv'. Signed-off-by: Simon Glass --- boot/cedit.c | 97 ++++++++++++++++++++++++++++++++++++++++++------- cmd/cedit.c | 22 +++++++++++ doc/usage/cmd/cedit.rst | 25 +++++++++++++ include/cedit.h | 9 +++++ test/boot/cedit.c | 33 +++++++++++++++++ 5 files changed, 172 insertions(+), 14 deletions(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index 6a74a380989..9399c01cda9 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -24,10 +25,12 @@ * * @buf: Buffer to use when writing settings to the devicetree * @node: Node to read from when reading settings from devicetree + * @verbose: true to show writing to environment variables */ struct cedit_iter_priv { struct abuf *buf; ofnode node; + bool verbose; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -209,6 +212,30 @@ static int check_space(int ret, struct abuf *buf) return 0; } +static int get_cur_menuitem_text(const struct scene_obj_menu *menu, + const char **strp) +{ + struct scene *scn = menu->obj.scene; + const struct scene_menitem *mi; + const struct scene_obj_txt *txt; + const char *str; + + mi = scene_menuitem_find(menu, menu->cur_item_id); + if (!mi) + return log_msg_ret("mi", -ENOENT); + + txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); + if (!txt) + return log_msg_ret("txt", -ENOENT); + + str = expo_get_str(scn->expo, txt->str_id); + if (!str) + return log_msg_ret("str", -ENOENT); + *strp = str; + + return 0; +} + static int h_write_settings(struct scene_obj *obj, void *vpriv) { struct cedit_iter_priv *priv = vpriv; @@ -221,9 +248,6 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) break; case SCENEOBJT_MENU: { const struct scene_obj_menu *menu; - const struct scene_obj_txt *txt; - struct scene *scn = obj->scene; - const struct scene_menitem *mi; const char *str; char name[80]; int ret, i; @@ -243,17 +267,9 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) if (ret) return log_msg_ret("wrt", -EFAULT); - mi = scene_menuitem_find(menu, menu->cur_item_id); - if (!mi) - return log_msg_ret("mi", -ENOENT); - - txt = scene_obj_find(scn, mi->label_id, SCENEOBJT_TEXT); - if (!txt) - return log_msg_ret("txt", -ENOENT); - - str = expo_get_str(scn->expo, txt->str_id); - if (!str) - return log_msg_ret("str", -ENOENT); + ret = get_cur_menuitem_text(menu, &str); + if (ret) + return log_msg_ret("mis", ret); snprintf(name, sizeof(name), "%s-str", obj->name); ret = -EAGAIN; @@ -370,3 +386,56 @@ int cedit_read_settings(struct expo *exp, oftree tree) return 0; } + +static int h_write_settings_env(struct scene_obj *obj, void *vpriv) +{ + const struct scene_obj_menu *menu; + struct cedit_iter_priv *priv = vpriv; + char name[80], var[60]; + const char *str; + int val, ret; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + snprintf(var, sizeof(var), "c.%s", obj->name); + + if (priv->verbose) + printf("%s=%d\n", var, val); + + ret = env_set_ulong(var, val); + if (ret) + return log_msg_ret("set", ret); + + ret = get_cur_menuitem_text(menu, &str); + if (ret) + return log_msg_ret("mis", ret); + + snprintf(name, sizeof(name), "c.%s-str", obj->name); + if (priv->verbose) + printf("%s=%s\n", name, str); + + ret = env_set(name, str); + if (ret) + return log_msg_ret("st2", ret); + + return 0; +} + +int cedit_write_settings_env(struct expo *exp, bool verbose) +{ + struct cedit_iter_priv priv; + int ret; + + /* write out the items */ + priv.verbose = verbose; + ret = expo_iter_scene_objs(exp, h_write_settings_env, &priv); + if (ret) { + log_debug("Failed to write settings to env (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index a155e080b1f..85629f7b83c 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -136,6 +136,26 @@ static int do_cedit_read_fdt(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool verbose; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + verbose = argc > 1 && !strcmp(argv[1], "-v"); + + ret = cedit_write_settings_env(cur_exp, verbose); + if (ret) { + printf("Failed to write settings to environment: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -167,6 +187,7 @@ static char cedit_help_text[] = "load - load config editor\n" "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" + "cedit write_env [-v] - write settings to env vars\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -174,5 +195,6 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), + U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 0a9f620b59b..426470a82ac 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -12,6 +12,7 @@ Synopis cedit run cedit write_fdt cedit read_fdt + cedit write_env [-v] Description ----------- @@ -52,6 +53,19 @@ cedit read_fdt Reads the user settings from a devicetree file and updates the cedit with those settings. +cedit write_env +~~~~~~~~~~~~~~~ + +Writes the settings to environment variables. For each menu item the selected +ID and its text string are written, similar to: + + setenv c. + setenv c.-str + +The `-v` flag enables verbose mode, where each variable is printed before it is +set. + + Example ------- @@ -73,3 +87,14 @@ That results in:: } => cedit read_fdt hostfs - settings.dtb + +This shows settings being stored in the environment:: + + => cedit write_env -v + => print + ... + c.cpu-speed=6 + c.cpu-speed-str=2 GHz + c.power-loss=10 + c.power-loss-str=Always Off + ... diff --git a/include/cedit.h b/include/cedit.h index bb6e87d4af7..f261207e209 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -80,4 +80,13 @@ int cedit_write_settings(struct expo *exp, struct abuf *buf); */ int cedit_read_settings(struct expo *exp, oftree tree); +/** + * cedit_write_settings_env() - Write settings to envrionment variables + * + * @exp: Expo to write settings from + * @verbose: true to print each var as it is set + * Return: 0 if OK, -ve on error + */ +int cedit_write_settings_env(struct expo *exp, bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 659c47ed35f..26a69f0323f 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -112,3 +113,35 @@ static int cedit_fdt(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_fdt, 0); + +/* Check the cedit write_env command */ +static int cedit_env(struct unit_test_state *uts) +{ + struct video_priv *vid_priv; + extern struct expo *cur_exp; + struct scene_obj_menu *menu; + struct scene *scn; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + /* get a menu to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + + ut_assertok(run_command("cedit write_env -v", 0)); + ut_assert_nextlinen("c.cpu-speed=7"); + ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz"); + ut_assert_nextlinen("c.power-loss=10"); + ut_assert_nextlinen("c.power-loss-str=Always Off"); + ut_assert_console_end(); + + ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); + ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); + + return 0; +} +BOOTSTD_TEST(cedit_env, 0); -- cgit v1.3.1 From bcf2b7202e960e7fb3df8d5e8ed0d6fa00a5a9fa Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:36 -0600 Subject: expo: cedit: Support reading settings from environment vars Add a command to read cedit settings from environment variables so that they can be restored as part of the environment. Signed-off-by: Simon Glass --- boot/cedit.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ cmd/cedit.c | 22 ++++++++++++++++++++++ doc/usage/cmd/cedit.rst | 19 +++++++++++++++++++ include/cedit.h | 8 ++++++++ test/boot/cedit.c | 12 +++++++++++- 5 files changed, 105 insertions(+), 1 deletion(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index 9399c01cda9..e3f6dc00399 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -439,3 +439,48 @@ int cedit_write_settings_env(struct expo *exp, bool verbose) return 0; } + +static int h_read_settings_env(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + struct scene_obj_menu *menu; + char var[60]; + int val, ret; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + snprintf(var, sizeof(var), "c.%s", obj->name); + + val = env_get_ulong(var, 10, 0); + if (priv->verbose) + printf("%s=%d\n", var, val); + if (!val) + return log_msg_ret("get", -ENOENT); + + /* + * note that no validation is done here, to make sure the ID is valid + * and actually points to a menu item + */ + menu->cur_item_id = val; + + return 0; +} + +int cedit_read_settings_env(struct expo *exp, bool verbose) +{ + struct cedit_iter_priv priv; + int ret; + + /* write out the items */ + priv.verbose = verbose; + ret = expo_iter_scene_objs(exp, h_read_settings_env, &priv); + if (ret) { + log_debug("Failed to read settings from env (err=%d)\n", ret); + return log_msg_ret("set", ret); + } + + return 0; +} diff --git a/cmd/cedit.c b/cmd/cedit.c index 85629f7b83c..b2548f44b57 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -156,6 +156,26 @@ static int do_cedit_write_env(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + bool verbose; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + verbose = argc > 1 && !strcmp(argv[1], "-v"); + + ret = cedit_read_settings_env(cur_exp, verbose); + if (ret) { + printf("Failed to read settings from environment: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -187,6 +207,7 @@ static char cedit_help_text[] = "load - load config editor\n" "cedit read_fdt - read settings\n" "cedit write_fdt - write settings\n" + "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -195,6 +216,7 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt), U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), + U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 426470a82ac..1f92b7306a7 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -13,6 +13,7 @@ Synopis cedit write_fdt cedit read_fdt cedit write_env [-v] + cedit read_env [-v] Description ----------- @@ -53,6 +54,16 @@ cedit read_fdt Reads the user settings from a devicetree file and updates the cedit with those settings. +cedit read_env +~~~~~~~~~~~~~~ + +Reads the settings from the environment variables. For each menu item ``, +cedit looks for a variable called `c.` with the ID of the selected menu +item. + +The `-v` flag enables verbose mode, where each variable is printed after it is +read. + cedit write_env ~~~~~~~~~~~~~~~ @@ -91,6 +102,10 @@ That results in:: This shows settings being stored in the environment:: => cedit write_env -v + c.cpu-speed=7 + c.cpu-speed-str=2.5 GHz + c.power-loss=12 + c.power-loss-str=Memory => print ... c.cpu-speed=6 @@ -98,3 +113,7 @@ This shows settings being stored in the environment:: c.power-loss=10 c.power-loss-str=Always Off ... + + => cedit read_env -v + c.cpu-speed=7 + c.power-loss=12 diff --git a/include/cedit.h b/include/cedit.h index f261207e209..fe10e6c829c 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -89,4 +89,12 @@ int cedit_read_settings(struct expo *exp, oftree tree); */ int cedit_write_settings_env(struct expo *exp, bool verbose); +/* + * cedit_read_settings_env() - Read settings from the environment + * + * @exp: Expo to read settings into + * @verbose: true to print each var before it is read + */ +int cedit_read_settings_env(struct expo *exp, bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 26a69f0323f..7cf0c3e4e93 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -114,7 +114,7 @@ static int cedit_fdt(struct unit_test_state *uts) } BOOTSTD_TEST(cedit_fdt, 0); -/* Check the cedit write_env command */ +/* Check the cedit write_env and read_env commands */ static int cedit_env(struct unit_test_state *uts) { struct video_priv *vid_priv; @@ -142,6 +142,16 @@ static int cedit_env(struct unit_test_state *uts) ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0)); ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str")); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + + ut_assertok(run_command("cedit read_env -v", 0)); + ut_assert_nextlinen("c.cpu-speed=7"); + ut_assert_nextlinen("c.power-loss=10"); + ut_assert_console_end(); + + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + return 0; } BOOTSTD_TEST(cedit_env, 0); -- cgit v1.3.1 From eb6c71b56282d3054dbffb83793e7d2c6745578e Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:37 -0600 Subject: expo: cedit: Support writing settings to CMOS RAM Add a command to write cedit settings to CMOS RAM so that it can be preserved across a reboot. This uses a simple bit-encoding, where each field has a 'bit position' and a 'bit length' in the schema. Signed-off-by: Simon Glass --- boot/cedit.c | 137 +++++++++++++++++++++++++++++++++++++++- boot/expo_build.c | 7 +- cmd/cedit.c | 36 +++++++++++ doc/develop/expo.rst | 13 ++++ doc/usage/cmd/cedit.rst | 22 +++++++ include/cedit.h | 13 ++++ include/expo.h | 6 +- test/boot/cedit.c | 30 +++++++++ test/boot/files/expo_layout.dts | 5 ++ 9 files changed, 266 insertions(+), 3 deletions(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index e3f6dc00399..725745aba55 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -15,22 +15,37 @@ #include #include #include +#include #include +#include #include #include #include "scene_internal.h" +enum { + CMOS_MAX_BITS = 2048, + CMOS_MAX_BYTES = CMOS_MAX_BITS / 8, +}; + +#define CMOS_BYTE(bit) ((bit) / 8) +#define CMOS_BIT(bit) ((bit) % 8) + /** * struct cedit_iter_priv - private data for cedit operations * * @buf: Buffer to use when writing settings to the devicetree * @node: Node to read from when reading settings from devicetree * @verbose: true to show writing to environment variables + * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it + * will be written + * @value: Value bits for CMOS RAM. This is the actual value written */ struct cedit_iter_priv { struct abuf *buf; ofnode node; bool verbose; + u8 *mask; + u8 *value; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -445,7 +460,7 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) struct cedit_iter_priv *priv = vpriv; struct scene_obj_menu *menu; char var[60]; - int val, ret; + int val; if (obj->type != SCENEOBJT_MENU) return 0; @@ -484,3 +499,123 @@ int cedit_read_settings_env(struct expo *exp, bool verbose) return 0; } + +/** + * get_cur_menuitem_seq() - Get the sequence number of a menu's current item + * + * Enumerates the items of a menu (0, 1, 2) and returns the sequence number of + * the currently selected item. If the first item is selected, this returns 0; + * if the second, 1; etc. + * + * @menu: Menu to check + * Return: Sequence number on success, else -ve error value + */ +static int get_cur_menuitem_seq(const struct scene_obj_menu *menu) +{ + const struct scene_menitem *mi; + int seq, found; + + seq = 0; + found = -1; + list_for_each_entry(mi, &menu->item_head, sibling) { + if (mi->id == menu->cur_item_id) { + found = seq; + break; + } + seq++; + } + + if (found == -1) + return log_msg_ret("nf", -ENOENT); + + return found; +} + +static int h_write_settings_cmos(struct scene_obj *obj, void *vpriv) +{ + const struct scene_obj_menu *menu; + struct cedit_iter_priv *priv = vpriv; + int val, ret; + uint i, seq; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + val = menu->cur_item_id; + + ret = get_cur_menuitem_seq(menu); + if (ret < 0) + return log_msg_ret("cur", ret); + seq = ret; + log_debug("%s: seq=%d\n", menu->obj.name, seq); + + /* figure out where to place this item */ + if (!obj->bit_length) + return log_msg_ret("len", -EINVAL); + if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS) + return log_msg_ret("bit", -E2BIG); + + for (i = 0; i < obj->bit_length; i++, seq >>= 1) { + uint bitnum = obj->start_bit + i; + + priv->mask[CMOS_BYTE(bitnum)] |= 1 << CMOS_BIT(bitnum); + if (seq & 1) + priv->value[CMOS_BYTE(bitnum)] |= BIT(CMOS_BIT(bitnum)); + log_debug("bit %x %x %x\n", bitnum, + priv->mask[CMOS_BYTE(bitnum)], + priv->value[CMOS_BYTE(bitnum)]); + } + + return 0; +} + +int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose) +{ + struct cedit_iter_priv priv; + int ret, i, count, first, last; + + /* write out the items */ + priv.mask = calloc(1, CMOS_MAX_BYTES); + if (!priv.mask) + return log_msg_ret("mas", -ENOMEM); + priv.value = calloc(1, CMOS_MAX_BYTES); + if (!priv.value) { + free(priv.mask); + return log_msg_ret("val", -ENOMEM); + } + + ret = expo_iter_scene_objs(exp, h_write_settings_cmos, &priv); + if (ret) { + log_debug("Failed to write CMOS (err=%d)\n", ret); + ret = log_msg_ret("set", ret); + goto done; + } + + /* write the data to the RTC */ + first = CMOS_MAX_BYTES; + last = -1; + for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { + if (priv.mask[i]) { + log_debug("Write byte %x: %x\n", i, priv.value[i]); + ret = rtc_write8(dev, i, priv.value[i]); + if (ret) { + ret = log_msg_ret("wri", ret); + goto done; + } + count++; + first = min(first, i); + last = max(last, i); + } + } + if (verbose) { + printf("Write %d bytes from offset %x to %x\n", count, first, + last); + } + +done: + free(priv.mask); + free(priv.value); + return ret; +} diff --git a/boot/expo_build.c b/boot/expo_build.c index e8c4a40d3f0..bb33cc2a33f 100644 --- a/boot/expo_build.c +++ b/boot/expo_build.c @@ -294,7 +294,7 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) { struct scene_obj *obj; const char *type; - u32 id; + u32 id, val; int ret; log_debug("- object %s\n", ofnode_get_name(node)); @@ -313,6 +313,11 @@ static int obj_build(struct build_info *info, ofnode node, struct scene *scn) if (ret) return log_msg_ret("bld", ret); + if (!ofnode_read_u32(node, "start-bit", &val)) + obj->start_bit = val; + if (!ofnode_read_u32(node, "bit-length", &val)) + obj->bit_length = val; + return 0; } diff --git a/cmd/cedit.c b/cmd/cedit.c index b2548f44b57..95d5c22c2f7 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,39 @@ static int do_cedit_read_env(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + bool verbose = false; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + verbose = true; + argc--; + argv++; + } + + if (argc > 1) + ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev); + else + ret = uclass_first_device_err(UCLASS_RTC, &dev); + if (ret) { + printf("Failed to get RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + + if (cedit_write_settings_cmos(cur_exp, dev, verbose)) { + printf("Failed to write settings to CMOS\n"); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -209,6 +243,7 @@ static char cedit_help_text[] = "cedit write_fdt - write settings\n" "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" + "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -218,5 +253,6 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), + U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/develop/expo.rst b/doc/develop/expo.rst index fde91494799..61b6855c72f 100644 --- a/doc/develop/expo.rst +++ b/doc/develop/expo.rst @@ -317,6 +317,18 @@ id Specifies the ID of the object. This is used when referring to the object. +Where CMOS RAM is used for reading and writing settings, the following +additional properties are required: + +start-bit + Specifies the first bit in the CMOS RAM to use for this setting. For a RAM + with 0x100 bytes, there are 0x800 bit locations. For example, register 0x80 + holds bits 0x400 to 0x407. + +bit-length + Specifies the number of CMOS RAM bits to use for this setting. The bits + extend from `start-bit` to `start-bit + bit-length - 1`. Note that the bits + must be contiguous. Menu nodes have the following additional properties: @@ -474,6 +486,7 @@ Some ideas for future work: - Support curses for proper serial-terminal menus - Add support for large menus which need to scroll - Add support for reading and writing configuration settings with cedit +- Update expo.py tool to check for overlapping names and CMOS locations .. Simon Glass .. 7-Oct-22 diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 1f92b7306a7..3d6f26e631d 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -14,6 +14,7 @@ Synopis cedit read_fdt cedit write_env [-v] cedit read_env [-v] + cedit write_cmos [-v] [dev] Description ----------- @@ -76,6 +77,18 @@ ID and its text string are written, similar to: The `-v` flag enables verbose mode, where each variable is printed before it is set. +cedit write_cmos +~~~~~~~~~~~~~~~~ + +Writes the settings to locations in the CMOS RAM. The locations used are +specified by the schema. See `expo_format_`. + +The `-v` flag enables verbose mode, which shows which CMOS locations were +updated. + +Normally the first RTC device is used to hold the data. You can specify a +different device by name using the `dev` parameter. + Example ------- @@ -117,3 +130,12 @@ This shows settings being stored in the environment:: => cedit read_env -v c.cpu-speed=7 c.power-loss=12 + +This shows writing to CMOS RAM. Notice that the bytes at 80 and 84 change:: + + => rtc read 80 8 + 00000080: 00 00 00 00 00 2f 2a 08 ...../*. + => cedit write_cmos + Write 2 bytes from offset 80 to 84 + => rtc read 80 8 + 00000080: 01 00 00 00 08 2f 2a 08 ...../*. diff --git a/include/cedit.h b/include/cedit.h index fe10e6c829c..2970965b5f6 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -97,4 +97,17 @@ int cedit_write_settings_env(struct expo *exp, bool verbose); */ int cedit_read_settings_env(struct expo *exp, bool verbose); +/** + * cedit_write_settings_cmos() - Write settings to CMOS RAM + * + * Write settings to the defined places in CMOS RAM + * + * @exp: Expo to write settings from + * @dev: UCLASS_RTC device containing space for this information + * Returns 0 if OK, -ve on error + * @verbose: true to print a summary at the end + */ +int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose); + #endif /* __CEDIT_H */ diff --git a/include/expo.h b/include/expo.h index da151074d20..a2b3a71c159 100644 --- a/include/expo.h +++ b/include/expo.h @@ -187,6 +187,8 @@ enum scene_obj_flags_t { * @type: Type of this object * @dim: Dimensions for this object * @flags: Flags for this object + * @bit_length: Number of bits used for this object in CMOS RAM + * @start_bit: Start bit to use for this object in CMOS RAM * @sibling: Node to link this object to its siblings */ struct scene_obj { @@ -195,7 +197,9 @@ struct scene_obj { uint id; enum scene_obj_t type; struct scene_dim dim; - int flags; + u8 flags; + u8 bit_length; + u16 start_bit; struct list_head sibling; }; diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 7cf0c3e4e93..010aae615b9 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -155,3 +155,33 @@ static int cedit_env(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(cedit_env, 0); + +/* Check the cedit write_cmos and read_cmos commands */ +static int cedit_cmos(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu, *menu2; + struct video_priv *vid_priv; + extern struct expo *cur_exp; + struct scene *scn; + + console_record_reset_enable(); + ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0)); + + ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn)); + + /* get the menus to fiddle with */ + menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU); + ut_assertnonnull(menu); + menu->cur_item_id = ID_CPU_SPEED_2; + + menu2 = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU); + ut_assertnonnull(menu2); + menu2->cur_item_id = ID_AC_MEMORY; + + ut_assertok(run_command("cedit write_cmos -v", 0)); + ut_assert_nextlinen("Write 2 bytes from offset 80 to 84"); + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(cedit_cmos, 0); diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts index 913140bace9..cb2a674d9d5 100644 --- a/test/boot/files/expo_layout.dts +++ b/test/boot/files/expo_layout.dts @@ -38,6 +38,9 @@ /* IDs for the menu items */ item-id = ; + + start-bit = <0x400>; + bit-length = <2>; }; power-loss { @@ -49,6 +52,8 @@ "Memory"; item-id = ; + start-bit = <0x422>; + bit-length = <2>; }; }; }; -- cgit v1.3.1 From cfc402db3954d7c852c322b232ad6d8842af6bf1 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 14 Aug 2023 16:40:38 -0600 Subject: expo: cedit: Support reading settings from CMOS RAM Add a command to read edit settings from CMOS RAM, using the cedit definition to indicate which registers and bits are used. Signed-off-by: Simon Glass --- boot/cedit.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++ boot/scene_internal.h | 12 ++++++ boot/scene_menu.c | 16 ++++++++ cmd/cedit.c | 36 ++++++++++++++++++ doc/usage/cmd/cedit.rst | 9 ++++- include/cedit.h | 12 ++++++ test/boot/cedit.c | 11 ++++++ 7 files changed, 194 insertions(+), 1 deletion(-) (limited to 'test/boot') diff --git a/boot/cedit.c b/boot/cedit.c index 725745aba55..73645f70b6c 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -39,6 +39,7 @@ enum { * @mask: Mask bits for the CMOS RAM. If a bit is set the byte containing it * will be written * @value: Value bits for CMOS RAM. This is the actual value written + * @dev: RTC device to write to */ struct cedit_iter_priv { struct abuf *buf; @@ -46,6 +47,7 @@ struct cedit_iter_priv { bool verbose; u8 *mask; u8 *value; + struct udevice *dev; }; int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) @@ -619,3 +621,100 @@ done: free(priv.value); return ret; } + +static int h_read_settings_cmos(struct scene_obj *obj, void *vpriv) +{ + struct cedit_iter_priv *priv = vpriv; + const struct scene_menitem *mi; + struct scene_obj_menu *menu; + int val, ret; + uint i; + + if (obj->type != SCENEOBJT_MENU) + return 0; + + menu = (struct scene_obj_menu *)obj; + + /* figure out where to place this item */ + if (!obj->bit_length) + return log_msg_ret("len", -EINVAL); + if (obj->start_bit + obj->bit_length > CMOS_MAX_BITS) + return log_msg_ret("bit", -E2BIG); + + val = 0; + for (i = 0; i < obj->bit_length; i++) { + uint bitnum = obj->start_bit + i; + uint offset = CMOS_BYTE(bitnum); + + /* read the byte if not already read */ + if (!priv->mask[offset]) { + ret = rtc_read8(priv->dev, offset); + if (ret < 0) + return log_msg_ret("rea", ret); + priv->value[offset] = ret; + + /* mark it as read */ + priv->mask[offset] = 0xff; + } + + if (priv->value[offset] & BIT(CMOS_BIT(bitnum))) + val |= BIT(i); + log_debug("bit %x %x\n", bitnum, val); + } + + /* update the current item */ + mi = scene_menuitem_find_seq(menu, val); + if (!mi) + return log_msg_ret("seq", -ENOENT); + + menu->cur_item_id = mi->id; + log_debug("Update menu %d cur_item_id %d\n", menu->obj.id, mi->id); + + return 0; +} + +int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose) +{ + struct cedit_iter_priv priv; + int ret, i, count, first, last; + + /* read in the items */ + priv.mask = calloc(1, CMOS_MAX_BYTES); + if (!priv.mask) + return log_msg_ret("mas", -ENOMEM); + priv.value = calloc(1, CMOS_MAX_BYTES); + if (!priv.value) { + free(priv.mask); + return log_msg_ret("val", -ENOMEM); + } + priv.dev = dev; + + ret = expo_iter_scene_objs(exp, h_read_settings_cmos, &priv); + if (ret) { + log_debug("Failed to read CMOS (err=%d)\n", ret); + ret = log_msg_ret("set", ret); + goto done; + } + + /* read the data to the RTC */ + first = CMOS_MAX_BYTES; + last = -1; + for (i = 0, count = 0; i < CMOS_MAX_BYTES; i++) { + if (priv.mask[i]) { + log_debug("Read byte %x: %x\n", i, priv.value[i]); + count++; + first = min(first, i); + last = max(last, i); + } + } + if (verbose) { + printf("Read %d bytes from offset %x to %x\n", count, first, + last); + } + +done: + free(priv.mask); + free(priv.value); + return ret; +} diff --git a/boot/scene_internal.h b/boot/scene_internal.h index 23e29cb349b..695a907dc6a 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -234,4 +234,16 @@ int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, int id); +/** + * scene_menuitem_find_seq() - Find the menu item at a sequential position + * + * This numbers the items from 0 and returns the seq'th one + * + * @menu: Menu to check + * @seq: Sequence number to look for + * Return: menu item if found, else NULL + */ +struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, + uint seq); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 602fe24580a..e0dcd0a4e04 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -46,6 +46,22 @@ struct scene_menitem *scene_menuitem_find(const struct scene_obj_menu *menu, return NULL; } +struct scene_menitem *scene_menuitem_find_seq(const struct scene_obj_menu *menu, + uint seq) +{ + struct scene_menitem *item; + uint i; + + i = 0; + list_for_each_entry(item, &menu->item_head, sibling) { + if (i == seq) + return item; + i++; + } + + return NULL; +} + /** * update_pointers() - Update the pointer object and handle highlights * diff --git a/cmd/cedit.c b/cmd/cedit.c index 95d5c22c2f7..2ff284f4cde 100644 --- a/cmd/cedit.c +++ b/cmd/cedit.c @@ -210,6 +210,40 @@ static int do_cedit_write_cmos(struct cmd_tbl *cmdtp, int flag, int argc, return 0; } +static int do_cedit_read_cmos(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + struct udevice *dev; + bool verbose = false; + int ret; + + if (check_cur_expo()) + return CMD_RET_FAILURE; + + if (argc > 1 && !strcmp(argv[1], "-v")) { + verbose = true; + argc--; + argv++; + } + + if (argc > 1) + ret = uclass_get_device_by_name(UCLASS_RTC, argv[1], &dev); + else + ret = uclass_first_device_err(UCLASS_RTC, &dev); + if (ret) { + printf("Failed to get RTC device: %dE\n", ret); + return CMD_RET_FAILURE; + } + + ret = cedit_read_settings_cmos(cur_exp, dev, verbose); + if (ret) { + printf("Failed to read settings from CMOS: %dE\n", ret); + return CMD_RET_FAILURE; + } + + return 0; +} + static int do_cedit_run(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { @@ -243,6 +277,7 @@ static char cedit_help_text[] = "cedit write_fdt - write settings\n" "cedit read_env [-v] - read settings from env vars\n" "cedit write_env [-v] - write settings to env vars\n" + "cedit read_cmos [-v] [dev] - read settings from CMOS RAM\n" "cedit write_cmos [-v] [dev] - write settings to CMOS RAM\n" "cedit run - run config editor"; #endif /* CONFIG_SYS_LONGHELP */ @@ -253,6 +288,7 @@ U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env), U_BOOT_SUBCMD_MKENT(write_env, 2, 1, do_cedit_write_env), + U_BOOT_SUBCMD_MKENT(read_cmos, 2, 1, do_cedit_read_cmos), U_BOOT_SUBCMD_MKENT(write_cmos, 2, 1, do_cedit_write_cmos), U_BOOT_SUBCMD_MKENT(run, 1, 1, do_cedit_run), ); diff --git a/doc/usage/cmd/cedit.rst b/doc/usage/cmd/cedit.rst index 3d6f26e631d..f415b48699e 100644 --- a/doc/usage/cmd/cedit.rst +++ b/doc/usage/cmd/cedit.rst @@ -135,7 +135,14 @@ This shows writing to CMOS RAM. Notice that the bytes at 80 and 84 change:: => rtc read 80 8 00000080: 00 00 00 00 00 2f 2a 08 ...../*. - => cedit write_cmos + => cedit write_cmos -v Write 2 bytes from offset 80 to 84 => rtc read 80 8 00000080: 01 00 00 00 08 2f 2a 08 ...../*. + => cedit read_cmos -v + Read 2 bytes from offset 80 to 84 + +Here is an example with the device specified:: + + => cedit write_cmos rtc@43 + => diff --git a/include/cedit.h b/include/cedit.h index 2970965b5f6..f43cafa5aa2 100644 --- a/include/cedit.h +++ b/include/cedit.h @@ -110,4 +110,16 @@ int cedit_read_settings_env(struct expo *exp, bool verbose); int cedit_write_settings_cmos(struct expo *exp, struct udevice *dev, bool verbose); +/** + * cedit_read_settings_cmos() - Read settings from CMOS RAM + * + * Read settings from the defined places in CMO RAM + * + * @exp: Expo to read settings into + * @dev: RTC device to read settings from + * @verbose: true to print a summary at the end + */ +int cedit_read_settings_cmos(struct expo *exp, struct udevice *dev, + bool verbose); + #endif /* __CEDIT_H */ diff --git a/test/boot/cedit.c b/test/boot/cedit.c index 010aae615b9..ab2b8a1f9ff 100644 --- a/test/boot/cedit.c +++ b/test/boot/cedit.c @@ -182,6 +182,17 @@ static int cedit_cmos(struct unit_test_state *uts) ut_assert_nextlinen("Write 2 bytes from offset 80 to 84"); ut_assert_console_end(); + /* reset the expo */ + menu->cur_item_id = ID_CPU_SPEED_1; + menu2->cur_item_id = ID_AC_OFF; + + ut_assertok(run_command("cedit read_cmos -v", 0)); + ut_assert_nextlinen("Read 2 bytes from offset 80 to 84"); + ut_assert_console_end(); + + ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id); + ut_asserteq(ID_AC_MEMORY, menu2->cur_item_id); + return 0; } BOOTSTD_TEST(cedit_cmos, 0); -- cgit v1.3.1 From c3867e2e98165106fc5f378eeaf3354cd18e87bd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 24 Aug 2023 13:55:39 -0600 Subject: bootflow: Show an empty filename when there is none At present 'bootflow list' shows for the filename when it is not present. Show an empty string instead, since that is more user-friendly. Signed-off-by: Simon Glass --- cmd/bootflow.c | 2 +- test/boot/bootflow.c | 22 ++++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) (limited to 'test/boot') diff --git a/cmd/bootflow.c b/cmd/bootflow.c index 3c3abaf8a3b..300ad3aaa76 100644 --- a/cmd/bootflow.c +++ b/cmd/bootflow.c @@ -71,7 +71,7 @@ static void show_bootflow(int index, struct bootflow *bflow, bool errors) printf("%3x %-11s %-6s %-9.9s %4x %-25.25s %s\n", index, bflow->method->name, bootflow_state_get_name(bflow->state), bflow->dev ? dev_get_uclass_name(dev_get_parent(bflow->dev)) : - "(none)", bflow->part, bflow->name, bflow->fname); + "(none)", bflow->part, bflow->name, bflow->fname ?: ""); if (errors) report_bootflow_err(bflow, bflow->err); } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 8a4e090e9bc..649237a9e22 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -167,21 +167,22 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assert_nextline("Seq Method State Uclass Part Name Filename"); ut_assert_nextlinen("---"); ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':"); - ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); - ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':"); - ut_assert_nextline(" 2 extlinux media mmc 0 mmc1.bootdev.whole "); + ut_assert_nextline(" 2 extlinux media mmc 0 mmc1.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-2: No such file or directory"); - ut_assert_nextline(" 3 efi media mmc 0 mmc1.bootdev.whole "); + ut_assert_nextline(" 3 efi media mmc 0 mmc1.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-2: No such file or directory"); ut_assert_nextline(" 4 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); ut_assert_nextline(" 5 efi fs mmc 1 mmc1.bootdev.part_1 efi/boot/bootsbox.efi"); ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':"); - ut_assert_skip_to_line(" 3f efi media mmc 0 mmc0.bootdev.whole "); + ut_assert_skip_to_line( + " 3f efi media mmc 0 mmc0.bootdev.whole "); ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported"); ut_assert_nextline("No more bootdevs"); ut_assert_nextlinen("---"); @@ -192,10 +193,11 @@ static int bootflow_cmd_scan_e(struct unit_test_state *uts) ut_assert_nextline("Showing all bootflows"); ut_assert_nextline("Seq Method State Uclass Part Name Filename"); ut_assert_nextlinen("---"); - ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); - ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); - ut_assert_skip_to_line(" 4 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); - ut_assert_skip_to_line(" 3f efi media mmc 0 mmc0.bootdev.whole "); + ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole "); + ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole "); + ut_assert_skip_to_line( + " 4 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf"); + ut_assert_skip_to_line(" 3f efi media mmc 0 mmc0.bootdev.whole "); ut_assert_nextlinen("---"); ut_assert_nextline("(64 bootflows, 1 valid)"); ut_assert_console_end(); @@ -384,7 +386,7 @@ static int bootflow_system(struct unit_test_state *uts) console_record_reset_enable(); ut_assertok(run_command("bootflow scan -lH", 0)); ut_assert_skip_to_line( - " 0 efi_mgr ready (none) 0 "); + " 0 efi_mgr ready (none) 0 "); ut_assert_skip_to_line("No more bootdevs"); ut_assert_skip_to_line("(2 bootflows, 2 valid)"); ut_assert_console_end(); -- cgit v1.3.1 From 2b9adcaca2055d891775c59096147f702f6423bd Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 24 Aug 2023 13:55:40 -0600 Subject: bootstd: test: Allow binding and using any mmc device We currently use mmc4 for tests. Update the function which sets this up so that it can handle any device. Signed-off-by: Simon Glass --- test/boot/bootflow.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'test/boot') diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 649237a9e22..54a878c4bd5 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -508,21 +508,24 @@ static int bootflow_cmd_boot(struct unit_test_state *uts) BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT); /** - * prep_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian + * prep_mmc_bootdev() - Set up an mmc bootdev so we can access other distros * * @uts: Unit test state + * @mmc_dev: MMC device to use, e.g. "mmc4" * Returns 0 on success, -ve on failure */ -static int prep_mmc4_bootdev(struct unit_test_state *uts) +static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev) { - static const char *order[] = {"mmc2", "mmc1", "mmc4", NULL}; + const char *order[] = {"mmc2", "mmc1", mmc_dev, NULL}; struct udevice *dev, *bootstd; struct bootstd_priv *std; const char **old_order; - ofnode node; + ofnode root, node; /* Enable the mmc4 node since we need a second bootflow */ - node = ofnode_path("/mmc4"); + root = oftree_root(oftree_default()); + node = ofnode_find_subnode(root, mmc_dev); + ut_assert(ofnode_valid(node)); ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false)); /* Enable the script bootmeth too */ @@ -530,7 +533,7 @@ static int prep_mmc4_bootdev(struct unit_test_state *uts) ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_script), "bootmeth_script", 0, ofnode_null(), &dev)); - /* Change the order to include mmc4 */ + /* Change the order to include the device */ std = dev_get_priv(bootstd); old_order = std->bootdev_order; std->bootdev_order = order; @@ -545,6 +548,19 @@ static int prep_mmc4_bootdev(struct unit_test_state *uts) return 0; } +/** + * prep_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian + * + * @uts: Unit test state + * Returns 0 on success, -ve on failure + */ +static int prep_mmc4_bootdev(struct unit_test_state *uts) +{ + ut_assertok(prep_mmc_bootdev(uts, "mmc4")); + + return 0; +} + /* Check 'bootflow menu' to select a bootflow */ static int bootflow_cmd_menu(struct unit_test_state *uts) { -- cgit v1.3.1 From d08db02d2d3d5ccf365e423b0c3dfb6fe311abd6 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 24 Aug 2023 13:55:41 -0600 Subject: bootstd: Add a test for bootmeth_cros The ChromiumOS bootmeth has no tests at present. Before adding more features. add a basic test. This creates a disk which can be scanned by the bootmeth, so make sure things work. It is quite rudimentary, since the kernel is faked, the root disk is missing and there is no cmdline stored. Enable the bootmeth for snow so it can build the unit test. Signed-off-by: Simon Glass --- arch/sandbox/dts/test.dts | 9 +++ configs/snow_defconfig | 1 + test/boot/bootflow.c | 33 ++++++++++- test/py/tests/test_ut.py | 142 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 183 insertions(+), 2 deletions(-) (limited to 'test/boot') diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts index 084cb88a232..b48456aebee 100644 --- a/arch/sandbox/dts/test.dts +++ b/arch/sandbox/dts/test.dts @@ -39,6 +39,8 @@ mmc1 = "/mmc1"; mmc2 = "/mmc2"; mmc3 = "/mmc3"; + mmc4 = "/mmc4"; + mmc5 = "/mmc5"; pci0 = &pci0; pci1 = &pci1; pci2 = &pci2; @@ -1055,6 +1057,13 @@ filename = "mmc4.img"; }; + /* This is used for ChromiumOS tests */ + mmc5 { + status = "disabled"; + compatible = "sandbox,mmc"; + filename = "mmc5.img"; + }; + pch { compatible = "sandbox,pch"; }; diff --git a/configs/snow_defconfig b/configs/snow_defconfig index bb066a64599..22ec8e5141f 100644 --- a/configs/snow_defconfig +++ b/configs/snow_defconfig @@ -29,6 +29,7 @@ CONFIG_DEBUG_UART=y CONFIG_FIT=y CONFIG_FIT_BEST_MATCH=y CONFIG_BOOTSTD_FULL=y +CONFIG_BOOTMETH_CROS=y CONFIG_DISTRO_DEFAULTS=y CONFIG_SILENT_CONSOLE=y CONFIG_BLOBLIST=y diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 54a878c4bd5..ae34370981c 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -27,6 +27,7 @@ DECLARE_GLOBAL_DATA_PTR; +extern U_BOOT_DRIVER(bootmeth_cros); extern U_BOOT_DRIVER(bootmeth_script); static int inject_response(struct unit_test_state *uts) @@ -514,7 +515,8 @@ BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT); * @mmc_dev: MMC device to use, e.g. "mmc4" * Returns 0 on success, -ve on failure */ -static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev) +static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev, + bool bind_cros) { const char *order[] = {"mmc2", "mmc1", mmc_dev, NULL}; struct udevice *dev, *bootstd; @@ -533,6 +535,13 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev) ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_script), "bootmeth_script", 0, ofnode_null(), &dev)); + /* Enable the cros bootmeth if needed */ + if (bind_cros) { + ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd)); + ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros), + "cros", 0, ofnode_null(), &dev)); + } + /* Change the order to include the device */ std = dev_get_priv(bootstd); old_order = std->bootdev_order; @@ -556,7 +565,7 @@ static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev) */ static int prep_mmc4_bootdev(struct unit_test_state *uts) { - ut_assertok(prep_mmc_bootdev(uts, "mmc4")); + ut_assertok(prep_mmc_bootdev(uts, "mmc4", false)); return 0; } @@ -963,3 +972,23 @@ static int bootflow_cmdline(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(bootflow_cmdline, 0); + +/* Test ChromiumOS bootmeth */ +static int bootflow_cros(struct unit_test_state *uts) +{ + ut_assertok(prep_mmc_bootdev(uts, "mmc5", true)); + ut_assertok(run_command("bootflow list", 0)); + + ut_assert_nextlinen("Showing all"); + ut_assert_nextlinen("Seq"); + ut_assert_nextlinen("---"); + ut_assert_nextlinen(" 0 extlinux"); + ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.whole "); + ut_assert_nextlinen("---"); + ut_assert_skip_to_line("(2 bootflows, 2 valid)"); + + ut_assert_console_end(); + + return 0; +} +BOOTSTD_TEST(bootflow_cros, 0); diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index 640129018c1..82932a662bf 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. +import collections import getpass import gzip import os @@ -282,6 +283,146 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) copy_prepared_image(cons, mmc_dev, fname) +def setup_cros_image(cons): + """Create a 20MB disk image with ChromiumOS partitions""" + Partition = collections.namedtuple('part', 'start,size,name') + parts = {} + disk_data = None + + def pack_kernel(cons, arch, kern, dummy): + """Pack a kernel containing some fake data + + Args: + cons (ConsoleBase): Console to use + arch (str): Architecture to use ('x86' or 'arm') + kern (str): Filename containing kernel + dummy (str): Dummy filename to use for config and bootloader + + Return: + bytes: Packed-kernel data + """ + kern_part = os.path.join(cons.config.result_dir, 'kern-part-{arch}.bin') + u_boot_utils.run_and_log( + cons, + f'futility vbutil_kernel --pack {kern_part} ' + '--keyblock doc/chromium/files/devkeys/kernel.keyblock ' + '--signprivate doc/chromium/files/devkeys/kernel_data_key.vbprivk ' + f'--version 1 --config {dummy} --bootloader {dummy} ' + f'--vmlinuz {kern}') + + with open(kern_part, 'rb') as inf: + kern_part_data = inf.read() + return kern_part_data + + def set_part_data(partnum, data): + """Set the contents of a disk partition + + This updates disk_data by putting data in the right place + + Args: + partnum (int): Partition number to set + data (bytes): Data for that partition + """ + nonlocal disk_data + + start = parts[partnum].start * sect_size + disk_data = disk_data[:start] + data + disk_data[start + len(data):] + + mmc_dev = 5 + fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img') + u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname) + #mnt = os.path.join(cons.config.persistent_data_dir, 'mnt') + #mkdir_cond(mnt) + u_boot_utils.run_and_log(cons, f'cgpt create {fname}') + + uuid_state = 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' + uuid_kern = 'fe3a2a5d-4f32-41a7-b725-accc3285a309' + uuid_root = '3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec' + uuid_rwfw = 'cab6e88e-abf3-4102-a07a-d4bb9be3c1d3' + uuid_reserved = '2e0a753d-9e48-43b0-8337-b15192cb1b5e' + uuid_efi = 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b' + + ptr = 40 + + # Number of sectors in 1MB + sect_size = 512 + sect_1mb = (1 << 20) // sect_size + + required_parts = [ + {'num': 0xb, 'label':'RWFW', 'type': uuid_rwfw, 'size': '1'}, + {'num': 6, 'label':'KERN_C', 'type': uuid_kern, 'size': '1'}, + {'num': 7, 'label':'ROOT_C', 'type': uuid_root, 'size': '1'}, + {'num': 9, 'label':'reserved', 'type': uuid_reserved, 'size': '1'}, + {'num': 0xa, 'label':'reserved', 'type': uuid_reserved, 'size': '1'}, + + {'num': 2, 'label':'KERN_A', 'type': uuid_kern, 'size': '1M'}, + {'num': 4, 'label':'KERN_B', 'type': uuid_kern, 'size': '1M'}, + + {'num': 8, 'label':'OEM', 'type': uuid_state, 'size': '1M'}, + {'num': 0xc, 'label':'EFI-SYSTEM', 'type': uuid_efi, 'size': '1M'}, + + {'num': 5, 'label':'ROOT_B', 'type': uuid_root, 'size': '1'}, + {'num': 3, 'label':'ROOT_A', 'type': uuid_root, 'size': '1'}, + {'num': 1, 'label':'STATE', 'type': uuid_state, 'size': '1M'}, + ] + + for part in required_parts: + size_str = part['size'] + if 'M' in size_str: + size = int(size_str[:-1]) * sect_1mb + else: + size = int(size_str) + u_boot_utils.run_and_log( + cons, + f"cgpt add -i {part['num']} -b {ptr} -s {size} -t {part['type']} {fname}") + ptr += size + + u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}') + out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}') + '''We expect something like this: + 8239 2048 1 Basic data + 45 2048 2 ChromeOS kernel + 8238 1 3 ChromeOS rootfs + 2093 2048 4 ChromeOS kernel + 8237 1 5 ChromeOS rootfs + 41 1 6 ChromeOS kernel + 42 1 7 ChromeOS rootfs + 4141 2048 8 Basic data + 43 1 9 ChromeOS reserved + 44 1 10 ChromeOS reserved + 40 1 11 ChromeOS firmware + 6189 2048 12 EFI System Partition + ''' + + # Create a dict (indexed by partition number) containing the above info + for line in out.splitlines(): + start, size, num, name = line.split(maxsplit=3) + parts[int(num)] = Partition(int(start), int(size), name) + + dummy = os.path.join(cons.config.result_dir, 'dummy.txt') + with open(dummy, 'wb') as outf: + outf.write(b'dummy\n') + + # For now we just use dummy kernels. This limits testing to just detecting + # a signed kernel. We could add support for the x86 data structures so that + # testing could cover getting the cmdline, setup.bin and other pieces. + kern = os.path.join(cons.config.result_dir, 'kern.bin') + with open(kern, 'wb') as outf: + outf.write(b'kernel\n') + + with open(fname, 'rb') as inf: + disk_data = inf.read() + + # put x86 kernel in partition 2 and arm one in partition 4 + set_part_data(2, pack_kernel(cons, 'x86', kern, dummy)) + set_part_data(4, pack_kernel(cons, 'arm', kern, dummy)) + + with open(fname, 'wb') as outf: + outf.write(disk_data) + + return fname + + def setup_cedit_file(cons): infname = os.path.join(cons.config.source_dir, 'test/boot/files/expo_layout.dts') @@ -329,6 +470,7 @@ def test_ut_dm_init_bootstd(u_boot_console): setup_bootflow_image(u_boot_console) setup_bootmenu_image(u_boot_console) setup_cedit_file(u_boot_console) + setup_cros_image(u_boot_console) # Restart so that the new mmc1.img is picked up u_boot_console.restart_uboot() -- cgit v1.3.1 From 71f634b822ae6613c43fc960d5afbe5b5d728fb4 Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Thu, 24 Aug 2023 13:55:45 -0600 Subject: bootstd: cros: Allow detection of any kernel partition The existing ChromiumOS bootmeth only supports reading a single kernel partition, either 2 or 4. In fact there are normally two options available. Use the GUID to detect kernel partitions, with the BOOTMETHF_ANY_PART flag, so that bootstd does not require a valid filesystem before calling the bootmeth. Tidy up and improve the logging while we are here. Signed-off-by: Simon Glass Suggested-by: Alper Nebi Yasak [trini: Add missing select of PARTITION_TYPE_GUID] Signed-off-by: Tom Rini --- boot/Kconfig | 3 +++ boot/bootmeth_cros.c | 48 ++++++++++++++++++++++++++++++------------------ test/boot/bootflow.c | 5 +++-- 3 files changed, 36 insertions(+), 20 deletions(-) (limited to 'test/boot') diff --git a/boot/Kconfig b/boot/Kconfig index 5e2d4286aea..86ccfd78031 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -466,6 +466,9 @@ config BOOTMETH_CROS bool "Bootdev support for Chromium OS" depends on X86 || ARM || SANDBOX default y if !ARM + select EFI_PARTITION + select PARTITION_TYPE_GUID + select PARTITION_UUIDS help Enables support for booting Chromium OS using bootdevs. This uses the kernel A slot and obtains the kernel command line from the parameters diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c index 1776fb1838c..20e0b1e89c3 100644 --- a/boot/bootmeth_cros.c +++ b/boot/bootmeth_cros.c @@ -16,16 +16,19 @@ #include #include #include +#include #include #include #include #include #include "bootmeth_cros.h" +static const efi_guid_t cros_kern_type = PARTITION_CROS_KERNEL; + /* * Layout of the ChromeOS kernel * - * Partitions 2 and 4 contain kernels + * Partitions 2 and 4 contain kernels with type GUID_CROS_KERNEL * * Contents are: * @@ -145,13 +148,25 @@ static int scan_part(struct udevice *blk, int partnum, { struct blk_desc *desc = dev_get_uclass_plat(blk); struct vb2_keyblock *hdr; + struct uuid type; ulong num_blks; int ret; + if (!partnum) + return log_msg_ret("efi", -ENOENT); + ret = part_get_info(desc, partnum, info); if (ret) return log_msg_ret("part", ret); + /* Check for kernel partition type */ + log_debug("part %x: type=%s\n", partnum, info->type_guid); + if (uuid_str_to_bin(info->type_guid, (u8 *)&type, UUID_STR_FORMAT_GUID)) + return log_msg_ret("typ", -EINVAL); + + if (memcmp(&cros_kern_type, &type, sizeof(type))) + return log_msg_ret("typ", -ENOEXEC); + /* Make a buffer for the header information */ num_blks = PROBE_SIZE >> desc->log2blksz; log_debug("Reading header, blk=%s, start=%lx, blocks=%lx\n", @@ -167,6 +182,7 @@ static int scan_part(struct udevice *blk, int partnum, if (memcmp(VB2_KEYBLOCK_MAGIC, hdr->magic, VB2_KEYBLOCK_MAGIC_SIZE)) { free(hdr); + log_debug("no magic\n"); return -ENOENT; } @@ -340,24 +356,16 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) struct vb2_keyblock *hdr; const char *uuid = NULL; struct cros_priv *priv; - int part, ret; - - log_debug("starting, part=%d\n", bflow->part); + int ret; - /* We consider the whole disk, not any one partition */ - if (bflow->part) - return log_msg_ret("max", -ENOENT); + log_debug("starting, part=%x\n", bflow->part); - /* Check partition 2 then 4 */ - part = 2; - ret = scan_part(bflow->blk, part, &info, &hdr); + /* Check for kernel partitions */ + ret = scan_part(bflow->blk, bflow->part, &info, &hdr); if (ret) { - part = 4; - ret = scan_part(bflow->blk, part, &info, &hdr); - if (ret) - return log_msg_ret("scan", ret); + log_debug("- scan failed: err=%d\n", ret); + return log_msg_ret("scan", ret); } - bflow->part = part; priv = malloc(sizeof(struct cros_priv)); if (!priv) { @@ -366,8 +374,8 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) } bflow->bootmeth_priv = priv; - log_info("Selected partition %d, header at %lx\n", bflow->part, - (ulong)map_to_sysmem(hdr)); + log_debug("Selected partition %d, header at %lx\n", bflow->part, + (ulong)map_to_sysmem(hdr)); /* Grab a few things from the preamble */ preamble = (void *)hdr + hdr->keyblock_size; @@ -381,8 +389,11 @@ static int cros_read_bootflow(struct udevice *dev, struct bootflow *bflow) ret = cros_read_info(bflow, uuid, preamble); preamble = NULL; free(hdr); - if (ret) + if (ret) { + free(priv->info_buf); + free(priv); return log_msg_ret("inf", ret); + } bflow->size = priv->body_size; bflow->state = BOOTFLOWST_READY; @@ -437,6 +448,7 @@ static int cros_bootmeth_bind(struct udevice *dev) struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); plat->desc = "ChromiumOS boot"; + plat->flags = BOOTMETHF_ANY_PART; return 0; } diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index ae34370981c..1ff2320c036 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -983,9 +983,10 @@ static int bootflow_cros(struct unit_test_state *uts) ut_assert_nextlinen("Seq"); ut_assert_nextlinen("---"); ut_assert_nextlinen(" 0 extlinux"); - ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.whole "); + ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.part_2 "); + ut_assert_nextlinen(" 2 cros ready mmc 4 mmc5.bootdev.part_4 "); ut_assert_nextlinen("---"); - ut_assert_skip_to_line("(2 bootflows, 2 valid)"); + ut_assert_skip_to_line("(3 bootflows, 3 valid)"); ut_assert_console_end(); -- cgit v1.3.1