[PATCH 00/14] bootstd: Infrastructure for multi-entry bootflow support
From: Simon Glass <sjg@chromium.org> This series prepares the bootstd infrastructure for supporting multiple bootflows from a single partition. Formats like extlinux can define several labels in one configuration file, and BLS installations can have multiple .conf files in loader/entries/ A follow-up series will add the actual multi-entry scanning. In summary: - Fix two iter->err inconsistencies in the bootflow scanning code that would prevent multi-entry iteration from working correctly - Add a free_bootflow() method to bootmeth_ops so bootmeths can properly clean up bflow->bootmeth_priv. Fix memory leaks in the cros, android and rauc drivers - Refactor the extlinux bootmeth to pass the PXE context explicitly and move it from platform data to a new private data struct - Convert the single PXE context to an alist of contexts, indexed by a new bootmeth_id field on the bootflow. This allows multiple parsed configurations to coexist - Add alist_add_placeholder() zeroing and suppress PXE parser warnings during non-interactive parsing Simon Glass (14): mcheck: Increase registry size alist: Zero new entries in alist_add_placeholder() bootstd: Set iter->err before BOOTFLOWIF_ALL early return bootstd: Clear iter->err on successful bootflow check pxe: Allow the parser to suppress warning messages bootstd: Add free_bootflow() method for bootmeths cros: Implement free_bootflow to fix memory leak android: Implement free_bootflow to fix memory leak rauc: Implement free_bootflow to fix memory leak extlinux: Pass pxe_context to extlinux_boot() and extlinux_read_all() extlinux: Move pxe_context to new extlinux_priv struct extlinux: Add remove() to fix pxe_context leak bootstd: Add bootmeth_id to struct bootflow extlinux: Convert pxe_context to an alist with bootmeth_id lookup boot/bootflow.c | 11 ++++-- boot/bootmeth-uclass.c | 10 ++++++ boot/bootmeth_android.c | 9 +++++ boot/bootmeth_cros.c | 9 +++++ boot/bootmeth_extlinux.c | 51 ++++++++++++++++++++++++--- boot/bootmeth_pxe.c | 27 ++++++++++++--- boot/bootmeth_rauc.c | 13 ++++++- boot/ext_pxe_common.c | 54 ++++++++++++++++++++--------- boot/pxe_parse.c | 27 +++++++++------ boot/pxe_utils.c | 1 + boot/vbe_abrec.h | 4 +++ boot/vbe_abrec_os.c | 13 ++++--- common/mcheck_core.inc.h | 2 +- doc/develop/bootstd/overview.rst | 12 ++++++- include/alist.h | 7 ++-- include/bootflow.h | 8 ++++- include/bootmeth.h | 24 +++++++++++++ include/extlinux.h | 59 ++++++++++++++++++++++++++++---- lib/alist.c | 8 ++++- test/boot/bootflow.c | 2 ++ test/lib/alist.c | 10 ++++++ 21 files changed, 299 insertions(+), 62 deletions(-) -- 2.43.0 base-commit: db6764b499965b88191ecd2419282087fdfe36c9 branch: bood
From: Simon Glass <sjg@chromium.org> The full test suite with multi-entry bootflow scanning causes more concurrent allocations, overflowing the 12000-entry mcheck registry. Increase it to 20000 to provide headroom. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/mcheck_core.inc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/mcheck_core.inc.h b/common/mcheck_core.inc.h index fa152790d45..65f83836d61 100644 --- a/common/mcheck_core.inc.h +++ b/common/mcheck_core.inc.h @@ -71,7 +71,7 @@ #define PADDINGFLOOD ((char)0x58) // Full test suite can exceed 10000 concurrent allocations -#define REGISTRY_SZ 12000 +#define REGISTRY_SZ 20000 #define CANARY_DEPTH 2 // avoid problems with BSS at early stage: -- 2.43.0
From: Simon Glass <sjg@chromium.org> It is convenient for alist_add_placeholder() to zero the entry before returning it. This saves callers from needing to do it themselves. Update an alist test to verify the new entry is zeroed. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/alist.h | 7 +++---- lib/alist.c | 8 +++++++- test/lib/alist.c | 10 ++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/include/alist.h b/include/alist.h index 69d7cdb722f..7b368a7e09a 100644 --- a/include/alist.h +++ b/include/alist.h @@ -174,12 +174,11 @@ void *alist_ensure_ptr(struct alist *lst, uint index); ((_struct *)alist_ensure_ptr(_lst, _index)) /** - * alist_add_placeholder() - Add a new item to the end of the list + * alist_add_placeholder() - Add a new zeroed item to the end of the list * * @lst: alist to add to - * Return: Pointer to the newly added position, or NULL if out of memory. Note - * that this is not inited so the caller must copy the requested struct to the - * returned pointer + * Return: Pointer to the newly added position (zeroed), or NULL if out of + * memory */ void *alist_add_placeholder(struct alist *lst); diff --git a/lib/alist.c b/lib/alist.c index 0ae07f0f5c0..b9ddd4103c4 100644 --- a/lib/alist.c +++ b/lib/alist.c @@ -165,7 +165,13 @@ void *alist_ensure_ptr(struct alist *lst, uint index) void *alist_add_placeholder(struct alist *lst) { - return alist_ensure_ptr(lst, lst->count); + void *ptr; + + ptr = alist_ensure_ptr(lst, lst->count); + if (ptr) + memset(ptr, '\0', lst->obj_size); + + return ptr; } void *alist_add_ptr(struct alist *lst, void *obj) diff --git a/test/lib/alist.c b/test/lib/alist.c index 108eaed8d92..8e958ed9398 100644 --- a/test/lib/alist.c +++ b/test/lib/alist.c @@ -219,8 +219,18 @@ static int lib_test_alist_add(struct unit_test_state *uts) ut_asserteq(123, ptr->val); ut_asserteq(456, ptr->other_val); + /* Add a non-zero entry then remove it, so the memory is dirty */ ptr2 = alist_add_placeholder(&lst); ut_assertnonnull(ptr2); + ptr2->val = 999; + ptr2->other_val = 888; + lst.count--; + + /* Now add again — the slot should be zeroed despite dirty memory */ + ptr2 = alist_add_placeholder(&lst); + ut_assertnonnull(ptr2); + ut_asserteq(0, ptr2->val); + ut_asserteq(0, ptr2->other_val); ptr2->val = 321; ptr2->other_val = 654; -- 2.43.0
From: Simon Glass <sjg@chromium.org> In bootflow_scan_first(), iter->err is not set before the BOOTFLOWIF_ALL early return, leaving it at its initial value of 0. This is inconsistent with bootflow_scan_next() which sets iter->err before the same early return. Move the iter->err assignment before the BOOTFLOWIF_ALL check so that subsequent calls to iter_incr() can correctly see the error from the previous iteration. Fixes: 5033e36637af ("bootstd: Add support for bootflows") Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 2 +- test/boot/bootflow.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index befe507af96..a8a73ee0c7f 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -655,11 +655,11 @@ int bootflow_scan_first(struct udevice *dev, const char *label, ret = bootflow_check(iter, bflow); if (ret) { log_debug("check - ret=%d\n", ret); + iter->err = ret; if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { if (iter->flags & BOOTFLOWIF_ALL) return log_msg_ret("all", ret); } - iter->err = ret; bootflow_free(bflow); ret = bootflow_scan_next(iter, bflow); if (ret) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 6c84a264bff..5ecb5f557b1 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -321,6 +321,7 @@ static int bootflow_iter(struct unit_test_state *uts) ut_asserteq(0, iter.max_part); ut_asserteq_str("extlinux", iter.method->name); ut_asserteq(0, bflow.err); + ut_asserteq(-EPROTONOSUPPORT, iter.err); ut_assert(!iter.doing_global); ut_assert(!iter.have_global); ut_asserteq(-1, iter.first_glob_method); -- 2.43.0
From: Simon Glass <sjg@chromium.org> When bootflow_check() succeeds in bootflow_scan_next(), iter->err is not cleared. It retains the error from the previous failed iteration, so callers of iter_incr() cannot reliably determine whether the last bootflow was found successfully. Clear iter->err to 0 on success, matching the convention that iter->err reflects the result of the most recent bootflow check. Fixes: 5033e36637af ("bootstd: Add support for bootflows") Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 4 +++- test/boot/bootflow.c | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index a8a73ee0c7f..4a55cc637fa 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -687,8 +687,10 @@ int bootflow_scan_next(struct bootflow_iter *iter, struct bootflow *bflow) } ret = bootflow_check(iter, bflow); log_debug("check - ret=%d\n", ret); - if (!ret) + if (!ret) { + iter->err = 0; return 0; + } iter->err = ret; if (ret != BF_NO_MORE_PARTS && ret != -ENOSYS) { if (iter->flags & BOOTFLOWIF_ALL) diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c index 5ecb5f557b1..3bea1cc6634 100644 --- a/test/boot/bootflow.c +++ b/test/boot/bootflow.c @@ -402,6 +402,7 @@ static int bootflow_iter(struct unit_test_state *uts) ut_asserteq(0x1e, iter.max_part); ut_asserteq_str("extlinux", iter.method->name); ut_asserteq(0, bflow.err); + ut_asserteq(0, iter.err); ut_asserteq(BOOTFLOWST_READY, bflow.state); bootflow_free(&bflow); -- 2.43.0
From: Simon Glass <sjg@chromium.org> The PXE parser prints informational messages like "Ignoring unknown command" and "Ignoring malformed menu command" unconditionally. This is useful during interactive boot, but not appropriate when parsing is done silently for metadata extraction, such as during bootflow scanning. Thread the pxe_context through parse_label() and parse_label_menu() so they can check ctx->quiet, and gate all three warning printf() calls on !ctx->quiet Set ctx->quiet in pxe_parse() since it is a non-interactive parsing API intended for callers that handle output themselves. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/pxe_parse.c | 27 ++++++++++++++++----------- boot/pxe_utils.c | 1 + 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/boot/pxe_parse.c b/boot/pxe_parse.c index a33e40002e9..44cbb9aba86 100644 --- a/boot/pxe_parse.c +++ b/boot/pxe_parse.c @@ -498,8 +498,9 @@ static int parse_menu(struct pxe_context *ctx, char **c, struct pxe_menu *cfg, err = parse_sliteral(c, &cfg->bmp, limit); break; default: - printf("Ignoring malformed menu command: %.*s\n", - (int)(*c - s), s); + if (!ctx->quiet) + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); } free(t.val); if (err < 0) @@ -513,8 +514,9 @@ static int parse_menu(struct pxe_context *ctx, char **c, struct pxe_menu *cfg, /* * Handles parsing a 'menu line' when we're parsing a label. */ -static int parse_label_menu(char **c, struct pxe_menu *cfg, - struct pxe_label *label, const char *limit) +static int parse_label_menu(struct pxe_context *ctx, char **c, + struct pxe_menu *cfg, struct pxe_label *label, + const char *limit) { struct token t; char *s; @@ -536,8 +538,9 @@ static int parse_label_menu(char **c, struct pxe_menu *cfg, parse_sliteral(c, &label->menu, limit); break; default: - printf("Ignoring malformed menu command: %.*s\n", - (int)(*c - s), s); + if (!ctx->quiet) + printf("Ignoring malformed menu command: %.*s\n", + (int)(*c - s), s); } free(t.val); @@ -584,7 +587,8 @@ static int parse_label_kernel(char **c, struct pxe_label *label, * get some input we otherwise don't have a handler defined * for. */ -static int parse_label(char **c, struct pxe_menu *cfg, const char *limit) +static int parse_label(struct pxe_context *ctx, char **c, struct pxe_menu *cfg, + const char *limit) { struct token t; int len; @@ -614,7 +618,7 @@ static int parse_label(char **c, struct pxe_menu *cfg, const char *limit) err = 0; switch (t.type) { case T_MENU: - err = parse_label_menu(c, cfg, label, limit); + err = parse_label_menu(ctx, c, cfg, label, limit); break; case T_KERNEL: case T_LINUX: @@ -770,7 +774,7 @@ int parse_pxefile_top(struct pxe_context *ctx, char *p, const char *limit, err = parse_integer(&p, &cfg->timeout, limit); break; case T_LABEL: - err = parse_label(&p, cfg, limit); + err = parse_label(ctx, &p, cfg, limit); break; case T_DEFAULT: case T_ONTIMEOUT: @@ -806,8 +810,9 @@ int parse_pxefile_top(struct pxe_context *ctx, char *p, const char *limit, free(t.val); return 1; default: - printf("Ignoring unknown command: %.*s\n", - (int)(p - s), s); + if (!ctx->quiet) + printf("Ignoring unknown command: %.*s\n", + (int)(p - s), s); eol_or_eof(&p); } diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c index e5f1f3e46c2..f5f8e38de2e 100644 --- a/boot/pxe_utils.c +++ b/boot/pxe_utils.c @@ -1426,6 +1426,7 @@ struct pxe_context *pxe_parse(ulong addr, ulong size, const char *bootfile) free(ctx); return NULL; } + ctx->quiet = true; ctx->pxe_file_size = size; abuf_init_addr(&buf, addr, size); -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a free_bootflow() op to struct bootmeth_ops so bootmeths can free internal allocations within bflow->bootmeth_priv. The bootmeth_free_bootflow() stub calls the op if provided, then always frees bootmeth_priv itself and sets it to NULL. This means drivers only need to free sub-allocations, not the priv struct. Update bootflow_free() to use the stub when a method is set. Document the bootmeth_priv lifecycle in overview.rst and bootflow.h. No drivers implement the op yet; the fallback to free() preserves the existing behaviour. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 4 +++- boot/bootmeth-uclass.c | 10 ++++++++++ doc/develop/bootstd/overview.rst | 7 +++++++ include/bootflow.h | 4 +++- include/bootmeth.h | 24 ++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index 4a55cc637fa..d6549b5ce9a 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -726,7 +726,9 @@ void bootflow_free(struct bootflow *bflow) free(bflow->buf); free(bflow->os_name); free(bflow->fdt_fname); - free(bflow->bootmeth_priv); + /* bootmeth_priv is only set when method is set */ + if (bflow->method) + bootmeth_free_bootflow(bflow->method, bflow); alist_for_each(img, &bflow->images) free(img->fname); diff --git a/boot/bootmeth-uclass.c b/boot/bootmeth-uclass.c index 5e0badfc7a9..0ca3e8fca32 100644 --- a/boot/bootmeth-uclass.c +++ b/boot/bootmeth-uclass.c @@ -84,6 +84,16 @@ int bootmeth_boot(struct udevice *dev, struct bootflow *bflow) return ops->boot(dev, bflow); } +void bootmeth_free_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + const struct bootmeth_ops *ops = bootmeth_get_ops(dev); + + if (ops->free_bootflow) + ops->free_bootflow(dev, bflow); + free(bflow->bootmeth_priv); + bflow->bootmeth_priv = NULL; +} + int bootmeth_read_file(struct udevice *dev, struct bootflow *bflow, const char *file_path, ulong *addrp, ulong align, enum bootflow_img_t type, ulong *sizep) diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index 3e3b502460d..0ff4868ba44 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -855,6 +855,13 @@ list of scanned bootflows just for that device. The bootflow itself is documented in bootflow_h_. It includes various bits of information about the bootflow and a buffer to hold the file. +The ``bootmeth_priv`` field allows a bootmeth to attach private data to each +bootflow, such as parsed configuration state. When the bootflow is freed, +``bootmeth_free_bootflow()`` calls the bootmeth's ``free_bootflow()`` op (if +provided) to free internal allocations, then frees ``bootmeth_priv`` itself. +Bootmeths that only store a flat struct in ``bootmeth_priv`` do not need to +implement the op. + Future ------ diff --git a/include/bootflow.h b/include/bootflow.h index 6c6f07db97d..65aebefd3b3 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -98,7 +98,9 @@ enum bootflow_flags_t { * @luks_version: LUKS version (1 or 2) if BOOTFLOWF_ENCRYPTED is set, else 0 * @cmdline: OS command line, or NULL if not known (allocated) * @x86_setup: Pointer to x86 setup block inside @buf, NULL if not present - * @bootmeth_priv: Private data for the bootmeth + * @bootmeth_priv: Private data for the bootmeth (allocated). Freed by + * bootmeth_free_bootflow() which calls the bootmeth's free_bootflow() op + * for internal cleanup, then frees the pointer itself. * @images: List of loaded images (struct bootstd_img) */ struct bootflow { diff --git a/include/bootmeth.h b/include/bootmeth.h index b5288843d03..2cc8b690bbf 100644 --- a/include/bootmeth.h +++ b/include/bootmeth.h @@ -155,6 +155,19 @@ struct bootmeth_ops { */ int (*boot)(struct udevice *dev, struct bootflow *bflow); + /** + * free_bootflow() - free bootmeth-private data in a bootflow + * + * This is called from bootmeth_free_bootflow() to allow the bootmeth + * to free any internal allocations within bflow->bootmeth_priv. The + * caller handles free(bflow->bootmeth_priv) afterwards, so the op + * should not free the priv struct itself. + * + * @dev: Bootmethod device + * @bflow: Bootflow being freed + */ + void (*free_bootflow)(struct udevice *dev, struct bootflow *bflow); + /** * set_property() - set the bootmeth property * @@ -309,6 +322,17 @@ int bootmeth_boot(struct udevice *dev, struct bootflow *bflow); */ int bootmeth_setup_iter_order(struct bootflow_iter *iter, bool include_global); +/** + * bootmeth_free_bootflow() - free bootmeth-private data in a bootflow + * + * Calls the bootmeth's free_bootflow() op if provided to free internal + * allocations, then frees bflow->bootmeth_priv and sets it to NULL. + * + * @dev: Bootmethod device + * @bflow: Bootflow being freed + */ +void bootmeth_free_bootflow(struct udevice *dev, struct bootflow *bflow); + /** * bootmeth_set_order() - Set the bootmeth order * -- 2.43.0
From: Simon Glass <sjg@chromium.org> The ChromiumOS bootmeth allocates an info_buf inside cros_priv but bootflow_free() only calls free() on the priv struct itself, so the buffer is never freed. Implement free_bootflow() to free info_buf before the priv struct. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_cros.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/boot/bootmeth_cros.c b/boot/bootmeth_cros.c index f92e4437b27..a09194ca270 100644 --- a/boot/bootmeth_cros.c +++ b/boot/bootmeth_cros.c @@ -467,10 +467,19 @@ static int cros_bootmeth_bind(struct udevice *dev) return 0; } +static void cros_free_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + struct cros_priv *priv = bflow->bootmeth_priv; + + if (priv) + free(priv->info_buf); +} + static struct bootmeth_ops cros_bootmeth_ops = { .check = cros_check, .read_bootflow = cros_read_bootflow, .read_file = cros_read_file, + .free_bootflow = cros_free_bootflow, .boot = cros_boot, #if CONFIG_IS_ENABLED(BOOTSTD_FULL) .read_all = cros_read_all, -- 2.43.0
From: Simon Glass <sjg@chromium.org> The Android bootmeth allocates a slot string inside android_priv but bootflow_free() only calls free() on the priv struct itself, so the string is never freed. In some paths the code sets bootmeth_priv to NULL without freeing anything. Implement free_bootflow() to free the slot string before the priv struct. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_android.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/boot/bootmeth_android.c b/boot/bootmeth_android.c index 20fcc2f46f9..3fa07680025 100644 --- a/boot/bootmeth_android.c +++ b/boot/bootmeth_android.c @@ -606,10 +606,19 @@ static int android_bootmeth_bind(struct udevice *dev) return 0; } +static void android_free_bootflow(struct udevice *dev, struct bootflow *bflow) +{ + struct android_priv *priv = bflow->bootmeth_priv; + + if (priv) + free(priv->slot); +} + static struct bootmeth_ops android_bootmeth_ops = { .check = android_check, .read_bootflow = android_read_bootflow, .read_file = android_read_file, + .free_bootflow = android_free_bootflow, .boot = android_boot, }; -- 2.43.0
From: Simon Glass <sjg@chromium.org> The RAUC bootmeth allocates a slots array with internal name strings inside distro_rauc_priv. A plain free() on the priv struct leaks all of these. The driver already has distro_rauc_priv_free() for proper cleanup but it is not called from bootflow_free(). Implement free_bootflow() to use the existing cleanup function. Since bootmeth_free_bootflow() now handles freeing the priv struct itself, remove that from distro_rauc_priv_free() and add explicit free() + NULL at the two call sites in read_bootflow and boot which do their own cleanup. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_rauc.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/boot/bootmeth_rauc.c b/boot/bootmeth_rauc.c index a81178db574..53d8dd7836f 100644 --- a/boot/bootmeth_rauc.c +++ b/boot/bootmeth_rauc.c @@ -63,7 +63,6 @@ static void distro_rauc_priv_free(struct distro_rauc_priv *priv) free(priv->slots[i]); } free(priv->slots); - free(priv); } static struct distro_rauc_slot *get_slot(struct distro_rauc_priv *priv, @@ -202,6 +201,8 @@ static int distro_rauc_read_bootflow(struct udevice *dev, struct bootflow *bflow ret = distro_rauc_scan_parts(bflow); if (ret < 0) { distro_rauc_priv_free(priv); + free(priv); + bflow->bootmeth_priv = NULL; free(boot_order_copy); return ret; } @@ -412,6 +413,8 @@ static int distro_rauc_boot(struct udevice *dev, struct bootflow *bflow) return log_msg_ret("boot", ret); distro_rauc_priv_free(priv); + free(priv); + bflow->bootmeth_priv = NULL; return 0; } @@ -426,10 +429,18 @@ static int distro_rauc_bootmeth_bind(struct udevice *dev) return 0; } +static void distro_rauc_free_bootflow(struct udevice *dev, + struct bootflow *bflow) +{ + if (bflow->bootmeth_priv) + distro_rauc_priv_free(bflow->bootmeth_priv); +} + static struct bootmeth_ops distro_rauc_bootmeth_ops = { .check = distro_rauc_check, .read_bootflow = distro_rauc_read_bootflow, .read_file = distro_rauc_read_file, + .free_bootflow = distro_rauc_free_bootflow, .boot = distro_rauc_boot, }; -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a struct pxe_context parameter to extlinux_boot() and extlinux_read_all() so the caller controls which context is used. This is a preparatory step towards removing the embedded ctx from struct extlinux_plat. All callers (extlinux, pxe, vbe_abrec) pass &plat->ctx, preserving the existing behaviour. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_extlinux.c | 12 ++++++++---- boot/bootmeth_pxe.c | 12 ++++++++---- boot/ext_pxe_common.c | 28 +++++++++++++--------------- boot/vbe_abrec_os.c | 11 +++++++---- include/extlinux.h | 10 ++++++---- 5 files changed, 42 insertions(+), 31 deletions(-) diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index 0cc8c2bf9a5..1fd2547d2b3 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -217,15 +217,19 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) static int extlinux_local_boot(struct udevice *dev, struct bootflow *bflow) { - return extlinux_boot(dev, bflow, extlinux_getfile, true, bflow->fname, - false); + struct extlinux_plat *plat = dev_get_plat(dev); + + return extlinux_boot(dev, bflow, &plat->ctx, extlinux_getfile, true, + bflow->fname, false); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int extlinux_local_read_all(struct udevice *dev, struct bootflow *bflow) { - return extlinux_read_all(dev, bflow, extlinux_getfile, true, - bflow->fname); + struct extlinux_plat *plat = dev_get_plat(dev); + + return extlinux_read_all(dev, bflow, &plat->ctx, extlinux_getfile, + true, bflow->fname); } #endif diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 6c022e31688..91e05a44841 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -145,15 +145,19 @@ static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) { - return extlinux_boot(dev, bflow, extlinux_pxe_getfile, false, - bflow->subdir, false); + struct extlinux_plat *plat = dev_get_plat(dev); + + return extlinux_boot(dev, bflow, &plat->ctx, extlinux_pxe_getfile, + false, bflow->subdir, false); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int extlinux_pxe_read_all(struct udevice *dev, struct bootflow *bflow) { - return extlinux_read_all(dev, bflow, extlinux_pxe_getfile, false, - bflow->subdir); + struct extlinux_plat *plat = dev_get_plat(dev); + + return extlinux_read_all(dev, bflow, &plat->ctx, + extlinux_pxe_getfile, false, bflow->subdir); } #endif diff --git a/boot/ext_pxe_common.c b/boot/ext_pxe_common.c index 59d878883bf..46302d3e962 100644 --- a/boot/ext_pxe_common.c +++ b/boot/ext_pxe_common.c @@ -97,25 +97,24 @@ static int extlinux_setup(struct udevice *dev, struct bootflow *bflow, } int extlinux_boot(struct udevice *dev, struct bootflow *bflow, - pxe_getfile_func getfile, bool allow_abs_path, - const char *bootfile, bool restart) + struct pxe_context *ctx, pxe_getfile_func getfile, + bool allow_abs_path, const char *bootfile, bool restart) { - struct extlinux_plat *plat = dev_get_plat(dev); ulong addr; int ret; /* if we have already selected a label, just boot it */ - if (plat->ctx.label) { - plat->ctx.fake_go = bflow->flags & BOOTFLOWF_FAKE_GO; - ret = pxe_boot(&plat->ctx); + if (ctx->label) { + ctx->fake_go = bflow->flags & BOOTFLOWF_FAKE_GO; + ret = pxe_boot(ctx); } else { ret = extlinux_setup(dev, bflow, getfile, allow_abs_path, - bootfile, &plat->ctx); + bootfile, ctx); if (ret) return log_msg_ret("elb", ret); - plat->ctx.restart = restart; + ctx->restart = restart; addr = map_to_sysmem(bflow->buf); - ret = pxe_process_str(&plat->ctx, addr, false); + ret = pxe_process_str(ctx, addr, false); } if (ret) return log_msg_ret("elb", -EFAULT); @@ -124,20 +123,19 @@ int extlinux_boot(struct udevice *dev, struct bootflow *bflow, } int extlinux_read_all(struct udevice *dev, struct bootflow *bflow, - pxe_getfile_func getfile, bool allow_abs_path, - const char *bootfile) + struct pxe_context *ctx, pxe_getfile_func getfile, + bool allow_abs_path, const char *bootfile) { - struct extlinux_plat *plat = dev_get_plat(dev); ulong addr; int ret; ret = extlinux_setup(dev, bflow, getfile, allow_abs_path, bootfile, - &plat->ctx); + ctx); if (ret) return log_msg_ret("era", ret); addr = map_to_sysmem(bflow->buf); - plat->ctx.pxe_file_size = bflow->size; - ret = pxe_probe(&plat->ctx, addr, false); + ctx->pxe_file_size = bflow->size; + ret = pxe_probe(ctx, addr, false); if (ret) return log_msg_ret("elb", -EFAULT); diff --git a/boot/vbe_abrec_os.c b/boot/vbe_abrec_os.c index 3bf2727a6e9..7d5c9bc9dcb 100644 --- a/boot/vbe_abrec_os.c +++ b/boot/vbe_abrec_os.c @@ -204,6 +204,7 @@ err_buf: static int vbe_abrec_boot(struct udevice *dev, struct bootflow *bflow) { + struct extlinux_plat *plat = dev_get_plat(dev); const struct bootflow_img *img; int ret; @@ -230,15 +231,17 @@ static int vbe_abrec_boot(struct udevice *dev, struct bootflow *bflow) printf("Loading OS FIT%s\n", img ? " keeping existing FDT" : ""); - return extlinux_boot(dev, bflow, vbe_abrec_getfile, true, bflow->fname, - img); + return extlinux_boot(dev, bflow, &plat->ctx, vbe_abrec_getfile, true, + bflow->fname, img); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int vbe_abrec_read_all(struct udevice *dev, struct bootflow *bflow) { - return extlinux_read_all(dev, bflow, vbe_abrec_getfile, true, - bflow->fname); + struct extlinux_plat *plat = dev_get_plat(dev); + + return extlinux_read_all(dev, bflow, &plat->ctx, vbe_abrec_getfile, + true, bflow->fname); } #endif diff --git a/include/extlinux.h b/include/extlinux.h index 4b5a8f316a8..cf9191874f0 100644 --- a/include/extlinux.h +++ b/include/extlinux.h @@ -54,6 +54,7 @@ int extlinux_set_property(struct udevice *dev, const char *property, * * @dev: bootmeth device * @bflow: Bootflow to boot + * @ctx: PXE context to use for booting * @getfile: Function to use to read files * @allow_abs_path: true to allow absolute paths * @bootfile: Bootfile whose directory loaded files are relative to, NULL if @@ -63,14 +64,15 @@ int extlinux_set_property(struct udevice *dev, const char *property, * Return: 0 if OK, -ve error code on failure */ int extlinux_boot(struct udevice *dev, struct bootflow *bflow, - pxe_getfile_func getfile, bool allow_abs_path, - const char *bootfile, bool restart); + struct pxe_context *ctx, pxe_getfile_func getfile, + bool allow_abs_path, const char *bootfile, bool restart); /** * extlinux_read_all() - read all files for a bootflow * * @dev: Bootmethod device to boot * @bflow: Bootflow to read + * @ctx: PXE context to use for reading * @getfile: Function to use to read files * @allow_abs_path: true to allow absolute paths * @bootfile: Bootfile whose directory loaded files are relative to, NULL if @@ -78,7 +80,7 @@ int extlinux_boot(struct udevice *dev, struct bootflow *bflow, * Return: 0 if OK, -EIO on I/O error, other -ve on other error */ int extlinux_read_all(struct udevice *dev, struct bootflow *bflow, - pxe_getfile_func getfile, bool allow_abs_path, - const char *bootfile); + struct pxe_context *ctx, pxe_getfile_func getfile, + bool allow_abs_path, const char *bootfile); #endif -- 2.43.0
From: Simon Glass <sjg@chromium.org> Move struct pxe_context from extlinux_plat (platform data) to a new extlinux_priv (private runtime data), since the context is runtime state rather than configuration. The info field stays in extlinux_plat as it is only used temporarily during context setup. Add priv_auto to the extlinux and pxe drivers. The VBE driver is not changed here as it has its own priv struct (abrec_priv). This is a preparatory step towards replacing the single context with an alist of contexts for multi-entry support. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_extlinux.c | 11 ++++++----- boot/bootmeth_pxe.c | 11 ++++++----- boot/ext_pxe_common.c | 4 ++-- include/extlinux.h | 13 ++++++++++--- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index 1fd2547d2b3..ad2f32ca8a5 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -217,18 +217,18 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) static int extlinux_local_boot(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct extlinux_priv *priv = dev_get_priv(dev); - return extlinux_boot(dev, bflow, &plat->ctx, extlinux_getfile, true, + return extlinux_boot(dev, bflow, &priv->ctx, extlinux_getfile, true, bflow->fname, false); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int extlinux_local_read_all(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct extlinux_priv *priv = dev_get_priv(dev); - return extlinux_read_all(dev, bflow, &plat->ctx, extlinux_getfile, + return extlinux_read_all(dev, bflow, &priv->ctx, extlinux_getfile, true, bflow->fname); } #endif @@ -267,5 +267,6 @@ U_BOOT_DRIVER(bootmeth_1extlinux) = { .of_match = extlinux_bootmeth_ids, .ops = &extlinux_bootmeth_ops, .bind = extlinux_bootmeth_bind, - .plat_auto = sizeof(struct extlinux_plat) + .plat_auto = sizeof(struct extlinux_plat), + .priv_auto = sizeof(struct extlinux_priv), }; diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 91e05a44841..55e7f60c5cd 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -145,18 +145,18 @@ static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct extlinux_priv *priv = dev_get_priv(dev); - return extlinux_boot(dev, bflow, &plat->ctx, extlinux_pxe_getfile, + return extlinux_boot(dev, bflow, &priv->ctx, extlinux_pxe_getfile, false, bflow->subdir, false); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int extlinux_pxe_read_all(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct extlinux_priv *priv = dev_get_priv(dev); - return extlinux_read_all(dev, bflow, &plat->ctx, + return extlinux_read_all(dev, bflow, &priv->ctx, extlinux_pxe_getfile, false, bflow->subdir); } #endif @@ -193,5 +193,6 @@ U_BOOT_DRIVER(bootmeth_zpxe) = { .of_match = extlinux_bootmeth_pxe_ids, .ops = &extlinux_bootmeth_pxe_ops, .bind = extlinux_bootmeth_pxe_bind, - .plat_auto = sizeof(struct extlinux_plat) + .plat_auto = sizeof(struct extlinux_plat), + .priv_auto = sizeof(struct extlinux_priv), }; diff --git a/boot/ext_pxe_common.c b/boot/ext_pxe_common.c index 46302d3e962..5a4b6455a53 100644 --- a/boot/ext_pxe_common.c +++ b/boot/ext_pxe_common.c @@ -85,8 +85,8 @@ static int extlinux_setup(struct udevice *dev, struct bootflow *bflow, plat->info.dev = dev; plat->info.bflow = bflow; - ret = pxe_setup_ctx(ctx, getfile, &plat->info, allow_abs_path, bootfile, - false, plat->use_fallback, bflow); + ret = pxe_setup_ctx(ctx, getfile, &plat->info, allow_abs_path, + bootfile, false, plat->use_fallback, bflow); if (ret) return log_msg_ret("ctx", ret); log_debug("bootfl flags %x\n", bflow->flags); diff --git a/include/extlinux.h b/include/extlinux.h index cf9191874f0..66500f4c8cf 100644 --- a/include/extlinux.h +++ b/include/extlinux.h @@ -23,18 +23,25 @@ struct extlinux_info { }; /** - * struct extlinux_plat - locate state for this bootmeth + * struct extlinux_plat - platform data for this bootmeth * * @use_falllback: true to boot with the fallback option - * @ctx: holds the PXE context, if it should be saved * @info: information used for the getfile() method */ struct extlinux_plat { bool use_fallback; - struct pxe_context ctx; struct extlinux_info info; }; +/** + * struct extlinux_priv - private runtime data for this bootmeth + * + * @ctx: holds the PXE context + */ +struct extlinux_priv { + struct pxe_context ctx; +}; + /** * extlinux_set_property() - set an extlinux property * -- 2.43.0
From: Simon Glass <sjg@chromium.org> The extlinux and PXE drivers allocate a pxe_context in priv but never free it on device removal, leaking the bootdir string and any parsed menu. Add extlinux_bootmeth_remove() which frees the context, shared by both drivers. Move the VBE driver to use a pxe_context embedded in its own abrec_priv, since extlinux_plat no longer contains it. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_extlinux.c | 12 ++++++++++++ boot/bootmeth_pxe.c | 1 + boot/vbe_abrec.h | 4 ++++ boot/vbe_abrec_os.c | 8 ++++---- include/extlinux.h | 10 ++++++++++ 5 files changed, 31 insertions(+), 4 deletions(-) diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index ad2f32ca8a5..ca9120c3f99 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -233,6 +233,17 @@ static int extlinux_local_read_all(struct udevice *dev, struct bootflow *bflow) } #endif +int extlinux_bootmeth_remove(struct udevice *dev) +{ + struct extlinux_priv *priv = dev_get_priv(dev); + + if (priv->ctx.cfg) + pxe_menu_uninit(priv->ctx.cfg); + pxe_destroy_ctx(&priv->ctx); + + return 0; +} + static int extlinux_bootmeth_bind(struct udevice *dev) { struct bootmeth_uc_plat *plat = dev_get_uclass_plat(dev); @@ -267,6 +278,7 @@ U_BOOT_DRIVER(bootmeth_1extlinux) = { .of_match = extlinux_bootmeth_ids, .ops = &extlinux_bootmeth_ops, .bind = extlinux_bootmeth_bind, + .remove = extlinux_bootmeth_remove, .plat_auto = sizeof(struct extlinux_plat), .priv_auto = sizeof(struct extlinux_priv), }; diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 55e7f60c5cd..772acc9107d 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -193,6 +193,7 @@ U_BOOT_DRIVER(bootmeth_zpxe) = { .of_match = extlinux_bootmeth_pxe_ids, .ops = &extlinux_bootmeth_pxe_ops, .bind = extlinux_bootmeth_pxe_bind, + .remove = extlinux_bootmeth_remove, .plat_auto = sizeof(struct extlinux_plat), .priv_auto = sizeof(struct extlinux_priv), }; diff --git a/boot/vbe_abrec.h b/boot/vbe_abrec.h index 590ad3cfacb..005e64af25f 100644 --- a/boot/vbe_abrec.h +++ b/boot/vbe_abrec.h @@ -9,6 +9,8 @@ #ifndef __VBE_ABREC_H #define __VBE_ABREC_H +#include <pxe_utils.h> + #include <vbe.h> #include <dm/ofnode_decl.h> @@ -34,6 +36,7 @@ struct udevice; * @version_size: Size of the version info * @storage: Storage device to use, in the form <uclass><devnum>, e.g. "mmc1" * @oem_devicetree: true if we should read an OEM devicetree + * @ctx: PXE context for extlinux boot/read_all */ struct abrec_priv { u32 area_start; @@ -45,6 +48,7 @@ struct abrec_priv { u32 version_size; const char *storage; bool oem_devicetree; + struct pxe_context ctx; }; /** struct abrec_state - state information read from media diff --git a/boot/vbe_abrec_os.c b/boot/vbe_abrec_os.c index 7d5c9bc9dcb..9d0136b059b 100644 --- a/boot/vbe_abrec_os.c +++ b/boot/vbe_abrec_os.c @@ -204,7 +204,7 @@ err_buf: static int vbe_abrec_boot(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct abrec_priv *priv = dev_get_priv(dev); const struct bootflow_img *img; int ret; @@ -231,16 +231,16 @@ static int vbe_abrec_boot(struct udevice *dev, struct bootflow *bflow) printf("Loading OS FIT%s\n", img ? " keeping existing FDT" : ""); - return extlinux_boot(dev, bflow, &plat->ctx, vbe_abrec_getfile, true, + return extlinux_boot(dev, bflow, &priv->ctx, vbe_abrec_getfile, true, bflow->fname, img); } #if CONFIG_IS_ENABLED(BOOTSTD_FULL) static int vbe_abrec_read_all(struct udevice *dev, struct bootflow *bflow) { - struct extlinux_plat *plat = dev_get_plat(dev); + struct abrec_priv *priv = dev_get_priv(dev); - return extlinux_read_all(dev, bflow, &plat->ctx, vbe_abrec_getfile, + return extlinux_read_all(dev, bflow, &priv->ctx, vbe_abrec_getfile, true, bflow->fname); } #endif diff --git a/include/extlinux.h b/include/extlinux.h index 66500f4c8cf..8630a8e3dc7 100644 --- a/include/extlinux.h +++ b/include/extlinux.h @@ -42,6 +42,16 @@ struct extlinux_priv { struct pxe_context ctx; }; +/** + * extlinux_bootmeth_remove() - Remove function for extlinux-based bootmeths + * + * Frees the PXE context. Shared by extlinux and PXE drivers. + * + * @dev: Bootmethod device + * Return: 0 if OK + */ +int extlinux_bootmeth_remove(struct udevice *dev); + /** * extlinux_set_property() - set an extlinux property * -- 2.43.0
From: Simon Glass <sjg@chromium.org> Add a bootmeth_id field to struct bootflow that bootmeths can use to store an identifier for this bootflow, e.g. an alist index for the PXE context used during scanning. Initialise it to -1 (not set) in bootflow_init(). Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootflow.c | 1 + doc/develop/bootstd/overview.rst | 5 ++++- include/bootflow.h | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/boot/bootflow.c b/boot/bootflow.c index d6549b5ce9a..dbda50b8231 100644 --- a/boot/bootflow.c +++ b/boot/bootflow.c @@ -712,6 +712,7 @@ void bootflow_init(struct bootflow *bflow, struct udevice *bootdev, bflow->dev = bootdev; bflow->method = meth; bflow->state = BOOTFLOWST_BASE; + bflow->bootmeth_id = -1; alist_init_struct(&bflow->images, struct bootflow_img); } diff --git a/doc/develop/bootstd/overview.rst b/doc/develop/bootstd/overview.rst index 0ff4868ba44..049aec56931 100644 --- a/doc/develop/bootstd/overview.rst +++ b/doc/develop/bootstd/overview.rst @@ -853,7 +853,10 @@ Each bootdev device has its own `struct bootdev_uc_plat` which holds a list of scanned bootflows just for that device. The bootflow itself is documented in bootflow_h_. It includes various bits of -information about the bootflow and a buffer to hold the file. +information about the bootflow and a buffer to hold the file. The ``bootmeth_id`` +field allows a bootmeth to associate an identifier with each bootflow, such as +an index into a list of parsed configurations. It is initialised to -1 (not +set) and its interpretation is up to the bootmeth. The ``bootmeth_priv`` field allows a bootmeth to attach private data to each bootflow, such as parsed configuration state. When the bootflow is freed, diff --git a/include/bootflow.h b/include/bootflow.h index 65aebefd3b3..dbdbca96596 100644 --- a/include/bootflow.h +++ b/include/bootflow.h @@ -101,6 +101,9 @@ enum bootflow_flags_t { * @bootmeth_priv: Private data for the bootmeth (allocated). Freed by * bootmeth_free_bootflow() which calls the bootmeth's free_bootflow() op * for internal cleanup, then frees the pointer itself. + * @bootmeth_id: Bootmeth-specific identifier for this bootflow, e.g. an + * alist index for the PXE context used during scanning. Interpretation + * is up to the bootmeth. * @images: List of loaded images (struct bootstd_img) */ struct bootflow { @@ -127,6 +130,7 @@ struct bootflow { char *cmdline; void *x86_setup; void *bootmeth_priv; + int bootmeth_id; struct alist images; }; -- 2.43.0
From: Simon Glass <sjg@chromium.org> Replace the single struct pxe_context in extlinux_priv with an alist of contexts, one per extlinux.conf file. Use bflow->bootmeth_id so that boot and read_all can locate the correct context from the alist. Add extlinux_get_ctx() in ext_pxe_common.c which returns an existing context by bootmeth_id or allocates a new one. This ensures read_all and boot share the same context, preserving the label state set up by pxe_probe(). Add extlinux_bootmeth_probe() and update extlinux_bootmeth_remove() for alist lifecycle management, used by both extlinux and PXE drivers. The VBE driver uses its own priv struct and continues with local contexts. Signed-off-by: Simon Glass <sjg@chromium.org> --- boot/bootmeth_extlinux.c | 34 +++++++++++++++++++++++++++++----- boot/bootmeth_pxe.c | 15 +++++++++++++-- boot/ext_pxe_common.c | 22 ++++++++++++++++++++++ boot/vbe_abrec_os.c | 2 +- include/extlinux.h | 32 +++++++++++++++++++++++++++++--- 5 files changed, 94 insertions(+), 11 deletions(-) diff --git a/boot/bootmeth_extlinux.c b/boot/bootmeth_extlinux.c index ca9120c3f99..286d176ae20 100644 --- a/boot/bootmeth_extlinux.c +++ b/boot/bootmeth_extlinux.c @@ -218,8 +218,13 @@ static int extlinux_read_bootflow(struct udevice *dev, struct bootflow *bflow) static int extlinux_local_boot(struct udevice *dev, struct bootflow *bflow) { struct extlinux_priv *priv = dev_get_priv(dev); + struct pxe_context *ctx; - return extlinux_boot(dev, bflow, &priv->ctx, extlinux_getfile, true, + ctx = extlinux_get_ctx(priv, bflow); + if (!ctx) + return log_msg_ret("ctx", -ENOMEM); + + return extlinux_boot(dev, bflow, ctx, extlinux_getfile, true, bflow->fname, false); } @@ -227,19 +232,37 @@ static int extlinux_local_boot(struct udevice *dev, struct bootflow *bflow) static int extlinux_local_read_all(struct udevice *dev, struct bootflow *bflow) { struct extlinux_priv *priv = dev_get_priv(dev); + struct pxe_context *ctx; + + ctx = extlinux_get_ctx(priv, bflow); + if (!ctx) + return log_msg_ret("ctx", -ENOMEM); - return extlinux_read_all(dev, bflow, &priv->ctx, extlinux_getfile, + return extlinux_read_all(dev, bflow, ctx, extlinux_getfile, true, bflow->fname); } #endif +int extlinux_bootmeth_probe(struct udevice *dev) +{ + struct extlinux_priv *priv = dev_get_priv(dev); + + alist_init_struct(&priv->ctxs, struct pxe_context); + + return 0; +} + int extlinux_bootmeth_remove(struct udevice *dev) { struct extlinux_priv *priv = dev_get_priv(dev); + struct pxe_context *ctx; - if (priv->ctx.cfg) - pxe_menu_uninit(priv->ctx.cfg); - pxe_destroy_ctx(&priv->ctx); + alist_for_each(ctx, &priv->ctxs) { + if (ctx->cfg) + pxe_menu_uninit(ctx->cfg); + pxe_destroy_ctx(ctx); + } + alist_uninit(&priv->ctxs); return 0; } @@ -278,6 +301,7 @@ U_BOOT_DRIVER(bootmeth_1extlinux) = { .of_match = extlinux_bootmeth_ids, .ops = &extlinux_bootmeth_ops, .bind = extlinux_bootmeth_bind, + .probe = extlinux_bootmeth_probe, .remove = extlinux_bootmeth_remove, .plat_auto = sizeof(struct extlinux_plat), .priv_auto = sizeof(struct extlinux_priv), diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c index 772acc9107d..cc2a415f3c4 100644 --- a/boot/bootmeth_pxe.c +++ b/boot/bootmeth_pxe.c @@ -146,8 +146,13 @@ static int extlinux_pxe_read_file(struct udevice *dev, struct bootflow *bflow, static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) { struct extlinux_priv *priv = dev_get_priv(dev); + struct pxe_context *ctx; - return extlinux_boot(dev, bflow, &priv->ctx, extlinux_pxe_getfile, + ctx = extlinux_get_ctx(priv, bflow); + if (!ctx) + return log_msg_ret("ctx", -ENOMEM); + + return extlinux_boot(dev, bflow, ctx, extlinux_pxe_getfile, false, bflow->subdir, false); } @@ -155,8 +160,13 @@ static int extlinux_pxe_boot(struct udevice *dev, struct bootflow *bflow) static int extlinux_pxe_read_all(struct udevice *dev, struct bootflow *bflow) { struct extlinux_priv *priv = dev_get_priv(dev); + struct pxe_context *ctx; + + ctx = extlinux_get_ctx(priv, bflow); + if (!ctx) + return log_msg_ret("ctx", -ENOMEM); - return extlinux_read_all(dev, bflow, &priv->ctx, + return extlinux_read_all(dev, bflow, ctx, extlinux_pxe_getfile, false, bflow->subdir); } #endif @@ -193,6 +203,7 @@ U_BOOT_DRIVER(bootmeth_zpxe) = { .of_match = extlinux_bootmeth_pxe_ids, .ops = &extlinux_bootmeth_pxe_ops, .bind = extlinux_bootmeth_pxe_bind, + .probe = extlinux_bootmeth_probe, .remove = extlinux_bootmeth_remove, .plat_auto = sizeof(struct extlinux_plat), .priv_auto = sizeof(struct extlinux_priv), diff --git a/boot/ext_pxe_common.c b/boot/ext_pxe_common.c index 5a4b6455a53..8a59b29ead8 100644 --- a/boot/ext_pxe_common.c +++ b/boot/ext_pxe_common.c @@ -75,6 +75,28 @@ int extlinux_set_property(struct udevice *dev, const char *property, return 0; } +struct pxe_context *extlinux_get_ctx(struct extlinux_priv *priv, + struct bootflow *bflow) +{ + struct pxe_context *ctx; + + /* Return existing context if one was already allocated */ + if (bflow->bootmeth_id >= 0) { + ctx = alist_getw(&priv->ctxs, bflow->bootmeth_id, + struct pxe_context); + if (ctx) + return ctx; + } + + /* Allocate a new one */ + ctx = alist_add_placeholder(&priv->ctxs); + if (!ctx) + return NULL; + bflow->bootmeth_id = priv->ctxs.count - 1; + + return ctx; +} + static int extlinux_setup(struct udevice *dev, struct bootflow *bflow, pxe_getfile_func getfile, bool allow_abs_path, const char *bootfile, struct pxe_context *ctx) diff --git a/boot/vbe_abrec_os.c b/boot/vbe_abrec_os.c index 9d0136b059b..9b41dcbf303 100644 --- a/boot/vbe_abrec_os.c +++ b/boot/vbe_abrec_os.c @@ -288,5 +288,5 @@ U_BOOT_DRIVER(vbe_abrec_os) = { .bind = bootmeth_vbe_abrec_os_bind, .probe = bootmeth_vbe_abrec_os_probe, .priv_auto = sizeof(struct abrec_priv), - .plat_auto = sizeof(struct extlinux_plat) + .plat_auto = sizeof(struct extlinux_plat), }; diff --git a/include/extlinux.h b/include/extlinux.h index 8630a8e3dc7..bad901cc3e6 100644 --- a/include/extlinux.h +++ b/include/extlinux.h @@ -36,22 +36,48 @@ struct extlinux_plat { /** * struct extlinux_priv - private runtime data for this bootmeth * - * @ctx: holds the PXE context + * @ctxs: list of parsed PXE contexts (alist of struct pxe_context), one per + * extlinux.conf file found during scanning */ struct extlinux_priv { - struct pxe_context ctx; + struct alist ctxs; }; +/** + * extlinux_bootmeth_probe() - Probe function for extlinux-based bootmeths + * + * Initialises the context alist in extlinux_priv. Must be called from the + * probe function of any driver that uses extlinux_priv. + * + * @dev: Bootmethod device + * Return: 0 if OK + */ +int extlinux_bootmeth_probe(struct udevice *dev); + /** * extlinux_bootmeth_remove() - Remove function for extlinux-based bootmeths * - * Frees the PXE context. Shared by extlinux and PXE drivers. + * Frees all cached PXE contexts in the alist. * * @dev: Bootmethod device * Return: 0 if OK */ int extlinux_bootmeth_remove(struct udevice *dev); +/** + * extlinux_get_ctx() - Get or allocate a PXE context for a bootflow + * + * If bflow->bootmeth_id already points to a valid context (e.g. from a + * prior read_all), return it. Otherwise allocate a new context in the + * alist and store its index in bflow->bootmeth_id. + * + * @priv: Private data for this bootmeth + * @bflow: Bootflow to get context for + * Return: Context, or NULL on allocation failure + */ +struct pxe_context *extlinux_get_ctx(struct extlinux_priv *priv, + struct bootflow *bflow); + /** * extlinux_set_property() - set an extlinux property * -- 2.43.0
participants (1)
-
Simon Glass