[PATCH 1/2] ulib: Refactor the Makefile to support multiple programs

From: Simon Glass <sjg@chromium.org> The current Makefile for the ulib examples is not very easy to modify. Split it into three parts: - config.mk containing the variables used by the build tools - a list of programs and the objects in each one - rules.mk with some rules to make it all work Use sys-objs to indicate objects which should be built with system headers. Use U-Boot headers by default. With this it is fairly simply to add a new program. Of course we could make use of U-Boot's kbuild implementation to tidy this up, but the purpose of the examples is to show how to do things outside the U-Boot build system. Co-developed-by: Claude <noreply@anthropic.com> Co-developed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> --- examples/ulib/Makefile | 72 +++++++---------------------------------- examples/ulib/config.mk | 47 +++++++++++++++++++++++++++ examples/ulib/rules.mk | 39 ++++++++++++++++++++++ 3 files changed, 97 insertions(+), 61 deletions(-) create mode 100644 examples/ulib/config.mk create mode 100644 examples/ulib/rules.mk diff --git a/examples/ulib/Makefile b/examples/ulib/Makefile index 81885cf8726..a11c5dad9be 100644 --- a/examples/ulib/Makefile +++ b/examples/ulib/Makefile @@ -25,72 +25,22 @@ # PLATFORM_LIBS - Platform-specific libraries # LIB_STATIC_LDS - Linker script for static library -# For standalone builds, provide default values -EXAMPLE_DIR ?= . -OUTDIR ?= . -CC ?= gcc -SDL_CONFIG ?= sdl2-config -PLATFORM_LIBS ?= $(shell $(SDL_CONFIG) --libs) -LIB_STATIC_LDS ?= static.lds -# The main Makefile passes in Q=@ for quiet output -Q ?= +# Include configuration and helper functions +include config.mk -# Common compiler flags for programs using system headers first -SYSTEM_CFLAGS := -I$(UBOOT_BUILD)/include -idirafter$(srctree)/include \ - -include $(srctree)/include/linux/compiler_attributes.h +# Programs to build +progs := demo -# Common compiler flags for programs using U-Boot headers first (like U-Boot -# internal build) -UBOOT_CFLAGS := -nostdinc -isystem $(shell $(CC) -print-file-name=include) \ - -I$(UBOOT_BUILD)/include \ - -I$(srctree)/include \ - -I$(srctree)/arch/sandbox/include \ - -include $(UBOOT_BUILD)/include/config.h \ - -include $(srctree)/include/linux/kconfig.h \ - -I$(srctree)/dts/upstream/include \ - -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=0 +# Program definitions - list of object files for each program +demo_objs := demo.o demo_helper.o -SHARED_LDFLAGS := -L$(UBOOT_BUILD) -lu-boot -Wl,-rpath,$(UBOOT_BUILD) +# Objects that need system headers (default is U-Boot headers) +sys-objs := demo_helper.o -STATIC_LDFLAGS := -Wl,-T,$(LIB_STATIC_LDS) \ - -Wl,--whole-archive $(UBOOT_BUILD)/libu-boot.a -Wl,--no-whole-archive \ - -lpthread -ldl $(PLATFORM_LIBS) -Wl,-z,noexecstack - -# Program definitions - can be single file or multi-object -DEMO_SRC := $(EXAMPLE_DIR)/demo.c -demo-objs := demo.o demo_helper.o -DEMO_BINS := $(OUTDIR)/demo $(OUTDIR)/demo_static - -ALL_BINS := $(DEMO_BINS) - -# Default target builds both programs -all: $(ALL_BINS) - -# Create the output directory if it doesn't exist -$(OUTDIR): - @mkdir -p $@ - -# Pattern rule for building object files with system headers first (default) -$(OUTDIR)/%.o: $(EXAMPLE_DIR)/%.c | $(OUTDIR) - $(CC) $(CFLAGS) $(SYSTEM_CFLAGS) -c -o $@ $< - -# Pattern rule for building object files with U-Boot headers first -$(OUTDIR)/%_uboot.o: $(EXAMPLE_DIR)/%.c | $(OUTDIR) - $(CC) $(CFLAGS) $(UBOOT_CFLAGS) -c -o $@ $< - -# The U-Boot library must be built before we can link against it -# Order-only prerequisites ensure libraries exist before linking -$(ALL_BINS): | $(UBOOT_BUILD)/libu-boot.a $(UBOOT_BUILD)/libu-boot.so $(OUTDIR) - -# Build demo (dynamically linked with libu-boot.so) -$(OUTDIR)/demo: $(if $(demo-objs),$(addprefix $(OUTDIR)/,$(demo-objs)),$(DEMO_SRC)) - $(if $(demo-objs),$(CC) $(CFLAGS) -o $@ $^ $(SHARED_LDFLAGS),$(CC) $(CFLAGS) $(SYSTEM_CFLAGS) -o $@ $< $(SHARED_LDFLAGS)) - -# Build demo_static (statically linked with libu-boot.a) -$(OUTDIR)/demo_static: $(if $(demo-objs),$(addprefix $(OUTDIR)/,$(demo-objs)),$(DEMO_SRC)) - $(if $(demo-objs),$(CC) $(CFLAGS) -o $@ $^ $(STATIC_LDFLAGS),$(CC) $(CFLAGS) $(SYSTEM_CFLAGS) -o $@ $< $(STATIC_LDFLAGS)) +# Include build rules (must come after variable definitions) +include rules.mk clean: - $(Q)rm -f $(DEMO_BINS) + $(Q)rm -f $(all_bins) $(OUTDIR)/*.o .PHONY: all clean diff --git a/examples/ulib/config.mk b/examples/ulib/config.mk new file mode 100644 index 00000000000..993f0923b4f --- /dev/null +++ b/examples/ulib/config.mk @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Configuration and helpers for U-Boot library examples +# +# Copyright 2025 Canonical Ltd. +# Written by Simon Glass <simon.glass@canonical.com> + +# For standalone builds, provide default values +EXAMPLE_DIR ?= . +OUTDIR ?= . +CC ?= gcc +SDL_CONFIG ?= sdl2-config +PLATFORM_LIBS ?= $(shell $(SDL_CONFIG) --libs) +LIB_STATIC_LDS ?= static.lds +# The main Makefile passes in Q=@ for quiet output +Q ?= + +# Common compiler flags for programs using system headers +SYSTEM_CFLAGS := -I$(UBOOT_BUILD)/include \ + -idirafter$(srctree)/include \ + -include $(srctree)/include/linux/compiler_attributes.h + +# Common compiler flags for programs using U-Boot headers (these match the +# U-Boot internal build) +UBOOT_CFLAGS := -nostdinc \ + -isystem $(shell $(CC) -print-file-name=include) \ + -I$(UBOOT_BUILD)/include \ + -I$(srctree)/include \ + -I$(srctree)/arch/sandbox/include \ + -include $(UBOOT_BUILD)/include/config.h \ + -include $(srctree)/include/linux/kconfig.h \ + -I$(srctree)/dts/upstream/include \ + "-DMBEDTLS_CONFIG_FILE=\"mbedtls_def_config.h\"" \ + -I$(srctree)/lib/mbedtls \ + -I$(srctree)/lib/mbedtls/port \ + -I$(srctree)/lib/mbedtls/external/mbedtls \ + -I$(srctree)/lib/mbedtls/external/mbedtls/include \ + -Wno-builtin-declaration-mismatch \ + -D__KERNEL__ -DCONFIG_SYS_TEXT_BASE=0 + +# Linking flags +SHARED_LDFLAGS := -L$(UBOOT_BUILD) -lu-boot -Wl,-rpath,$(UBOOT_BUILD) + +STATIC_LDFLAGS := -Wl,-T,$(LIB_STATIC_LDS) \ + -Wl,--whole-archive $(UBOOT_BUILD)/libu-boot.a \ + -Wl,--no-whole-archive \ + -lpthread -ldl $(PLATFORM_LIBS) -Wl,-z,noexecstack diff --git a/examples/ulib/rules.mk b/examples/ulib/rules.mk new file mode 100644 index 00000000000..aba6c63d8b6 --- /dev/null +++ b/examples/ulib/rules.mk @@ -0,0 +1,39 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Build rules for U-Boot library examples +# +# Copyright 2025 Canonical Ltd. +# Written by Simon Glass <simon.glass@canonical.com> + +# Generate normal and statically linked binary names from progs variable +all_bins := $(foreach prog,$(progs),$(OUTDIR)/$(prog) \ + $(OUTDIR)/$(prog)_static) + +# Default target builds both programs +all: $(all_bins) + +# System headers rule for objects that need system APIs +$(foreach obj,$(sys-objs),$(eval $(OUTDIR)/$(obj): \ + $(EXAMPLE_DIR)/$(obj:.o=.c) | $(OUTDIR) ; \ + $$(CC) $$(CFLAGS) $$(SYSTEM_CFLAGS) -c -o $$@ $$<)) + +# Automatic build rules for all programs +$(foreach prog,$(progs),$(eval $(OUTDIR)/$(prog): \ + $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) ; \ + $$(CC) $$(CFLAGS) -o $$@ $$^ $$(SHARED_LDFLAGS))) +$(foreach prog,$(progs),$(eval $(OUTDIR)/$(prog)_static: \ + $$(addprefix $(OUTDIR)/,$$($(prog)_objs)) ; \ + $$(CC) $$(CFLAGS) -o $$@ $$^ $$(STATIC_LDFLAGS))) + +# Create the output directory if it doesn't exist +$(OUTDIR): + @mkdir -p $@ + +# Default rule: compile with U-Boot headers +$(OUTDIR)/%.o: $(EXAMPLE_DIR)/%.c | $(OUTDIR) + $(CC) $(CFLAGS) $(UBOOT_CFLAGS) -c -o $@ $< + +# The U-Boot library must be built before we can link against it +# Order-only prerequisites ensure libraries exist before linking +$(all_bins): | $(UBOOT_BUILD)/libu-boot.a $(UBOOT_BUILD)/libu-boot.so \ + $(OUTDIR) \ No newline at end of file -- 2.43.0 base-commit: cd204772930a099842949968fb7dece04ed1026f branch: ulig

