@@ -1,2 +1,4 @@
+/boot
+/boot_static
/demo
/demo_static
@@ -29,13 +29,14 @@
include config.mk
# Programs to build
-progs := demo
+progs := demo boot
# Program definitions - list of object files for each program
demo_objs := demo.o demo_helper.o
+boot_objs := boot.o bootflow.o
# Objects that need system headers (default is U-Boot headers)
-sys-objs := demo_helper.o
+sys-objs := boot.o demo_helper.o
# Include build rules (must come after variable definitions)
include rules.mk
@@ -18,8 +18,8 @@ This creates:
Example Programs
----------------
-The examples are built automatically as part of the U-Boot build. So far there
-is only one.
+The examples are built automatically as part of the U-Boot build. Each program
+demonstrates different aspects of the U-Boot library.
**demo.c** - Demonstrates using U-Boot library functions
@@ -28,6 +28,14 @@ is only one.
- Reads and displays system information
- Shows the U-Boot version
+**boot.c** - Demonstrates booting an OS using U-Boot bootflow
+
+- Shows bootflow scanning and booting functionality
+- Uses system headers for main program logic (boot.c)
+- Uses U-Boot headers for bootflow internals (bootflow.c)
+- Demonstrates attaching disk images and scanning for bootable OS
+- Attempts to boot the first discovered bootflow
+
Building Examples
-----------------
@@ -50,14 +58,21 @@ Running Examples
# Run the demo (static version)
./demo_static
+ # Run the boot example (requires disk image at /home/sglass/u/mmc1.img)
+ LD_LIBRARY_PATH=/tmp/b/sandbox ./boot
+
+ # Run the boot example (static version)
+ ./boot_static
+
Key Points
----------
-- Avoid including U-Boot headers that conflict with system headers. This
- Makefile gives priority to the system headers
+- Files are compiled with U-Boot headers by default, except those listed in
+ sys-objs which use system headers
- Use ulib_init() to init the library
- Use ulib_uninit() to clean up
- Set LD_LIBRARY_PATH when running dynamically linked programs
+- Each program automatically gets both dynamic and static versions built
Copying for External Use
-------------------------
new file mode 100644
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Boot test program using U-Boot library
+ *
+ * This demonstrates basic initialization and cleanup of the U-Boot library.
+ * It will be used for testing bootstd functionality using ulib.
+ *
+ * Copyright 2025 Canonical Ltd.
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+/* Use system headers, not U-Boot headers */
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <u-boot-api.h>
+#include <u-boot-lib.h>
+
+/* Forward declaration for bootflow function */
+int bootflow_internal_scan(void);
+
+/* Forward declaration for host function (simplified) */
+int host_create_attach_file(const char *label, const char *filename,
+ int removable, unsigned long blksz,
+ void *devp);
+
+static void fatal(const char *msg)
+{
+ fprintf(stderr, "Error: %s\n", msg);
+ exit(1);
+}
+
+static int try_boot(void)
+{
+ int ret;
+
+ printf("Scanning for bootflows...\n");
+
+ /* MMC device attachment will be done in bootflow_internal_scan() */
+
+ ret = bootflow_internal_scan();
+ if (ret) {
+ printf("Internal scan failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ ret = ulib_init(argv[0]);
+ if (ret)
+ fatal("Failed to init U-Boot library");
+
+ ret = try_boot();
+ if (ret)
+ printf("Boot attempt failed: %d\n", ret);
+
+ ulib_uninit();
+ return ret;
+}
new file mode 100644
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Bootflow internal functions using U-Boot headers
+ *
+ * This demonstrates functions that need direct access to U-Boot internal
+ * structures and would be compiled with U-Boot headers first.
+ *
+ * Copyright 2025 Canonical Ltd.
+ * Written by Simon Glass <simon.glass@canonical.com>
+ */
+
+/* This file uses U-Boot headers first */
+#include <bootflow.h>
+#include <bootdev.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm/device.h>
+#include <sandbox_host.h>
+#include <u-boot-api.h>
+
+static void show_bootflow(int num, struct bootflow *bflow)
+{
+ ub_printf("Bootflow %d:\n", num);
+ ub_printf(" name: '%s'\n", bflow->name ? bflow->name : "(null)");
+ ub_printf(" state: %s\n", bootflow_state_get_name(bflow->state));
+ ub_printf(" method: '%s'\n",
+ bflow->method ? bflow->method->name : "(null)");
+ ub_printf(" fname: '%s'\n", bflow->fname ? bflow->fname : "(null)");
+ ub_printf(" dev: '%s'\n", bflow->dev ? bflow->dev->name : "(null)");
+ ub_printf(" part: %d\n", bflow->part);
+ ub_printf(" size: %d\n", bflow->size);
+ ub_printf(" err: %d\n", bflow->err);
+ if (bflow->os_name)
+ ub_printf(" os_name: '%s'\n", bflow->os_name);
+ if (bflow->logo)
+ ub_printf(" logo: present (%zu bytes)\n", bflow->logo_size);
+ ub_printf("\n");
+}
+
+int bootflow_internal_scan(void)
+{
+ struct bootflow bflow;
+ struct bootflow_iter iter;
+ struct bootstd_priv *std;
+ struct bootflow *first_bflow;
+ struct udevice *host_dev;
+ int ret, count = 0;
+
+ ub_printf("Internal bootflow scan using U-Boot headers first\n");
+
+ /* Get bootstd private data */
+ ret = bootstd_get_priv(&std);
+ if (ret) {
+ ub_printf("bootstd_get_priv() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Set bootmethod order to only use extlinux and efi */
+ ret = bootmeth_set_order("extlinux efi");
+ if (ret) {
+ ub_printf("bootmeth_set_order() failed: %d\n", ret);
+ return ret;
+ }
+ ub_printf("Set bootmethod order to: extlinux efi\n");
+
+ /* Now we can actually use bootflow.h definitions! */
+ ub_printf("BOOTFLOWST_MEDIA = %d\n", BOOTFLOWST_MEDIA);
+ ub_printf("sizeof(struct bootflow) = %zu\n", sizeof(struct bootflow));
+ ub_printf("sizeof(struct bootflow_iter) = %zu\n",
+ sizeof(struct bootflow_iter));
+
+ /* Attach the MMC image file to make bootflows available */
+ ub_printf("Attaching mmc1.img file...\n");
+ ret = host_create_attach_file("mmc1", "/home/sglass/u/mmc1.img", false,
+ 512, &host_dev);
+ if (ret) {
+ ub_printf("host_create_attach_file() failed: %d\n", ret);
+
+ return ret;
+ }
+
+ /* List all available bootdevs */
+ ub_printf("Available bootdevs:\n");
+ bootdev_list(true);
+
+ /* Try to scan for the first bootflow */
+ ret = bootflow_scan_first(NULL, NULL, &iter, BOOTFLOWIF_SHOW,
+ &bflow);
+ if (ret) {
+ ub_printf("bootflow_scan_first() failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Iterate through all bootflows */
+ do {
+ count++;
+ show_bootflow(count, &bflow);
+
+ /* Add bootflow to the global list */
+ ret = bootstd_add_bootflow(&bflow);
+ if (ret < 0) {
+ ub_printf("bootstd_add_bootflow() failed: %d\n", ret);
+ bootflow_free(&bflow);
+ }
+
+ /* Get next bootflow */
+ ret = bootflow_scan_next(&iter, &bflow);
+ } while (!ret);
+
+ ub_printf("Found %d total bootflows\n", count);
+
+ /* Clean up the iterator */
+ bootflow_iter_uninit(&iter);
+
+ /* Return immediately if no bootflows found */
+ if (!count) {
+ ub_printf("No bootflows found to boot\n");
+ return 0;
+ }
+
+ /* Boot the first bootflow */
+ /* Get the first bootflow from the global list */
+ first_bflow = alist_getw(&std->bootflows, 0, struct bootflow);
+ if (!first_bflow) {
+ ub_printf("Failed to get first bootflow from global list\n");
+ return -1;
+ }
+
+ ub_printf("\bBooting: %s\n",
+ first_bflow->name ? first_bflow->name : "(unnamed)");
+ if (first_bflow->os_name)
+ ub_printf("OS: %s\n", first_bflow->os_name);
+
+ ret = bootflow_boot(first_bflow);
+ if (ret)
+ ub_printf("bootflow_boot() failed: %dE\n", ret);
+ else
+ ub_printf("bootflow_boot() succeeded (shouldn't reach here!)\n");
+
+ return 0;
+}