From patchwork Sat Jan 10 20:28:56 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1470 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=1768077072; bh=q2MEHZ6jV8zF/ypI2IRnLIfNXnUMQ7Tijgnk3aDs60U=; 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=CBe9zG1b5b9Tyg12gVd5dg2TpXHNqXNFZ2WBglk4vnzEWBCl3+yT4iUEskkLUnwJ+ NhquYHpTNHewIGXm9bavLOMmdaSJKQ6ik2RF/6tjSDgQ964AGmDZv10jRpKJbAO2l4 v8G7PKjRrU4kT2Z2mXc1Rp43TT9kjWUis5w9VKM7eDr9YLDTY2ghIN2L+DYIKCaXXv ffjrJT33C3aRd9w9s1QPGdDguSE1s0mDmDW0765+mTtLaFphXrMmCRRCoow3s/fOiy v8jVzncM58fBpDTCHU/a76cIaoXYGDmrsspkiA72JYb86uV59Dg/hu6QZGEFCN2CSc tN2CIJU68CZDw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 9D8EF692A6 for ; Sat, 10 Jan 2026 13:31:12 -0700 (MST) 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 WgSEuo6YZcVs for ; Sat, 10 Jan 2026 13:31:12 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768077072; bh=q2MEHZ6jV8zF/ypI2IRnLIfNXnUMQ7Tijgnk3aDs60U=; 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=CBe9zG1b5b9Tyg12gVd5dg2TpXHNqXNFZ2WBglk4vnzEWBCl3+yT4iUEskkLUnwJ+ NhquYHpTNHewIGXm9bavLOMmdaSJKQ6ik2RF/6tjSDgQ964AGmDZv10jRpKJbAO2l4 v8G7PKjRrU4kT2Z2mXc1Rp43TT9kjWUis5w9VKM7eDr9YLDTY2ghIN2L+DYIKCaXXv ffjrJT33C3aRd9w9s1QPGdDguSE1s0mDmDW0765+mTtLaFphXrMmCRRCoow3s/fOiy v8jVzncM58fBpDTCHU/a76cIaoXYGDmrsspkiA72JYb86uV59Dg/hu6QZGEFCN2CSc tN2CIJU68CZDw== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 8782F69184 for ; Sat, 10 Jan 2026 13:31:12 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768077071; bh=Q6NSG6eu+bB0kAtJlEq1jc9V9uhGR1IiFS69neTkhk8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=UnqMq8HuUutw+cnM9NzLCw1nRD6OSPcC9lFzeY1ndxctofpJw9xYry+E3Y/iKzfy3 Xlsjul1zJSVU5bOO6y6S9S4NNJhWM4s90XGqf1lOHbp3qPbcq0gKzSjteu9O5LmQlS RyAixsxgCFAGBVeef9YSOJae76pG7TooAXtdnr8ml9OHP1Op8U6lPG4JOyaFKUhiqR 7V7kN31Qnq8CPbW4h6jqbrwBVFj/lJ+2UPlc93tAkxElYttPZxEuojvujYbWBHPODH tHujHD2Xfj8yLN8paOpk3dpoKgrYbu+QdEogOkzxt1iLv2S5KUGXSnrfe2ghGTHY/+ 43wjJFa7aZntw== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id 51CB66927E; Sat, 10 Jan 2026 13:31:11 -0700 (MST) 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 yII_hWT1e-ou; Sat, 10 Jan 2026 13:31:11 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1768077065; bh=XKYRtgZvzt6uRyL7ei1Ia+1HwEwgkzZuifQy5tx9upY=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BHDw7c9vjH8MrFz05h5xxTEHgmnHuz5fWKImsDIVR1XQhRZaeYR2PW+eeLPWaFar9 UMni+ak6v+dNQg0SDXGMf791ctroIYbXPLMWCE30QkGzaHhvFDPvbWUITkHQwAD/lV x/jVZ0pc63umu/4BsSVkSz6GIv30Osi8ElHwtfXNvOZJ3cm59hA2+6CYPtRtwsqI2k j1LzDVAcBzwMCVMUUv7Xd07VcYOFdpRPlLyl6likxfZ2YzlgQtYGrPcelXnrN5Jro+ OyMMeosm0uWr299V60f/0YqTpfM7G9Sqi41ONYW9kM5Kv3nkocFW4qgEihAVrjFrAw FNq/OvMqzVl9w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id 6C72869291; Sat, 10 Jan 2026 13:31:05 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Sat, 10 Jan 2026 13:28:56 -0700 Message-ID: <20260110202906.187370-25-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260110202906.187370-1-sjg@u-boot.org> References: <20260110202906.187370-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: BTK6EOI2XC76JAW3SFHOUZVG5LJ463QD X-Message-ID-Hash: BTK6EOI2XC76JAW3SFHOUZVG5LJ463QD 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 Opus 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 24/26] test: pxe: Add a test for the callback-free files API 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 Add pxe_test_files_api_norun() to test the new callback-free, file-loading API: 1. Parse config with pxe_parse() to get a menu with labels 2. Verify label->files contains expected files (kernel, initrd, fdt, overlays) with correct types and paths 3. Simulate loading by calling pxe_load() for each file 4. Verify addresses and sizes are recorded correctly Also update existing tests to expect the correct file counts now that kernel, initrd, and FDT are added to the files list during parsing. Co-developed-by: Claude Opus 4.5 Signed-off-by: Simon Glass --- test/boot/pxe.c | 182 +++++++++++++++++++++++++++++-- test/py/tests/test_pxe_parser.py | 11 ++ 2 files changed, 184 insertions(+), 9 deletions(-) diff --git a/test/boot/pxe.c b/test/boot/pxe.c index 56b1dfb4c49..80779b86aad 100644 --- a/test/boot/pxe.c +++ b/test/boot/pxe.c @@ -51,24 +51,22 @@ struct pxe_test_info { }; /** - * pxe_test_getfile() - Read a file from the host filesystem + * load_file() - Load a file from the host filesystem * - * This callback is used by the PXE parser to read included files. + * @path: Path to file within the mounted filesystem + * @addr: Address to load file to + * @sizep: Returns file size + * Return: 0 on success, -ve on error */ -static int pxe_test_getfile(struct pxe_context *ctx, const char *file_path, - ulong *addrp, ulong align, - enum bootflow_img_t type, ulong *sizep) +static int load_file(const char *path, ulong addr, ulong *sizep) { loff_t len_read; int ret; - if (!*addrp) - return -ENOTSUPP; - ret = fs_set_blk_dev("host", "0:0", FS_TYPE_ANY); if (ret) return ret; - ret = fs_legacy_read(file_path, *addrp, 0, 0, &len_read); + ret = fs_legacy_read(path, addr, 0, 0, &len_read); if (ret) return ret; *sizep = len_read; @@ -76,6 +74,21 @@ static int pxe_test_getfile(struct pxe_context *ctx, const char *file_path, return 0; } +/** + * pxe_test_getfile() - Read a file from the host filesystem + * + * This callback is used by the PXE parser to read included files. + */ +static int pxe_test_getfile(struct pxe_context *ctx, const char *file_path, + ulong *addrp, ulong align, + enum bootflow_img_t type, ulong *sizep) +{ + if (!*addrp) + return -ENOTSUPP; + + return load_file(file_path, *addrp, sizep); +} + /** * pxe_check_menu() - Check the standard menu output lines * @@ -1323,3 +1336,154 @@ static int pxe_test_fit_embedded_fdt_norun(struct unit_test_state *uts) PXE_TEST_ARGS(pxe_test_fit_embedded_fdt_norun, UTF_CONSOLE | UTF_MANUAL, { "fs_image", UT_ARG_STR }, { "cfg_path", UT_ARG_STR }); + +/** + * Test callback-free file loading API with pxe_load() + * + * This tests the new callback-free API where the caller: + * 1. Calls pxe_parse() to get a menu with labels containing files lists + * 2. Iterates over label->files and loads each file manually + * 3. Calls pxe_load() to record where each file was loaded + * + * This approach eliminates the need for getfile callbacks during loading. + */ +static int pxe_test_files_api_norun(struct unit_test_state *uts) +{ + const char *fs_image = ut_str(PXE_ARG_FS_IMAGE); + const char *cfg_path = ut_str(PXE_ARG_CFG_PATH); + struct pxe_context *ctx; + struct pxe_label *label; + struct pxe_menu *menu; + const struct pxe_file *file; + struct pxe_file *filep; + ulong addr = PXE_LOAD_ADDR; + ulong file_addr; + ulong size; + char *buf; + uint i; + + ut_assertnonnull(fs_image); + ut_assertnonnull(cfg_path); + + /* Bind the filesystem image */ + ut_assertok(run_commandf("host bind 0 %s", fs_image)); + + /* Load the config file first */ + ut_assertok(load_file(cfg_path, addr, &size)); + + /* Add null terminator - parser expects null-terminated string */ + buf = map_sysmem(addr, 0); + buf[size] = '\0'; + unmap_sysmem(buf); + + ctx = pxe_parse(addr, size, cfg_path); + ut_assertnonnull(ctx); + menu = ctx->cfg; + + /* Parsing with no getfile callback should produce no output */ + ut_assert_console_end(); + + /* + * Process includes manually - load each include file and parse it. + * Use an index-based loop since parsing may add more includes. + */ + for (i = 0; i < menu->includes.count; i++) { + const struct pxe_include *inc; + + inc = alist_get(&menu->includes, i, struct pxe_include); + ut_assertok(load_file(inc->path, addr, &size)); + + ut_asserteq(1, pxe_parse_include(ctx, inc, addr, size)); + } + + /* Include parsing should also produce no output */ + ut_assert_console_end(); + + /* Get the first label */ + label = list_first_entry(&menu->labels, struct pxe_label, list); + ut_asserteq_str("linux", label->name); + + /* + * Verify the files list contains expected files: + * - kernel (/vmlinuz) + * - initrd (/initrd.img) + * - fdt (/dtb/board.dtb) + * - 2 overlays (/dtb/overlay1.dtbo, /dtb/overlay2.dtbo) + */ + ut_asserteq(5, label->files.count); + + /* Check each file has correct type and path */ + file = alist_get(&label->files, 0, struct pxe_file); + ut_asserteq(PFT_KERNEL, file->type); + ut_asserteq_str("/vmlinuz", file->path); + ut_asserteq(0, file->addr); /* Not loaded yet */ + + file = alist_get(&label->files, 1, struct pxe_file); + ut_asserteq(PFT_INITRD, file->type); + ut_asserteq_str("/initrd.img", file->path); + + file = alist_get(&label->files, 2, struct pxe_file); + ut_asserteq(PFT_FDT, file->type); + ut_asserteq_str("/dtb/board.dtb", file->path); + + file = alist_get(&label->files, 3, struct pxe_file); + ut_asserteq(PFT_FDTOVERLAY, file->type); + ut_asserteq_str("/dtb/overlay1.dtbo", file->path); + + file = alist_get(&label->files, 4, struct pxe_file); + ut_asserteq(PFT_FDTOVERLAY, file->type); + ut_asserteq_str("/dtb/overlay2.dtbo", file->path); + + /* + * Load each file and call pxe_load() to record address/size. + * This demonstrates the callback-free file loading API. + */ + file_addr = PXE_KERNEL_ADDR; + alist_for_each(filep, &label->files) { + ulong size; + + ut_assertok(load_file(filep->path, file_addr, &size)); + pxe_load(filep, file_addr, size); + file_addr += ALIGN(size, SZ_64K); + } + + /* Verify files were loaded with valid addresses and sizes */ + file = alist_get(&label->files, 0, struct pxe_file); + ut_asserteq(PXE_KERNEL_ADDR, file->addr); + ut_assert(file->size > 0); + + /* + * Set up context for boot from the files list. + * In the callback-free API, the caller populates ctx fields. + */ + ctx->kern_addr = alist_get(&label->files, 0, struct pxe_file)->addr; + ctx->initrd_addr = alist_get(&label->files, 1, struct pxe_file)->addr; + ctx->fdt_addr = alist_get(&label->files, 2, struct pxe_file)->addr; + ctx->label = label; + + /* Boot - sandbox simulates this */ + pxe_boot(ctx); + + /* Sandbox cannot boot the kernel, so we get this error */ + ut_assert_nextline("Unrecognized zImage"); + ut_assert_console_end(); + + /* Verify the files are in memory at the correct addresses */ + file = alist_get(&label->files, 0, struct pxe_file); + buf = map_sysmem(file->addr, file->size); + ut_asserteq_mem("kernel", buf, 6); + unmap_sysmem(buf); + + file = alist_get(&label->files, 1, struct pxe_file); + buf = map_sysmem(file->addr, file->size); + ut_asserteq_mem("ramdisk", buf, 7); + unmap_sysmem(buf); + + /* Clean up */ + pxe_cleanup(ctx); + + return 0; +} +PXE_TEST_ARGS(pxe_test_files_api_norun, UTF_CONSOLE | UTF_MANUAL, + { "fs_image", UT_ARG_STR }, + { "cfg_path", UT_ARG_STR }); diff --git a/test/py/tests/test_pxe_parser.py b/test/py/tests/test_pxe_parser.py index bbcd63d58e2..5dfe9210b11 100644 --- a/test/py/tests/test_pxe_parser.py +++ b/test/py/tests/test_pxe_parser.py @@ -545,3 +545,14 @@ class TestPxeParser: with ubman.log.section('Test PXE FIT embedded FDT'): ubman.run_ut('pxe', 'pxe_test_fit_embedded_fdt', fs_image=fs_img, cfg_path=cfg_path) + + def test_pxe_files_api(self, ubman, pxe_image): + """Test three-phase files API (pxe_parse, pxe_load, pxe_boot) + + This tests the callback-free API where files are collected during + parsing and the caller loads them directly, then calls pxe_boot(). + """ + fs_img, cfg_path = pxe_image + with ubman.log.section('Test PXE files API'): + ubman.run_ut('pxe', 'pxe_test_files_api', + fs_image=fs_img, cfg_path=cfg_path)