From: Simon Glass <sjg@chromium.org> Add an example of a program which boots an OS by calling into the U-Boot library. Co-developed-by: Claude <noreply@anthropic.com> Co-developed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Simon Glass <sjg@chromium.org> --- examples/ulib/.gitignore | 2 + examples/ulib/Makefile | 5 +- examples/ulib/README | 23 +++++-- examples/ulib/boot.c | 64 ++++++++++++++++++ examples/ulib/bootflow.c | 141 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 229 insertions(+), 6 deletions(-) create mode 100644 examples/ulib/boot.c create mode 100644 examples/ulib/bootflow.c diff --git a/examples/ulib/.gitignore b/examples/ulib/.gitignore index d2f0dfa7a93..f275acf9df1 100644 --- a/examples/ulib/.gitignore +++ b/examples/ulib/.gitignore @@ -1,2 +1,4 @@ +/boot +/boot_static /demo /demo_static diff --git a/examples/ulib/Makefile b/examples/ulib/Makefile index a11c5dad9be..36de37daf51 100644 --- a/examples/ulib/Makefile +++ b/examples/ulib/Makefile @@ -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 diff --git a/examples/ulib/README b/examples/ulib/README index e6f48bf79cd..23fb2a3d097 100644 --- a/examples/ulib/README +++ b/examples/ulib/README @@ -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 ------------------------- diff --git a/examples/ulib/boot.c b/examples/ulib/boot.c new file mode 100644 index 00000000000..ad8a52d5bc9 --- /dev/null +++ b/examples/ulib/boot.c @@ -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; +} diff --git a/examples/ulib/bootflow.c b/examples/ulib/bootflow.c new file mode 100644 index 00000000000..e100a0ee492 --- /dev/null +++ b/examples/ulib/bootflow.c @@ -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; +} -- 2.43.0 base-commit: cd204772930a099842949968fb7dece04ed1026f branch: ulig
participants (1)
-
Simon Glass