diff options
Diffstat (limited to 'boot')
-rw-r--r-- | boot/Kconfig | 77 | ||||
-rw-r--r-- | boot/Makefile | 3 | ||||
-rw-r--r-- | boot/bootflow_internal.h | 11 | ||||
-rw-r--r-- | boot/bootflow_menu.c | 382 | ||||
-rw-r--r-- | boot/bootm.c | 27 | ||||
-rw-r--r-- | boot/bootm_os.c | 11 | ||||
-rw-r--r-- | boot/bootmeth-uclass.c | 25 | ||||
-rw-r--r-- | boot/bootmeth_android.c | 1 | ||||
-rw-r--r-- | boot/bootmeth_efi.c | 1 | ||||
-rw-r--r-- | boot/bootmeth_pxe.c | 1 | ||||
-rw-r--r-- | boot/bootmeth_rauc.c | 432 | ||||
-rw-r--r-- | boot/cedit.c | 163 | ||||
-rw-r--r-- | boot/expo.c | 72 | ||||
-rw-r--r-- | boot/fdt_support.c | 17 | ||||
-rw-r--r-- | boot/image-android.c | 11 | ||||
-rw-r--r-- | boot/image-board.c | 56 | ||||
-rw-r--r-- | boot/image-fdt.c | 96 | ||||
-rw-r--r-- | boot/image-pre-load.c | 1 | ||||
-rw-r--r-- | boot/image.c | 1 | ||||
-rw-r--r-- | boot/pxe_utils.c | 4 | ||||
-rw-r--r-- | boot/scene.c | 477 | ||||
-rw-r--r-- | boot/scene_internal.h | 71 | ||||
-rw-r--r-- | boot/scene_menu.c | 169 | ||||
-rw-r--r-- | boot/scene_textedit.c | 61 | ||||
-rw-r--r-- | boot/scene_textline.c | 47 |
25 files changed, 1699 insertions, 518 deletions
diff --git a/boot/Kconfig b/boot/Kconfig index 30eb5b328d7..2ff6f003738 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -11,6 +11,17 @@ config ANDROID_BOOT_IMAGE This enables support for booting images which use the Android image format header. +config ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR + bool "Android Boot Image ignore addr" + default n + help + This ignore kernel/ramdisk load addr specified in androidboot header. + + There is a concern on exposing the whole memory to image loading is + dangerous. Also, since it's not always possible to change the load + addr by repacking the boot.img (mainly due to AVB signature mismatch), + we need a way to use kernel_addr_r and ramdisk_addr_r. + config TIMESTAMP bool "Show image date and time when displaying image information" default y if CMD_DATE @@ -855,6 +866,56 @@ config EXPO The expo can be presented in graphics form using a vidconsole, or in text form on a serial console. +config BOOTMETH_RAUC + bool "Bootdev support for RAUC A/B systems" + depends on CMDLINE + select BOOTMETH_GLOBAL + select HUSH_PARSER + help + Enables support for booting RAUC A/B systems from MMC devices. This + makes the bootdevs look for a 'boot.scr.uimg' or 'boot.scr' in the + respective boot partitions, describing how to boot the distro. + +if BOOTMETH_RAUC + +config BOOTMETH_RAUC_BOOT_ORDER + string "RAUC boot order" + default "A B" + help + Sets the default boot order. This must be list of space-separated + strings naming the individual boot slots. Each entry in this string + should correspond to an existing slot on the target's flash device. + +config BOOTMETH_RAUC_PARTITIONS + string "RAUC boot and root partitions indexes" + default "1,2 3,4" + help + Sets the partition indexes of boot and root slots. This must be a list + of comma-separated pair values, which in turn are separated by spaces. + The first value in pair is for the boot partition and second for the + root partition. + +config BOOTMETH_RAUC_DEFAULT_TRIES + int "RAUC slot default tries" + default 3 + help + Sets how many times a slot should be tried booting, before considering + it to be bad. + +config BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES + bool "Reset slot tries when all RAUC slots have zero tries left" + default y + help + When all slots have zero tries left or no valid slot was found, reset + to the default boot order set by BOOTMETH_RAUC_BOOT_ORDER and set the + slot tries to their default value specified by + BOOTMETH_RAUC_DEFAULT_TRIES. + + This prevents a system from remaining in an unbootable state, after + all slot tries were decremented to zero. + +endif # BOOTMETH_RAUC + config BOOTMETH_SANDBOX def_bool y depends on SANDBOX @@ -1789,6 +1850,16 @@ config OF_BOARD_SETUP board-specific information in the device tree for use by the OS. The device tree is then passed to the OS. +config OF_BOARD_SETUP_EXTENDED + bool "Set up latest board-specific details in device tree before boot" + imply OF_BOARD_SETUP + help + This causes U-Boot to call ft_board_setup_ex() before booting into + the Operating System. Similar function as ft_board_setup(). However, + its modifications are not overwritten by other system changes and are + applied to the device tree as the very last step before boot. + The device tree is then passed to the OS. + config OF_SYSTEM_SETUP bool "Set up system-specific details in device tree before boot" help @@ -1845,7 +1916,7 @@ config USE_BOOTARGS config BOOTARGS string "Boot arguments" - depends on USE_BOOTARGS && !USE_DEFAULT_ENV_FILE + depends on USE_BOOTARGS && !ENV_USE_DEFAULT_ENV_TEXT_FILE help This can be used to pass arguments to the bootm command. The value of CONFIG_BOOTARGS goes into the environment value "bootargs". Note that @@ -1880,7 +1951,7 @@ config USE_BOOTCOMMAND config BOOTCOMMAND string "bootcmd value" - depends on USE_BOOTCOMMAND && !USE_DEFAULT_ENV_FILE + depends on USE_BOOTCOMMAND && !ENV_USE_DEFAULT_ENV_TEXT_FILE default "bootflow scan -lb" if BOOTSTD_DEFAULTS && CMD_BOOTFLOW_FULL default "bootflow scan" if BOOTSTD_DEFAULTS && !CMD_BOOTFLOW_FULL default "run distro_bootcmd" if !BOOTSTD_BOOTCOMMAND && DISTRO_DEFAULTS @@ -1903,7 +1974,7 @@ config USE_PREBOOT config PREBOOT string "preboot default value" - depends on USE_PREBOOT && !USE_DEFAULT_ENV_FILE + depends on USE_PREBOOT && !ENV_USE_DEFAULT_ENV_TEXT_FILE default "usb start" if USB_KEYBOARD default "" help diff --git a/boot/Makefile b/boot/Makefile index 71dafaefa76..3da6f7a0914 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)BOOTMETH_EXTLINUX_PXE) += bootmeth_pxe.o obj-$(CONFIG_$(PHASE_)BOOTMETH_EFILOADER) += bootmeth_efi.o obj-$(CONFIG_$(PHASE_)BOOTMETH_CROS) += bootm.o bootm_os.o bootmeth_cros.o obj-$(CONFIG_$(PHASE_)BOOTMETH_QFW) += bootmeth_qfw.o +obj-$(CONFIG_$(PHASE_)BOOTMETH_RAUC) += bootmeth_rauc.o obj-$(CONFIG_$(PHASE_)BOOTMETH_SANDBOX) += bootmeth_sandbox.o obj-$(CONFIG_$(PHASE_)BOOTMETH_SCRIPT) += bootmeth_script.o obj-$(CONFIG_$(PHASE_)CEDIT) += cedit.o @@ -58,7 +59,7 @@ obj-$(CONFIG_CMD_ADTIMG) += image-android-dt.o obj-$(CONFIG_$(PHASE_)LOAD_FIT) += common_fit.o obj-$(CONFIG_$(PHASE_)EXPO) += expo.o scene.o expo_build.o -obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o +obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o scene_textedit.o ifdef CONFIG_COREBOOT_SYSINFO obj-$(CONFIG_$(PHASE_)EXPO) += expo_build_cb.o endif diff --git a/boot/bootflow_internal.h b/boot/bootflow_internal.h index 38cf02a55b5..4cdb6966a7b 100644 --- a/boot/bootflow_internal.h +++ b/boot/bootflow_internal.h @@ -14,7 +14,10 @@ enum { START, /* strings */ - STR_PROMPT, + STR_PROMPT1A, + STR_PROMPT1B, + STR_PROMPT2, + STR_AUTOBOOT, STR_MENU_TITLE, STR_POINTER, @@ -23,10 +26,14 @@ enum { /* objects */ OBJ_U_BOOT_LOGO, + OBJ_BOX, OBJ_MENU, - OBJ_PROMPT, + OBJ_PROMPT1A, + OBJ_PROMPT1B, + OBJ_PROMPT2, OBJ_MENU_TITLE, OBJ_POINTER, + OBJ_AUTOBOOT, /* strings for menu items */ STR_LABEL = 100, diff --git a/boot/bootflow_menu.c b/boot/bootflow_menu.c index 9d0dc352f97..4a442e16850 100644 --- a/boot/bootflow_menu.c +++ b/boot/bootflow_menu.c @@ -25,21 +25,22 @@ * struct menu_priv - information about the menu * * @num_bootflows: Number of bootflows in the menu + * @last_bootdev: bootdev of the last bootflow added to the menu, NULL if none */ struct menu_priv { int num_bootflows; + struct udevice *last_bootdev; }; int bootflow_menu_new(struct expo **expp) { - struct udevice *last_bootdev; struct scene_obj_menu *menu; struct menu_priv *priv; - struct bootflow *bflow; struct scene *scn; struct expo *exp; + bool use_font; void *logo; - int ret, i; + int ret; priv = calloc(1, sizeof(*priv)); if (!priv) @@ -53,153 +54,177 @@ int bootflow_menu_new(struct expo **expp) if (ret < 0) return log_msg_ret("scn", ret); - ret |= scene_txt_str(scn, "prompt", OBJ_PROMPT, STR_PROMPT, - "UP and DOWN to choose, ENTER to select", NULL); + ret = scene_box(scn, "box", OBJ_BOX, 2, NULL); + if (ret < 0) + return log_msg_ret("bmb", ret); + ret |= scene_obj_set_bbox(scn, OBJ_BOX, 30, 90, 1366 - 30, 720); ret = scene_menu(scn, "main", OBJ_MENU, &menu); ret |= scene_obj_set_pos(scn, OBJ_MENU, MARGIN_LEFT, 100); ret |= scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE, "U-Boot - Boot Menu", NULL); - ret |= scene_menu_set_title(scn, OBJ_MENU, OBJ_PROMPT); + ret |= scene_obj_set_bbox(scn, OBJ_MENU_TITLE, 0, 32, + SCENEOB_DISPLAY_MAX, 30); + ret |= scene_obj_set_halign(scn, OBJ_MENU_TITLE, SCENEOA_CENTRE); logo = video_get_u_boot_logo(); if (logo) { ret |= scene_img(scn, "ulogo", OBJ_U_BOOT_LOGO, logo, NULL); - ret |= scene_obj_set_pos(scn, OBJ_U_BOOT_LOGO, -4, 4); + ret |= scene_obj_set_pos(scn, OBJ_U_BOOT_LOGO, 1165, 100); } + ret |= scene_txt_str(scn, "prompt1a", OBJ_PROMPT1A, STR_PROMPT1A, + "Use the \x18 and \x19 keys to select which entry is highlighted.", + NULL); + ret |= scene_txt_str(scn, "prompt1b", OBJ_PROMPT1B, STR_PROMPT1B, + "Use the UP and DOWN keys to select which entry is highlighted.", + NULL); + ret |= scene_txt_str(scn, "prompt2", OBJ_PROMPT2, STR_PROMPT2, + "Press enter to boot the selected OS, 'e' to edit the commands " + "before booting or 'c' for a command-line. ESC to return to " + "previous menu", NULL); + ret |= scene_txt_str(scn, "autoboot", OBJ_AUTOBOOT, STR_AUTOBOOT, + "The highlighted entry will be executed automatically in %ds.", + NULL); + ret |= scene_obj_set_bbox(scn, OBJ_PROMPT1A, 0, 590, + SCENEOB_DISPLAY_MAX, 30); + ret |= scene_obj_set_bbox(scn, OBJ_PROMPT1B, 0, 620, + SCENEOB_DISPLAY_MAX, 30); + ret |= scene_obj_set_bbox(scn, OBJ_PROMPT2, 100, 650, + 1366 - 100, 700); + ret |= scene_obj_set_bbox(scn, OBJ_AUTOBOOT, 0, 720, + SCENEOB_DISPLAY_MAX, 750); + ret |= scene_obj_set_halign(scn, OBJ_PROMPT1A, SCENEOA_CENTRE); + ret |= scene_obj_set_halign(scn, OBJ_PROMPT1B, SCENEOA_CENTRE); + ret |= scene_obj_set_halign(scn, OBJ_PROMPT2, SCENEOA_CENTRE); + ret |= scene_obj_set_valign(scn, OBJ_PROMPT2, SCENEOA_CENTRE); + ret |= scene_obj_set_halign(scn, OBJ_AUTOBOOT, SCENEOA_CENTRE); + + use_font = IS_ENABLED(CONFIG_CONSOLE_TRUETYPE); + scene_obj_set_hide(scn, OBJ_PROMPT1A, use_font); + scene_obj_set_hide(scn, OBJ_PROMPT1B, !use_font); + scene_obj_set_hide(scn, OBJ_AUTOBOOT, use_font); + ret |= scene_txt_str(scn, "cur_item", OBJ_POINTER, STR_POINTER, ">", NULL); ret |= scene_menu_set_pointer(scn, OBJ_MENU, OBJ_POINTER); if (ret < 0) return log_msg_ret("new", -EINVAL); - last_bootdev = NULL; - for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; - ret = bootflow_next_glob(&bflow), i++) { - struct bootmeth_uc_plat *ucp; - char str[2], *label, *key; - uint preview_id; - bool add_gap; + exp->show_highlight = true; - if (bflow->state != BOOTFLOWST_READY) - continue; + *expp = exp; - /* No media to show for BOOTMETHF_GLOBAL bootmeths */ - ucp = dev_get_uclass_plat(bflow->method); - if (ucp->flags & BOOTMETHF_GLOBAL) - continue; + return 0; +} - *str = i < 10 ? '0' + i : 'A' + i - 10; - str[1] = '\0'; - key = strdup(str); - if (!key) - return log_msg_ret("key", -ENOMEM); - label = strdup(dev_get_parent(bflow->dev)->name); - if (!label) { - free(key); - return log_msg_ret("nam", -ENOMEM); - } - - add_gap = last_bootdev != bflow->dev; - last_bootdev = bflow->dev; - - ret = expo_str(exp, "prompt", STR_POINTER, ">"); - ret |= scene_txt_str(scn, "label", ITEM_LABEL + i, - STR_LABEL + i, label, NULL); - ret |= scene_txt_str(scn, "desc", ITEM_DESC + i, STR_DESC + i, - bflow->os_name ? bflow->os_name : - bflow->name, NULL); - ret |= scene_txt_str(scn, "key", ITEM_KEY + i, STR_KEY + i, key, - NULL); - preview_id = 0; - if (bflow->logo) { - preview_id = ITEM_PREVIEW + i; - ret |= scene_img(scn, "preview", preview_id, - bflow->logo, NULL); - } - ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + i, - ITEM_KEY + i, ITEM_LABEL + i, - ITEM_DESC + i, preview_id, - add_gap ? SCENEMIF_GAP_BEFORE : 0, - NULL); - - if (ret < 0) - return log_msg_ret("itm", -EINVAL); - priv->num_bootflows++; +int bootflow_menu_add(struct expo *exp, struct bootflow *bflow, int seq, + struct scene **scnp) +{ + struct menu_priv *priv = exp->priv; + char str[2], *label, *key; + struct udevice *media; + struct scene *scn; + const char *name; + uint preview_id; + uint scene_id; + bool add_gap; + int ret; + + ret = expo_first_scene_id(exp); + if (ret < 0) + return log_msg_ret("scn", ret); + scene_id = ret; + scn = expo_lookup_scene_id(exp, scene_id); + + *str = seq < 10 ? '0' + seq : 'A' + seq - 10; + str[1] = '\0'; + key = strdup(str); + if (!key) + return log_msg_ret("key", -ENOMEM); + + media = dev_get_parent(bflow->dev); + if (device_get_uclass_id(media) == UCLASS_MASS_STORAGE) + name = "usb"; + else + name = media->name; + label = strdup(name); + + if (!label) { + free(key); + return log_msg_ret("nam", -ENOMEM); } - ret = scene_arrange(scn); - if (ret) - return log_msg_ret("arr", ret); + add_gap = priv->last_bootdev != bflow->dev; + + /* disable this gap for now, since it looks a little ugly */ + add_gap = false; + priv->last_bootdev = bflow->dev; + + ret = expo_str(exp, "prompt", STR_POINTER, ">"); + ret |= scene_txt_str(scn, "label", ITEM_LABEL + seq, + STR_LABEL + seq, label, NULL); + ret |= scene_txt_str(scn, "desc", ITEM_DESC + seq, STR_DESC + seq, + bflow->os_name ? bflow->os_name : + bflow->name, NULL); + ret |= scene_txt_str(scn, "key", ITEM_KEY + seq, STR_KEY + seq, key, + NULL); + preview_id = 0; + if (bflow->logo) { + preview_id = ITEM_PREVIEW + seq; + ret |= scene_img(scn, "preview", preview_id, + bflow->logo, NULL); + } + ret |= scene_menuitem(scn, OBJ_MENU, "item", ITEM + seq, + ITEM_KEY + seq, ITEM_LABEL + seq, + ITEM_DESC + seq, preview_id, + add_gap ? SCENEMIF_GAP_BEFORE : 0, + NULL); - *expp = exp; + if (ret < 0) + return log_msg_ret("itm", -EINVAL); + priv->num_bootflows++; + *scnp = scn; return 0; } -int bootflow_menu_apply_theme(struct expo *exp, ofnode node) +int bootflow_menu_add_all(struct expo *exp) { - struct menu_priv *priv = exp->priv; + struct bootflow *bflow; struct scene *scn; - u32 font_size; - int ret; + int ret, i; - log_debug("Applying theme %s\n", ofnode_get_name(node)); - scn = expo_lookup_scene_id(exp, MAIN); - if (!scn) - return log_msg_ret("scn", -ENOENT); - - /* Avoid error-checking optional items */ - if (!ofnode_read_u32(node, "font-size", &font_size)) { - int i; - - log_debug("font size %d\n", font_size); - scene_txt_set_font(scn, OBJ_PROMPT, NULL, font_size); - scene_txt_set_font(scn, OBJ_POINTER, NULL, font_size); - for (i = 0; i < priv->num_bootflows; i++) { - ret = scene_txt_set_font(scn, ITEM_DESC + i, NULL, - font_size); - if (ret) - return log_msg_ret("des", ret); - scene_txt_set_font(scn, ITEM_KEY + i, NULL, font_size); - scene_txt_set_font(scn, ITEM_LABEL + i, NULL, - font_size); - } - } + for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; + ret = bootflow_next_glob(&bflow), i++) { + struct bootmeth_uc_plat *ucp; - ret = scene_arrange(scn); - if (ret) - return log_msg_ret("arr", ret); + if (bflow->state != BOOTFLOWST_READY) + continue; + + /* No media to show for BOOTMETHF_GLOBAL bootmeths */ + ucp = dev_get_uclass_plat(bflow->method); + if (ucp->flags & BOOTMETHF_GLOBAL) + continue; + + ret = bootflow_menu_add(exp, bflow, i, &scn); + if (ret) + return log_msg_ret("bao", ret); + } return 0; } -int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, - struct bootflow **bflowp) +int bootflow_menu_setup(struct bootstd_priv *std, bool text_mode, + struct expo **expp) { - struct cli_ch_state s_cch, *cch = &s_cch; - struct bootflow *sel_bflow; struct udevice *dev; struct expo *exp; - uint sel_id; - bool done; int ret; - cli_ch_init(cch); - - sel_bflow = NULL; - *bflowp = NULL; - ret = bootflow_menu_new(&exp); if (ret) - return log_msg_ret("exp", ret); - - if (ofnode_valid(std->theme)) { - ret = bootflow_menu_apply_theme(exp, std->theme); - if (ret) - return log_msg_ret("thm", ret); - } + return log_msg_ret("bmn", ret); /* For now we only support a video console */ ret = uclass_first_device_err(UCLASS_VIDEO, &dev); @@ -216,78 +241,91 @@ int bootflow_menu_run(struct bootstd_priv *std, bool text_mode, if (text_mode) expo_set_text_mode(exp, text_mode); - done = false; - do { - struct expo_action act; - int ichar, key; + *expp = exp; - ret = expo_render(exp); - if (ret) - break; - - ichar = cli_ch_process(cch, 0); - if (!ichar) { - while (!ichar && !tstc()) { - schedule(); - mdelay(2); - ichar = cli_ch_process(cch, -ETIMEDOUT); - } - if (!ichar) { - ichar = getchar(); - ichar = cli_ch_process(cch, ichar); - } - } - - key = 0; - if (ichar) { - key = bootmenu_conv_key(ichar); - if (key == BKEY_NONE) - key = ichar; - } - if (!key) - continue; + return 0; +} - ret = expo_send_key(exp, key); - if (ret) - break; - - ret = expo_action_get(exp, &act); - if (!ret) { - switch (act.type) { - case EXPOACT_SELECT: - sel_id = act.select.id; - done = true; - break; - case EXPOACT_QUIT: - done = true; - break; - default: - break; - } - } - } while (!done); +int bootflow_menu_start(struct bootstd_priv *std, bool text_mode, + struct expo **expp) +{ + struct scene *scn; + struct expo *exp; + uint scene_id; + int ret; + ret = bootflow_menu_setup(std, text_mode, &exp); if (ret) - return log_msg_ret("end", ret); - - if (sel_id) { - struct bootflow *bflow; - int i; - - for (ret = bootflow_first_glob(&bflow), i = 0; !ret && i < 36; - ret = bootflow_next_glob(&bflow), i++) { - if (i == sel_id - ITEM) { - sel_bflow = bflow; - break; - } - } + return log_msg_ret("bmd", ret); + + ret = bootflow_menu_add_all(exp); + if (ret) + return log_msg_ret("bma", ret); + + if (ofnode_valid(std->theme)) { + ret = expo_apply_theme(exp, std->theme); + if (ret) + return log_msg_ret("thm", ret); } - expo_destroy(exp); + ret = expo_calc_dims(exp); + if (ret) + return log_msg_ret("bmd", ret); + + ret = expo_first_scene_id(exp); + if (ret < 0) + return log_msg_ret("scn", ret); + scene_id = ret; + scn = expo_lookup_scene_id(exp, scene_id); + + scene_set_highlight_id(scn, OBJ_MENU); + + ret = scene_arrange(scn); + if (ret) + return log_msg_ret("arr", ret); + + *expp = exp; + + return 0; +} + +int bootflow_menu_poll(struct expo *exp, int *seqp) +{ + struct bootflow *sel_bflow; + struct expo_action act; + struct scene *scn; + int item, ret; + + sel_bflow = NULL; - if (!sel_bflow) + scn = expo_lookup_scene_id(exp, exp->scene_id); + + item = scene_menu_get_cur_item(scn, OBJ_MENU); + *seqp = item > 0 ? item - ITEM : -1; + + ret = expo_poll(exp, &act); + if (ret) + return log_msg_ret("bmp", ret); + + switch (act.type) { + case EXPOACT_SELECT: + *seqp = act.select.id - ITEM; + break; + case EXPOACT_POINT_ITEM: { + struct scene *scn = expo_lookup_scene_id(exp, MAIN); + + if (!scn) + return log_msg_ret("bms", -ENOENT); + ret = scene_menu_select_item(scn, OBJ_MENU, act.select.id); + if (ret) + return log_msg_ret("bmp", ret); + return -ERESTART; + } + case EXPOACT_QUIT: + return -EPIPE; + default: return -EAGAIN; - *bflowp = sel_bflow; + } return 0; } diff --git a/boot/bootm.c b/boot/bootm.c index 108ca7fb472..4bdca22ea8c 100644 --- a/boot/bootm.c +++ b/boot/bootm.c @@ -623,12 +623,16 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) */ if (os.type == IH_TYPE_KERNEL_NOLOAD && os.comp != IH_COMP_NONE) { ulong req_size = ALIGN(image_len * 4, SZ_1M); + phys_addr_t addr; - load = lmb_alloc(req_size, SZ_2M); - if (!load) + err = lmb_alloc_mem(LMB_MEM_ALLOC_ANY, SZ_2M, &addr, + req_size, LMB_NONE); + if (err) return 1; - os.load = load; - images->ep = load; + + load = (ulong)addr; + os.load = (ulong)addr; + images->ep = (ulong)addr; debug("Allocated %lx bytes at %lx for kernel (size %lx) decompression\n", req_size, load, image_len); } @@ -698,9 +702,18 @@ static int bootm_load_os(struct bootm_headers *images, int boot_progress) images->os.end = relocated_addr + image_size; } - if (CONFIG_IS_ENABLED(LMB)) - lmb_reserve(images->os.load, (load_end - images->os.load), - LMB_NONE); + if (CONFIG_IS_ENABLED(LMB)) { + phys_addr_t load; + + load = (phys_addr_t)images->os.load; + err = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &load, + (load_end - images->os.load), LMB_NONE); + if (err) { + log_err("Unable to allocate memory %#lx for loading OS\n", + images->os.load); + return 1; + } + } return 0; } diff --git a/boot/bootm_os.c b/boot/bootm_os.c index a3c7cb5332e..88f7c183867 100644 --- a/boot/bootm_os.c +++ b/boot/bootm_os.c @@ -402,6 +402,17 @@ static int do_bootm_elf(int flag, struct bootm_info *bmi) if (flag != BOOTM_STATE_OS_GO) return 0; + /* + * Required per RISC-V boot protocol: + * a0(argc) = hartid of the current core + * a1(argv) = address of the devicetree in memory + * https://www.kernel.org/doc/html/latest/arch/riscv/boot.html#register-state + */ +#if defined(CONFIG_RISCV) + bmi->argc = gd->arch.boot_hart; + bmi->argv = (char **)bmi->images->ft_addr; +#endif + bootelf(bmi->images->ep, flags, bmi->argc, bmi->argv); return 1; diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 014b7588e8d..188f6ea1895 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -12,6 +12,7 @@ #include <bootmeth.h> #include <bootstd.h> #include <dm.h> +#include <dm/device-internal.h> #include <env_internal.h> #include <fs.h> #include <malloc.h> @@ -135,10 +136,12 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) * We don't support skipping global bootmeths. Instead, the user * should omit them from the ordering */ - if (!include_global) - return log_msg_ret("glob", -EPERM); + if (!include_global) { + ret = log_msg_ret("glob", -EPERM); + goto err_order; + } memcpy(order, std->bootmeth_order, - count * sizeof(struct bootmeth *)); + count * sizeof(struct udevice *)); if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL)) { for (i = 0; i < count; i++) { @@ -146,6 +149,12 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) struct bootmeth_uc_plat *ucp; bool is_global; + ret = device_probe(dev); + if (ret) { + ret = log_msg_ret("probe", ret); + goto err_order; + } + ucp = dev_get_uclass_plat(dev); is_global = ucp->flags & BOOTMETHF_GLOBAL; @@ -190,8 +199,10 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) } count = upto; } - if (!count) - return log_msg_ret("count2", -ENOENT); + if (!count) { + ret = log_msg_ret("count2", -ENOENT); + goto err_order; + } if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) && include_global && iter->first_glob_method != -1 && iter->first_glob_method != count) { @@ -202,6 +213,10 @@ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global) iter->num_methods = count; return 0; + +err_order: + free(order); + return ret; } int bootmeth_set_order(const char *order_str) diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c index 654ebfdf1fc..8c2bde10e17 100644 --- a/boot/bootmeth_android.c +++ b/boot/bootmeth_android.c @@ -18,6 +18,7 @@ #include <bootm.h> #include <bootmeth.h> #include <dm.h> +#include <env.h> #include <image.h> #include <malloc.h> #include <mapmem.h> diff --git a/boot/bootmeth_efi.c b/boot/bootmeth_efi.c index 0c9b4c3d59d..0af23df3a4a 100644 --- a/boot/bootmeth_efi.c +++ b/boot/bootmeth_efi.c @@ -15,6 +15,7 @@ #include <dm.h> #include <efi.h> #include <efi_loader.h> +#include <env.h> #include <fs.h> #include <malloc.h> #include <mapmem.h> diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 6e5e0f99ea4..faa8d729b15 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -13,6 +13,7 @@ #include <bootmeth.h> #include <command.h> #include <dm.h> +#include <env.h> #include <extlinux.h> #include <fs.h> #include <log.h> diff --git a/boot/bootmeth_rauc.c b/boot/bootmeth_rauc.c new file mode 100644 index 00000000000..fc60e6e355d --- /dev/null +++ b/boot/bootmeth_rauc.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Bootmethod for distro boot with RAUC + * + * Copyright 2025 PHYTEC Messtechnik GmbH + * Written by Martin Schwan <m.schwan@phytec.de> + */ + +#define LOG_CATEGORY UCLASS_BOOTSTD + +#include <blk.h> +#include <bootflow.h> +#include <bootmeth.h> +#include <bootstd.h> +#include <dm.h> +#include <env.h> +#include <fs.h> +#include <malloc.h> +#include <mapmem.h> +#include <string.h> +#include <asm/cache.h> + +/* Length of env var "BOOT_*_LEFT" */ +#define BOOT_LEFT_LEN (5 + 32 + 5) + +static const char * const script_names[] = { "boot.scr", "boot.scr.uimg", NULL }; + +/** + * struct distro_rauc_slot - Slot information + * + * A slot describes the unit of a bootable system consisting of one or multiple + * partitions. This usually includes a root filesystem, kernel and potentially other + * files, like device trees and boot scripts for that particular distribution. + * + * @name The slot name + * @boot_part The boot partition number on disk + * @root_part The root partition number on disk + */ +struct distro_rauc_slot { + char *name; + int boot_part; + int root_part; +}; + +/** + * struct distro_rauc_priv - Private data + * + * @slots All slots of the device in default order + * @boot_order String of the current boot order containing the active slot names + */ +struct distro_rauc_priv { + struct distro_rauc_slot **slots; +}; + +static struct distro_rauc_slot *get_slot(struct distro_rauc_priv *priv, + const char *slot_name) +{ + int i; + + for (i = 0; priv->slots[i]->name; i++) { + if (!strcmp(priv->slots[i]->name, slot_name)) + return priv->slots[i]; + } + + return NULL; +} + +static int distro_rauc_check(struct udevice *dev, struct bootflow_iter *iter) +{ + /* + * This distro only works on whole MMC devices, as multiple partitions + * are needed for an A/B system. + */ + if (bootflow_iter_check_mmc(iter)) + return log_msg_ret("mmc", -EOPNOTSUPP); + if (iter->part) + return log_msg_ret("part", -EOPNOTSUPP); + + return 0; +} + +static int distro_rauc_scan_boot_part(struct bootflow *bflow) +{ + struct blk_desc *desc; + struct distro_rauc_priv *priv; + char *boot_order; + const char **boot_order_list; + bool exists; + int ret; + int i; + int j; + + desc = dev_get_uclass_plat(bflow->blk); + + priv = bflow->bootmeth_priv; + if (!priv || !priv->slots) + return log_msg_ret("priv", -EINVAL); + + boot_order = env_get("BOOT_ORDER"); + boot_order_list = str_to_list(boot_order); + for (i = 0; boot_order_list[i]; i++) { + exists = false; + for (j = 0; script_names[j]; j++) { + const struct distro_rauc_slot *slot; + + slot = get_slot(priv, boot_order_list[i]); + if (!slot) + return log_msg_ret("env", -ENOENT); + ret = fs_set_blk_dev_with_part(desc, slot->boot_part); + if (ret) + return log_msg_ret("blk", ret); + exists |= fs_exists(script_names[j]); + } + if (!exists) + return log_msg_ret("fs", -ENOENT); + } + str_free_list(boot_order_list); + + return 0; +} + +static int distro_rauc_read_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + struct distro_rauc_priv *priv; + int ret; + char *slot; + int i; + char *partitions; + char *boot_order; + const char *default_boot_order; + const char **default_boot_order_list; + char *boot_order_copy; + char boot_left[BOOT_LEFT_LEN]; + char *parts; + + /* Get RAUC variables or set their default values */ + boot_order = env_get("BOOT_ORDER"); + if (!boot_order) { + log_debug("BOOT_ORDER did not exist yet, setting default value\n"); + if (env_set("BOOT_ORDER", CONFIG_BOOTMETH_RAUC_BOOT_ORDER)) + return log_msg_ret("env", -EPERM); + boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER; + } + default_boot_order = CONFIG_BOOTMETH_RAUC_BOOT_ORDER; + default_boot_order_list = str_to_list(default_boot_order); + for (i = 0; default_boot_order_list[i]; i++) { + sprintf(boot_left, "BOOT_%s_LEFT", default_boot_order_list[i]); + if (!env_get(boot_left)) { + log_debug("%s did not exist yet, setting default value\n", + boot_left); + if (env_set_ulong(boot_left, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES)) + return log_msg_ret("env", -EPERM); + } + } + str_free_list(default_boot_order_list); + + priv = calloc(1, sizeof(struct distro_rauc_priv)); + if (!priv) + return log_msg_ret("buf", -ENOMEM); + priv->slots = calloc(1, sizeof(struct distro_rauc_slot)); + + /* Copy default boot_order, so we can leave the original unmodified */ + boot_order_copy = strdup(default_boot_order); + partitions = strdup(CONFIG_BOOTMETH_RAUC_PARTITIONS); + + for (i = 1; + (parts = strsep(&partitions, " ")) && + (slot = strsep(&boot_order_copy, " ")); + i++) { + struct distro_rauc_slot *s; + + s = calloc(1, sizeof(struct distro_rauc_slot)); + s->name = strdup(slot); + s->boot_part = simple_strtoul(strsep(&parts, ","), NULL, 10); + s->root_part = simple_strtoul(strsep(&parts, ","), NULL, 10); + priv->slots = realloc(priv->slots, (i + 1) * + sizeof(struct distro_rauc_slot)); + priv->slots[i - 1] = s; + priv->slots[i]->name = NULL; + } + + bflow->bootmeth_priv = priv; + + ret = distro_rauc_scan_boot_part(bflow); + if (ret < 0) { + for (i = 0; priv->slots[i]->name; i++) { + free(priv->slots[i]->name); + free(priv->slots[i]); + } + free(priv); + free(boot_order_copy); + bflow->bootmeth_priv = NULL; + return ret; + } + + bflow->state = BOOTFLOWST_READY; + + return 0; +} + +static int distro_rauc_read_file(struct udevice *dev, struct bootflow *bflow, + const char *file_path, ulong addr, + enum bootflow_img_t type, ulong *sizep) +{ + /* + * Reading individual files is not supported since we only operate on + * whole MMC devices (because we require multiple partitions). + */ + return log_msg_ret("Unsupported", -ENOSYS); +} + +static int distro_rauc_load_boot_script(struct bootflow *bflow, + const struct distro_rauc_slot *slot) +{ + struct blk_desc *desc; + struct distro_rauc_priv *priv; + struct udevice *bootstd; + const char *const *prefixes; + int ret; + int i; + int j; + + ret = uclass_first_device_err(UCLASS_BOOTSTD, &bootstd); + if (ret) + return log_msg_ret("std", ret); + prefixes = bootstd_get_prefixes(bootstd); + + desc = dev_get_uclass_plat(bflow->blk); + priv = bflow->bootmeth_priv; + if (!priv || !priv->slots) + return log_msg_ret("priv", -EINVAL); + + bflow->part = slot->boot_part; + if (!bflow->part) + return log_msg_ret("part", -ENOENT); + + ret = bootmeth_setup_fs(bflow, desc); + if (ret) + return log_msg_ret("set", ret); + + for (i = 0; prefixes[i] && bflow->state != BOOTFLOWST_FILE; i++) { + for (j = 0; script_names[j] && bflow->state != BOOTFLOWST_FILE; j++) { + if (!bootmeth_try_file(bflow, desc, prefixes[i], script_names[j])) { + log_debug("Found file '%s%s' in %s.part_%x\n", + prefixes[i], script_names[j], + bflow->dev->name, bflow->part); + bflow->subdir = strdup(prefixes[i]); + } + } + } + if (bflow->state != BOOTFLOWST_FILE) + return log_msg_ret("file", -ENOENT); + + ret = bootmeth_alloc_file(bflow, 0x10000, ARCH_DMA_MINALIGN, + (enum bootflow_img_t)IH_TYPE_SCRIPT); + if (ret) + return log_msg_ret("read", ret); + + return 0; +} + +static int find_active_slot(char **slot_name, ulong *slot_tries) +{ + ulong tries; + char boot_left[BOOT_LEFT_LEN]; + char *boot_order; + const char **boot_order_list; + bool slot_found = false; + int ret; + int i; + + boot_order = env_get("BOOT_ORDER"); + if (!boot_order) + return log_msg_ret("env", -ENOENT); + boot_order_list = str_to_list(boot_order); + for (i = 0; boot_order_list[i] && !slot_found; i++) { + sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]); + tries = env_get_ulong(boot_left, 10, ULONG_MAX); + if (tries == ULONG_MAX) + return log_msg_ret("env", -ENOENT); + + if (tries) { + ret = env_set_ulong(boot_left, tries - 1); + if (ret) + return log_msg_ret("env", ret); + *slot_name = strdup(boot_order_list[i]); + *slot_tries = tries; + slot_found = true; + } + } + str_free_list(boot_order_list); + + if (!slot_found) { + if (IS_ENABLED(CONFIG_BOOTMETH_RAUC_RESET_ALL_ZERO_TRIES)) { + log_warning("WARNING: No valid slot found\n"); + log_info("INFO: Resetting boot order and all slot tries\n"); + boot_order_list = str_to_list(CONFIG_BOOTMETH_RAUC_BOOT_ORDER); + for (i = 0; boot_order_list[i]; i++) { + sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]); + ret = env_set_ulong(boot_left, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES); + if (ret) + return log_msg_ret("env", ret); + } + str_free_list(boot_order_list); + ret = env_save(); + if (ret) + return log_msg_ret("env", ret); + do_reset(NULL, 0, 0, NULL); + } + log_err("ERROR: No valid slot found\n"); + return -EINVAL; + } + + return 0; +} + +static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow) +{ + struct blk_desc *desc; + struct distro_rauc_priv *priv; + const struct distro_rauc_slot *slot; + char *boot_order; + const char **boot_order_list; + char *active_slot; + ulong active_slot_tries; + char raucargs[64]; + char boot_left[BOOT_LEFT_LEN]; + ulong addr; + int ret = 0; + int i; + + desc = dev_get_uclass_plat(bflow->blk); + if (desc->uclass_id != UCLASS_MMC) + return log_msg_ret("blk", -EINVAL); + priv = bflow->bootmeth_priv; + + /* Device info variables */ + ret = env_set("devtype", blk_get_devtype(bflow->blk)); + if (ret) + return log_msg_ret("env", ret); + + ret = env_set_hex("devnum", desc->devnum); + if (ret) + return log_msg_ret("env", ret); + + /* Find active, valid slot */ + ret = find_active_slot(&active_slot, &active_slot_tries); + if (ret) + return log_msg_ret("env", ret); + + /* Kernel command line arguments */ + sprintf(raucargs, "rauc.slot=%s", active_slot); + ret = env_set("raucargs", raucargs); + if (ret) + return log_msg_ret("env", ret); + + /* Active slot info */ + slot = get_slot(priv, active_slot); + if (!slot) + return log_msg_ret("env", -ENOENT); + ret = env_set_hex("distro_bootpart", slot->boot_part); + if (ret) + return log_msg_ret("env", ret); + ret = env_set_hex("distro_rootpart", slot->root_part); + if (ret) + return log_msg_ret("env", ret); + ret = env_save(); + if (ret) + return log_msg_ret("env", ret); + + /* Load distro boot script */ + ret = distro_rauc_load_boot_script(bflow, slot); + if (ret) + return log_msg_ret("load", ret); + + log_info("INFO: Booting slot %s, %lu of %d tries left\n", + active_slot, active_slot_tries, CONFIG_BOOTMETH_RAUC_DEFAULT_TRIES); + + log_debug("devtype: %s\n", env_get("devtype")); + log_debug("devnum: %s\n", env_get("devnum")); + log_debug("distro_bootpart: %s\n", env_get("distro_bootpart")); + log_debug("distro_rootpart: %s\n", env_get("distro_rootpart")); + log_debug("raucargs: %s\n", env_get("raucargs")); + boot_order = env_get("BOOT_ORDER"); + if (!boot_order) + return log_msg_ret("env", -EPERM); + log_debug("BOOT_ORDER: %s\n", boot_order); + boot_order_list = str_to_list(boot_order); + for (i = 0; boot_order_list[i]; i++) { + sprintf(boot_left, "BOOT_%s_LEFT", boot_order_list[i]); + log_debug("%s: %s\n", boot_left, env_get(boot_left)); + } + str_free_list(boot_order_list); + + /* Run distro boot script */ + addr = map_to_sysmem(bflow->buf); + ret = cmd_source_script(addr, NULL, NULL); + if (ret) + return log_msg_ret("boot", ret); + + return 0; +} + +static int distro_rauc_bootmeth_bind(struct udevice *dev) +{ + struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); + + plat->desc = "RAUC distro boot from MMC"; + plat->flags = BOOTMETHF_GLOBAL; + + return 0; +} + +static struct bootmeth_ops distro_rauc_bootmeth_ops = { + .check = distro_rauc_check, + .read_bootflow = distro_rauc_read_bootflow, + .read_file = distro_rauc_read_file, + .boot = distro_rauc_boot, +}; + +static const struct udevice_id distro_rauc_bootmeth_ids[] = { + { .compatible = "u-boot,distro-rauc" }, + { } +}; + +U_BOOT_DRIVER(bootmeth_rauc) = { + .name = "bootmeth_rauc", + .id = UCLASS_BOOTMETH, + .of_match = distro_rauc_bootmeth_ids, + .ops = &distro_rauc_bootmeth_ops, + .bind = distro_rauc_bootmeth_bind, +}; diff --git a/boot/cedit.c b/boot/cedit.c index d69290c172e..56dc7c6af15 100644 --- a/boot/cedit.c +++ b/boot/cedit.c @@ -81,6 +81,8 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: scene_obj_set_pos(scn, obj->id, 50, y); @@ -100,19 +102,16 @@ int cedit_arange(struct expo *exp, struct video_priv *vpriv, uint scene_id) return 0; } -int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, +int cedit_prepare(struct expo *exp, struct udevice *vid_dev, struct scene **scnp) { + struct udevice *dev = vid_dev; struct video_priv *vid_priv; - struct udevice *dev; struct scene *scn; uint scene_id; int ret; /* For now we only support a video console */ - ret = uclass_first_device_err(UCLASS_VIDEO, &dev); - if (ret) - return log_msg_ret("vid", ret); ret = expo_set_display(exp, dev); if (ret) return log_msg_ret("dis", ret); @@ -127,6 +126,7 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, return log_msg_ret("sid", ret); exp->popup = true; + exp->show_highlight = true; /* This is not supported for now */ if (0) @@ -143,104 +143,96 @@ int cedit_prepare(struct expo *exp, struct video_priv **vid_privp, if (ret) return log_msg_ret("dim", ret); - *vid_privp = vid_priv; *scnp = scn; return scene_id; } +int cedit_do_action(struct expo *exp, struct scene *scn, + struct video_priv *vid_priv, struct expo_action *act) +{ + int ret; + + switch (act->type) { + case EXPOACT_NONE: + return -EAGAIN; + case EXPOACT_POINT_ITEM: + ret = scene_menu_select_item(scn, scn->highlight_id, + act->select.id); + if (ret) + return log_msg_ret("cdp", ret); + break; + case EXPOACT_POINT_OBJ: + scene_set_highlight_id(scn, act->select.id); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_OPEN: + scene_set_open(scn, act->select.id, true); + cedit_arange(exp, vid_priv, scn->id); + switch (scn->highlight_id) { + case EXPOID_SAVE: + exp->done = true; + exp->save = true; + break; + case EXPOID_DISCARD: + exp->done = true; + break; + } + break; + case EXPOACT_CLOSE: + scene_set_open(scn, act->select.id, false); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_SELECT: + scene_set_open(scn, scn->highlight_id, false); + cedit_arange(exp, vid_priv, scn->id); + break; + case EXPOACT_QUIT: + log_debug("quitting\n"); + exp->done = true; + break; + } + + return 0; +} + int cedit_run(struct expo *exp) { - struct cli_ch_state s_cch, *cch = &s_cch; struct video_priv *vid_priv; - uint scene_id; + struct udevice *dev; struct scene *scn; - bool done, save; + uint scene_id; int ret; - cli_ch_init(cch); - ret = cedit_prepare(exp, &vid_priv, &scn); + ret = uclass_first_device_err(UCLASS_VIDEO, &dev); + if (ret) + return log_msg_ret("vid", ret); + vid_priv = dev_get_uclass_priv(dev); + + ret = cedit_prepare(exp, dev, &scn); if (ret < 0) return log_msg_ret("prep", ret); scene_id = ret; - done = false; - save = false; + exp->done = false; + exp->save = false; do { struct expo_action act; - int ichar, key; ret = expo_render(exp); if (ret) - break; + return log_msg_ret("cer", ret); - ichar = cli_ch_process(cch, 0); - if (!ichar) { - while (!ichar && !tstc()) { - schedule(); - mdelay(2); - ichar = cli_ch_process(cch, -ETIMEDOUT); - } - if (!ichar) { - ichar = getchar(); - ichar = cli_ch_process(cch, ichar); - } - } - - key = 0; - if (ichar) { - key = bootmenu_conv_key(ichar); - if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) - key = ichar; - } - if (!key) - continue; - - ret = expo_send_key(exp, key); - if (ret) - break; - - ret = expo_action_get(exp, &act); - if (!ret) { - switch (act.type) { - case EXPOACT_POINT_OBJ: - scene_set_highlight_id(scn, act.select.id); - cedit_arange(exp, vid_priv, scene_id); - break; - case EXPOACT_OPEN: - scene_set_open(scn, act.select.id, true); - cedit_arange(exp, vid_priv, scene_id); - switch (scn->highlight_id) { - case EXPOID_SAVE: - done = true; - save = true; - break; - case EXPOID_DISCARD: - done = true; - break; - } - break; - case EXPOACT_CLOSE: - scene_set_open(scn, act.select.id, false); - cedit_arange(exp, vid_priv, scene_id); - break; - case EXPOACT_SELECT: - scene_set_open(scn, scn->highlight_id, false); - cedit_arange(exp, vid_priv, scene_id); - break; - case EXPOACT_QUIT: - log_debug("quitting\n"); - done = true; - break; - default: - break; - } - } - } while (!done); + ret = expo_poll(exp, &act); + if (!ret) + cedit_do_action(exp, scn, vid_priv, &act); + else if (ret != -EAGAIN) + return log_msg_ret("cep", ret); + } while (!exp->done); if (ret) return log_msg_ret("end", ret); - if (!save) + if (!exp->save) return -EACCES; return 0; @@ -286,7 +278,7 @@ static int get_cur_menuitem_text(const struct scene_obj_menu *menu, if (!txt) return log_msg_ret("txt", -ENOENT); - str = expo_get_str(scn->expo, txt->str_id); + str = expo_get_str(scn->expo, txt->gen.str_id); if (!str) return log_msg_ret("str", -ENOENT); *strp = str; @@ -396,6 +388,8 @@ static int h_write_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -449,8 +443,7 @@ int cedit_write_settings(struct expo *exp, struct abuf *buf) void *fdt; int ret; - abuf_init(buf); - if (!abuf_realloc(buf, CEDIT_SIZE_INC)) + if (!abuf_init_size(buf, CEDIT_SIZE_INC)) return log_msg_ret("buf", -ENOMEM); fdt = abuf_data(buf); @@ -496,6 +489,8 @@ static int h_read_settings(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: { const struct scene_obj_textline *tline; @@ -567,6 +562,8 @@ static int h_write_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; @@ -650,6 +647,8 @@ static int h_read_settings_env(struct scene_obj *obj, void *vpriv) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: menu = (struct scene_obj_menu *)obj; diff --git a/boot/expo.c b/boot/expo.c index 8ce645e5a8f..94413acd381 100644 --- a/boot/expo.c +++ b/boot/expo.c @@ -10,8 +10,12 @@ #include <dm.h> #include <expo.h> +#include <log.h> #include <malloc.h> +#include <menu.h> #include <video.h> +#include <watchdog.h> +#include <linux/delay.h> #include "scene_internal.h" int expo_new(const char *name, void *priv, struct expo **expp) @@ -30,6 +34,7 @@ int expo_new(const char *name, void *priv, struct expo **expp) INIT_LIST_HEAD(&exp->scene_head); INIT_LIST_HEAD(&exp->str_head); exp->next_id = EXPOID_BASE_ID; + cli_ch_init(&exp->cch); *expp = exp; @@ -81,7 +86,7 @@ int expo_str(struct expo *exp, const char *name, uint id, const char *str) return log_msg_ret("obj", -ENOMEM); estr->id = resolve_id(exp, id); - estr->str = str; + abuf_init_const(&estr->buf, str, strlen(str) + 1); list_add_tail(&estr->sibling, &exp->str_head); return estr->id; @@ -93,12 +98,33 @@ const char *expo_get_str(struct expo *exp, uint id) list_for_each_entry(estr, &exp->str_head, sibling) { if (estr->id == id) - return estr->str; + return estr->buf.data; } return NULL; } +int expo_edit_str(struct expo *exp, uint id, struct abuf *orig, + struct abuf **copyp) +{ + struct expo_string *estr; + struct abuf old; + + list_for_each_entry(estr, &exp->str_head, sibling) { + if (estr->id == id) { + old = estr->buf; + if (!abuf_copy(&old, &estr->buf)) + return -ENOMEM; + *copyp = &estr->buf; + if (orig) + *orig = old; + return 0; + } + } + + return -ENOENT; +} + int expo_set_display(struct expo *exp, struct udevice *dev) { struct udevice *cons; @@ -251,6 +277,7 @@ int expo_apply_theme(struct expo *exp, ofnode node) { struct scene *scn; struct expo_theme *theme = &exp->theme; + bool white_on_black; int ret; log_debug("Applying theme %s\n", ofnode_get_name(node)); @@ -261,6 +288,9 @@ int expo_apply_theme(struct expo *exp, ofnode node) ofnode_read_u32(node, "menuitem-gap-y", &theme->menuitem_gap_y); ofnode_read_u32(node, "menu-title-margin-x", &theme->menu_title_margin_x); + white_on_black = ofnode_read_bool(node, "white-on-black"); + if (exp->display) + video_set_white_on_black(exp->display, white_on_black); list_for_each_entry(scn, &exp->scene_head, sibling) { ret = scene_apply_theme(scn, theme); @@ -285,3 +315,41 @@ int expo_iter_scene_objs(struct expo *exp, expo_scene_obj_iterator iter, return 0; } + +int expo_poll(struct expo *exp, struct expo_action *act) +{ + int ichar, key, ret; + + ichar = cli_ch_process(&exp->cch, 0); + if (!ichar) { + int i; + + for (i = 0; i < 10 && !ichar && !tstc(); i++) { + schedule(); + mdelay(2); + ichar = cli_ch_process(&exp->cch, -ETIMEDOUT); + } + while (!ichar && tstc()) { + ichar = getchar(); + ichar = cli_ch_process(&exp->cch, ichar); + } + } + + key = 0; + if (ichar) { + key = bootmenu_conv_key(ichar); + if (key == BKEY_NONE || key >= BKEY_FIRST_EXTRA) + key = ichar; + } + if (!key) + return -EAGAIN; + + ret = expo_send_key(exp, key); + if (ret) + return log_msg_ret("epk", ret); + ret = expo_action_get(exp, act); + if (ret) + return log_msg_ret("eag", ret); + + return 0; +} diff --git a/boot/fdt_support.c b/boot/fdt_support.c index 92f2f534ee0..b7331bb76b3 100644 --- a/boot/fdt_support.c +++ b/boot/fdt_support.c @@ -224,15 +224,24 @@ int fdt_initrd(void *fdt, ulong initrd_start, ulong initrd_end) int is_u64; uint64_t addr, size; - /* just return if the size of initrd is zero */ - if (initrd_start == initrd_end) - return 0; - /* find or create "/chosen" node. */ nodeoffset = fdt_find_or_add_subnode(fdt, 0, "chosen"); if (nodeoffset < 0) return nodeoffset; + /* + * Although we didn't setup an initrd, there could be a stale + * initrd setting from the previous boot firmware in the live + * device tree. So, make sure there is no setting left if we + * don't want an initrd. + */ + if (initrd_start == initrd_end) { + fdt_delprop(fdt, nodeoffset, "linux,initrd-start"); + fdt_delprop(fdt, nodeoffset, "linux,initrd-end"); + + return 0; + } + total = fdt_num_mem_rsv(fdt); /* diff --git a/boot/image-android.c b/boot/image-android.c index 459cdb8456c..1cd2060bb3f 100644 --- a/boot/image-android.c +++ b/boot/image-android.c @@ -268,7 +268,8 @@ static ulong android_image_get_kernel_addr(struct andr_image_data *img_data, * * Otherwise, we will return the actual value set by the user. */ - if (img_data->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR) { + if (img_data->kernel_addr == ANDROID_IMAGE_DEFAULT_KERNEL_ADDR || + IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR)) { if (comp == IH_COMP_NONE) return img_data->kernel_ptr; return env_get_ulong("kernel_addr_r", 16, 0); @@ -464,7 +465,8 @@ int android_image_get_ramdisk(const void *hdr, const void *vendor_boot_img, */ if (img_data.header_version > 2) { /* Ramdisk can't be used in-place, copy it to ramdisk_addr_r */ - if (img_data.ramdisk_addr == ANDROID_IMAGE_DEFAULT_RAMDISK_ADDR) { + if (img_data.ramdisk_addr == ANDROID_IMAGE_DEFAULT_RAMDISK_ADDR || + IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR)) { ramdisk_ptr = env_get_ulong("ramdisk_addr_r", 16, 0); if (!ramdisk_ptr) { printf("Invalid ramdisk_addr_r to copy ramdisk into\n"); @@ -489,7 +491,8 @@ int android_image_get_ramdisk(const void *hdr, const void *vendor_boot_img, /* Ramdisk can be used in-place, use current ptr */ if (img_data.ramdisk_addr == 0 || img_data.ramdisk_addr == ANDROID_IMAGE_DEFAULT_RAMDISK_ADDR || - img_data.ramdisk_addr == img_data.kernel_addr) { + img_data.ramdisk_addr == img_data.kernel_addr || + IS_ENABLED(CONFIG_ANDROID_BOOT_IMAGE_IGNORE_BLOB_ADDR)) { *rd_data = img_data.ramdisk_ptr; } else { ramdisk_ptr = img_data.ramdisk_addr; @@ -677,7 +680,7 @@ bool android_image_get_dtb_by_index(ulong hdr_addr, ulong vendor_boot_img, { struct andr_image_data img_data; const struct andr_boot_img_hdr_v0 *hdr; - const struct andr_vnd_boot_img_hdr *vhdr; + const struct andr_vnd_boot_img_hdr *vhdr = NULL; hdr = map_sysmem(hdr_addr, sizeof(*hdr)); if (vendor_boot_img != -1) diff --git a/boot/image-board.c b/boot/image-board.c index 514f8e63f9c..005d60caf5c 100644 --- a/boot/image-board.c +++ b/boot/image-board.c @@ -16,6 +16,7 @@ #include <fpga.h> #include <image.h> #include <init.h> +#include <lmb.h> #include <log.h> #include <mapmem.h> #include <rtc.h> @@ -538,6 +539,7 @@ int boot_get_ramdisk(char const *select, struct bootm_headers *images, int boot_ramdisk_high(ulong rd_data, ulong rd_len, ulong *initrd_start, ulong *initrd_end) { + int err; char *s; phys_addr_t initrd_high; int initrd_copy_to_ram = 1; @@ -559,25 +561,30 @@ int boot_ramdisk_high(ulong rd_data, ulong rd_len, ulong *initrd_start, if (rd_data) { if (!initrd_copy_to_ram) { /* zero-copy ramdisk support */ + phys_addr_t initrd_addr; + debug(" in-place initrd\n"); *initrd_start = rd_data; *initrd_end = rd_data + rd_len; - lmb_reserve(rd_data, rd_len, LMB_NONE); + initrd_addr = (phys_addr_t)rd_data; + err = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &initrd_addr, + rd_len, LMB_NONE); + if (err) { + puts("in-place initrd alloc failed\n"); + goto error; + } } else { - if (initrd_high) - *initrd_start = - (ulong)lmb_alloc_base(rd_len, - 0x1000, - initrd_high, - LMB_NONE); - else - *initrd_start = (ulong)lmb_alloc(rd_len, - 0x1000); + enum lmb_mem_type type = initrd_high ? + LMB_MEM_ALLOC_MAX : LMB_MEM_ALLOC_ANY; - if (*initrd_start == 0) { + err = lmb_alloc_mem(type, 0x1000, &initrd_high, rd_len, + LMB_NONE); + if (err) { puts("ramdisk - allocation error\n"); goto error; } + + *initrd_start = (ulong)initrd_high; bootstage_mark(BOOTSTAGE_ID_COPY_RAMDISK); *initrd_end = *initrd_start + rd_len; @@ -828,9 +835,10 @@ int boot_get_loadable(struct bootm_headers *images) */ int boot_get_cmdline(ulong *cmd_start, ulong *cmd_end) { - int barg; + int barg, err; char *cmdline; char *s; + phys_addr_t addr; /* * Help the compiler detect that this function is only called when @@ -840,12 +848,14 @@ int boot_get_cmdline(ulong *cmd_start, ulong *cmd_end) return 0; barg = IF_ENABLED_INT(CONFIG_SYS_BOOT_GET_CMDLINE, CONFIG_SYS_BARGSIZE); - cmdline = (char *)(ulong)lmb_alloc_base(barg, 0xf, - env_get_bootm_mapsize() + env_get_bootm_low(), - LMB_NONE); - if (!cmdline) + addr = env_get_bootm_mapsize() + env_get_bootm_low(); + + err = lmb_alloc_mem(LMB_MEM_ALLOC_MAX, 0xf, &addr, barg, LMB_NONE); + if (err) return -1; + cmdline = (char *)(uintptr_t)addr; + s = env_get("bootargs"); if (!s) s = ""; @@ -874,14 +884,16 @@ int boot_get_cmdline(ulong *cmd_start, ulong *cmd_end) */ int boot_get_kbd(struct bd_info **kbd) { - *kbd = (struct bd_info *)(ulong)lmb_alloc_base(sizeof(struct bd_info), - 0xf, - env_get_bootm_mapsize() + - env_get_bootm_low(), - LMB_NONE); - if (!*kbd) + int err; + phys_addr_t addr; + + addr = env_get_bootm_mapsize() + env_get_bootm_low(); + err = lmb_alloc_mem(LMB_MEM_ALLOC_MAX, 0xf, &addr, + sizeof(struct bd_info), LMB_NONE); + if (err) return -1; + *kbd = (struct bd_info *)(uintptr_t)addr; **kbd = *gd->bd; debug("## kernel board info at 0x%08lx\n", (ulong)*kbd); diff --git a/boot/image-fdt.c b/boot/image-fdt.c index 8f718ad29f6..3f0ac54f76f 100644 --- a/boot/image-fdt.c +++ b/boot/image-fdt.c @@ -72,13 +72,15 @@ static const struct legacy_img_hdr *image_get_fdt(ulong fdt_addr) static void boot_fdt_reserve_region(u64 addr, u64 size, u32 flags) { long ret; + phys_addr_t rsv_addr; - ret = lmb_reserve(addr, size, flags); + rsv_addr = (phys_addr_t)addr; + ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &rsv_addr, size, flags); if (!ret) { debug(" reserving fdt memory region: addr=%llx size=%llx flags=%x\n", (unsigned long long)addr, (unsigned long long)size, flags); - } else if (ret != -EEXIST) { + } else if (ret != -EEXIST && ret != -EINVAL) { puts("ERROR: reserving fdt memory region failed "); printf("(addr=%llx size=%llx flags=%x)\n", (unsigned long long)addr, @@ -155,7 +157,7 @@ void boot_fdt_add_mem_rsv_regions(void *fdt_blob) */ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size) { - u64 start, size, usable, addr, low, mapsize; + u64 start, size, usable, low, mapsize; void *fdt_blob = *of_flat_tree; void *of_start = NULL; char *fdt_high; @@ -163,6 +165,7 @@ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size) int bank; int err; int disable_relocation = 0; + phys_addr_t addr; /* nothing to do */ if (*of_size == 0) @@ -180,23 +183,32 @@ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size) /* If fdt_high is set use it to select the relocation address */ fdt_high = env_get("fdt_high"); if (fdt_high) { - ulong desired_addr = hextoul(fdt_high, NULL); + ulong high_addr = hextoul(fdt_high, NULL); - if (desired_addr == ~0UL) { + if (high_addr == ~0UL) { /* All ones means use fdt in place */ of_start = fdt_blob; - lmb_reserve(map_to_sysmem(of_start), of_len, LMB_NONE); - disable_relocation = 1; - } else if (desired_addr) { - addr = lmb_alloc_base(of_len, 0x1000, desired_addr, - LMB_NONE); - of_start = map_sysmem(addr, of_len); - if (of_start == NULL) { - puts("Failed using fdt_high value for Device Tree"); + addr = map_to_sysmem(fdt_blob); + err = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &addr, + of_len, LMB_NONE); + if (err) { + printf("Failed to reserve memory for fdt at %#llx\n", + (u64)addr); goto error; } + + disable_relocation = 1; } else { - addr = lmb_alloc(of_len, 0x1000); + enum lmb_mem_type type = high_addr ? + LMB_MEM_ALLOC_MAX : LMB_MEM_ALLOC_ANY; + + addr = high_addr; + err = lmb_alloc_mem(type, 0x1000, &addr, of_len, + LMB_NONE); + if (err) { + puts("Failed to allocate memory for Device Tree relocation\n"); + goto error; + } of_start = map_sysmem(addr, of_len); } } else { @@ -218,11 +230,15 @@ int boot_relocate_fdt(char **of_flat_tree, ulong *of_size) * for LMB allocation. */ usable = min(start + size, low + mapsize); - addr = lmb_alloc_base(of_len, 0x1000, usable, LMB_NONE); - of_start = map_sysmem(addr, of_len); - /* Allocation succeeded, use this block. */ - if (of_start != NULL) - break; + addr = usable; + err = lmb_alloc_mem(LMB_MEM_ALLOC_MAX, 0x1000, + &addr, of_len, LMB_NONE); + if (!err) { + of_start = map_sysmem(addr, of_len); + /* Allocation succeeded, use this block. */ + if (of_start) + break; + } /* * Reduce the mapping size in the next bank @@ -571,6 +587,7 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) { ulong *initrd_start = &images->initrd_start; ulong *initrd_end = &images->initrd_end; + bool skip_board_fixup = false; int ret, fdt_ret, of_size; if (IS_ENABLED(CONFIG_OF_ENV_SETUP)) { @@ -621,18 +638,18 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) fdt_fixup_pstore(blob); #endif if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) { - const char *skip_board_fixup; + skip_board_fixup = (env_get_ulong("skip_board_fixup", 10, 0) == 1); - skip_board_fixup = env_get("skip_board_fixup"); - if (skip_board_fixup && ((int)simple_strtol(skip_board_fixup, NULL, 10) == 1)) { - printf("skip board fdt fixup\n"); - } else { - fdt_ret = ft_board_setup(blob, gd->bd); - if (fdt_ret) { - printf("ERROR: board-specific fdt fixup failed: %s\n", - fdt_strerror(fdt_ret)); - goto err; - } + if (skip_board_fixup) + printf("skip all board fdt fixup\n"); + } + + if (IS_ENABLED(CONFIG_OF_BOARD_SETUP) && !skip_board_fixup) { + fdt_ret = ft_board_setup(blob, gd->bd); + if (fdt_ret) { + printf("ERROR: board-specific fdt fixup failed: %s\n", + fdt_strerror(fdt_ret)); + goto err; } } if (IS_ENABLED(CONFIG_OF_SYSTEM_SETUP)) { @@ -674,7 +691,7 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) /* Delete the old LMB reservation */ if (CONFIG_IS_ENABLED(LMB) && lmb) - lmb_free(map_to_sysmem(blob), fdt_totalsize(blob)); + lmb_free(map_to_sysmem(blob), fdt_totalsize(blob), LMB_NONE); ret = fdt_shrink_to_minimum(blob, 0); if (ret < 0) @@ -682,13 +699,20 @@ int image_setup_libfdt(struct bootm_headers *images, void *blob, bool lmb) of_size = ret; /* Create a new LMB reservation */ - if (CONFIG_IS_ENABLED(LMB) && lmb) - lmb_reserve(map_to_sysmem(blob), of_size, LMB_NONE); + if (CONFIG_IS_ENABLED(LMB) && lmb) { + phys_addr_t fdt_addr; + + fdt_addr = map_to_sysmem(blob); + ret = lmb_alloc_mem(LMB_MEM_ALLOC_ADDR, 0, &fdt_addr, + of_size, LMB_NONE); + if (ret) { + printf("Failed to reserve memory for the fdt at %#llx\n", + (u64)fdt_addr); + } + } -#if defined(CONFIG_ARCH_KEYSTONE) - if (IS_ENABLED(CONFIG_OF_BOARD_SETUP)) + if (IS_ENABLED(CONFIG_OF_BOARD_SETUP_EXTENDED) && !skip_board_fixup) ft_board_setup_ex(blob, gd->bd); -#endif return 0; err: diff --git a/boot/image-pre-load.c b/boot/image-pre-load.c index adf3b341a20..2f851ebb28c 100644 --- a/boot/image-pre-load.c +++ b/boot/image-pre-load.c @@ -7,6 +7,7 @@ #include "mkimage.h" #else #include <asm/global_data.h> +#include <env.h> #include <mapmem.h> DECLARE_GLOBAL_DATA_PTR; #endif /* !USE_HOSTCC*/ diff --git a/boot/image.c b/boot/image.c index 139c5bd035a..abac2c7034b 100644 --- a/boot/image.c +++ b/boot/image.c @@ -184,6 +184,7 @@ static const table_entry_t uimage_type[] = { { IH_TYPE_RENESAS_SPKG, "spkgimage", "Renesas SPKG Image" }, { IH_TYPE_STARFIVE_SPL, "sfspl", "StarFive SPL Image" }, { IH_TYPE_TFA_BL31, "tfa-bl31", "TFA BL31 Image", }, + { IH_TYPE_STM32IMAGE_V2, "stm32imagev2", "STMicroelectronics STM32 Image V2.0" }, { -1, "", "", }, }; diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 82f217aaf86..eb4d7723481 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -1348,7 +1348,7 @@ static int parse_pxefile_top(struct pxe_context *ctx, char *p, unsigned long bas case T_ONTIMEOUT: err = parse_sliteral(&p, &label_name); - if (label_name) { + if (err >= 0 && label_name) { if (cfg->default_label) free(cfg->default_label); @@ -1360,7 +1360,7 @@ static int parse_pxefile_top(struct pxe_context *ctx, char *p, unsigned long bas case T_FALLBACK: err = parse_sliteral(&p, &label_name); - if (label_name) { + if (err >= 0 && label_name) { if (cfg->fallback_label) free(cfg->fallback_label); diff --git a/boot/scene.c b/boot/scene.c index fb82ffe768c..fa8f540bfb0 100644 --- a/boot/scene.c +++ b/boot/scene.c @@ -8,6 +8,7 @@ #define LOG_CATEGORY LOGC_EXPO +#include <alist.h> #include <dm.h> #include <expo.h> #include <malloc.h> @@ -31,8 +32,7 @@ int scene_new(struct expo *exp, const char *name, uint id, struct scene **scnp) return log_msg_ret("name", -ENOMEM); } - abuf_init(&scn->buf); - if (!abuf_realloc(&scn->buf, EXPO_MAX_CHARS + 1)) { + if (!abuf_init_size(&scn->buf, EXPO_MAX_CHARS + 1)) { free(scn->name); free(scn); return log_msg_ret("buf", -ENOMEM); @@ -143,6 +143,32 @@ int scene_img(struct scene *scn, const char *name, uint id, char *data, return img->obj.id; } +int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, + const char *name, uint str_id, const char *str) +{ + int ret; + + if (str) { + ret = expo_str(exp, name, str_id, str); + if (ret < 0) + return log_msg_ret("str", ret); + if (str_id && ret != str_id) + return log_msg_ret("id", -EEXIST); + str_id = ret; + } else { + ret = resolve_id(exp, str_id); + if (ret < 0) + return log_msg_ret("nst", ret); + if (str_id && ret != str_id) + return log_msg_ret("nid", -EEXIST); + } + + gen->str_id = str_id; + alist_init_struct(&gen->lines, struct vidconsole_mline); + + return 0; +} + int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, struct scene_obj_txt **txtp) { @@ -155,8 +181,9 @@ int scene_txt(struct scene *scn, const char *name, uint id, uint str_id, if (ret < 0) return log_msg_ret("obj", ret); - txt->str_id = str_id; - + ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, NULL); + if (ret) + return log_msg_ret("stg", ret); if (txtp) *txtp = txt; @@ -169,27 +196,41 @@ int scene_txt_str(struct scene *scn, const char *name, uint id, uint str_id, struct scene_obj_txt *txt; int ret; - ret = expo_str(scn->expo, name, str_id, str); - if (ret < 0) - return log_msg_ret("str", ret); - if (str_id && ret != str_id) - return log_msg_ret("id", -EEXIST); - str_id = ret; - ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXT, sizeof(struct scene_obj_txt), (struct scene_obj **)&txt); if (ret < 0) return log_msg_ret("obj", ret); - txt->str_id = str_id; - + ret = scene_txt_generic_init(scn->expo, &txt->gen, name, str_id, str); + if (ret) + return log_msg_ret("tsg", ret); if (txtp) *txtp = txt; return txt->obj.id; } +int scene_box(struct scene *scn, const char *name, uint id, uint width, + struct scene_obj_box **boxp) +{ + struct scene_obj_box *box; + int ret; + + ret = scene_obj_add(scn, name, id, SCENEOBJT_BOX, + sizeof(struct scene_obj_box), + (struct scene_obj **)&box); + if (ret < 0) + return log_msg_ret("obj", ret); + + box->width = width; + + if (boxp) + *boxp = box; + + return box->obj.id; +} + int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, uint font_size) { @@ -198,8 +239,8 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, txt = scene_obj_find(scn, id, SCENEOBJT_TEXT); if (!txt) return log_msg_ret("find", -ENOENT); - txt->font_name = font_name; - txt->font_size = font_size; + txt->gen.font_name = font_name; + txt->gen.font_size = font_size; return 0; } @@ -207,12 +248,17 @@ int scene_txt_set_font(struct scene *scn, uint id, const char *font_name, int scene_obj_set_pos(struct scene *scn, uint id, int x, int y) { struct scene_obj *obj; + int w, h; obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); - obj->dim.x = x; - obj->dim.y = y; + w = obj->bbox.x1 - obj->bbox.x0; + h = obj->bbox.y1 - obj->bbox.y0; + obj->bbox.x0 = x; + obj->bbox.y0 = y; + obj->bbox.x1 = obj->bbox.x0 + w; + obj->bbox.y1 = obj->bbox.y0 + h; return 0; } @@ -224,8 +270,62 @@ int scene_obj_set_size(struct scene *scn, uint id, int w, int h) obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("find", -ENOENT); - obj->dim.w = w; - obj->dim.h = h; + obj->bbox.x1 = obj->bbox.x0 + w; + obj->bbox.y1 = obj->bbox.y0 + h; + obj->flags |= SCENEOF_SIZE_VALID; + + return 0; +} + +int scene_obj_set_width(struct scene *scn, uint id, int w) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + obj->bbox.x1 = obj->bbox.x0 + w; + + return 0; +} + +int scene_obj_set_bbox(struct scene *scn, uint id, int x0, int y0, int x1, + int y1) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("find", -ENOENT); + obj->bbox.x0 = x0; + obj->bbox.y0 = y0; + obj->bbox.x1 = x1; + obj->bbox.y1 = y1; + obj->flags |= SCENEOF_SIZE_VALID; + + return 0; +} + +int scene_obj_set_halign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osh", -ENOENT); + obj->horiz = aln; + + return 0; +} + +int scene_obj_set_valign(struct scene *scn, uint id, enum scene_obj_align aln) +{ + struct scene_obj *obj; + + obj = scene_obj_find(scn, id, SCENEOBJT_NONE); + if (!obj) + return log_msg_ret("osv", -ENOENT); + obj->vert = aln; return 0; } @@ -255,6 +355,49 @@ int scene_obj_flag_clrset(struct scene *scn, uint id, uint clr, uint set) return 0; } +static void handle_alignment(enum scene_obj_align horiz, + enum scene_obj_align vert, + struct scene_obj_bbox *bbox, + struct scene_obj_dims *dims, + int xsize, int ysize, + struct scene_obj_offset *offset) +{ + int width, height; + + if (bbox->x1 == SCENEOB_DISPLAY_MAX) + bbox->x1 = xsize ?: 1280; + if (bbox->y1 == SCENEOB_DISPLAY_MAX) + bbox->y1 = ysize ?: 1024; + + width = bbox->x1 - bbox->x0; + height = bbox->y1 - bbox->y0; + + switch (horiz) { + case SCENEOA_CENTRE: + offset->xofs = (width - dims->x) / 2; + break; + case SCENEOA_RIGHT: + offset->xofs = width - dims->x; + break; + case SCENEOA_LEFT: + offset->xofs = 0; + break; + } + + switch (vert) { + case SCENEOA_CENTRE: + offset->yofs = (height - dims->y) / 2; + break; + case SCENEOA_BOTTOM: + offset->yofs = height - dims->y; + break; + case SCENEOA_TOP: + default: + offset->yofs = 0; + break; + } +} + int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) { struct scene_obj *obj; @@ -267,6 +410,7 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) case SCENEOBJT_NONE: case SCENEOBJT_MENU: case SCENEOBJT_TEXTLINE: + case SCENEOBJT_BOX: break; case SCENEOBJT_IMAGE: { struct scene_obj_img *img = (struct scene_obj_img *)obj; @@ -278,14 +422,20 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) *widthp = width; return height; } - case SCENEOBJT_TEXT: { - struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; + case SCENEOBJT_TEXT: + case SCENEOBJT_TEXTEDIT: { + struct scene_txt_generic *gen; struct expo *exp = scn->expo; struct vidconsole_bbox bbox; + int len, ret, limit; const char *str; - int len, ret; - str = expo_get_str(exp, txt->str_id); + if (obj->type == SCENEOBJT_TEXT) + gen = &((struct scene_obj_txt *)obj)->gen; + else + gen = &((struct scene_obj_txtedit *)obj)->gen; + + str = expo_get_str(exp, gen->str_id); if (!str) return log_msg_ret("str", -ENOENT); len = strlen(str); @@ -297,8 +447,12 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) return 16; } - ret = vidconsole_measure(scn->expo->cons, txt->font_name, - txt->font_size, str, -1, &bbox, NULL); + limit = obj->flags & SCENEOF_SIZE_VALID ? + obj->bbox.x1 - obj->bbox.x0 : -1; + + ret = vidconsole_measure(scn->expo->cons, gen->font_name, + gen->font_size, str, limit, &bbox, + &gen->lines); if (ret) return log_msg_ret("mea", ret); if (widthp) @@ -317,12 +471,14 @@ int scene_obj_get_hw(struct scene *scn, uint id, int *widthp) * @obj: Object to render * @box_only: true to show a box around the object, but keep the normal * background colour inside + * @cur_item: true to render the background only for the current menu item */ -static void scene_render_background(struct scene_obj *obj, bool box_only) +static void scene_render_background(struct scene_obj *obj, bool box_only, + bool cur_item) { + struct vidconsole_bbox bbox[SCENEBB_count], *sel; struct expo *exp = obj->scene->expo; const struct expo_theme *theme = &exp->theme; - struct vidconsole_bbox bbox, label_bbox; struct udevice *dev = exp->display; struct video_priv *vid_priv; struct udevice *cons = exp->cons; @@ -341,24 +497,113 @@ static void scene_render_background(struct scene_obj *obj, bool box_only) } /* see if this object wants to render a background */ - if (scene_obj_calc_bbox(obj, &bbox, &label_bbox)) + if (scene_obj_calc_bbox(obj, bbox)) + return; + + sel = cur_item ? &bbox[SCENEBB_curitem] : &bbox[SCENEBB_label]; + if (!sel->valid) return; vidconsole_push_colour(cons, fore, back, &old); - video_fill_part(dev, label_bbox.x0 - inset, label_bbox.y0 - inset, - label_bbox.x1 + inset, label_bbox.y1 + inset, + video_fill_part(dev, sel->x0 - inset, sel->y0 - inset, + sel->x1 + inset, sel->y1 + inset, vid_priv->colour_fg); vidconsole_pop_colour(cons, &old); if (box_only) { - video_fill_part(dev, label_bbox.x0, label_bbox.y0, - label_bbox.x1, label_bbox.y1, + video_fill_part(dev, sel->x0, sel->y0, sel->x1, sel->y1, vid_priv->colour_bg); } } +static int scene_txt_render(struct expo *exp, struct udevice *dev, + struct udevice *cons, struct scene_obj *obj, + struct scene_txt_generic *gen, int x, int y, + int menu_inset) +{ + const struct vidconsole_mline *mline, *last; + struct video_priv *vid_priv; + struct vidconsole_colour old; + enum colour_idx fore, back; + struct scene_obj_dims dims; + struct scene_obj_bbox bbox; + const char *str; + int ret; + + if (!cons) + return -ENOTSUPP; + + if (gen->font_name || gen->font_size) { + ret = vidconsole_select_font(cons, gen->font_name, + gen->font_size); + } else { + ret = vidconsole_select_font(cons, NULL, 0); + } + if (ret && ret != -ENOSYS) + return log_msg_ret("font", ret); + str = expo_get_str(exp, gen->str_id); + if (!str) + return 0; + + vid_priv = dev_get_uclass_priv(dev); + if (vid_priv->white_on_black) { + fore = VID_BLACK; + back = VID_WHITE; + } else { + fore = VID_LIGHT_GRAY; + back = VID_BLACK; + } + + if (obj->flags & SCENEOF_POINT) { + int inset; + + inset = exp->popup ? menu_inset : 0; + vidconsole_push_colour(cons, fore, back, &old); + video_fill_part(dev, x - inset, y, + obj->bbox.x1, obj->bbox.y1, + vid_priv->colour_bg); + } + + mline = alist_get(&gen->lines, 0, typeof(*mline)); + last = alist_get(&gen->lines, gen->lines.count - 1, typeof(*mline)); + if (mline) + dims.y = last->bbox.y1 - mline->bbox.y0; + bbox.y0 = obj->bbox.y0; + bbox.y1 = obj->bbox.y1; + + if (!mline) { + vidconsole_set_cursor_pos(cons, x, y); + vidconsole_put_string(cons, str); + } + + alist_for_each(mline, &gen->lines) { + struct scene_obj_offset offset; + + bbox.x0 = obj->bbox.x0; + bbox.x1 = obj->bbox.x1; + dims.x = mline->bbox.x1 - mline->bbox.x0; + handle_alignment(obj->horiz, obj->vert, &bbox, &dims, + obj->bbox.x1 - obj->bbox.x0, + obj->bbox.y1 - obj->bbox.y0, &offset); + + x = obj->bbox.x0 + offset.xofs; + y = obj->bbox.y0 + offset.yofs + mline->bbox.y0; + if (y > bbox.y1) + break; /* clip this line and any following */ + vidconsole_set_cursor_pos(cons, x, y); + vidconsole_put_stringn(cons, str + mline->start, mline->len); + } + if (obj->flags & SCENEOF_POINT) + vidconsole_pop_colour(cons, &old); + + return 0; +} + /** * scene_obj_render() - Render an object * + * @obj: Object to render + * @text_mode: true to use text mode + * Return: 0 if OK, -ve on error */ static int scene_obj_render(struct scene_obj *obj, bool text_mode) { @@ -367,10 +612,12 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) const struct expo_theme *theme = &exp->theme; struct udevice *dev = exp->display; struct udevice *cons = text_mode ? NULL : exp->cons; + struct video_priv *vid_priv; int x, y, ret; - x = obj->dim.x; - y = obj->dim.y; + y = obj->bbox.y0; + x = obj->bbox.x0 + obj->ofs.xofs; + vid_priv = dev_get_uclass_priv(dev); switch (obj->type) { case SCENEOBJT_NONE: @@ -388,59 +635,26 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) } case SCENEOBJT_TEXT: { struct scene_obj_txt *txt = (struct scene_obj_txt *)obj; - const char *str; - - if (!cons) - return -ENOTSUPP; - if (txt->font_name || txt->font_size) { - ret = vidconsole_select_font(cons, - txt->font_name, - txt->font_size); - } else { - ret = vidconsole_select_font(cons, NULL, 0); - } - if (ret && ret != -ENOSYS) - return log_msg_ret("font", ret); - str = expo_get_str(exp, txt->str_id); - if (str) { - struct video_priv *vid_priv; - struct vidconsole_colour old; - enum colour_idx fore, back; - - vid_priv = dev_get_uclass_priv(dev); - if (vid_priv->white_on_black) { - fore = VID_BLACK; - back = VID_WHITE; - } else { - fore = VID_LIGHT_GRAY; - back = VID_BLACK; - } - - if (obj->flags & SCENEOF_POINT) { - vidconsole_push_colour(cons, fore, back, &old); - video_fill_part(dev, x - theme->menu_inset, y, - x + obj->dim.w, - y + obj->dim.h, - vid_priv->colour_bg); - } - vidconsole_set_cursor_pos(cons, x, y); - vidconsole_put_string(cons, str); - if (obj->flags & SCENEOF_POINT) - vidconsole_pop_colour(cons, &old); - } + ret = scene_txt_render(exp, dev, cons, obj, &txt->gen, x, y, + theme->menu_inset); break; } case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; - if (exp->popup && (obj->flags & SCENEOF_OPEN)) { - if (!cons) - return -ENOTSUPP; + if (exp->popup) { + if (obj->flags & SCENEOF_OPEN) { + if (!cons) + return -ENOTSUPP; - /* draw a background behind the menu items */ - scene_render_background(obj, false); + /* draw a background behind the menu items */ + scene_render_background(obj, false, false); + } + } else if (exp->show_highlight) { + /* do nothing */ } + /* * With a vidconsole, the text and item pointer are rendered as * normal objects so we don't need to do anything here. The menu @@ -457,9 +671,23 @@ static int scene_obj_render(struct scene_obj *obj, bool text_mode) } case SCENEOBJT_TEXTLINE: if (obj->flags & SCENEOF_OPEN) - scene_render_background(obj, true); + scene_render_background(obj, true, false); + break; + case SCENEOBJT_BOX: { + struct scene_obj_box *box = (struct scene_obj_box *)obj; + + video_draw_box(dev, obj->bbox.x0, obj->bbox.y0, obj->bbox.x1, + obj->bbox.y1, box->width, vid_priv->colour_fg); break; } + case SCENEOBJT_TEXTEDIT: { + struct scene_obj_txtedit *ted = (struct scene_obj_txtedit *)obj; + + ret = scene_txt_render(exp, dev, cons, obj, &ted->gen, x, y, + theme->menu_inset); + break; + } + } return 0; } @@ -477,6 +705,8 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -510,18 +740,33 @@ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr) int scene_arrange(struct scene *scn) { struct expo_arrange_info arr; + int xsize = 0, ysize = 0; struct scene_obj *obj; + struct udevice *dev; int ret; + dev = scn->expo->display; + if (dev) { + struct video_priv *priv = dev_get_uclass_priv(dev); + + xsize = priv->xsize; + ysize = priv->ysize; + } + ret = scene_calc_arrange(scn, &arr); if (ret < 0) return log_msg_ret("arr", ret); list_for_each_entry(obj, &scn->obj_head, sibling) { + handle_alignment(obj->horiz, obj->vert, &obj->bbox, &obj->dims, + xsize, ysize, &obj->ofs); + switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -567,6 +812,8 @@ int scene_render_deps(struct scene *scn, uint id) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_MENU: scene_menu_render_deps(scn, @@ -686,6 +933,7 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: break; case SCENEOBJT_MENU: { struct scene_obj_menu *menu; @@ -705,6 +953,9 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return log_msg_ret("key", ret); break; } + case SCENEOBJT_TEXTEDIT: + /* TODO(sjg@chromium.org): Implement this */ + break; } return 0; } @@ -724,25 +975,27 @@ int scene_send_key(struct scene *scn, int key, struct expo_action *event) return 0; } -int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, - struct vidconsole_bbox *label_bbox) +int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox bbox[]) { switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: return -ENOSYS; case SCENEOBJT_MENU: { struct scene_obj_menu *menu = (struct scene_obj_menu *)obj; - scene_menu_calc_bbox(menu, bbox, label_bbox); + scene_menu_calc_bbox(menu, bbox); break; } case SCENEOBJT_TEXTLINE: { struct scene_obj_textline *tline; tline = (struct scene_obj_textline *)obj; - scene_textline_calc_bbox(tline, bbox, label_bbox); + scene_textline_calc_bbox(tline, &bbox[SCENEBB_all], + &bbox[SCENEBB_label]); break; } } @@ -759,6 +1012,8 @@ int scene_calc_dims(struct scene *scn, bool do_menus) switch (obj->type) { case SCENEOBJT_NONE: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: case SCENEOBJT_IMAGE: { int width; @@ -766,8 +1021,13 @@ int scene_calc_dims(struct scene *scn, bool do_menus) ret = scene_obj_get_hw(scn, obj->id, &width); if (ret < 0) return log_msg_ret("get", ret); - obj->dim.w = width; - obj->dim.h = ret; + obj->dims.x = width; + obj->dims.y = ret; + if (!(obj->flags & SCENEOF_SIZE_VALID)) { + obj->bbox.x1 = obj->bbox.x0 + width; + obj->bbox.y1 = obj->bbox.y0 + ret; + obj->flags |= SCENEOF_SIZE_VALID; + } } break; } @@ -812,8 +1072,13 @@ int scene_apply_theme(struct scene *scn, struct expo_theme *theme) case SCENEOBJT_NONE: case SCENEOBJT_IMAGE: case SCENEOBJT_MENU: + case SCENEOBJT_BOX: case SCENEOBJT_TEXTLINE: break; + case SCENEOBJT_TEXTEDIT: + scene_txted_set_font(scn, obj->id, NULL, + theme->font_size); + break; case SCENEOBJT_TEXT: scene_txt_set_font(scn, obj->id, NULL, theme->font_size); @@ -854,6 +1119,8 @@ static int scene_obj_open(struct scene *scn, struct scene_obj *obj) case SCENEOBJT_IMAGE: case SCENEOBJT_MENU: case SCENEOBJT_TEXT: + case SCENEOBJT_BOX: + case SCENEOBJT_TEXTEDIT: break; case SCENEOBJT_TEXTLINE: ret = scene_textline_open(scn, @@ -905,28 +1172,42 @@ int scene_iter_objs(struct scene *scn, expo_scene_obj_iterator iter, return 0; } +int scene_bbox_join(const struct vidconsole_bbox *src, int inset, + struct vidconsole_bbox *dst) +{ + if (dst->valid) { + dst->x0 = min(dst->x0, src->x0 - inset); + dst->y0 = min(dst->y0, src->y0); + dst->x1 = max(dst->x1, src->x1 + inset); + dst->y1 = max(dst->y1, src->y1); + } else { + dst->x0 = src->x0 - inset; + dst->y0 = src->y0; + dst->x1 = src->x1 + inset; + dst->y1 = src->y1; + dst->valid = true; + } + + return 0; +} + int scene_bbox_union(struct scene *scn, uint id, int inset, struct vidconsole_bbox *bbox) { struct scene_obj *obj; + struct vidconsole_bbox local; if (!id) return 0; obj = scene_obj_find(scn, id, SCENEOBJT_NONE); if (!obj) return log_msg_ret("obj", -ENOENT); - if (bbox->valid) { - bbox->x0 = min(bbox->x0, obj->dim.x - inset); - bbox->y0 = min(bbox->y0, obj->dim.y); - bbox->x1 = max(bbox->x1, obj->dim.x + obj->dim.w + inset); - bbox->y1 = max(bbox->y1, obj->dim.y + obj->dim.h); - } else { - bbox->x0 = obj->dim.x - inset; - bbox->y0 = obj->dim.y; - bbox->x1 = obj->dim.x + obj->dim.w + inset; - bbox->y1 = obj->dim.y + obj->dim.h; - bbox->valid = true; - } + local.x0 = obj->bbox.x0; + local.y0 = obj->bbox.y0; + local.x1 = obj->bbox.x1; + local.y1 = obj->bbox.y1; + local.valid = true; + scene_bbox_join(&local, inset, bbox); return 0; } diff --git a/boot/scene_internal.h b/boot/scene_internal.h index ec9008ea593..95927472875 100644 --- a/boot/scene_internal.h +++ b/boot/scene_internal.h @@ -9,11 +9,45 @@ #ifndef __SCENE_INTERNAL_H #define __SCENE_INTERNAL_H +#include <linux/types.h> + +struct expo; +struct expo_action; +struct expo_arrange_info; +struct expo_theme; +struct scene_obj; +struct scene_obj_menu; +struct scene_obj_textline; +struct scene_obj_txtedit; +struct scene_txt_generic; struct vidconsole_bbox; +enum scene_obj_t; + typedef int (*expo_scene_obj_iterator)(struct scene_obj *obj, void *priv); /** + * enum scene_bbox_t - Parts of an object which can have a bounding box + * + * Objects can provide any or all of these bounding boxes + * + * @SCENEBB_label: Menu-item label + * @SCENEBB_key: Menu-item key label + * @SCENEBB_desc: Menu-item Description + * @SCENEBB_curitem: Current item (pointed to) + * @SCENEBB_all: All the above objects combined + */ +enum scene_bbox_t { + SCENEBB_label, + SCENEBB_key, + SCENEBB_desc, + SCENEBB_curitem, + SCENEBB_all, + + SCENEBB_count, +}; + +/** * expo_lookup_scene_id() - Look up a scene ID * * @exp: Expo to use @@ -292,6 +326,19 @@ struct scene_menitem *scene_menuitem_find_val(const struct scene_obj_menu *menu, int val); /** + * scene_bbox_join() - update bouding box with a given src box + * + * Updates @dst so that it encompasses the bounding box @src + * + * @src: Input bounding box + * @inset: Amount of inset to use for width + * @dst: Bounding box to update + * Return: 0 if OK, -ve on error + */ +int scene_bbox_join(const struct vidconsole_bbox *src, int inset, + struct vidconsole_bbox *dst); + +/** * scene_bbox_union() - update bouding box with the demensions of an object * * Updates @bbox so that it encompasses the bounding box of object @id @@ -319,13 +366,11 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline); * scene_menu_calc_bbox() - Calculate bounding boxes for the menu * * @menu: Menu to process - * @bbox: Returns bounding box of menu including prompts - * @label_bbox: Returns bounding box of labels + * @bbox: List of bounding box to fill in * Return: 0 if OK, -ve on error */ void scene_menu_calc_bbox(struct scene_obj_menu *menu, - struct vidconsole_bbox *bbox, - struct vidconsole_bbox *label_bbox); + struct vidconsole_bbox *bbox); /** * scene_textline_calc_bbox() - Calculate bounding box for the textline @@ -343,12 +388,10 @@ void scene_textline_calc_bbox(struct scene_obj_textline *menu, * scene_obj_calc_bbox() - Calculate bounding boxes for an object * * @obj: Object to process - * @bbox: Returns bounding box of object including prompts - * @label_bbox: Returns bounding box of labels (active area) + * @bbox: Returns bounding boxes for object * Return: 0 if OK, -ve on error */ -int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox, - struct vidconsole_bbox *label_bbox); +int scene_obj_calc_bbox(struct scene_obj *obj, struct vidconsole_bbox *bbox); /** * scene_textline_open() - Open a textline object @@ -384,4 +427,16 @@ int scene_textline_close(struct scene *scn, struct scene_obj_textline *tline); */ int scene_calc_arrange(struct scene *scn, struct expo_arrange_info *arr); +/** + * scene_txt_generic_init() - Set up the generic part of a text object + * + * @exp: Expo containing the object + * @gen: Generic text info + * @name: Object name + * @str_id: String ID for the text + * @str: Initial text string for the object, or NULL to just use str_id + */ +int scene_txt_generic_init(struct expo *exp, struct scene_txt_generic *gen, + const char *name, uint str_id, const char *str); + #endif /* __SCENE_INTERNAL_H */ diff --git a/boot/scene_menu.c b/boot/scene_menu.c index 17150af145d..23404172093 100644 --- a/boot/scene_menu.c +++ b/boot/scene_menu.c @@ -87,7 +87,7 @@ struct scene_menitem *scene_menuitem_find_val(const struct scene_obj_menu *menu, static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) { struct scene *scn = menu->obj.scene; - const bool stack = scn->expo->popup; + const bool stack = scn->expo->show_highlight; const struct scene_menitem *item; int ret; @@ -102,15 +102,23 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) label = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE); ret = scene_obj_set_pos(scn, menu->pointer_id, - menu->obj.dim.x + 200, label->dim.y); + menu->obj.bbox.x0 + 200, label->bbox.y0); if (ret < 0) return log_msg_ret("ptr", ret); } if (stack) { + uint id; + int val; + point &= scn->highlight_id == menu->obj.id; - scene_obj_flag_clrset(scn, item->label_id, SCENEOF_POINT, - point ? SCENEOF_POINT : 0); + val = point ? SCENEOF_POINT : 0; + id = item->desc_id; + if (!id) + id = item->label_id; + if (!id) + id = item->key_id; + scene_obj_flag_clrset(scn, id, SCENEOF_POINT, val); } return 0; @@ -121,64 +129,98 @@ static int update_pointers(struct scene_obj_menu *menu, uint id, bool point) * * Sets the currently pointed-to / highlighted menu item */ -static void menu_point_to_item(struct scene_obj_menu *menu, uint item_id) +static int menu_point_to_item(struct scene_obj_menu *menu, uint item_id) { - if (menu->cur_item_id) - update_pointers(menu, menu->cur_item_id, false); + int ret; + + if (menu->cur_item_id) { + ret = update_pointers(menu, menu->cur_item_id, false); + if (ret) + return log_msg_ret("mpi", ret); + } menu->cur_item_id = item_id; - update_pointers(menu, item_id, true); + ret = update_pointers(menu, item_id, true); + if (ret) + return log_msg_ret("mpu", ret); + + return 0; } void scene_menu_calc_bbox(struct scene_obj_menu *menu, - struct vidconsole_bbox *bbox, - struct vidconsole_bbox *label_bbox) + struct vidconsole_bbox *bbox) { const struct expo_theme *theme = &menu->obj.scene->expo->theme; const struct scene_menitem *item; + int inset = theme->menu_inset; + int i; - bbox->valid = false; - scene_bbox_union(menu->obj.scene, menu->title_id, 0, bbox); + for (i = 0; i < SCENEBB_count; i++) + bbox[i].valid = false; - label_bbox->valid = false; + scene_bbox_union(menu->obj.scene, menu->title_id, 0, + &bbox[SCENEBB_all]); list_for_each_entry(item, &menu->item_head, sibling) { - scene_bbox_union(menu->obj.scene, item->label_id, - theme->menu_inset, bbox); - scene_bbox_union(menu->obj.scene, item->key_id, 0, bbox); - scene_bbox_union(menu->obj.scene, item->desc_id, 0, bbox); - scene_bbox_union(menu->obj.scene, item->preview_id, 0, bbox); - - /* Get the bounding box of all labels */ - scene_bbox_union(menu->obj.scene, item->label_id, - theme->menu_inset, label_bbox); + struct vidconsole_bbox local; + + local.valid = false; + scene_bbox_union(menu->obj.scene, item->label_id, inset, + &local); + scene_bbox_union(menu->obj.scene, item->key_id, 0, &local); + scene_bbox_union(menu->obj.scene, item->desc_id, 0, &local); + scene_bbox_union(menu->obj.scene, item->preview_id, 0, &local); + + scene_bbox_join(&local, 0, &bbox[SCENEBB_all]); + + /* Get the bounding box of all individual fields */ + scene_bbox_union(menu->obj.scene, item->label_id, inset, + &bbox[SCENEBB_label]); + scene_bbox_union(menu->obj.scene, item->key_id, inset, + &bbox[SCENEBB_key]); + scene_bbox_union(menu->obj.scene, item->desc_id, inset, + &bbox[SCENEBB_desc]); + + if (menu->cur_item_id == item->id) + scene_bbox_join(&local, 0, &bbox[SCENEBB_curitem]); } /* - * subtract the final menuitem's gap to keep the insert the same top - * and bottom + * subtract the final menuitem's gap to keep the inset the same top and + * bottom */ - label_bbox->y1 -= theme->menuitem_gap_y; + bbox[SCENEBB_label].y1 -= theme->menuitem_gap_y; } int scene_menu_calc_dims(struct scene_obj_menu *menu) { - struct vidconsole_bbox bbox, label_bbox; + struct vidconsole_bbox bbox[SCENEBB_count], *cur; const struct scene_menitem *item; - scene_menu_calc_bbox(menu, &bbox, &label_bbox); + scene_menu_calc_bbox(menu, bbox); - /* Make all labels the same size */ - if (label_bbox.valid) { - list_for_each_entry(item, &menu->item_head, sibling) { - scene_obj_set_size(menu->obj.scene, item->label_id, - label_bbox.x1 - label_bbox.x0, - label_bbox.y1 - label_bbox.y0); - } + /* Make all field types the same width */ + list_for_each_entry(item, &menu->item_head, sibling) { + cur = &bbox[SCENEBB_label]; + if (cur->valid) + scene_obj_set_width(menu->obj.scene, item->label_id, + cur->x1 - cur->x0); + cur = &bbox[SCENEBB_key]; + if (cur->valid) + scene_obj_set_width(menu->obj.scene, item->key_id, + cur->x1 - cur->x0); + cur = &bbox[SCENEBB_desc]; + if (cur->valid) + scene_obj_set_width(menu->obj.scene, item->desc_id, + cur->x1 - cur->x0); } - if (bbox.valid) { - menu->obj.dim.w = bbox.x1 - bbox.x0; - menu->obj.dim.h = bbox.y1 - bbox.y0; + cur = &bbox[SCENEBB_all]; + if (cur->valid) { + menu->obj.dims.x = cur->x1 - cur->x0; + menu->obj.dims.y = cur->y1 - cur->y0; + + menu->obj.bbox.x1 = cur->x1; + menu->obj.bbox.y1 = cur->y1; } return 0; @@ -196,12 +238,12 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = menu->obj.dim.x; - y = menu->obj.dim.y; + x = menu->obj.bbox.x0; + y = menu->obj.bbox.y0; if (menu->title_id) { int width; - ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.dim.x, y); + ret = scene_obj_set_pos(scn, menu->title_id, menu->obj.bbox.x0, y); if (ret < 0) return log_msg_ret("tit", ret); @@ -286,6 +328,9 @@ int scene_menu_arrange(struct scene *scn, struct expo_arrange_info *arr, if (sel_id) menu_point_to_item(menu, sel_id); + menu->obj.bbox.x1 = menu->obj.bbox.x0 + menu->obj.dims.x; + menu->obj.bbox.y1 = menu->obj.bbox.y0 + menu->obj.dims.y; + menu->obj.flags |= SCENEOF_SIZE_VALID; return 0; } @@ -322,7 +367,7 @@ static struct scene_menitem *scene_menu_find_key(struct scene *scn, txt = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); if (txt) { - str = expo_get_str(scn->expo, txt->str_id); + str = expo_get_str(scn->expo, txt->gen.str_id); if (str && *str == key) return item; } @@ -397,8 +442,6 @@ int scene_menu_send_key(struct scene *scn, struct scene_obj_menu *menu, int key, break; } - menu_point_to_item(menu, item->id); - return 0; } @@ -483,6 +526,33 @@ int scene_menu_set_pointer(struct scene *scn, uint id, uint pointer_id) return 0; } +int scene_menu_select_item(struct scene *scn, uint id, uint cur_item_id) +{ + struct scene_obj_menu *menu; + int ret; + + menu = scene_obj_find(scn, id, SCENEOBJT_MENU); + if (!menu) + return log_msg_ret("menu", -ENOENT); + + ret = menu_point_to_item(menu, cur_item_id); + if (ret) + return log_msg_ret("msi", ret); + + return 0; +} + +int scene_menu_get_cur_item(struct scene *scn, uint id) +{ + struct scene_obj_menu *menu; + + menu = scene_obj_find(scn, id, SCENEOBJT_MENU); + if (!menu) + return log_msg_ret("menu", -ENOENT); + + return menu->cur_item_id; +} + int scene_menu_display(struct scene_obj_menu *menu) { struct scene *scn = menu->obj.scene; @@ -500,15 +570,16 @@ int scene_menu_display(struct scene_obj_menu *menu) if (!txt) return log_msg_ret("txt", -EINVAL); - str = expo_get_str(exp, txt->str_id); - printf("%s\n\n", str); + str = expo_get_str(exp, txt->gen.str_id); + printf("%s\n\n", str ? str : ""); } if (list_empty(&menu->item_head)) return 0; pointer = scene_obj_find(scn, menu->pointer_id, SCENEOBJT_TEXT); - pstr = expo_get_str(scn->expo, pointer->str_id); + if (pointer) + pstr = expo_get_str(scn->expo, pointer->gen.str_id); list_for_each_entry(item, &menu->item_head, sibling) { struct scene_obj_txt *key = NULL, *label = NULL; @@ -517,15 +588,15 @@ int scene_menu_display(struct scene_obj_menu *menu) key = scene_obj_find(scn, item->key_id, SCENEOBJT_TEXT); if (key) - kstr = expo_get_str(exp, key->str_id); + kstr = expo_get_str(exp, key->gen.str_id); label = scene_obj_find(scn, item->label_id, SCENEOBJT_TEXT); if (label) - lstr = expo_get_str(exp, label->str_id); + lstr = expo_get_str(exp, label->gen.str_id); desc = scene_obj_find(scn, item->desc_id, SCENEOBJT_TEXT); if (desc) - dstr = expo_get_str(exp, desc->str_id); + dstr = expo_get_str(exp, desc->gen.str_id); printf("%3s %3s %-10s %s\n", pointer && menu->cur_item_id == item->id ? pstr : "", diff --git a/boot/scene_textedit.c b/boot/scene_textedit.c new file mode 100644 index 00000000000..8242eb39806 --- /dev/null +++ b/boot/scene_textedit.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Implementation of a menu in a scene + * + * Copyright 2025 Google LLC + * Written by Simon Glass <sjg@chromium.org> + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include <expo.h> +#include <log.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include "scene_internal.h" + +enum { + INITIAL_SIZE = SZ_4K, +}; + +int scene_texted(struct scene *scn, const char *name, uint id, uint str_id, + struct scene_obj_txtedit **teditp) +{ + struct scene_obj_txtedit *ted; + char *buf; + int ret; + + ret = scene_obj_add(scn, name, id, SCENEOBJT_TEXTEDIT, + sizeof(struct scene_obj_txtedit), + (struct scene_obj **)&ted); + if (ret < 0) + return log_msg_ret("obj", ret); + + abuf_init(&ted->buf); + if (!abuf_realloc(&ted->buf, INITIAL_SIZE)) + return log_msg_ret("buf", -ENOMEM); + buf = abuf_data(&ted->buf); + *buf = '\0'; + + ret = scene_txt_generic_init(scn->expo, &ted->gen, name, str_id, buf); + if (ret) + return log_msg_ret("teg", ret); + if (teditp) + *teditp = ted; + + return ted->obj.id; +} + +int scene_txted_set_font(struct scene *scn, uint id, const char *font_name, + uint font_size) +{ + struct scene_obj_txtedit *ted; + + ted = scene_obj_find(scn, id, SCENEOBJT_TEXTEDIT); + if (!ted) + return log_msg_ret("find", -ENOENT); + ted->gen.font_name = font_name; + ted->gen.font_size = font_size; + + return 0; +} diff --git a/boot/scene_textline.c b/boot/scene_textline.c index 6adef7cc173..7bc35a997dc 100644 --- a/boot/scene_textline.c +++ b/boot/scene_textline.c @@ -31,8 +31,7 @@ int scene_textline(struct scene *scn, const char *name, uint id, uint max_chars, (struct scene_obj **)&tline); if (ret < 0) return log_msg_ret("obj", -ENOMEM); - abuf_init(&tline->buf); - if (!abuf_realloc(&tline->buf, max_chars + 1)) + if (!abuf_init_size(&tline->buf, max_chars + 1)) return log_msg_ret("buf", -ENOMEM); buf = abuf_data(&tline->buf); *buf = '\0'; @@ -62,7 +61,8 @@ void scene_textline_calc_bbox(struct scene_obj_textline *tline, int scene_textline_calc_dims(struct scene_obj_textline *tline) { - struct scene *scn = tline->obj.scene; + struct scene_obj *obj = &tline->obj; + struct scene *scn = obj->scene; struct vidconsole_bbox bbox; struct scene_obj_txt *txt; int ret; @@ -71,17 +71,22 @@ int scene_textline_calc_dims(struct scene_obj_textline *tline) if (!txt) return log_msg_ret("dim", -ENOENT); - ret = vidconsole_nominal(scn->expo->cons, txt->font_name, - txt->font_size, tline->max_chars, &bbox); + ret = vidconsole_nominal(scn->expo->cons, txt->gen.font_name, + txt->gen.font_size, tline->max_chars, &bbox); if (ret) return log_msg_ret("nom", ret); if (bbox.valid) { - tline->obj.dim.w = bbox.x1 - bbox.x0; - tline->obj.dim.h = bbox.y1 - bbox.y0; - - scene_obj_set_size(scn, tline->edit_id, tline->obj.dim.w, - tline->obj.dim.h); + obj->dims.x = bbox.x1 - bbox.x0; + obj->dims.y = bbox.y1 - bbox.y0; + if (!(obj->flags & SCENEOF_SIZE_VALID)) { + obj->bbox.x1 = obj->bbox.x0 + obj->dims.x; + obj->bbox.y1 = obj->bbox.y0 + obj->dims.y; + obj->flags |= SCENEOF_SIZE_VALID; + } + scene_obj_set_size(scn, tline->edit_id, + obj->bbox.x1 - obj->bbox.x0, + obj->bbox.y1 - obj->bbox.y0); } return 0; @@ -95,16 +100,16 @@ int scene_textline_arrange(struct scene *scn, struct expo_arrange_info *arr, int x, y; int ret; - x = tline->obj.dim.x; - y = tline->obj.dim.y; + x = tline->obj.bbox.x0; + y = tline->obj.bbox.y0; if (tline->label_id) { - ret = scene_obj_set_pos(scn, tline->label_id, tline->obj.dim.x, - y); + ret = scene_obj_set_pos(scn, tline->label_id, + tline->obj.bbox.x0, y); if (ret < 0) return log_msg_ret("tit", ret); ret = scene_obj_set_pos(scn, tline->edit_id, - tline->obj.dim.x + 200, y); + tline->obj.bbox.x0 + 200, y); if (ret < 0) return log_msg_ret("tit", ret); @@ -186,10 +191,10 @@ int scene_textline_render_deps(struct scene *scn, if (!txt) return log_msg_ret("cur", -ENOENT); - if (txt->font_name || txt->font_size) { + if (txt->gen.font_name || txt->gen.font_size) { ret = vidconsole_select_font(cons, - txt->font_name, - txt->font_size); + txt->gen.font_name, + txt->gen.font_size); } else { ret = vidconsole_select_font(cons, NULL, 0); } @@ -198,8 +203,8 @@ int scene_textline_render_deps(struct scene *scn, if (ret) return log_msg_ret("sav", ret); - vidconsole_set_cursor_visible(cons, true, txt->obj.dim.x, - txt->obj.dim.y, scn->cls.num); + vidconsole_set_cursor_visible(cons, true, txt->obj.bbox.x0, + txt->obj.bbox.y0, scn->cls.num); } return 0; @@ -220,7 +225,7 @@ int scene_textline_open(struct scene *scn, struct scene_obj_textline *tline) if (!txt) return log_msg_ret("cur", -ENOENT); - vidconsole_set_cursor_pos(cons, txt->obj.dim.x, txt->obj.dim.y); + vidconsole_set_cursor_pos(cons, txt->obj.bbox.x0, txt->obj.bbox.y0); vidconsole_entry_start(cons); cli_cread_init(&scn->cls, abuf_data(&tline->buf), tline->max_chars); scn->cls.insert = true; |