Merge patch series "bootm: Clean up arch-specific, pre-OS clean-up"

Simon Glass <sjg@chromium.org> says:

Each arch does something slightly different before booting the OS. Some
archs even do different things depending on the CPU type.

It is quite hard to know what actually happens in the final milliseconds
before the OS boot.

This series attempts to start cleaning up U-Boot in this area.

The basic intent is to create a new bootm_final() function which can be
called by all archs. It provides some flags for a couple of necessary
variations but otherwise it is generic.

All architectures are converted over to use this new function.

board_quiesce_devices() is moved into bootm_final() so that all archs
benefit from it.

This series fixes a bug in device_remove() is fixed where removing a
parent with specialised flags (e.g. DM_REMOVE_ACTIVE_ALL) could leave
children activated, since they do not match the flags. This fixes is
needed to avoid bootm_final() causing test failures on sandbox.

Future work could take this a little further:
- Convert EFI loader to use the same function
- Improve comments for cleanup_before_linux() across architectures
- Support fake-run tracing on all archs

Link: https://lore.kernel.org/r/20260306023638.2678886-1-sjg@chromium.org
This commit is contained in:
Tom Rini 2026-03-18 18:36:50 -06:00
commit eb00c71050
17 changed files with 91 additions and 131 deletions

View File

