"Das U-Boot" Source Tree

x86: coreboot: Allow building an expo for editing CMOS config

Coreboot provides the CMOS layout in the tables it passes to U-Boot.
Use that to build an editor for the CMOS settings.

Signed-off-by: Simon Glass <sjg@chromium.org>

authored by

Simon Glass and committed by
Tom Rini
ae3b5928 e25c34dd

+405 -1
+3
boot/Makefile
··· 59 59 60 60 obj-$(CONFIG_$(PHASE_)EXPO) += expo.o scene.o expo_build.o 61 61 obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o 62 + ifdef CONFIG_COREBOOT_SYSINFO 63 + obj-$(CONFIG_$(SPL_TPL_)EXPO) += expo_build_cb.o 64 + endif 62 65 63 66 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE) += vbe.o 64 67 obj-$(CONFIG_$(PHASE_)BOOTMETH_VBE_REQUEST) += vbe_request.o
+245
boot/expo_build_cb.c
··· 1 + // SPDX-License-Identifier: GPL-2.0+ 2 + /* 3 + * Building an expo from an FDT description 4 + * 5 + * Copyright 2022 Google LLC 6 + * Written by Simon Glass <sjg@chromium.org> 7 + */ 8 + 9 + #define LOG_CATEGORY LOGC_EXPO 10 + 11 + #include <cedit.h> 12 + #include <ctype.h> 13 + #include <errno.h> 14 + #include <expo.h> 15 + #include <log.h> 16 + #include <malloc.h> 17 + #include <vsprintf.h> 18 + #include <asm/cb_sysinfo.h> 19 + 20 + /** 21 + * struct build_info - Information to use when building 22 + */ 23 + struct build_info { 24 + const struct cb_cmos_option_table *tab; 25 + struct cedit_priv *priv; 26 + }; 27 + 28 + /** 29 + * convert_to_title() - Convert text to 'title' format and allocate a string 30 + * 31 + * Converts "this_is_a_test" to "This is a test" so it looks better 32 + * 33 + * @text: Text to convert 34 + * Return: Allocated string, or NULL if out of memory 35 + */ 36 + static char *convert_to_title(const char *text) 37 + { 38 + int len = strlen(text); 39 + char *buf, *s; 40 + 41 + buf = malloc(len + 1); 42 + if (!buf) 43 + return NULL; 44 + 45 + for (s = buf; *text; s++, text++) { 46 + if (s == buf) 47 + *s = toupper(*text); 48 + else if (*text == '_') 49 + *s = ' '; 50 + else 51 + *s = *text; 52 + } 53 + *s = '\0'; 54 + 55 + return buf; 56 + } 57 + 58 + /** 59 + * menu_build() - Build a menu and add it to a scene 60 + * 61 + * See doc/developer/expo.rst for a description of the format 62 + * 63 + * @info: Build information 64 + * @entry: CMOS entry to build a menu for 65 + * @scn: Scene to add the menu to 66 + * @objp: Returns the object pointer 67 + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format 68 + * error, -ENOENT if there is a references to a non-existent string 69 + */ 70 + static int menu_build(struct build_info *info, 71 + const struct cb_cmos_entries *entry, struct scene *scn, 72 + struct scene_obj **objp) 73 + { 74 + struct scene_obj_menu *menu; 75 + const void *ptr, *end; 76 + uint menu_id; 77 + char *title; 78 + int ret, i; 79 + 80 + ret = scene_menu(scn, entry->name, 0, &menu); 81 + if (ret < 0) 82 + return log_msg_ret("men", ret); 83 + menu_id = ret; 84 + 85 + title = convert_to_title(entry->name); 86 + if (!title) 87 + return log_msg_ret("con", -ENOMEM); 88 + 89 + /* Set the title */ 90 + ret = scene_txt_str(scn, "title", 0, 0, title, NULL); 91 + if (ret < 0) 92 + return log_msg_ret("tit", ret); 93 + menu->title_id = ret; 94 + 95 + end = (void *)info->tab + info->tab->size; 96 + for (ptr = (void *)info->tab + info->tab->header_length, i = 0; 97 + ptr < end; i++) { 98 + const struct cb_cmos_enums *enums = ptr; 99 + struct scene_menitem *item; 100 + uint label; 101 + 102 + ptr += enums->size; 103 + if (enums->tag != CB_TAG_OPTION_ENUM || 104 + enums->config_id != entry->config_id) 105 + continue; 106 + 107 + ret = scene_txt_str(scn, enums->text, 0, 0, enums->text, NULL); 108 + if (ret < 0) 109 + return log_msg_ret("tit", ret); 110 + label = ret; 111 + 112 + ret = scene_menuitem(scn, menu_id, simple_xtoa(i), 0, 0, label, 113 + 0, 0, 0, &item); 114 + if (ret < 0) 115 + return log_msg_ret("mi", ret); 116 + item->value = enums->value; 117 + } 118 + *objp = &menu->obj; 119 + 120 + return 0; 121 + } 122 + 123 + /** 124 + * scene_build() - Build a scene and all its objects 125 + * 126 + * See doc/developer/expo.rst for a description of the format 127 + * 128 + * @info: Build information 129 + * @scn: Scene to add the object to 130 + * Returns: 0 if OK, -ENOMEM if out of memory, -EINVAL if there is a format 131 + * error, -ENOENT if there is a references to a non-existent string 132 + */ 133 + static int scene_build(struct build_info *info, struct expo *exp) 134 + { 135 + struct scene_obj_menu *menu; 136 + const void *ptr, *end; 137 + struct scene_obj *obj; 138 + struct scene *scn; 139 + uint label, menu_id; 140 + int ret; 141 + 142 + ret = scene_new(exp, "cmos", 0, &scn); 143 + if (ret < 0) 144 + return log_msg_ret("scn", ret); 145 + 146 + ret = scene_txt_str(scn, "title", 0, 0, "CMOS RAM settings", NULL); 147 + if (ret < 0) 148 + return log_msg_ret("add", ret); 149 + scn->title_id = ret; 150 + 151 + ret = scene_txt_str(scn, "prompt", 0, 0, 152 + "UP and DOWN to choose, ENTER to select", NULL); 153 + if (ret < 0) 154 + return log_msg_ret("add", ret); 155 + 156 + end = (void *)info->tab + info->tab->size; 157 + for (ptr = (void *)info->tab + info->tab->header_length; ptr < end;) { 158 + const struct cb_cmos_entries *entry; 159 + const struct cb_record *rec = ptr; 160 + 161 + entry = ptr; 162 + ptr += rec->size; 163 + if (rec->tag != CB_TAG_OPTION) 164 + continue; 165 + switch (entry->config) { 166 + case 'e': 167 + ret = menu_build(info, entry, scn, &obj); 168 + break; 169 + default: 170 + continue; 171 + } 172 + if (ret < 0) 173 + return log_msg_ret("add", ret); 174 + 175 + obj->start_bit = entry->bit; 176 + obj->bit_length = entry->length; 177 + } 178 + 179 + ret = scene_menu(scn, "save", EXPOID_SAVE, &menu); 180 + if (ret < 0) 181 + return log_msg_ret("men", ret); 182 + menu_id = ret; 183 + 184 + ret = scene_txt_str(scn, "save", 0, 0, "Save and exit", NULL); 185 + if (ret < 0) 186 + return log_msg_ret("sav", ret); 187 + label = ret; 188 + ret = scene_menuitem(scn, menu_id, "save", 0, 0, label, 189 + 0, 0, 0, NULL); 190 + if (ret < 0) 191 + return log_msg_ret("mi", ret); 192 + 193 + ret = scene_menu(scn, "nosave", EXPOID_DISCARD, &menu); 194 + if (ret < 0) 195 + return log_msg_ret("men", ret); 196 + menu_id = ret; 197 + 198 + ret = scene_txt_str(scn, "nosave", 0, 0, "Exit without saving", NULL); 199 + if (ret < 0) 200 + return log_msg_ret("nos", ret); 201 + label = ret; 202 + ret = scene_menuitem(scn, menu_id, "exit", 0, 0, label, 203 + 0, 0, 0, NULL); 204 + if (ret < 0) 205 + return log_msg_ret("mi", ret); 206 + 207 + return 0; 208 + } 209 + 210 + static int build_it(struct build_info *info, struct expo **expp) 211 + { 212 + struct expo *exp; 213 + int ret; 214 + 215 + ret = expo_new("coreboot", NULL, &exp); 216 + if (ret) 217 + return log_msg_ret("exp", ret); 218 + expo_set_dynamic_start(exp, EXPOID_BASE_ID); 219 + 220 + ret = scene_build(info, exp); 221 + if (ret < 0) 222 + return log_msg_ret("scn", ret); 223 + 224 + *expp = exp; 225 + 226 + return 0; 227 + } 228 + 229 + int cb_expo_build(struct expo **expp) 230 + { 231 + struct build_info info; 232 + struct expo *exp; 233 + int ret; 234 + 235 + info.tab = lib_sysinfo.option_table; 236 + if (!info.tab) 237 + return log_msg_ret("tab", -ENOENT); 238 + 239 + ret = build_it(&info, &exp); 240 + if (ret) 241 + return log_msg_ret("bui", ret); 242 + *expp = exp; 243 + 244 + return 0; 245 + }
+28
cmd/cedit.c
··· 67 67 return 0; 68 68 } 69 69 70 + #ifdef CONFIG_COREBOOT_SYSINFO 71 + static int do_cedit_cb_load(struct cmd_tbl *cmdtp, int flag, int argc, 72 + char *const argv[]) 73 + { 74 + struct expo *exp; 75 + int ret; 76 + 77 + if (argc > 1) 78 + return CMD_RET_USAGE; 79 + 80 + ret = cb_expo_build(&exp); 81 + if (ret) { 82 + printf("Failed to build expo: %dE\n", ret); 83 + return CMD_RET_FAILURE; 84 + } 85 + 86 + cur_exp = exp; 87 + 88 + return 0; 89 + } 90 + #endif /* CONFIG_COREBOOT_SYSINFO */ 91 + 70 92 static int do_cedit_write_fdt(struct cmd_tbl *cmdtp, int flag, int argc, 71 93 char *const argv[]) 72 94 { ··· 271 293 272 294 U_BOOT_LONGHELP(cedit, 273 295 "load <interface> <dev[:part]> <filename> - load config editor\n" 296 + #ifdef CONFIG_COREBOOT_SYSINFO 297 + "cb_load - load coreboot CMOS editor\n" 298 + #endif 274 299 "cedit read_fdt <i/f> <dev[:part]> <filename> - read settings\n" 275 300 "cedit write_fdt <i/f> <dev[:part]> <filename> - write settings\n" 276 301 "cedit read_env [-v] - read settings from env vars\n" ··· 281 306 282 307 U_BOOT_CMD_WITH_SUBCMDS(cedit, "Configuration editor", cedit_help_text, 283 308 U_BOOT_SUBCMD_MKENT(load, 5, 1, do_cedit_load), 309 + #ifdef CONFIG_COREBOOT_SYSINFO 310 + U_BOOT_SUBCMD_MKENT(cb_load, 5, 1, do_cedit_cb_load), 311 + #endif 284 312 U_BOOT_SUBCMD_MKENT(read_fdt, 5, 1, do_cedit_read_fdt), 285 313 U_BOOT_SUBCMD_MKENT(write_fdt, 5, 1, do_cedit_write_fdt), 286 314 U_BOOT_SUBCMD_MKENT(read_env, 2, 1, do_cedit_read_env),
+6
doc/board/coreboot/coreboot.rst
··· 182 182 boot as a coreboot payload, based on a known-good build of coreboot. 183 183 184 184 To update the `coreboot.rom` file which is used, see ``tools/Dockerfile`` 185 + 186 + Editing CMOS RAM settings 187 + ------------------------- 188 + 189 + U-Boot supports creating a configuration editor to edit coreboot CMOS-RAM 190 + settings. See :ref:`cedit_cb_load`.
+1 -1
doc/develop/cedit.rst
··· 172 172 173 173 For now, reading and writing settings is not automatic. See the 174 174 :doc:`../usage/cmd/cedit` for how to do this on the command line or in a 175 - script. 175 + script. For x86 devices, see :ref:`cedit_cb_load`.
+3
doc/usage/cmd/cbcmos.rst
··· 40 40 Checksum 6600 written 41 41 => cbc check 42 42 => 43 + 44 + See also :ref:`cedit_cb_load` which shows an example that includes the 45 + configuration editor.
+76
doc/usage/cmd/cedit.rst
··· 18 18 cedit write_env [-v] 19 19 cedit read_env [-v] 20 20 cedit write_cmos [-v] [dev] 21 + cedit cb_load 21 22 22 23 Description 23 24 ----------- ··· 92 93 Normally the first RTC device is used to hold the data. You can specify a 93 94 different device by name using the `dev` parameter. 94 95 96 + .. _cedit_cb_load: 97 + 98 + cedit cb_load 99 + ~~~~~~~~~~~~~ 100 + 101 + This is supported only on x86 devices booted from coreboot. It creates a new 102 + configuration editor which can be used to edit CMOS settings. 95 103 96 104 Example 97 105 ------- ··· 158 166 159 167 => cedit write_cmos rtc@43 160 168 => 169 + 170 + This example shows editing coreboot CMOS-RAM settings. A script could be used 171 + to automate this:: 172 + 173 + => cbsysinfo 174 + Coreboot table at 500, size 5c4, records 1d (dec 29), decoded to 000000007dce3f40, forwarded to 000000007ff9a000 175 + 176 + CPU KHz : 0 177 + Serial I/O port: 00000000 178 + base : 00000000 179 + pointer : 000000007ff9a370 180 + type : 1 181 + base : 000003f8 182 + baud : 0d115200 183 + regwidth : 1 184 + input_hz : 0d1843200 185 + PCI addr : 00000010 186 + Mem ranges : 7 187 + id: type || base || size 188 + 0: 10:table 0000000000000000 0000000000001000 189 + 1: 01:ram 0000000000001000 000000000009f000 190 + 2: 02:reserved 00000000000a0000 0000000000060000 191 + 3: 01:ram 0000000000100000 000000007fe6d000 192 + 4: 10:table 000000007ff6d000 0000000000093000 193 + 5: 02:reserved 00000000fec00000 0000000000001000 194 + 6: 02:reserved 00000000ff800000 0000000000800000 195 + option_table: 000000007ff9a018 196 + Bit Len Cfg ID Name 197 + 0 180 r 0 reserved_memory 198 + 180 1 e 4 boot_option 0:Fallback 1:Normal 199 + 184 4 h 0 reboot_counter 200 + 190 8 r 0 reserved_century 201 + 1b8 8 r 0 reserved_ibm_ps2_century 202 + 1c0 1 e 1 power_on_after_fail 0:Disable 1:Enable 203 + 1c4 4 e 6 debug_level 5:Notice 6:Info 7:Debug 8:Spew 204 + 1d0 80 r 0 vbnv 205 + 3f0 10 h 0 check_sum 206 + CMOS start : 1c0 207 + CMOS end : 1cf 208 + CMOS csum loc: 3f0 209 + VBNV start : ffffffff 210 + VBNV size : ffffffff 211 + ... 212 + Unimpl. : 10 37 40 213 + 214 + Check that the CMOS RAM checksum is correct, then create a configuration editor 215 + and load the settings from CMOS RAM:: 216 + 217 + => cbcmos check 218 + => cedit cb 219 + => cedit read_cmos 220 + 221 + Now run the cedit. In this case the user selected 'save' so `cedit run` returns 222 + success:: 223 + 224 + => if cedit run; then cedit write_cmos -v; fi 225 + Write 2 bytes from offset 30 to 38 226 + => echo $? 227 + 0 228 + 229 + Update the checksum in CMOS RAM:: 230 + 231 + => cbcmos check 232 + Checksum 6100 error: calculated 7100 233 + => cbcmos update 234 + Checksum 7100 written 235 + => cbcmos check 236 + =>
+8
include/expo.h
··· 762 762 */ 763 763 int expo_build(ofnode root, struct expo **expp); 764 764 765 + /** 766 + * cb_expo_build() - Build an expo for coreboot CMOS RAM 767 + * 768 + * @expp: Returns the expo created 769 + * Return: 0 if OK, -ve on error 770 + */ 771 + int cb_expo_build(struct expo **expp); 772 + 765 773 #endif /*__EXPO_H */
+35
test/cmd/coreboot.c
··· 6 6 * Written by Simon Glass <sjg@chromium.org> 7 7 */ 8 8 9 + #include <cedit.h> 9 10 #include <command.h> 10 11 #include <dm.h> 12 + #include <expo.h> 11 13 #include <rtc.h> 14 + #include <test/cedit-test.h> 12 15 #include <test/cmd.h> 13 16 #include <test/test.h> 14 17 #include <test/ut.h> 18 + #include "../../boot/scene_internal.h" 15 19 16 20 enum { 17 21 CSUM_LOC = 0x3f0 / 8, ··· 82 86 return 0; 83 87 } 84 88 CMD_TEST(test_cmd_cbcmos, UTF_CONSOLE); 89 + 90 + /* test 'cedit cb_load' command */ 91 + static int test_cmd_cedit_cb_load(struct unit_test_state *uts) 92 + { 93 + struct scene_obj_menu *menu; 94 + struct video_priv *vid_priv; 95 + struct scene_obj_txt *txt; 96 + struct scene *scn; 97 + struct expo *exp; 98 + int scn_id; 99 + 100 + ut_assertok(run_command("cedit cb_load", 0)); 101 + ut_assertok(run_command("cedit read_cmos", 0)); 102 + ut_assert_console_end(); 103 + 104 + exp = cur_exp; 105 + scn_id = cedit_prepare(exp, &vid_priv, &scn); 106 + ut_assert(scn_id > 0); 107 + ut_assertnonnull(scn); 108 + 109 + /* just do a very basic test that the first menu is present */ 110 + menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE); 111 + ut_assertnonnull(menu); 112 + 113 + txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE); 114 + ut_assertnonnull(txt); 115 + ut_asserteq_str("Boot option", expo_get_str(exp, txt->str_id)); 116 + 117 + return 0; 118 + } 119 + CMD_TEST(test_cmd_cedit_cb_load, UTF_CONSOLE);