[Concept,14/14] extlinux: Convert pxe_context to an alist with bootmeth_id lookup

Message ID 20260322235719.1729267-15-sjg@u-boot.org
State New
Headers
Series bootstd: Infrastructure for multi-entry bootflow support |

Commit Message

Simon Glass March 22, 2026, 11:57 p.m. UTC
  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(-)
  

Patch

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
  *