@@ -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.
@@ -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;
}
}
@@ -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) {
@@ -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;
@@ -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;
@@ -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);