ulib: Add a boot example

Add an example of a program which boots an OS by calling into the U-Boot
library.

Series-to: concept
Co-developed-by: Claude <noreply@anthropic.com>
Co-developed-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Simon Glass <sjg@chromium.org>
Series-links: 1:28
This commit is contained in:
Simon Glass
2025-09-09 15:44:35 -06:00
parent 4ba77a661f
commit 7811a8ae52
5 changed files with 229 additions and 6 deletions

View File

@@ -1,2 +1,4 @@
/boot
/boot_static
/demo
/demo_static

View File

@@ -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

View File

@@ -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
-------------------------

64
examples/ulib/boot.c Normal file
View File

@@ -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;
}

141
examples/ulib/bootflow.c Normal file
View File

@@ -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;
}