[Concept,23/26] boot: pxe: Add a callback-free PXE API

Message ID 20260110202906.187370-24-sjg@u-boot.org
State New
Headers
Series boot: pxe: Add three-phase API and fix memory leaks |

Commit Message

Simon Glass Jan. 10, 2026, 8:28 p.m. UTC
  From: Simon Glass <simon.glass@canonical.com>

Add functions for a callback-free PXE API:

- pxe_parse() - Parse a PXE config file and return an allocated context
- pxe_load() - Record file load address and size
- pxe_cleanup() - Free menu and destroy context

Also rename pxe_do_boot() to pxe_boot() for consistency.

This API allows callers to parse PXE files, load files manually without
callbacks, and boot the selected label.

Co-developed-by: Claude Opus 4.5 <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
---

 boot/ext_pxe_common.c |  2 +-
 boot/pxe_utils.c      | 45 +++++++++++++++++++++++++++++-
 include/pxe_utils.h   | 64 +++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 106 insertions(+), 5 deletions(-)
  

Patch

diff --git a/boot/ext_pxe_common.c b/boot/ext_pxe_common.c
index 2bc76a4d0fb..59d878883bf 100644
--- a/boot/ext_pxe_common.c
+++ b/boot/ext_pxe_common.c
@@ -107,7 +107,7 @@  int extlinux_boot(struct udevice *dev, struct bootflow *bflow,
 	/* 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_do_boot(&plat->ctx);
+		ret = pxe_boot(&plat->ctx);
 	} else {
 		ret = extlinux_setup(dev, bflow, getfile, allow_abs_path,
 				     bootfile, &plat->ctx);
diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c
index 45f4d365878..5c1d08feebf 100644
--- a/boot/pxe_utils.c
+++ b/boot/pxe_utils.c
@@ -1326,6 +1326,49 @@  static struct pxe_menu *pxe_prepare(struct pxe_context *ctx,
 	return cfg;
 }
 
+struct pxe_context *pxe_parse(ulong addr, ulong size, const char *bootfile)
+{
+	struct pxe_context *ctx;
+	struct abuf buf;
+	int ret;
+
+	ctx = calloc(1, sizeof(*ctx));
+	if (!ctx)
+		return NULL;
+
+	ret = pxe_setup_ctx(ctx, NULL, NULL, true, bootfile, false, false,
+			    NULL);
+	if (ret) {
+		free(ctx);
+		return NULL;
+	}
+	ctx->pxe_file_size = size;
+
+	abuf_init_addr(&buf, addr, size);
+	ctx->cfg = parse_pxefile(ctx, &buf);
+	if (!ctx->cfg) {
+		pxe_destroy_ctx(ctx);
+		free(ctx);
+		return NULL;
+	}
+
+	return ctx;
+}
+
+void pxe_load(struct pxe_file *file, ulong addr, ulong size)
+{
+	file->addr = addr;
+	file->size = size;
+}
+
+void pxe_cleanup(struct pxe_context *ctx)
+{
+	if (ctx->cfg)
+		pxe_menu_uninit(ctx->cfg);
+	pxe_destroy_ctx(ctx);
+	free(ctx);
+}
+
 int pxe_process(struct pxe_context *ctx, ulong addr, ulong size, bool prompt)
 {
 	struct pxe_menu *cfg;
@@ -1366,7 +1409,7 @@  int pxe_probe(struct pxe_context *ctx, ulong pxefile_addr_r, bool prompt)
 	return 0;
 }
 
-int pxe_do_boot(struct pxe_context *ctx)
+int pxe_boot(struct pxe_context *ctx)
 {
 	int ret;
 
diff --git a/include/pxe_utils.h b/include/pxe_utils.h
index 6f2ac604d6e..bc9e3f29417 100644
--- a/include/pxe_utils.h
+++ b/include/pxe_utils.h
@@ -428,7 +428,7 @@  int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6);
  * pxe_probe() - Process a PXE file to find the label to boot
  *
  * This fills in the label, etc. fields in @ctx, assuming it funds something to
- * boot. Then pxe_do_boot() can be called to boot it.
+ * boot. Then pxe_boot() can be called to boot it.
  *
  * @ctx: PXE context created with pxe_setup_ctx()
  * @pxefile_addr_r: Address to load file
@@ -438,13 +438,13 @@  int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6);
 int pxe_probe(struct pxe_context *ctx, ulong pxefile_addr_r, bool prompt);
 
 /**
- * pxe_do_boot() - Boot the selected label
+ * pxe_boot() - Boot the selected label
  *
  * This boots the label discovered by pxe_probe()
  *
  * Return: Does not return, on success, otherwise returns a -ve error code
  */
-int pxe_do_boot(struct pxe_context *ctx);
+int pxe_boot(struct pxe_context *ctx);
 
 /**
  * pxe_select_label() - Select a label from a parsed menu
@@ -539,6 +539,16 @@  void label_destroy(struct pxe_label *label);
  * to parse it and merge any labels into the target menu. This may add
  * more entries to cfg->includes if the included file has its own includes.
  *
+ * Example usage::
+ *
+ *    ctx = pxe_parse(addr, size, bootfile);
+ *
+ *    // Load and process any includes
+ *    alist_for_each(inc, &ctx->cfg->includes) {
+ *        // read file inc->path to address 'addr', getting 'size'
+ *        pxe_parse_include(ctx, inc, addr, size);
+ *    }
+ *
  * @ctx: PXE context
  * @inc: Include info with path and target menu
  * @addr: Memory address where file is located
@@ -548,4 +558,52 @@  void label_destroy(struct pxe_label *label);
 int pxe_parse_include(struct pxe_context *ctx, const struct pxe_include *inc,
 		      ulong addr, ulong size);
 
+/**
+ * pxe_parse() - Parse a PXE config file and return an allocated context
+ *
+ * This allocates a PXE context, sets it up, and parses the config file at the
+ * given address. The menu is stored in ctx->cfg and can be used to inspect
+ * labels or select one for loading.
+ *
+ * Use pxe_cleanup() to clean up when done.
+ *
+ * Note: This does not process include files. Call pxe_process_includes()
+ * after this if needed.
+ *
+ * @addr: Address where config file is loaded
+ * @size: Size of config file in bytes
+ * @bootfile: Path to config file (for relative path resolution)
+ * Return: Allocated context on success, NULL on error
+ */
+struct pxe_context *pxe_parse(ulong addr, ulong size, const char *bootfile);
+
+/**
+ * pxe_load() - Report that a file has been loaded
+ *
+ * After loading a file from a label's files list, call this to record
+ * the load address and size. This information is used later during boot.
+ *
+ * Example::
+ *
+ *    alist_for_each(file, &label->files) {
+ *        // choose 'addr', load file->path there, getting 'size'
+ *        pxe_load(file, addr, size);
+ *    }
+ *
+ * @file: File that was loaded
+ * @addr: Address where file was loaded
+ * @size: Size of loaded file
+ */
+void pxe_load(struct pxe_file *file, ulong addr, ulong size);
+
+/**
+ * pxe_cleanup() - Free PXE menu and destroy context
+ *
+ * This combines pxe_menu_uninit() and pxe_destroy_ctx() for convenience
+ * when cleaning up after using the callback-free API.
+ *
+ * @ctx: Context to destroy (ctx->cfg is freed if set)
+ */
+void pxe_cleanup(struct pxe_context *ctx);
+
 #endif /* __PXE_UTILS_H */