@ -50,17 +50,13 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
{
ulong kernel_entry;
unsigned int r0, r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = images->ep;
debug("## Transferring control to Linux (at address %08lx)...\n",
kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
printf("\nStarting kernel ...%s\n\n", fake ?
"(fake run for tracing)" : "");
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
bootm_final(flag);
if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len) {
r0 = 2;
@ -72,7 +68,7 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
cleanup_before_linux();
if (!fake)
if (!(flag & BOOTM_STATE_OS_FAKE_GO))
board_jump_and_run(kernel_entry, r0, 0, r2);
}

View File

@ -42,42 +42,6 @@ DECLARE_GLOBAL_DATA_PTR;
static struct tag *params;
__weak void board_quiesce_devices(void)
{
}
/**
* announce_and_cleanup() - Print message and prepare for kernel boot
*
* @fake: non-zero to do everything except actually boot
*/
static void announce_and_cleanup(int fake)
{
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#ifdef CONFIG_BOOTSTAGE_FDT
bootstage_fdt_add_report();
#endif
bootstage_stash_default();
#ifdef CONFIG_BOOTSTAGE_REPORT
bootstage_report();
#endif
board_quiesce_devices();
printf("\nStarting kernel ...%s\n\n", fake ?
"(fake run for tracing)" : "");
/*
* Call remove function of all devices with a removal flag set.
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
* dm_remove_devices_active() ensures that vital devices are removed in
* a second round.
*/
dm_remove_devices_active();
cleanup_before_linux();
}
static void setup_start_tag (struct bd_info *bd)
{
params = (struct tag *)bd->bi_boot_params;
@ -294,8 +258,6 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
{
void (*kernel_entry)(void *fdt_addr, void *res0, void *res1,
void *res2);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(void *fdt_addr, void *res0, void *res1,
void *res2))images->ep;
@ -303,9 +265,10 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
(ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
bootm_final(flag);
cleanup_before_linux();
if (!fake) {
if (!(flag & BOOTM_STATE_OS_FAKE_GO)) {
#ifdef CONFIG_ARMV8_PSCI
armv8_setup_psci();
#endif
@ -340,8 +303,6 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
char *s;
void (*kernel_entry)(int zero, int arch, uint params);
unsigned long r2;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
kernel_entry = (void (*)(int, int, uint))images->ep;
#ifdef CONFIG_CPU_V7M
ulong addr = (ulong)kernel_entry | 1;
@ -366,14 +327,15 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
debug("## Transferring control to Linux (at address %08lx)" \
"...\n", (ulong) kernel_entry);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
announce_and_cleanup(fake);
bootm_final(flag);
cleanup_before_linux();
if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len)
r2 = (unsigned long)images->ft_addr;
else
r2 = gd->bd->bi_boot_params;
if (fake)
if (flag & BOOTM_STATE_OS_FAKE_GO)
return;
#ifdef CONFIG_ARMV7_NONSEC

View File

@ -62,6 +62,8 @@ int do_bootm_linux(int flag, struct bootm_info *bmi)
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
bootm_final(0);
/*
* Linux Kernel Parameters (passing board info data):
* sp+00: Ignore, side effect of using jsr to jump to kernel

View File

@ -26,8 +26,6 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
ulong dt = (ulong)images->ft_addr;
ulong rd_start = images->initrd_start;
ulong cmdline = images->cmdline_start;
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
thekernel = (void (*)(char *, ulong, ulong))images->ep;
debug("## Transferring control to Linux (at address 0x%08lx) ",
@ -36,13 +34,11 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
cmdline, rd_start, dt);
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
printf("\nStarting kernel ...%s\n\n", fake ?
"(fake run for tracing)" : "");
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
bootm_final(flag);
flush_cache_all();
if (!fake) {
if (!(flag & BOOTM_STATE_OS_FAKE_GO)) {
/*
* Linux Kernel Parameters (passing device tree):
* r5: pointer to command line

View File

@ -268,12 +268,7 @@ static void boot_jump_linux(struct bootm_headers *images)
if (CONFIG_IS_ENABLED(MALTA))
linux_extra = gd->ram_size;
#if IS_ENABLED(CONFIG_BOOTSTAGE_FDT)
bootstage_fdt_add_report();
#endif
#if IS_ENABLED(CONFIG_BOOTSTAGE_REPORT)
bootstage_report();
#endif
bootm_final(0);
if (CONFIG_IS_ENABLED(RESTORE_EXCEPTION_VECTOR_BASE))
trap_restore();

View File

@ -41,6 +41,8 @@ int do_bootm_linux(int flag, struct bootm_info *bmi)
if ((flag != 0) && (flag != BOOTM_STATE_OS_GO))
return 1;
bootm_final(0);
/* flushes data and instruction caches before calling the kernel */
disable_interrupts();
flush_dcache_all();

View File

@ -54,12 +54,7 @@ static void boot_jump_linux(struct bootm_headers *images)
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
#ifdef CONFIG_BOOTSTAGE_FDT
bootstage_fdt_add_report();
#endif
#ifdef CONFIG_BOOTSTAGE_REPORT
bootstage_report();
#endif
bootm_final(0);
#if defined(CONFIG_SYS_INIT_RAM_LOCK) && !defined(CONFIG_E500)
unlock_ram_in_cache();

View File

@ -16,7 +16,6 @@ int cleanup_before_linux(void);
/* board/.../... */
int board_init(void);
void board_quiesce_devices(void);
int riscv_board_reserved_mem_fixup(void *fdt);
int riscv_fdt_copy_resv_mem_node(const void *src_fdt, void *dest_fdt);

View File

@ -25,39 +25,6 @@
DECLARE_GLOBAL_DATA_PTR;
__weak void board_quiesce_devices(void)
{
}
/**
* announce_and_cleanup() - Print message and prepare for kernel boot
*
* @fake: non-zero to do everything except actually boot
*/
static void announce_and_cleanup(int fake)
{
printf("\nStarting kernel ...%s\n\n", fake ?
"(fake run for tracing)" : "");
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#ifdef CONFIG_BOOTSTAGE_FDT
bootstage_fdt_add_report();
#endif
#if CONFIG_IS_ENABLED(BOOTSTAGE_REPORT)
bootstage_report();
#endif
board_quiesce_devices();
/*
* Call remove function of all devices with a removal flag set.
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
*/
dm_remove_devices_active();
cleanup_before_linux();
}
static void boot_prep_linux(struct bootm_headers *images)
{
if (CONFIG_IS_ENABLED(OF_LIBFDT) && IS_ENABLED(CONFIG_LMB) && images->ft_len) {
@ -75,7 +42,6 @@ static void boot_prep_linux(struct bootm_headers *images)
static void boot_jump_linux(struct bootm_headers *images, int flag)
{
void (*kernel)(ulong hart, void *dtb);
int fake = (flag & BOOTM_STATE_OS_FAKE_GO);
#ifdef CONFIG_SMP
int ret;
#endif
@ -87,9 +53,10 @@ static void boot_jump_linux(struct bootm_headers *images, int flag)
debug("## Transferring control to kernel (at address %08lx) ...\n",
(ulong)kernel);
announce_and_cleanup(fake);
bootm_final(flag);
cleanup_before_linux();
if (!fake) {
if (!(flag & BOOTM_STATE_OS_FAKE_GO)) {
if (CONFIG_IS_ENABLED(OF_LIBFDT) && images->ft_len) {
#ifdef CONFIG_SMP
ret = smp_call_function(images->ep,

View File

@ -73,6 +73,7 @@ int do_bootm_linux(int flag, struct bootm_info *bmi)
if (flag & (BOOTM_STATE_OS_GO | BOOTM_STATE_OS_FAKE_GO)) {
bootstage_mark(BOOTSTAGE_ID_RUN_OS);
bootm_final(flag);
printf("## Transferring control to Linux (at address %08lx)...\n",
images->ep);
printf("sandbox: continuing, as we cannot run Linux\n");

View File

@ -92,6 +92,8 @@ int do_bootm_linux(int flag, struct bootm_info *bmi)
images->rd_end - images->rd_start);
}
bootm_final(0);
/* Boot kernel */
kernel();

View File

@ -34,22 +34,10 @@ DECLARE_GLOBAL_DATA_PTR;
void bootm_announce_and_cleanup(void)
{
printf("\nStarting kernel ...\n\n");
#ifdef CONFIG_SYS_COREBOOT
timestamp_add_now(TS_START_KERNEL);
#endif
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
#if IS_ENABLED(CONFIG_BOOTSTAGE_REPORT)
bootstage_report();
#endif
/*
* Call remove function of all devices with a removal flag set.
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
*/
dm_remove_devices_active();
bootm_final(0);
}
#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL)

View File

@ -178,6 +178,8 @@ int do_bootm_linux(int flag, struct bootm_info *bmi)
printf("Transferring Control to Linux @0x%08lx ...\n\n",
(ulong)images->ep);
bootm_final(flag);
flush_dcache_range((unsigned long)params_start, (unsigned long)params);
if (flag & BOOTM_STATE_OS_FAKE_GO)

View File

@ -7,6 +7,7 @@
#ifndef USE_HOSTCC
#include <bootm.h>
#include <bootstage.h>
#include <dm/root.h>
#include <cli.h>
#include <command.h>
#include <cpu_func.h>
@ -1194,6 +1195,30 @@ void __weak switch_to_non_secure_mode(void)
{
}
void bootm_final(int flag)
{
printf("\nStarting kernel ...%s\n\n",
(flag & BOOTM_STATE_OS_FAKE_GO) ?
" (fake run for tracing)" : "");
bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel");
if (IS_ENABLED(CONFIG_BOOTSTAGE_FDT) && IS_ENABLED(CONFIG_CMD_FDT))
bootstage_fdt_add_report();
bootstage_stash_default();
if (IS_ENABLED(CONFIG_BOOTSTAGE_REPORT))
bootstage_report();
board_quiesce_devices();
/*
* Call remove function of all devices with a removal flag set.
* This may be useful for last-stage operations, like cancelling
* of DMA operation or releasing device internal buffers.
*/
dm_remove_devices_active();
}
#else /* USE_HOSTCC */
#if defined(CONFIG_FIT_SIGNATURE)

View File

@ -198,7 +198,7 @@ static int flags_remove(uint flags, uint drv_flags)
int device_remove(struct udevice *dev, uint flags)
{
const struct driver *drv;
int ret;
int ret, cret;
if (!dev)
return -EINVAL;
@ -210,25 +210,35 @@ int device_remove(struct udevice *dev, uint flags)
if (ret)
return ret;
/*
* If the child returns EKEYREJECTED, continue. It just means that it
* didn't match the flags.
*/
ret = device_chld_remove(dev, NULL, flags);
if (ret && ret != -EKEYREJECTED)
return ret;
/*
* Remove the device if called with the "normal" remove flag set,
* or if the remove flag matches any of the drivers remove flags
*/
drv = dev->driver;
assert(drv);
ret = flags_remove(flags, drv->flags);
if (ret) {
log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n",
dev->name, flags, drv->flags, ret);
cret = flags_remove(flags, drv->flags);
/*
* Remove all children. If this device is being removed due to
* active-DMA or OS-prepare flags, drop the active-flag requirement
* for children so they are removed even without matching active
* flags, since a deactivated device must not have activated
* children. Preserve other flags (e.g. DM_REMOVE_NON_VITAL) so
* that vital children are still protected.
*
* If the child returns EKEYREJECTED, continue. It just means that it
* didn't match the flags.
*/
ret = device_chld_remove(dev, NULL,
cret ? flags :
(flags & ~DM_REMOVE_ACTIVE_ALL));
if (ret && ret != -EKEYREJECTED)
return ret;
if (cret) {
log_debug("%s: When removing: flags=%x, drv->flags=%x, err=%d\n",
dev->name, flags, drv->flags, cret);
return cret;
}
ret = uclass_pre_remove_device(dev);

View File

@ -321,4 +321,14 @@ void zimage_dump(struct boot_params *base_ptr, bool show_cmdline);
*/
int bootm_boot_start(ulong addr, const char *cmdline);
/**
* bootm_final() - Announce and do cleanup before boot
*
* This performs the common pre-boot steps: printing the "Starting kernel"
* message, recording bootstage data, and removing active devices.
*
* @flag: Boot state flags (BOOTM_STATE_OS_FAKE_GO prints a fake-run message)
*/
void bootm_final(int flag);
#endif

View File

@ -435,6 +435,14 @@ static inline uint32_t bootstage_accum(enum bootstage_id id)
return 0;
}
static inline void bootstage_report(void)
{
}
static inline void bootstage_fdt_add_report(void)
{
}
static inline int bootstage_stash(void *base, int size)
{
return 0; /* Pretend to succeed */