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:
2
examples/ulib/.gitignore
vendored
2
examples/ulib/.gitignore
vendored
@@ -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
|
||||
-------------------------
|
||||
|
||||
64
examples/ulib/boot.c
Normal file
64
examples/ulib/boot.c
Normal 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
141
examples/ulib/bootflow.c
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user