From patchwork Fri Oct 10 03:42:34 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 572 Return-Path: X-Original-To: u-boot-concept@u-boot.org Delivered-To: u-boot-concept@u-boot.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760067817; bh=jZE1Bs3nXrXe1ThWFL+qFKAaGoJBf3RxrlD3b2xwRvA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=tDVyz2A6WX3kW8vE4SkZsvursMnh5oPELYS3PIxhneCe6LcncpfAQEVk630BxOkkw /AYdvdVfCSSEXVrQ/cSj9iipYgE4mOkWRorKrMWNIDMse84BJhY7TzFRPRCEf12vty llCvyC4Jj3qktBHTftucCghLs/66xtTtzKwgj0HHB2RNanFFMf3ctzvTcIp6pTtx1x 9ucV7tlOTavE+pv39Trwaj/tF9oCwh/MG45tHCReo50Zw7eOUpmF6nO04DOzxWYBXs WPbH3vnbdf+gF3Lu4njfCZg11mIxgTNM9QBkEiM2MgWjI3IMuQ1OVe4+o4baDwAgNw xkt1IGdjzFnSA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D3EB768030 for ; Thu, 9 Oct 2025 21:43:37 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10024) with ESMTP id dXj_0zWghDhI for ; Thu, 9 Oct 2025 21:43:37 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760067817; bh=jZE1Bs3nXrXe1ThWFL+qFKAaGoJBf3RxrlD3b2xwRvA=; h=From:To:Date:In-Reply-To:References:CC:Subject:List-Id: List-Archive:List-Help:List-Owner:List-Post:List-Subscribe: List-Unsubscribe:From; b=tDVyz2A6WX3kW8vE4SkZsvursMnh5oPELYS3PIxhneCe6LcncpfAQEVk630BxOkkw /AYdvdVfCSSEXVrQ/cSj9iipYgE4mOkWRorKrMWNIDMse84BJhY7TzFRPRCEf12vty llCvyC4Jj3qktBHTftucCghLs/66xtTtzKwgj0HHB2RNanFFMf3ctzvTcIp6pTtx1x 9ucV7tlOTavE+pv39Trwaj/tF9oCwh/MG45tHCReo50Zw7eOUpmF6nO04DOzxWYBXs WPbH3vnbdf+gF3Lu4njfCZg11mIxgTNM9QBkEiM2MgWjI3IMuQ1OVe4+o4baDwAgNw xkt1IGdjzFnSA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C258967FA3 for ; Thu, 9 Oct 2025 21:43:37 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760067815; bh=jtwTEHT9FNbuutjnoy5KYgi60Sou+BgyJGCdfhRuSSg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=X77k8iXZTJw2rGQeWcncQRbkFerpVJ5B/0lBSkQienwR1zfrHR4NPfxSIsjmpFYOq w0EqingFANHkwRj/ecw2ci0WiPJpHV0e1OOnL/jM7tDja/jCVpwAvrIWTd7XE8qxWC MvmHi2uAUlcACOKx7II+I826sC9W2rS+VQrmsbVPU5PumblFUG/PvFplKQ1bB+oAz2 9ML1ZMjtHXxS9Wpk0BDaPn/K5cFzRTLspsK97NI1hXJwDtH3sw/Xpau0vvZiMY5UJD b1HRjeLbf/aAOih9yhhtYDMPfhN8rZHOaEeeJ8mKwj1z1JJend6+tyaZ76UOnfftEm uY5MB9uy6fqcw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 755CB68084; Thu, 9 Oct 2025 21:43:35 -0600 (MDT) X-Virus-Scanned: Debian amavis at Received: from mail.u-boot.org ([127.0.0.1]) by localhost (mail.u-boot.org [127.0.0.1]) (amavis, port 10026) with ESMTP id 06TdRUtmxQDS; Thu, 9 Oct 2025 21:43:35 -0600 (MDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1760067813; bh=LCeWDRZXusYnDNJippV/LG/R2GHVh5zPNL/ZtGPIgG8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Dgbv90K60uqVJh22ITt7aqACu0MuqoHdtZRG7fwdTE98y9yt8KN+wQYi33fKvgbzx 9VpxQ9A1yD1GxBJ9lgYMfBdyiNt0J1y2zMaLWovfYQyFke/yPXK+jnV/DuIvit2Y6R BH47HscRwJwqUnzfs6fJNB+Scmu6welpnEp0WpngLzMY/IjqUvdTgA8GnwlIFvt7XX UWnwLXPT3HuK1hNxfsfTY8qHzzEzWJP/HIZ5MZUw15v30VeXyfgIw4FY3rPs5qgSe4 qh+z9ANplhum1S2/JVTImrTSHQpvhHZvhdPq1n/BOxVXKQpc0pUfvWeY/Y8fSD03zh kGaCOMW2QY/Zw== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id B0FB267F7F; Thu, 9 Oct 2025 21:43:32 -0600 (MDT) From: Simon Glass To: U-Boot Concept Date: Thu, 9 Oct 2025 21:42:34 -0600 Message-ID: <20251010034255.1099728-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20251010034255.1099728-1-sjg@u-boot.org> References: <20251010034255.1099728-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: VC45PXYZJXWV7K2FGGX5TMFNMXAGYPKE X-Message-ID-Hash: VC45PXYZJXWV7K2FGGX5TMFNMXAGYPKE X-MailFrom: sjg@u-boot.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: Heinrich Schuchardt , Simon Glass , Claude X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 06/18] expo: Provide a way to dump an expo List-Id: Discussion and patches related to U-Boot Concept Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Simon Glass For debugging it is sometimes helpful to dump an expo. Add an implementation of this, writing to a membuf. Add a MAINTAINERS entry for expo, including this next file. Co-developed-by: Claude Signed-off-by: Simon Glass --- MAINTAINERS | 3 + boot/Kconfig | 9 ++ boot/Makefile | 1 + boot/expo_dump.c | 271 +++++++++++++++++++++++++++++++++++++++++++++++ include/expo.h | 18 ++++ test/boot/expo.c | 46 ++++++++ 6 files changed, 348 insertions(+) create mode 100644 boot/expo_dump.c diff --git a/MAINTAINERS b/MAINTAINERS index e858331455c..2a2a42aec5e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -944,6 +944,8 @@ F: boot/bootflow.c F: boot/bootm_final.c F: boot/bootmeth*.c F: boot/bootstd.c +F: boot/expo*.c +F: boot/scene*.c F: cmd/bootdev.c F: cmd/bootflow.c F: doc/develop/bootstd/ @@ -955,6 +957,7 @@ F: include/bootdev.h F: include/bootflow.h F: include/bootmeth.h F: include/bootstd.h +F: include/expo.h F: net/eth_bootdevice.c F: test/boot/ diff --git a/boot/Kconfig b/boot/Kconfig index fb34a10106b..b255245335b 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -982,6 +982,15 @@ config EXPO The expo can be presented in graphics form using a vidconsole, or in text form on a serial console. +config EXPO_DUMP + bool "Allow dumping the contents of an expo" + depends on EXPO + default y if SANDBOX + help + This provides a way to dump an expo as test, including most of the + details in the structures. This can be useful when debugging or + comparing two expos. + config EXPO_TEST bool "Enable test mode for expo" depends on EXPO diff --git a/boot/Makefile b/boot/Makefile index bb1888f1656..3cb138ae022 100644 --- a/boot/Makefile +++ b/boot/Makefile @@ -57,6 +57,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_DUMP) += expo_dump.o obj-$(CONFIG_$(PHASE_)EXPO) += scene_menu.o scene_textline.o scene_textedit.o obj-$(CONFIG_$(PHASE_)EXPO_TEST) += expo_test.o ifdef CONFIG_COREBOOT_SYSINFO diff --git a/boot/expo_dump.c b/boot/expo_dump.c new file mode 100644 index 00000000000..711b26eb220 --- /dev/null +++ b/boot/expo_dump.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Dump functions for expo objects + * + * Copyright 2025 Canonical Ltd + * Written by Simon Glass + */ + +#define LOG_CATEGORY LOGC_EXPO + +#include +#include +#include +#include +#include +#include "scene_internal.h" + +/** + * struct dump_ctx - Context for dumping expo structures + * + * @mb: Membuf to write output to + * @scn: Current scene being dumped (or NULL if not in a scene) + * @indent: Current indentation level (number of spaces) + */ +struct dump_ctx { + struct membuf *mb; + struct scene *scn; + int indent; +}; + +/** + * outf() - Output a formatted string with indentation + * + * @ctx: Dump context containing membuf, scene, and indent level + * @fmt: Format string + * @...: Arguments for format string + */ +static void outf(struct dump_ctx *ctx, const char *fmt, ...) +{ + char buf[256]; + va_list args; + int len; + + va_start(args, fmt); + membuf_printf(ctx->mb, "%*s", ctx->indent, ""); + len = vsnprintf(buf, sizeof(buf), fmt, args); + membuf_put(ctx->mb, buf, len); + va_end(args); +} + +static const char *obj_name(struct dump_ctx *ctx, uint id) +{ + struct scene_obj *obj; + + if (!id) + return "(none)"; + + obj = scene_obj_find(ctx->scn, id, SCENEOBJT_NONE); + if (!obj) + return "(not found)"; + + return obj->name; +} + +static void dump_menu(struct dump_ctx *ctx, struct scene_obj_menu *menu) +{ + struct scene_obj *obj = &menu->obj; + struct scene_menitem *item; + + outf(ctx, "Menu: pointer_id %d title_id %d manual %d\n", + menu->pointer_id, menu->title_id, + !!(obj->flags & SCENEOF_MANUAL)); + + ctx->indent += 2; + list_for_each_entry(item, &menu->item_head, sibling) { + outf(ctx, "Item %d: name '%s' label_id %d desc_id %d\n", + item->id, item->name, item->label_id, item->desc_id); + } + ctx->indent -= 2; +} + +static void dump_text(struct dump_ctx *ctx, struct scene_obj_txt *txt) +{ + const char *str = expo_get_str(ctx->scn->expo, txt->gen.str_id); + + outf(ctx, "Text: str_id %d font_name '%s' font_size %d\n", + txt->gen.str_id, + txt->gen.font_name ? txt->gen.font_name : "(default)", + txt->gen.font_size); + ctx->indent += 2; + outf(ctx, "str '%s'\n", str ? str : "(null)"); + ctx->indent -= 2; +} + +static void dump_box(struct dump_ctx *ctx, struct scene_obj_box *box) +{ + outf(ctx, "Box: fill %d width %d\n", box->fill, box->width); +} + +static void dump_image(struct dump_ctx *ctx, struct scene_obj_img *img) +{ + outf(ctx, "Image: data %lx\n", (ulong)map_to_sysmem(img->data)); +} + +static void dump_textline(struct dump_ctx *ctx, + struct scene_obj_textline *tline) +{ + outf(ctx, "Textline: label_id %d edit_id %d\n", + tline->label_id, tline->edit_id); + ctx->indent += 2; + outf(ctx, "max_chars %d pos %d\n", tline->max_chars, tline->pos); + ctx->indent -= 2; +} + +static void dump_textedit(struct dump_ctx *ctx, + struct scene_obj_txtedit *tedit) +{ + outf(ctx, "Textedit: str_id %d font_name '%s' font_size %d\n", + tedit->gen.str_id, + tedit->gen.font_name ? tedit->gen.font_name : "(default)", + tedit->gen.font_size); +} + +static void obj_dump_(struct dump_ctx *ctx, struct scene_obj *obj) +{ + char flags_buf[256]; + bool first = true; + int bit; + int pos = 0; + + outf(ctx, "Object %d (%s): type %s\n", obj->id, obj->name, + scene_obj_type_name(obj->type)); + ctx->indent += 2; + + /* Build flags string */ + for (bit = 0; bit < 16; bit++) { + uint flag = BIT(bit); + + if (obj->flags & flag) { + pos += snprintf(flags_buf + pos, sizeof(flags_buf) - pos, + "%s%s", first ? "" : ", ", + scene_flag_name(flag)); + first = false; + } + } + outf(ctx, "flags %s\n", pos > 0 ? flags_buf : ""); + outf(ctx, "bbox: (%d,%d)-(%d,%d)\n", + obj->bbox.x0, obj->bbox.y0, obj->bbox.x1, obj->bbox.y1); + outf(ctx, "dims: %dx%d\n", obj->dims.x, obj->dims.y); + + switch (obj->type) { + case SCENEOBJT_NONE: + break; + case SCENEOBJT_IMAGE: + dump_image(ctx, (struct scene_obj_img *)obj); + break; + case SCENEOBJT_TEXT: + dump_text(ctx, (struct scene_obj_txt *)obj); + break; + case SCENEOBJT_BOX: + dump_box(ctx, (struct scene_obj_box *)obj); + break; + case SCENEOBJT_MENU: + dump_menu(ctx, (struct scene_obj_menu *)obj); + break; + case SCENEOBJT_TEXTLINE: + dump_textline(ctx, (struct scene_obj_textline *)obj); + break; + case SCENEOBJT_TEXTEDIT: + dump_textedit(ctx, (struct scene_obj_txtedit *)obj); + break; + } + ctx->indent -= 2; +} + +static void scene_dump_(struct dump_ctx *ctx) +{ + struct scene_obj *obj; + + outf(ctx, "Scene %d: name '%s'\n", ctx->scn->id, ctx->scn->name); + ctx->indent += 2; + outf(ctx, "title_id %d (%s)\n", + ctx->scn->title_id, obj_name(ctx, ctx->scn->title_id)); + outf(ctx, "highlight_id %d (%s)\n", + ctx->scn->highlight_id, obj_name(ctx, ctx->scn->highlight_id)); + + list_for_each_entry(obj, &ctx->scn->obj_head, sibling) { + /* Skip hidden objects */ + if (obj->flags & SCENEOF_HIDE) + continue; + obj_dump_(ctx, obj); + } + ctx->indent -= 2; +} + +void scene_dump(struct membuf *mb, struct scene *scn, int indent) +{ + struct dump_ctx ctx; + + ctx.mb = mb; + ctx.scn = scn; + ctx.indent = indent; + + scene_dump_(&ctx); +} + +static void expo_dump_(struct dump_ctx *ctx, struct expo *exp) +{ + struct scene *scn; + struct expo_theme *theme = &exp->theme; + + outf(ctx, "Expo: name '%s'\n", exp->name); + ctx->indent = 2; + outf(ctx, "display %s\n", + exp->display ? exp->display->name : "(null)"); + outf(ctx, "cons %s\n", exp->cons ? exp->cons->name : "(none)"); + outf(ctx, "mouse %s\n", exp->mouse ? exp->mouse->name : "(none)"); + outf(ctx, "scene_id %d\n", exp->scene_id); + outf(ctx, "next_id %d\n", exp->next_id); + outf(ctx, "req_width %d\n", exp->req_width); + outf(ctx, "req_height %d\n", exp->req_height); + outf(ctx, "text_mode %d\n", exp->text_mode); + outf(ctx, "popup %d\n", exp->popup); + outf(ctx, "show_highlight %d\n", exp->show_highlight); + outf(ctx, "mouse_enabled %d\n", exp->mouse_enabled); + outf(ctx, "mouse_ptr %p\n", exp->mouse_ptr); + outf(ctx, "mouse_size %dx%d\n", exp->mouse_size.w, + exp->mouse_size.h); + outf(ctx, "mouse_pos (%d,%d)\n", exp->mouse_pos.x, + exp->mouse_pos.y); + outf(ctx, "damage (%d,%d)-(%d,%d)\n", exp->damage.x0, exp->damage.y0, + exp->damage.x1, exp->damage.y1); + outf(ctx, "done %d\n", exp->done); + outf(ctx, "save %d\n", exp->save); + outf(ctx, "last_key_ms %ld\n", exp->last_key_ms); + + if (exp->display) { + struct video_priv *vid_priv = dev_get_uclass_priv(exp->display); + + outf(ctx, "video: %dx%d white_on_black %d\n", + vid_priv->xsize, vid_priv->ysize, + vid_priv->white_on_black); + } + + outf(ctx, "Theme:\n"); + ctx->indent = 4; + outf(ctx, "font_size %d\n", theme->font_size); + outf(ctx, "white_on_black %d\n", theme->white_on_black); + outf(ctx, "menu_inset %d\n", theme->menu_inset); + outf(ctx, "menuitem_gap_y %d\n", theme->menuitem_gap_y); + + ctx->indent = 0; + outf(ctx, "\nScenes:\n"); + ctx->indent = 2; + list_for_each_entry(scn, &exp->scene_head, sibling) { + ctx->scn = scn; + scene_dump_(ctx); + outf(ctx, "\n"); + } +} + +void expo_dump(struct expo *exp, struct membuf *mb) +{ + struct dump_ctx ctx; + + ctx.mb = mb; + ctx.scn = NULL; + ctx.indent = 0; + + expo_dump_(&ctx, exp); +} diff --git a/include/expo.h b/include/expo.h index b51d946f367..619af627840 100644 --- a/include/expo.h +++ b/include/expo.h @@ -14,6 +14,7 @@ #include #include +struct membuf; struct udevice; #include @@ -1240,4 +1241,21 @@ void expo_damage_reset(struct expo *exp); */ void expo_damage_add(struct expo *exp, const struct vid_bbox *bbox); +/** + * expo_dump() - Dump expo structure to a membuf + * + * @mb: membuf to write to + * @exp: Expo to dump + */ +void expo_dump(struct expo *exp, struct membuf *mb); + +/** + * scene_dump() - Dump scene structure to a membuf + * + * @mb: membuf to write to + * @scn: Scene to dump + * @indent: Indentation level + */ +void scene_dump(struct membuf *mb, struct scene *scn, int indent); + #endif /*__EXPO_H */ diff --git a/test/boot/expo.c b/test/boot/expo.c index 7c27da165c5..e8716b86991 100644 --- a/test/boot/expo.c +++ b/test/boot/expo.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -1258,3 +1259,48 @@ static int expo_scene_obj_type_name(struct unit_test_state *uts) return 0; } BOOTSTD_TEST(expo_scene_obj_type_name, 0); + +/* Test expo_dump() */ +static int expo_dump_test(struct unit_test_state *uts) +{ + struct scene_obj_menu *menu; + struct abuf buf, logo_copy; + struct scene *scn; + struct expo *exp; + struct membuf mb; + char mb_buf[4096]; + char *data; + int len; + + membuf_init(&mb, mb_buf, sizeof(mb_buf)); + + ut_assertok(create_test_expo(uts, &exp, &scn, &menu, &buf, &logo_copy)); + + /* Arrange the scene so objects have proper dimensions */ + ut_assertok(scene_arrange(scn)); + + /* Dump the expo */ + expo_dump(exp, &mb); + + /* Get the dumped data */ + len = membuf_getraw(&mb, sizeof(mb_buf), false, &data); + ut_assert(len > 0); + ut_assertnonnull(data); + + /* Nul-terminate for strstr to work */ + if (len < sizeof(mb_buf)) + data[len] = '\0'; + + /* Check for a few elements in the output */ + ut_assert(strstr(data, "Expo: name")); + ut_assert(strstr(data, "my menus")); + ut_assert(strstr(data, "Scene")); + ut_assert(strstr(data, "main")); + + abuf_uninit(&buf); + abuf_uninit(&logo_copy); + expo_destroy(exp); + + return 0; +} +BOOTSTD_TEST(expo_dump_test, UTF_DM | UTF_SCAN_FDT);