From patchwork Fri Feb 13 20:24:11 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Simon Glass X-Patchwork-Id: 1853 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=1771014304; bh=IFZtM8hWItHBvY/dxi8jmj+I2C1Md2V/x9pkL6rTK0E=; 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=AJ7Xos9BPq8DTGp+iZ1lgTCKNrVs33yX3yiK554nsslbdmzvxXuC7XV9/RMe/03iS xTe6JwW5q7CvTrMiMA9N5anLL56zPoHVWObKwBitzgoe5rsNdqBECb8jkLlowbEzI0 3AKQoZr2auAuFhpMF2Jt7B664Ts+V01ng5mUGVGg61ZAudbulQ8XZgghKg1cSFQxS4 FmY9STSHUmR/EfFDxotLdjXRdbF9YIbsNCwQO87h+54Cs3Ypb3yre7QaqUEyTOtXZu GFiZsWjEh5x/QUb3642nSJFM79Rxt47d3XgsNXaCdbMmUp46Fc/QpdMB4ZoLKE2VTc STR35bL6o+kmA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D93EB69B0E for ; Fri, 13 Feb 2026 13:25:04 -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 PQ5QGQb1sj_W for ; Fri, 13 Feb 2026 13:25:04 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771014304; bh=IFZtM8hWItHBvY/dxi8jmj+I2C1Md2V/x9pkL6rTK0E=; 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=AJ7Xos9BPq8DTGp+iZ1lgTCKNrVs33yX3yiK554nsslbdmzvxXuC7XV9/RMe/03iS xTe6JwW5q7CvTrMiMA9N5anLL56zPoHVWObKwBitzgoe5rsNdqBECb8jkLlowbEzI0 3AKQoZr2auAuFhpMF2Jt7B664Ts+V01ng5mUGVGg61ZAudbulQ8XZgghKg1cSFQxS4 FmY9STSHUmR/EfFDxotLdjXRdbF9YIbsNCwQO87h+54Cs3Ypb3yre7QaqUEyTOtXZu GFiZsWjEh5x/QUb3642nSJFM79Rxt47d3XgsNXaCdbMmUp46Fc/QpdMB4ZoLKE2VTc STR35bL6o+kmA== Received: from mail.u-boot.org (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id C857869B34 for ; Fri, 13 Feb 2026 13:25:04 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771014302; bh=HBmlg9nYlSImZJHOcFZTFYBv6usS2FtAbWaYgzdizE4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VnuPnqeg2C4YTbibVl7ZQyiBS5izOQtO6aykyXXqM5MDlXDFqMsC4DATiAF38tB6s mxwYLwSUyKCD4kffKNTVz26WyoNTh4vu00E9qRSAle/hZU6L3y5gjn+QxJjqzeIZEV jwMWLrqwdrvC7d1Uni79LwMGdP/fzt4wCgFK0xwzOu8V53LTshbfdScqtJ8KP82FWa loro3yWnqEBloUL+41harQZXcHsEvrTR2ue3HrQaGJSvR0PqAWS5d5pHIaMHdS0QFv VNxpoiGvT5lJHBo+xiXlU91m3wSQ8QmGtaxWpKIFki+4YZOM449pVXBs6fiIQgDYXu 5KG30mOuzn5eA== Received: from localhost (localhost [127.0.0.1]) by mail.u-boot.org (Postfix) with ESMTP id D279469B34; Fri, 13 Feb 2026 13:25:02 -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 a2_0PtcFH8vc; Fri, 13 Feb 2026 13:25:02 -0700 (MST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=u-boot.org; s=default; t=1771014297; bh=Hi2PEoCvHoFYww0NROai8qKYxD6X8HfPBtZaXBUF2rE=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Ci+iKwfaJmWbyogdarrrCEF3txKlCRImINgxLj99g6C83eJ9TsbJobxv0UZVetsxh S2oz5KbTOfwov5a6tsgXb7bx8hInl9wxcm0l/Z4H66JmSULEzx7b3QAQtnBymDGRa3 Bccxoeee4o5kFq/WKI4NolICk0aCBqOHYTlcGuL1mDKSqc9C+Ym/WV00GhDOxEEmHk 96eATA5CZJ8OhKNapI2pDa6ls73c8rYYBSAq7gB8yGjmFR48F/gSp0jMuZGfoNZj7G 3d3JSTbo8L4BlFrlJ4le3YH8I+bZ2YxQFlHMeMUPj1rBXNQNgDIfgKX0wPgtGxAMv+ YDjk8JAxlBL0w== Received: from u-boot.org (unknown [73.34.74.121]) by mail.u-boot.org (Postfix) with ESMTPSA id F00F469B0E; Fri, 13 Feb 2026 13:24:56 -0700 (MST) From: Simon Glass To: U-Boot Concept Date: Fri, 13 Feb 2026 13:24:11 -0700 Message-ID: <20260213202417.223068-7-sjg@u-boot.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260213202417.223068-1-sjg@u-boot.org> References: <20260213202417.223068-1-sjg@u-boot.org> MIME-Version: 1.0 Message-ID-Hash: JKX2LHJOEXH5QS637FKNWJQ4DRAMIJFF X-Message-ID-Hash: JKX2LHJOEXH5QS637FKNWJQ4DRAMIJFF 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: Simon Glass , "Claude Sonnet 4 . 5" X-Mailman-Version: 3.3.10 Precedence: list Subject: [Concept] [PATCH 6/8] pxe: Support multiple initrd files using alist 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 Replace the single char *initrd field in struct pxe_label with an alist of initrd paths (struct alist initrds) to support multiple initrd files. This enhancement enables BLS (Boot Loader Specification) support for multiple initrd lines, which is a key feature of the BLS format. Changes made: - Replace char *initrd with struct alist initrds in pxe_label - Initialize initrds alist in label_create() - Free initrds alist entries in label_destroy() - Update PXE parser to append multiple INITRD directives to the alist - Update pxe_load_files() to load all initrds consecutively in memory - Update BLS bootmeth to add all ramdisk images to initrds alist - Update tests to check initrds.count instead of checking initrd pointer The PXE file loading code now: - Loads each initrd file consecutively starting at ramdisk_addr_r - Tracks the total size of all initrds for bootm - Handles FIT images where initrd path matches kernel (shares address) This maintains backwards compatibility with existing single-initrd PXE files while enabling multi-initrd support for BLS and future use cases. Co-developed-by: Claude Sonnet 4.5 Signed-off-by: Simon Glass --- boot/Kconfig | 17 +++++++ boot/bootmeth_bls.c | 44 +++++++++++------ boot/pxe_parse.c | 64 ++++++++++++++++++++----- boot/pxe_utils.c | 114 ++++++++++++++++++++++++++++++++++++++------ include/pxe_utils.h | 5 +- test/boot/pxe.c | 12 +++-- 6 files changed, 209 insertions(+), 47 deletions(-) diff --git a/boot/Kconfig b/boot/Kconfig index 63a0cf0bd7a..cff58795bc1 100644 --- a/boot/Kconfig +++ b/boot/Kconfig @@ -469,6 +469,22 @@ config PXE_UTILS help Utilities for parsing PXE file formats. +config PXE_INITRD_LIST + bool "Support multiple initrd files in PXE/extlinux" + depends on PXE_UTILS + help + Enable support for multiple initrd files in PXE/extlinux + configurations. This allows loading multiple initrd images + consecutively in memory, as required by Boot Loader Specification + (BLS) Type #1 entries. + + When enabled, the PXE parser can handle multiple 'initrd' lines + and load all specified initrd files. When disabled, only a single + initrd is supported, reducing code size. + + Say Y if you need BLS support or multiple initrds. Say N to save + code space if you only use single initrd configurations. + config BOOT_DEFAULTS_FEATURES bool select SUPPORT_RAW_INITRD @@ -693,6 +709,7 @@ config BOOTMETH_EXTLINUX_LOCALBOOT config BOOTMETH_BLS bool "Bootdev support for Boot Loader Specification (BLS) Type #1" select PXE_UTILS + select PXE_INITRD_LIST default y if SANDBOX help Enables support for Boot Loader Specification (BLS) Type #1 entries. diff --git a/boot/bootmeth_bls.c b/boot/bootmeth_bls.c index c9a2afbb6f3..2c66e14c641 100644 --- a/boot/bootmeth_bls.c +++ b/boot/bootmeth_bls.c @@ -125,25 +125,39 @@ static int bls_to_pxe_label(struct bootflow *bflow, goto err; } - /* Extract kernel, initrd and FDT from the bootflow images */ + /* Extract kernel, initrds and FDT from the bootflow images */ alist_for_each(img, &bflow->images) { - char **fieldp; - - if (img->type == (enum bootflow_img_t)IH_TYPE_KERNEL) - fieldp = &label->kernel; - else if (img->type == (enum bootflow_img_t)IH_TYPE_RAMDISK) - fieldp = &label->initrd; - else if (img->type == (enum bootflow_img_t)IH_TYPE_FLATDT) - fieldp = &label->fdt; - else - continue; - - if (!*fieldp) { - *fieldp = strdup(img->fname); - if (!*fieldp) { + char *fname; + + fname = strdup(img->fname); + if (!fname) { + ret = -ENOMEM; + goto err; + } + + switch ((int)img->type) { + case IH_TYPE_KERNEL: + if (!label->kernel) + label->kernel = fname; + else + free(fname); + break; + case IH_TYPE_RAMDISK: + if (!alist_add(&label->initrds, fname)) { + free(fname); ret = -ENOMEM; goto err; } + break; + case IH_TYPE_FLATDT: + if (!label->fdt) + label->fdt = fname; + else + free(fname); + break; + default: + free(fname); + break; } } diff --git a/boot/pxe_parse.c b/boot/pxe_parse.c index 42f9125cb1d..28f957cfa70 100644 --- a/boot/pxe_parse.c +++ b/boot/pxe_parse.c @@ -107,6 +107,8 @@ static struct pxe_label *label_create(void) return NULL; memset(label, 0, sizeof(struct pxe_label)); alist_init_struct(&label->files, struct pxe_file); + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) + alist_init(&label->initrds, sizeof(char *), 4); return label; } @@ -121,7 +123,15 @@ void label_destroy(struct pxe_label *label) free(label->kernel); free(label->config); free(label->append); - free(label->initrd); + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + const char **initrd; + + alist_for_each(initrd, &label->initrds) + free((void *)*initrd); + alist_uninit(&label->initrds); + } else { + free(label->initrd); + } free(label->fdt); free(label->fdtdir); alist_for_each(file, &label->files) @@ -579,6 +589,7 @@ static int parse_label(char **c, struct pxe_menu *cfg, const char *limit) struct token t; int len; char *s = *c; + char *initrd_path; struct pxe_label *label; int err; @@ -612,26 +623,57 @@ static int parse_label(char **c, struct pxe_menu *cfg, const char *limit) break; case T_APPEND: err = parse_sliteral(c, &label->append, limit); - if (label->initrd) - break; + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (label->initrds.count) + break; + } else { + if (label->initrd) + break; + } s = strstr(label->append, "initrd="); if (!s) break; s += 7; len = (int)(strchr(s, ' ') - s); - label->initrd = malloc(len + 1); - strlcpy(label->initrd, s, len); - label->initrd[len] = '\0'; + initrd_path = malloc(len + 1); + if (!initrd_path) { + err = -ENOMEM; + break; + } + strlcpy(initrd_path, s, len + 1); + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (!alist_add(&label->initrds, initrd_path)) { + free(initrd_path); + err = -ENOMEM; + break; + } + } else { + label->initrd = initrd_path; + } + err = label_add_file(label, initrd_path, PFT_INITRD); break; case T_INITRD: - if (!label->initrd) { - err = parse_sliteral(c, &label->initrd, limit); - if (err < 0) + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (label->initrds.count) + break; + } else { + if (label->initrd) break; - err = label_add_file(label, label->initrd, - PFT_INITRD); } + err = parse_sliteral(c, &initrd_path, limit); + if (err < 0) + break; + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (!alist_add(&label->initrds, initrd_path)) { + free(initrd_path); + err = -ENOMEM; + break; + } + } else { + label->initrd = initrd_path; + } + err = label_add_file(label, initrd_path, PFT_INITRD); break; case T_FDT: if (!label->fdt) { diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index 5c1d08feebf..e5f1f3e46c2 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -622,11 +622,71 @@ static int label_run_boot(struct pxe_context *ctx, struct pxe_label *label, */ static int generate_localboot(struct pxe_label *label) { + char *initrd_path; + label->kernel = strdup("/vmlinuz"); label->kernel_label = strdup(label->kernel); - label->initrd = strdup("/initrd.img"); - if (!label->kernel || !label->kernel_label || !label->initrd) + initrd_path = strdup("/initrd.img"); + if (!label->kernel || !label->kernel_label || !initrd_path) return -ENOMEM; + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (!alist_add(&label->initrds, initrd_path)) { + free(initrd_path); + return -ENOMEM; + } + } else { + label->initrd = initrd_path; + } + + return 0; +} + +/** + * pxe_load_initrds() - Load all initrd files consecutively + * + * @ctx: PXE context + * @label: Label containing initrd file paths + * @initrd_addr: Starting address for first initrd + * @total_sizep: Returns total size of all initrds + * Return: 0 on success, -EIO on error + */ +static int pxe_load_initrds(struct pxe_context *ctx, struct pxe_label *label, + ulong initrd_addr, ulong *total_sizep) +{ + const char **initrd_path; + ulong total_size = 0; + int ret; + int i; + + /* Load each initrd consecutively */ + for (i = 0; i < label->initrds.count; i++) { + ulong size; + + initrd_path = alist_get(&label->initrds, i, char *); + /* Use ramdisk_addr_r for first, then append */ + if (i == 0) { + ret = get_relfile_envaddr(ctx, *initrd_path, + "ramdisk_addr_r", SZ_2M, + (enum bootflow_img_t)IH_TYPE_RAMDISK, + &initrd_addr, &size); + ctx->initrd_addr = initrd_addr; + } else { + /* Load subsequent initrds after the previous one */ + ulong addr = initrd_addr + total_size; + + ret = get_relfile_envaddr(ctx, *initrd_path, + NULL, SZ_2M, + (enum bootflow_img_t)IH_TYPE_RAMDISK, + &addr, &size); + } + if (ret < 0) { + printf("Skipping %s for failure retrieving initrd %s\n", + label->name, *initrd_path); + return -EIO; + } + total_size += size; + } + *total_sizep = total_size; return 0; } @@ -634,6 +694,9 @@ static int generate_localboot(struct pxe_label *label) int pxe_load_files(struct pxe_context *ctx, struct pxe_label *label, char *fdtfile) { + const char **initrd_path; + ulong initrd_addr = 0; + ulong total_size = 0; int ret; if (!label->kernel) { @@ -649,18 +712,38 @@ int pxe_load_files(struct pxe_context *ctx, struct pxe_label *label, return -EIO; } - /* For FIT, the label can be identical to kernel one */ - if (label->initrd && !strcmp(label->kernel_label, label->initrd)) { - ctx->initrd_addr = ctx->kern_addr; - } else if (label->initrd) { - ret = get_relfile_envaddr(ctx, label->initrd, "ramdisk_addr_r", - SZ_2M, - (enum bootflow_img_t)IH_TYPE_RAMDISK, - &ctx->initrd_addr, &ctx->initrd_size); - if (ret < 0) { - printf("Skipping %s for failure retrieving initrd\n", - label->name); - return -EIO; + /* Load initrds if present */ + if (IS_ENABLED(CONFIG_PXE_INITRD_LIST)) { + if (label->initrds.count) { + /* For FIT, check if first initrd is identical to kernel */ + initrd_path = alist_get(&label->initrds, 0, char *); + if (!strcmp(label->kernel_label, *initrd_path)) { + ctx->initrd_addr = ctx->kern_addr; + ctx->initrd_size = ctx->kern_size; + } else { + ret = pxe_load_initrds(ctx, label, initrd_addr, + &total_size); + if (ret) + return ret; + ctx->initrd_size = total_size; + } + } + } else { + if (label->initrd) { + if (!strcmp(label->kernel_label, label->initrd)) { + ctx->initrd_addr = ctx->kern_addr; + ctx->initrd_size = ctx->kern_size; + } else { + if (get_relfile_envaddr(ctx, label->initrd, + "ramdisk_addr_r", SZ_2M, + (enum bootflow_img_t)IH_TYPE_RAMDISK, + &ctx->initrd_addr, + &ctx->initrd_size) < 0) { + printf("Skipping %s for failure retrieving initrd\n", + label->name); + return -EIO; + } + } } } @@ -697,7 +780,8 @@ int pxe_load_label(struct pxe_context *ctx, struct pxe_label *label) if (label->localboot) { if (label->localboot_val >= 0) { - if (IS_ENABLED(CONFIG_BOOTMETH_EXTLINUX_LOCALBOOT)) { + if (IS_ENABLED(CONFIG_BOOTMETH_EXTLINUX_LOCALBOOT) && + !label->kernel) { ret = generate_localboot(label); if (ret) return ret; diff --git a/include/pxe_utils.h b/include/pxe_utils.h index 48d36bdd14c..3367230eb1c 100644 --- a/include/pxe_utils.h +++ b/include/pxe_utils.h @@ -39,7 +39,9 @@ * @kernel: the path to the kernel file to use for this label * @config: FIT configuration to use (after '#'), or NULL if none * @append: kernel command line to use when booting this label - * @initrd: path to the initrd to use for this label. + * @initrd: path to single initrd (used if !CONFIG_PXE_INITRD_LIST) + * @initrds: list of initrd paths (alist of char *) (used if + * CONFIG_PXE_INITRD_LIST) * @fdt: path to FDT to use * @fdtdir: path to FDT directory to use * @files: list of files to load (alist of struct pxe_file) @@ -60,6 +62,7 @@ struct pxe_label { char *config; char *append; char *initrd; + struct alist initrds; char *fdt; char *fdtdir; struct alist files; diff --git a/test/boot/pxe.c b/test/boot/pxe.c index f4a124eafda..cd831807b94 100644 --- a/test/boot/pxe.c +++ b/test/boot/pxe.c @@ -203,7 +203,9 @@ static int pxe_test_parse_norun(struct unit_test_state *uts) ut_asserteq_str("/vmlinuz", label->kernel); ut_assertnull(label->config); ut_asserteq_str("root=/dev/sda1 quiet", label->append); - ut_asserteq_str("/initrd.img", label->initrd); + ut_asserteq(1, label->initrds.count); + ut_asserteq_str("/initrd.img", + *alist_get(&label->initrds, 0, char *)); ut_asserteq_str("/dtb/board.dtb", label->fdt); ut_assertnull(label->fdtdir); ut_asserteq(5, label->files.count); @@ -233,7 +235,7 @@ static int pxe_test_parse_norun(struct unit_test_state *uts) ut_asserteq_str("/vmlinuz-rescue", label->kernel); ut_assertnull(label->config); ut_asserteq_str("single", label->append); - ut_assertnull(label->initrd); + ut_asserteq(0, label->initrds.count); ut_assertnull(label->fdt); ut_asserteq_str("/dtb/", label->fdtdir); ut_asserteq(1, label->files.count); @@ -255,7 +257,7 @@ static int pxe_test_parse_norun(struct unit_test_state *uts) ut_assertnull(label->kernel); ut_assertnull(label->config); ut_assertnull(label->append); - ut_assertnull(label->initrd); + ut_asserteq(0, label->initrds.count); ut_assertnull(label->fdt); ut_assertnull(label->fdtdir); ut_asserteq(0, label->files.count); @@ -275,7 +277,7 @@ static int pxe_test_parse_norun(struct unit_test_state *uts) ut_asserteq_str("/boot/image.fit", label->kernel); ut_asserteq_str("#config-1", label->config); ut_asserteq_str("console=ttyS0", label->append); - ut_assertnull(label->initrd); + ut_asserteq(0, label->initrds.count); ut_assertnull(label->fdt); ut_assertnull(label->fdtdir); ut_asserteq(1, label->files.count); @@ -297,7 +299,7 @@ static int pxe_test_parse_norun(struct unit_test_state *uts) ut_asserteq_str("/boot/included-kernel", label->kernel); ut_assertnull(label->config); ut_asserteq_str("root=/dev/sdb1", label->append); - ut_assertnull(label->initrd); + ut_asserteq(0, label->initrds.count); ut_assertnull(label->fdt); ut_assertnull(label->fdtdir); ut_asserteq(1, label->files.count);