@@ -114,30 +114,88 @@ static int extlinux_check_luks(struct bootflow *bflow)
return 0;
}
+/**
+ * extlinux_parse_config() - Parse the extlinux config and cache the result
+ *
+ * Parses the configuration file including any include directives, and caches
+ * the result in plat->ctx.cfg. The filesystem must still be accessible for
+ * loading includes.
+ *
+ * @dev: Bootmeth device (needed for file-loading callback)
+ * @bflow: Bootflow containing the config buffer
+ * Return: 0 if OK, -ve on error
+ */
+static int extlinux_parse_config(struct udevice *dev, struct bootflow *bflow,
+ struct pxe_context *ctx)
+{
+ struct extlinux_plat *plat = dev_get_plat(dev);
+ struct abuf buf;
+ ulong addr;
+ int ret;
+
+ plat->info.dev = dev;
+ plat->info.bflow = bflow;
+ ret = pxe_setup_ctx(ctx, extlinux_getfile, &plat->info, true,
+ bflow->fname, false, plat->use_fallback, bflow);
+ if (ret)
+ return log_msg_ret("ctx", ret);
+ ctx->quiet = true;
+ ctx->pxe_file_size = bflow->size;
+
+ addr = map_to_sysmem(bflow->buf);
+ abuf_init_addr(&buf, addr, bflow->size);
+ ctx->cfg = parse_pxefile(ctx, &buf);
+ if (!ctx->cfg) {
+ pxe_destroy_ctx(ctx);
+ return log_msg_ret("prs", -EINVAL);
+ }
+
+ ret = pxe_process_includes(ctx, ctx->cfg, addr);
+ if (ret) {
+ pxe_menu_uninit(ctx->cfg);
+ ctx->cfg = NULL;
+ pxe_destroy_ctx(ctx);
+ return log_msg_ret("inc", ret);
+ }
+
+ return 0;
+}
+
/**
* extlinux_fill_info() - Decode the extlinux file to find out its info
*
- * Uses pxe_parse() to parse the configuration file and extract the label
- * selected by @bflow->entry to use as the bootflow OS name.
+ * On the first call (entry 0), calls extlinux_parse_config() to parse
+ * into a context from the alist. For entry > 0, reuses the cached
+ * context.
*
+ * @dev: Bootmeth device (needed for file-loading callback)
* @bflow: Bootflow to process (entry selects which label)
* Return: 0 if OK, -ENOENT if entry index exceeds available labels, other
* -ve on error
*/
-static int extlinux_fill_info(struct bootflow *bflow)
+static int extlinux_fill_info(struct udevice *dev, struct bootflow *bflow)
{
+ struct extlinux_priv *priv = dev_get_priv(dev);
struct pxe_context *ctx;
struct pxe_label *label;
const char *name;
- ulong addr;
int i;
log_debug("parsing bflow file size %x entry %d\n", bflow->size,
bflow->entry);
- addr = map_to_sysmem(bflow->buf);
- ctx = pxe_parse(addr, bflow->size, bflow->fname);
+
+ ctx = extlinux_get_ctx(priv, bflow);
if (!ctx)
- return log_msg_ret("prs", -EINVAL);
+ return log_msg_ret("ctx", -ENOMEM);
+
+ /* Parse the config on first entry; reuse the cached result after */
+ if (!ctx->cfg) {
+ int ret;
+
+ ret = extlinux_parse_config(dev, bflow, ctx);
+ if (ret)
+ return log_msg_ret("prs", ret);
+ }
/* Walk to the requested label */
i = 0;
@@ -147,29 +205,21 @@ static int extlinux_fill_info(struct bootflow *bflow)
i++;
}
- /* No more entries at this index */
- pxe_cleanup(ctx);
return -ENOENT;
found:
name = label->menu ? label->menu : label->name;
if (name) {
bflow->os_name = strdup(name);
- if (!bflow->os_name) {
- pxe_cleanup(ctx);
- return log_msg_ret("os", -ENOMEM);
- }
+ if (!bflow->os_name)
+ return log_msg_ret("osn", -ENOMEM);
}
if (label->name) {
bflow->entry_name = strdup(label->name);
- if (!bflow->entry_name) {
- pxe_cleanup(ctx);
- return log_msg_ret("xnt", -ENOMEM);
- }
+ if (!bflow->entry_name)
+ return log_msg_ret("ent", -ENOMEM);
}
- pxe_cleanup(ctx);
-
return 0;
}
@@ -215,7 +265,7 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow)
if (ret)
return log_msg_ret("read", ret);
- ret = extlinux_fill_info(bflow);
+ ret = extlinux_fill_info(dev, bflow);
if (ret)
return log_msg_ret("inf", ret);
@@ -129,18 +129,36 @@ int extlinux_boot(struct udevice *dev, struct bootflow *bflow,
if (ctx->label) {
ctx->fake_go = bflow->flags & BOOTFLOWF_FAKE_GO;
ret = pxe_boot(ctx);
+ if (ret)
+ return log_msg_ret("pxb", -EFAULT);
+ return 0;
+ }
+
+ /*
+ * If the config was cached during scanning, update the callback fields
+ * for file loading. Otherwise set up the context from scratch.
+ */
+ if (ctx->cfg) {
+ struct extlinux_plat *plat = dev_get_plat(dev);
+
+ plat->info.dev = dev;
+ plat->info.bflow = bflow;
+ ctx->getfile = getfile;
+ ctx->userdata = &plat->info;
+ ctx->bflow = bflow;
} else {
ret = extlinux_setup(dev, bflow, getfile, allow_abs_path,
bootfile, ctx);
if (ret)
return log_msg_ret("elb", ret);
- ctx->restart = restart;
- addr = map_to_sysmem(bflow->buf);
-
- ret = pxe_boot_entry(ctx, addr, bflow->entry);
}
+ ctx->restart = restart;
+ ctx->fake_go = bflow->flags & BOOTFLOWF_FAKE_GO;
+ addr = map_to_sysmem(bflow->buf);
+
+ ret = pxe_boot_entry(ctx, addr, bflow->entry);
if (ret)
- return log_msg_ret("elb", -EFAULT);
+ return log_msg_ret("ent", -EFAULT);
return 0;
}
@@ -1513,30 +1513,40 @@ int pxe_boot(struct pxe_context *ctx)
int pxe_boot_entry(struct pxe_context *ctx, ulong addr, int entry)
{
+ bool free_cfg = false;
struct pxe_label *label;
struct pxe_menu *cfg;
- int ret, i = 0;
- void *ptr;
+ int i = 0;
+ int ret;
- ptr = map_sysmem(addr, 0);
- ctx->pxe_file_size = strnlen(ptr, SZ_64K);
- unmap_sysmem(ptr);
+ /* Use cached config if available, otherwise parse */
+ cfg = ctx->cfg;
+ if (!cfg) {
+ void *ptr;
- cfg = pxe_prepare(ctx, addr, false);
- if (!cfg)
- return log_msg_ret("prp", -EINVAL);
+ ptr = map_sysmem(addr, 0);
+ ctx->pxe_file_size = strnlen(ptr, SZ_64K);
+ unmap_sysmem(ptr);
+
+ cfg = pxe_prepare(ctx, addr, false);
+ if (!cfg)
+ return log_msg_ret("prp", -EINVAL);
+ free_cfg = true;
+ }
list_for_each_entry(label, &cfg->labels, list) {
if (i == entry)
goto found;
i++;
}
- pxe_menu_uninit(cfg);
+ if (free_cfg)
+ pxe_menu_uninit(cfg);
return log_msg_ret("lab", -ENOENT);
found:
ret = label_boot(ctx, label);
- pxe_menu_uninit(cfg);
+ if (free_cfg)
+ pxe_menu_uninit(cfg);
return ret;
}
@@ -101,6 +101,10 @@ static int bootctl_oslist_usb(struct unit_test_state *uts)
ut_assertok(bc_oslist_next(dev, &iter, &info));
ut_asserteq_str("hub1.p4.usb_mass_storage.lun0.bootdev.part_1", bflow->name);
+ /* second entry from flash3 (Ubuntu has two extlinux labels) */
+ ut_assertok(bc_oslist_next(dev, &iter, &info));
+ ut_asserteq_str("hub1.p4.usb_mass_storage.lun0.bootdev.part_1", bflow->name);
+
ut_asserteq(-ENODEV, bc_oslist_next(dev, &iter, &info));
return 0;
@@ -456,6 +460,10 @@ static int check_multiboot_ui(struct unit_test_state *uts,
ut_assertok(bc_oslist_next(oslist_dev, &iter, &info[0]));
ut_asserteq_str("mmc11.bootdev.part_1", info[0].bflow.name);
+ /* skip the second mmc11 entry (Ubuntu has two extlinux labels) */
+ ut_assertok(bc_oslist_next(oslist_dev, &iter, &info[1]));
+ ut_asserteq_str("mmc11.bootdev.part_1", info[1].bflow.name);
+
ut_assertok(bc_oslist_next(oslist_dev, &iter, &info[1]));
ut_asserteq_str("hub1.p4.usb_mass_storage.lun0.bootdev.part_1",
info[1].bflow.name);
@@ -292,7 +292,7 @@ static int bootflow_scan_boot(struct unit_test_state *uts)
ut_assertok(run_command("bootflow scan -b", 0));
ut_assert_nextline(
"** Booting bootflow 'mmc1.bootdev.part_1' with extlinux");
- ut_assert_nextline("Ignoring unknown command: ui");
+ /* cached parse from scanning suppresses parser warnings */
/*
* We expect it to get through to boot although sandbox always returns
@@ -636,7 +636,7 @@ static int bootflow_cmd_boot(struct unit_test_state *uts)
ut_asserteq(1, run_command("bootflow boot", 0));
ut_assert_nextline(
"** Booting bootflow 'mmc1.bootdev.part_1' with extlinux");
- ut_assert_nextline("Ignoring unknown command: ui");
+ /* cached parse from scanning suppresses parser warnings */
/*
* We expect it to get through to boot although sandbox always returns
@@ -1861,6 +1861,8 @@ static int bootflow_cmd_bls(struct unit_test_state *uts)
{
struct bootstd_priv *std;
const char **old_order;
+ struct bootflow *bflow;
+ bool test_first;
ut_assertok(prep_mmc_bootdev(uts, "mmc15", true, &old_order));
ut_assertok(run_command("bootflow scan", 0));
@@ -1871,13 +1873,25 @@ static int bootflow_cmd_bls(struct unit_test_state *uts)
free(std->bootdev_order);
std->bootdev_order = old_order;
+ /*
+ * BLS entry order depends on the filesystem, so detect which
+ * .conf file came first and check accordingly
+ */
+ bflow = alist_getw(&std->bootflows, 1, struct bootflow);
+ test_first = !strcmp(bflow->os_name, "Test Boot");
+
ut_assertok(run_command("bootflow list", 0));
ut_assert_nextline("Showing all bootflows");
ut_assert_nextline(HEADER);
ut_assert_nextlinen("---");
ut_assert_nextlinen(" 0 extlinux");
- ut_assert_nextline(" 1 bls ready mmc 1 0 mmc15.bootdev.part_1 /loader/entries/6.8.0.conf");
- ut_assert_nextline(" 2 bls ready mmc 1 1 mmc15.bootdev.part_1 /loader/entries/6.8.0-rescue.conf");
+ if (test_first) {
+ ut_assert_nextline(" 1 bls ready mmc 1 0 mmc15.bootdev.part_1 /loader/entries/6.8.0.conf");
+ ut_assert_nextline(" 2 bls ready mmc 1 1 mmc15.bootdev.part_1 /loader/entries/6.8.0-rescue.conf");
+ } else {
+ ut_assert_nextline(" 1 bls ready mmc 1 0 mmc15.bootdev.part_1 /loader/entries/6.8.0-rescue.conf");
+ ut_assert_nextline(" 2 bls ready mmc 1 1 mmc15.bootdev.part_1 /loader/entries/6.8.0.conf");
+ }
ut_assert_nextlinen("---");
ut_assert_nextline("(3 bootflows, 3 valid)");
ut_assert_console_end();
@@ -1892,12 +1906,15 @@ static int bootflow_cmd_bls(struct unit_test_state *uts)
ut_assert_nextline("Method: bls");
ut_assert_nextline("State: ready");
ut_assert_nextline("Partition: 1");
- ut_assert_nextline("Entry: 0: Test Boot");
+ ut_assert_nextline("Entry: 0: %s",
+ test_first ? "Test Boot" : "Rescue Boot");
if (IS_ENABLED(CONFIG_BLK_LUKS))
ut_assert_nextline("Encrypted: no");
ut_assert_nextline("Subdir: (none)");
- ut_assert_nextline("Filename: /loader/entries/6.8.0.conf");
- ut_assert_skip_to_line("OS: Test Boot");
+ ut_assert_nextline("Filename: /loader/entries/%s",
+ test_first ? "6.8.0.conf" : "6.8.0-rescue.conf");
+ ut_assert_skip_to_line("OS: %s",
+ test_first ? "Test Boot" : "Rescue Boot");
ut_assert_skip_to_line("Error: 0");
ut_assert_console_end();
@@ -1906,8 +1923,10 @@ static int bootflow_cmd_bls(struct unit_test_state *uts)
ut_assert_console_end();
ut_assertok(run_command("bootflow info", 0));
ut_assert_nextline("Name: mmc15.bootdev.part_1");
- ut_assert_skip_to_line("Filename: /loader/entries/6.8.0-rescue.conf");
- ut_assert_skip_to_line("OS: Rescue Boot");
+ ut_assert_skip_to_line("Filename: /loader/entries/%s",
+ test_first ? "6.8.0-rescue.conf" : "6.8.0.conf");
+ ut_assert_skip_to_line("OS: %s",
+ test_first ? "Rescue Boot" : "Test Boot");
ut_assert_skip_to_line("Error: 0");
ut_assert_console_end();
@@ -34,7 +34,8 @@ def copy_partition(ubman, fsfile, outname):
def setup_extlinux_image(config, log, devnum, basename, vmlinux, initrd, dtbdir,
script, part2_size=1, use_fde=0, luks_kdf='pbkdf2',
- encrypt_keyfile=None, master_keyfile=None):
+ encrypt_keyfile=None, master_keyfile=None,
+ extra_conf=None):
"""Create a 20MB disk image with a single FAT partition
Args:
@@ -54,6 +55,8 @@ def setup_extlinux_image(config, log, devnum, basename, vmlinux, initrd, dtbdir,
If provided, takes precedence over passphrase.
master_keyfile (str, optional): Path to file containing the raw master
key. If provided, this exact key is used as the LUKS master key.
+ extra_conf (dict, optional): Extra files to create in the extlinux
+ directory, as {filename: content} pairs.
"""
fsh = FsHelper(config, 'vfat', 18, prefix=basename)
fsh.setup()
@@ -65,6 +68,12 @@ def setup_extlinux_image(config, log, devnum, basename, vmlinux, initrd, dtbdir,
with open(conf, 'w', encoding='ascii') as fd:
print(script, file=fd)
+ if extra_conf:
+ for fname, content in extra_conf.items():
+ with open(os.path.join(ext, fname), 'w',
+ encoding='ascii') as fd:
+ print(content, file=fd)
+
inf = os.path.join(config.persistent_data_dir, 'inf')
with open(inf, 'wb') as fd:
fd.write(gzip.compress(b'vmlinux'))
@@ -51,13 +51,16 @@ label l0
append root=/dev/disk/by-uuid/bcfdda4a-8249-4f40-9f0f-7c1a76b6cbe8 ro earlycon
-label l0r
+include rescue.conf
+''' % (version, vmlinux, initrd)
+ rescue = '''label l0r
menu label Ubuntu %s 6.8.0-53-generic (rescue target)
linux /boot/%s
initrd /boot/%s
-''' % ((version, vmlinux, initrd) * 2)
+''' % (version, vmlinux, initrd)
setup_extlinux_image(config, log, devnum, basename, vmlinux, initrd, dtbdir,
script, part2_size=60 if use_fde else 1,
use_fde=use_fde, luks_kdf=luks_kdf,
encrypt_keyfile=encrypt_keyfile,
- master_keyfile=master_keyfile)
+ master_keyfile=master_keyfile,
+ extra_conf={'rescue.conf': rescue})