am335x: add support for loading u-boot from multiple offsets

The am335x knows nothing about eMMC boot partitions, so in order to
implement an update procedure of the bootloader which is robust
against power failure or other interruptions, one must make use of the
fact that ROM code on the am335x looks for a valid first stage
bootloader at several different offsets. Updating that can then be
implemented by (assume we put MLO at offsets 128K and 256K):

(1) At least one of the two slots must contain a valid header, since
we successfully booted. Pick the other one.

(2) Overwrite the first sector of the slot chosen in step (a) with all
zeroes.

(3) Write everything but the first sector to the chosen slot.

(4) Write the first sector (containing the magic signature that the
boot ROM uses to identify a valid image) to the chosen slot.

(5) Repeat steps (2)-(4) for the other slot.

It's not possible to simply write the whole MLO in one go, especially
not when updating the 128K slot, because an interruption after the
first sector is written would make the ROM code believe the image is
valid.

But this only caters for SPL itself; regardless of where SPL was
loaded from, it would go on to load U-Boot proper from
SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR. So in order to update the whole
bootloader, we need to teach SPL to use a different offset for U-Boot
proper depending on where SPL itself was loaded from (*). With that,
the update procedure is just amended by a step

(3a) Write U-Boot proper to the offset corresponding to the SPL slot
being updated.

We can know (*) because the ROM code sets a new bit in a certain
"trace vector" before each successive attempt.

Signed-off-by: Rasmus Villemoes <ravi@prevas.dk>
This commit is contained in:
Rasmus Villemoes
2026-01-29 11:54:32 +01:00
committed by Tom Rini
parent eed514b11d
commit 589d0a687b
2 changed files with 68 additions and 0 deletions

View File

@@ -156,6 +156,43 @@ config SYS_DEFAULT_LPDDR2_TIMINGS
endchoice
config SPL_AM33XX_MMCSD_MULTIPLE
bool "Support multiple locations of next boot phase"
depends on AM33XX
depends on SPL_SYS_MMCSD_RAW_MODE
depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR
help
The boot ROM on the am33xx looks for the first stage
bootloader at several hard-coded offsets in the mmc device
(0, 128K, 256K, 384K) and uses the first location which has
a valid header. This can be used to implement failsafe
update of that first stage (SPL). But in order for the
update of the whole bootloader to be failsafe, SPL must load
U-Boot proper from a location dependent on where SPL itself
was loaded from. This option allows you to specify four
different offsets corresponding to the different places
where SPL could have been loaded from.
if SPL_AM33XX_MMCSD_MULTIPLE
config SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_0K
hex "Address on the MMC to load U-Boot from when SPL was loaded from offset 0K"
default SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR
config SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_128K
hex "Address on the MMC to load U-Boot from when SPL was loaded from offset 128K"
default SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR
config SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_256K
hex "Address on the MMC to load U-Boot from when SPL was loaded from offset 256K"
default SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR
config SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_384K
hex "Address on the MMC to load U-Boot from when SPL was loaded from offset 384K"
default SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR
endif
source "arch/arm/mach-omap2/omap3/Kconfig"
source "arch/arm/mach-omap2/omap5/Kconfig"

View File

@@ -318,3 +318,34 @@ static void tee_image_process(ulong tee_image, size_t tee_size)
}
U_BOOT_FIT_LOADABLE_HANDLER(IH_TYPE_TEE, tee_image_process);
#endif
#ifdef CONFIG_SPL_AM33XX_MMCSD_MULTIPLE
#define AM335X_TRACE_VECTOR2 0x4030CE44
unsigned long arch_spl_mmc_get_uboot_raw_sector(struct mmc *mmc, unsigned long raw_sect)
{
u32 bits = *(u32 *)AM335X_TRACE_VECTOR2;
bits &= 0xf000;
/*
* The ROM code sets the "trial bit 3", bit 15, first, when
* attempting offset 0, then "trial bit 2", bit 14, when
* attempting offset 128K, and so on. If the tracing vector
* has completely unexpected contents, fall back to the
* raw_sect we were given.
*/
switch (bits) {
case 0x8000: raw_sect = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_0K; break;
case 0xc000: raw_sect = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_128K; break;
case 0xe000: raw_sect = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_256K; break;
case 0xf000: raw_sect = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR_384K; break;
default:
printf("Warning: Unexpected trial bits 0x%04x in trace vector 2, falling back to 0x%lx\n",
bits, raw_sect);
}
return raw_sect;
}
#endif