[RESEND PATCH v2 0/3] sunxi: Improve automatic eMMC boot partition support

(resent to also include forgotten U-Boot list)
The Allwinner BootROM supports loading the SPL from eMMC boot partitions, but so far the SPL support for this case was a bit lacking, as it was a compile time decision, and even required a manual config change. This actually got accidentally fixed in v2021.04 (c0b417b2f1a3 "sunxi: support loading with SPL > 32KB"), but still some gaps remain: When just enabling an eMMC boot partition, but having no bootable code there, the BROM will just continue with booting from the user data partition, but U-Boot does not detect this and will try to load U-Boot proper from that boot partition.
This series fixes this, by replicating the BROMs decisions in the SPL, to decide on the partition to continue loading from.
Patch 1/3 prepares a generic function to take an extra argument, while patch 2/3 implements the actual algorithm. As this now allows to always have eMMC boot support on, patch 3/3 just activates this for a number of boards I could test it on.
I'd like to encourage other people to test this on boards with eMMC, to enable this in the respective defconfig, maybe even centrally.
Some more details on eMMC boot partition support and how to install U-Boot in there can be found in the linux-sunxi Wiki: http://linux-sunxi.org/Bootable_eMMC
Cheers, Andre
Changelog v1 .. v2: - drop patch 1/5 (bugfix): already merged - drop patch 3/5, as it was obsoleted by c0b417b2f1a3 - remove check for 32KB SPL limit (not applicable for H6 and beyond) - remove extra sector offset adjustment (obsoleted by c0b417b2f1a3)
Andre Przywara (3): spl: mmc: extend spl_mmc_boot_mode() to take mmc argument sunxi: eMMC: Improve automatic boot source detection sunxi: defconfig: enable eMMC boot partition support
arch/arm/mach-imx/spl.c | 2 +- arch/arm/mach-k3/am6_init.c | 2 +- arch/arm/mach-k3/j721e_init.c | 2 +- arch/arm/mach-omap2/boot-common.c | 2 +- arch/arm/mach-rockchip/spl.c | 2 +- arch/arm/mach-socfpga/spl_a10.c | 2 +- arch/arm/mach-socfpga/spl_gen5.c | 2 +- arch/arm/mach-stm32mp/spl.c | 2 +- arch/arm/mach-sunxi/board.c | 80 ++++++++++++++++++++++ arch/arm/mach-uniphier/mmc-boot-mode.c | 5 +- common/spl/spl_mmc.c | 4 +- configs/bananapi_m64_defconfig | 1 + configs/emlid_neutis_n5_devboard_defconfig | 1 + configs/pine64-lts_defconfig | 1 + configs/pine_h64_defconfig | 1 + include/spl.h | 3 +- 16 files changed, 97 insertions(+), 15 deletions(-)

Platforms can overwrite the weak definition of spl_mmc_boot_mode() to determine where to load U-Boot proper from. For most of them this is a trivial decision based on Kconfig variables, but it might be desirable the probe the actual device to answer this question.
Pass the pointer to the mmc struct to that function, so implementations can make use of that.
Compile-tested for all users changed.
Signed-off-by: Andre Przywara andre.przywara@arm.com Reviewed-by: Stefano Babic sbabic@denx.de Reviewed-by: Ley Foon Tan ley.foon.tan@inte.com (for SoCFPGA) Acked-by: Lokesh Vutla lokeshvutla@ti.com (for OMAP and K3) --- (resent to also include forgotten U-Boot list)
arch/arm/mach-imx/spl.c | 2 +- arch/arm/mach-k3/am6_init.c | 2 +- arch/arm/mach-k3/j721e_init.c | 2 +- arch/arm/mach-omap2/boot-common.c | 2 +- arch/arm/mach-rockchip/spl.c | 2 +- arch/arm/mach-socfpga/spl_a10.c | 2 +- arch/arm/mach-socfpga/spl_gen5.c | 2 +- arch/arm/mach-stm32mp/spl.c | 2 +- arch/arm/mach-uniphier/mmc-boot-mode.c | 5 +---- common/spl/spl_mmc.c | 4 ++-- include/spl.h | 3 ++- 11 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/arch/arm/mach-imx/spl.c b/arch/arm/mach-imx/spl.c index 36033d611c9..797097b9c79 100644 --- a/arch/arm/mach-imx/spl.c +++ b/arch/arm/mach-imx/spl.c @@ -201,7 +201,7 @@ int g_dnl_get_board_bcd_device_number(int gcnum)
#if defined(CONFIG_SPL_MMC_SUPPORT) /* called from spl_mmc to see type of boot mode for storage (RAW or FAT) */ -u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { #if defined(CONFIG_MX7) || defined(CONFIG_IMX8M) || defined(CONFIG_IMX8) switch (get_boot_device()) { diff --git a/arch/arm/mach-k3/am6_init.c b/arch/arm/mach-k3/am6_init.c index 425b3f93c86..f4b039e8a6a 100644 --- a/arch/arm/mach-k3/am6_init.c +++ b/arch/arm/mach-k3/am6_init.c @@ -253,7 +253,7 @@ void board_init_f(ulong dummy) spl_enable_dcache(); }
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { #if defined(CONFIG_SUPPORT_EMMC_BOOT) u32 devstat = readl(CTRLMMR_MAIN_DEVSTAT); diff --git a/arch/arm/mach-k3/j721e_init.c b/arch/arm/mach-k3/j721e_init.c index e9e076c9e72..fc0db2a4001 100644 --- a/arch/arm/mach-k3/j721e_init.c +++ b/arch/arm/mach-k3/j721e_init.c @@ -232,7 +232,7 @@ void board_init_f(ulong dummy) spl_enable_dcache(); }
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { switch (boot_device) { case BOOT_DEVICE_MMC1: diff --git a/arch/arm/mach-omap2/boot-common.c b/arch/arm/mach-omap2/boot-common.c index 1268a325038..f71fe65a0b1 100644 --- a/arch/arm/mach-omap2/boot-common.c +++ b/arch/arm/mach-omap2/boot-common.c @@ -189,7 +189,7 @@ u32 spl_boot_device(void) return gd->arch.omap_boot_device; }
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { return gd->arch.omap_boot_mode; } diff --git a/arch/arm/mach-rockchip/spl.c b/arch/arm/mach-rockchip/spl.c index 02c40fb37ed..082a5bfb20a 100644 --- a/arch/arm/mach-rockchip/spl.c +++ b/arch/arm/mach-rockchip/spl.c @@ -65,7 +65,7 @@ u32 spl_boot_device(void) return boot_device; }
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { return MMCSD_MODE_RAW; } diff --git a/arch/arm/mach-socfpga/spl_a10.c b/arch/arm/mach-socfpga/spl_a10.c index b5f43f09d19..c6dcd309bfc 100644 --- a/arch/arm/mach-socfpga/spl_a10.c +++ b/arch/arm/mach-socfpga/spl_a10.c @@ -94,7 +94,7 @@ u32 spl_boot_device(void) }
#ifdef CONFIG_SPL_MMC_SUPPORT -u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4) return MMCSD_MODE_FS; diff --git a/arch/arm/mach-socfpga/spl_gen5.c b/arch/arm/mach-socfpga/spl_gen5.c index 7c716117685..e5d1e64c449 100644 --- a/arch/arm/mach-socfpga/spl_gen5.c +++ b/arch/arm/mach-socfpga/spl_gen5.c @@ -53,7 +53,7 @@ u32 spl_boot_device(void) }
#ifdef CONFIG_SPL_MMC_SUPPORT -u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4) return MMCSD_MODE_FS; diff --git a/arch/arm/mach-stm32mp/spl.c b/arch/arm/mach-stm32mp/spl.c index b53659a698a..4c8085b8305 100644 --- a/arch/arm/mach-stm32mp/spl.c +++ b/arch/arm/mach-stm32mp/spl.c @@ -53,7 +53,7 @@ u32 spl_boot_device(void) return BOOT_DEVICE_MMC1; }
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { return MMCSD_MODE_RAW; } diff --git a/arch/arm/mach-uniphier/mmc-boot-mode.c b/arch/arm/mach-uniphier/mmc-boot-mode.c index e47e5df6480..09cad743c55 100644 --- a/arch/arm/mach-uniphier/mmc-boot-mode.c +++ b/arch/arm/mach-uniphier/mmc-boot-mode.c @@ -7,10 +7,8 @@ #include <mmc.h> #include <spl.h>
-u32 spl_mmc_boot_mode(const u32 boot_device) +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { - struct mmc *mmc; - /* * work around a bug in the Boot ROM of LD4, Pro4, and sLD8: * @@ -24,7 +22,6 @@ u32 spl_mmc_boot_mode(const u32 boot_device) * Fixup mmc->part_config here because it is used to determine the * partition which the U-Boot image is read from. */ - mmc = find_mmc_device(0); mmc->part_config &= ~EXT_CSD_BOOT_PART_NUM(PART_ACCESS_MASK); mmc->part_config |= EXT_CSD_BOOT_PARTITION_ENABLE;
diff --git a/common/spl/spl_mmc.c b/common/spl/spl_mmc.c index add2785b4e3..309abaf961a 100644 --- a/common/spl/spl_mmc.c +++ b/common/spl/spl_mmc.c @@ -300,7 +300,7 @@ static int spl_mmc_do_fs_boot(struct spl_image_info *spl_image, struct mmc *mmc, } #endif
-u32 __weak spl_mmc_boot_mode(const u32 boot_device) +u32 __weak spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) { #if defined(CONFIG_SPL_FS_FAT) || defined(CONFIG_SPL_FS_EXT4) return MMCSD_MODE_FS; @@ -351,7 +351,7 @@ int spl_mmc_load(struct spl_image_info *spl_image, } }
- boot_mode = spl_mmc_boot_mode(bootdev->boot_device); + boot_mode = spl_mmc_boot_mode(mmc, bootdev->boot_device); err = -EINVAL; switch (boot_mode) { case MMCSD_MODE_EMMCBOOT: diff --git a/include/spl.h b/include/spl.h index cee9a42ddb5..c8f470dd5f2 100644 --- a/include/spl.h +++ b/include/spl.h @@ -14,6 +14,7 @@ #include <asm/global_data.h> #include <asm/spl.h> #include <handoff.h> +#include <mmc.h>
struct blk_desc; struct image_header; @@ -343,7 +344,7 @@ u32 spl_boot_device(void); * Note: It is important to use the boot_device parameter instead of e.g. * spl_boot_device() as U-Boot is not always loaded from the same device as SPL. */ -u32 spl_mmc_boot_mode(const u32 boot_device); +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device);
/** * spl_mmc_boot_partition() - MMC partition to load U-Boot from.

Hi Andre,
On Mon, 12 Jul 2021 at 04:07, Andre Przywara andre.przywara@arm.com wrote:
Platforms can overwrite the weak definition of spl_mmc_boot_mode() to determine where to load U-Boot proper from. For most of them this is a trivial decision based on Kconfig variables, but it might be desirable the probe the actual device to answer this question.
Pass the pointer to the mmc struct to that function, so implementations can make use of that.
Compile-tested for all users changed.
Signed-off-by: Andre Przywara andre.przywara@arm.com Reviewed-by: Stefano Babic sbabic@denx.de Reviewed-by: Ley Foon Tan ley.foon.tan@inte.com (for SoCFPGA) Acked-by: Lokesh Vutla lokeshvutla@ti.com (for OMAP and K3)
(resent to also include forgotten U-Boot list)
arch/arm/mach-imx/spl.c | 2 +- arch/arm/mach-k3/am6_init.c | 2 +- arch/arm/mach-k3/j721e_init.c | 2 +- arch/arm/mach-omap2/boot-common.c | 2 +- arch/arm/mach-rockchip/spl.c | 2 +- arch/arm/mach-socfpga/spl_a10.c | 2 +- arch/arm/mach-socfpga/spl_gen5.c | 2 +- arch/arm/mach-stm32mp/spl.c | 2 +- arch/arm/mach-uniphier/mmc-boot-mode.c | 5 +---- common/spl/spl_mmc.c | 4 ++-- include/spl.h | 3 ++- 11 files changed, 13 insertions(+), 15 deletions(-)
Reviewed-by: Simon Glass sjg@chromium.org
But I think this should come from a sysinfo driver.
Regards, Simon

When the Allwinner BROM loads the SPL from an eMMC boot partition, it sets the boot source byte to the same value as when booting from the user data partition. This prevents us from determining the boot source to load U-Boot proper from the proper partition for sure.
The generic SPL MMC code already looks at the enabled boot partition number, to load U-Boot proper from the same partition, but this fails if there is nothing bootable in this partition, as the BROM then silently falls back to the user data partition, which the SPL misses.
To learn about the actual boot source anyway, we repeat the algorithm the BROM used to select the boot partition in the first place: - Test EXT_CSD[179] to check if an eMMC boot partition is enabled. - Test EXT_CSD[177] to check for valid MMC interface settings. - Check if BOOT_ACK is enabled. - Check the beginning of the first sector for a valid eGON signature. - Load the whole SPL. - Recalculate the checksum to verify the SPL is valid.
If one of those steps fails, we bail out and continue loading from the user data partition. Otherwise we load from the selected boot partition.
Since the boot source is needed twice in the boot process, we cache the result of this test to avoid doing this costly test multiple times.
This allows the very same image file to be put onto an SD card, into the eMMC user data partition or into the eMMC boot partition, and safely loads the whole of U-Boot from there.
Signed-off-by: Andre Przywara andre.przywara@arm.com ---
(resent to also include forgotten U-Boot list)
arch/arm/mach-sunxi/board.c | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+)
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index e979e426dd1..2552c34733b 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -334,6 +334,86 @@ u32 spl_boot_device(void) return sunxi_get_boot_device(); }
+/* + * When booting from an eMMC boot partition, the SPL puts the same boot + * source code into SRAM A1 as when loading the SPL from the normal + * eMMC user data partition: 0x2. So to know where we have been loaded + * from, we repeat the BROM algorithm here: checking for a valid eGON boot + * image at offset 0 of a (potentially) selected boot partition. + * If any of the conditions is not met, it must have been the eMMC user + * data partition. + */ +static bool sunxi_valid_emmc_boot(struct mmc *mmc) +{ + struct blk_desc *bd = mmc_get_blk_desc(mmc); + uint32_t *buffer = (void *)(uintptr_t)CONFIG_SYS_TEXT_BASE; + int bootpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config); + uint32_t spl_size, emmc_checksum, chksum = 0; + ulong count; + + /* The BROM requires BOOT_ACK to be enabled. */ + if (!EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config)) + return false; + + /* + * The BOOT_BUS_CONDITION register must be 4-bit SDR, with (0x09) + * or without (0x01) high speed timings. + */ + if ((mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x01 && + (mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x09) + return false; + + /* Partition 0 is the user data partition, bootpart must be 1 or 2. */ + if (bootpart != 1 && bootpart != 2) + return false; + + mmc_switch_part(mmc, bootpart); + + /* Read the first block to do some sanity checks on the eGON header. */ + count = blk_dread(bd, 0, 1, buffer); + if (count != 1 || !is_boot0_magic(buffer + 1)) + return false; + + /* Read the rest of the SPL now we know it's halfway sane. */ + spl_size = buffer[4]; + count = blk_dread(bd, 1, DIV_ROUND_UP(spl_size, bd->blksz) - 1, + buffer + bd->blksz / 4); + + /* Save the checksum and replace it with the "stamp value". */ + emmc_checksum = buffer[3]; + buffer[3] = 0x5f0a6c39; + + /* The checksum is a simple ignore-carry addition of all words. */ + for (count = 0; count < spl_size / 4; count++) + chksum += buffer[count]; + + debug("eMMC boot part SPL checksum: stored: 0x%08x, computed: 0x%08x\n", + emmc_checksum, chksum); + + return emmc_checksum == chksum; +} + +u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) +{ + static u32 result = ~0; + + if (result != ~0) + return result; + + result = MMCSD_MODE_RAW; + if (!IS_SD(mmc) && IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) { + if (sunxi_valid_emmc_boot(mmc)) + result = MMCSD_MODE_EMMCBOOT; + else + mmc_switch_part(mmc, 0); + } + + debug("%s(): %s part\n", __func__, + result == MMCSD_MODE_RAW ? "user" : "boot"); + + return result; +} + void board_init_f(ulong dummy) { spl_init();

Hi Andre,
On 7/12/21 7:06 PM, Andre Przywara wrote:
When the Allwinner BROM loads the SPL from an eMMC boot partition, it sets the boot source byte to the same value as when booting from the user data partition. This prevents us from determining the boot source to load U-Boot proper from the proper partition for sure.
The generic SPL MMC code already looks at the enabled boot partition number, to load U-Boot proper from the same partition, but this fails if there is nothing bootable in this partition, as the BROM then silently falls back to the user data partition, which the SPL misses.
To learn about the actual boot source anyway, we repeat the algorithm the BROM used to select the boot partition in the first place:
- Test EXT_CSD[179] to check if an eMMC boot partition is enabled.
- Test EXT_CSD[177] to check for valid MMC interface settings.
- Check if BOOT_ACK is enabled.
- Check the beginning of the first sector for a valid eGON signature.
- Load the whole SPL.
- Recalculate the checksum to verify the SPL is valid.
If one of those steps fails, we bail out and continue loading from the user data partition. Otherwise we load from the selected boot partition.
Since the boot source is needed twice in the boot process, we cache the result of this test to avoid doing this costly test multiple times.
This allows the very same image file to be put onto an SD card, into the eMMC user data partition or into the eMMC boot partition, and safely loads the whole of U-Boot from there.
Signed-off-by: Andre Przywara andre.przywara@arm.com
(resent to also include forgotten U-Boot list)
arch/arm/mach-sunxi/board.c | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+)
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index e979e426dd1..2552c34733b 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -334,6 +334,86 @@ u32 spl_boot_device(void) return sunxi_get_boot_device(); }
+/*
- When booting from an eMMC boot partition, the SPL puts the same boot
- source code into SRAM A1 as when loading the SPL from the normal
- eMMC user data partition: 0x2. So to know where we have been loaded
- from, we repeat the BROM algorithm here: checking for a valid eGON boot
- image at offset 0 of a (potentially) selected boot partition.
- If any of the conditions is not met, it must have been the eMMC user
- data partition.
- */
+static bool sunxi_valid_emmc_boot(struct mmc *mmc) +{
- struct blk_desc *bd = mmc_get_blk_desc(mmc);
- uint32_t *buffer = (void *)(uintptr_t)CONFIG_SYS_TEXT_BASE;
- int bootpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
- uint32_t spl_size, emmc_checksum, chksum = 0;
- ulong count;
- /* The BROM requires BOOT_ACK to be enabled. */
- if (!EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config))
return false;
- /*
* The BOOT_BUS_CONDITION register must be 4-bit SDR, with (0x09)
* or without (0x01) high speed timings.
*/
- if ((mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x01 &&
(mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x09)
return false;
- /* Partition 0 is the user data partition, bootpart must be 1 or 2. */
- if (bootpart != 1 && bootpart != 2)
return false;
- mmc_switch_part(mmc, bootpart);
It can be failed to switch to bootpart. Doesn't need to control error?
Best Regards, Jaehoon Chung
- /* Read the first block to do some sanity checks on the eGON header. */
- count = blk_dread(bd, 0, 1, buffer);
- if (count != 1 || !is_boot0_magic(buffer + 1))
return false;
- /* Read the rest of the SPL now we know it's halfway sane. */
- spl_size = buffer[4];
- count = blk_dread(bd, 1, DIV_ROUND_UP(spl_size, bd->blksz) - 1,
buffer + bd->blksz / 4);
- /* Save the checksum and replace it with the "stamp value". */
- emmc_checksum = buffer[3];
- buffer[3] = 0x5f0a6c39;
- /* The checksum is a simple ignore-carry addition of all words. */
- for (count = 0; count < spl_size / 4; count++)
chksum += buffer[count];
- debug("eMMC boot part SPL checksum: stored: 0x%08x, computed: 0x%08x\n",
emmc_checksum, chksum);
- return emmc_checksum == chksum;
+}
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) +{
- static u32 result = ~0;
- if (result != ~0)
return result;
- result = MMCSD_MODE_RAW;
- if (!IS_SD(mmc) && IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) {
if (sunxi_valid_emmc_boot(mmc))
result = MMCSD_MODE_EMMCBOOT;
else
mmc_switch_part(mmc, 0);
- }
- debug("%s(): %s part\n", __func__,
result == MMCSD_MODE_RAW ? "user" : "boot");
- return result;
+}
void board_init_f(ulong dummy) { spl_init();

On Mon, 12 Jul 2021 19:57:04 +0900 Jaehoon Chung jh80.chung@samsung.com wrote:
Hi Andre,
On 7/12/21 7:06 PM, Andre Przywara wrote:
When the Allwinner BROM loads the SPL from an eMMC boot partition, it sets the boot source byte to the same value as when booting from the user data partition. This prevents us from determining the boot source to load U-Boot proper from the proper partition for sure.
The generic SPL MMC code already looks at the enabled boot partition number, to load U-Boot proper from the same partition, but this fails if there is nothing bootable in this partition, as the BROM then silently falls back to the user data partition, which the SPL misses.
To learn about the actual boot source anyway, we repeat the algorithm the BROM used to select the boot partition in the first place:
- Test EXT_CSD[179] to check if an eMMC boot partition is enabled.
- Test EXT_CSD[177] to check for valid MMC interface settings.
- Check if BOOT_ACK is enabled.
- Check the beginning of the first sector for a valid eGON signature.
- Load the whole SPL.
- Recalculate the checksum to verify the SPL is valid.
If one of those steps fails, we bail out and continue loading from the user data partition. Otherwise we load from the selected boot partition.
Since the boot source is needed twice in the boot process, we cache the result of this test to avoid doing this costly test multiple times.
This allows the very same image file to be put onto an SD card, into the eMMC user data partition or into the eMMC boot partition, and safely loads the whole of U-Boot from there.
Signed-off-by: Andre Przywara andre.przywara@arm.com
(resent to also include forgotten U-Boot list)
arch/arm/mach-sunxi/board.c | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+)
diff --git a/arch/arm/mach-sunxi/board.c b/arch/arm/mach-sunxi/board.c index e979e426dd1..2552c34733b 100644 --- a/arch/arm/mach-sunxi/board.c +++ b/arch/arm/mach-sunxi/board.c @@ -334,6 +334,86 @@ u32 spl_boot_device(void) return sunxi_get_boot_device(); }
+/*
- When booting from an eMMC boot partition, the SPL puts the same boot
- source code into SRAM A1 as when loading the SPL from the normal
- eMMC user data partition: 0x2. So to know where we have been loaded
- from, we repeat the BROM algorithm here: checking for a valid eGON boot
- image at offset 0 of a (potentially) selected boot partition.
- If any of the conditions is not met, it must have been the eMMC user
- data partition.
- */
+static bool sunxi_valid_emmc_boot(struct mmc *mmc) +{
- struct blk_desc *bd = mmc_get_blk_desc(mmc);
- uint32_t *buffer = (void *)(uintptr_t)CONFIG_SYS_TEXT_BASE;
- int bootpart = EXT_CSD_EXTRACT_BOOT_PART(mmc->part_config);
- uint32_t spl_size, emmc_checksum, chksum = 0;
- ulong count;
- /* The BROM requires BOOT_ACK to be enabled. */
- if (!EXT_CSD_EXTRACT_BOOT_ACK(mmc->part_config))
return false;
- /*
* The BOOT_BUS_CONDITION register must be 4-bit SDR, with (0x09)
* or without (0x01) high speed timings.
*/
- if ((mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x01 &&
(mmc->ext_csd[EXT_CSD_BOOT_BUS_WIDTH] & 0x1b) != 0x09)
return false;
- /* Partition 0 is the user data partition, bootpart must be 1 or 2. */
- if (bootpart != 1 && bootpart != 2)
return false;
- mmc_switch_part(mmc, bootpart);
It can be failed to switch to bootpart. Doesn't need to control error?
Yeah, good point, we should check this. If we can't switch to the boot partition, that hopefully means the BROM couldn't do either. In any case we can't continue booting from there, so we can as well return false here.
Cheers, Andre
Best Regards, Jaehoon Chung
- /* Read the first block to do some sanity checks on the eGON header. */
- count = blk_dread(bd, 0, 1, buffer);
- if (count != 1 || !is_boot0_magic(buffer + 1))
return false;
- /* Read the rest of the SPL now we know it's halfway sane. */
- spl_size = buffer[4];
- count = blk_dread(bd, 1, DIV_ROUND_UP(spl_size, bd->blksz) - 1,
buffer + bd->blksz / 4);
- /* Save the checksum and replace it with the "stamp value". */
- emmc_checksum = buffer[3];
- buffer[3] = 0x5f0a6c39;
- /* The checksum is a simple ignore-carry addition of all words. */
- for (count = 0; count < spl_size / 4; count++)
chksum += buffer[count];
- debug("eMMC boot part SPL checksum: stored: 0x%08x, computed: 0x%08x\n",
emmc_checksum, chksum);
- return emmc_checksum == chksum;
+}
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device) +{
- static u32 result = ~0;
- if (result != ~0)
return result;
- result = MMCSD_MODE_RAW;
- if (!IS_SD(mmc) && IS_ENABLED(CONFIG_SUPPORT_EMMC_BOOT)) {
if (sunxi_valid_emmc_boot(mmc))
result = MMCSD_MODE_EMMCBOOT;
else
mmc_switch_part(mmc, 0);
- }
- debug("%s(): %s part\n", __func__,
result == MMCSD_MODE_RAW ? "user" : "boot");
- return result;
+}
void board_init_f(ulong dummy) { spl_init();

Now that the SPL can safely detect whether it was loaded from an eMMC boot partition or the normal user data partition, let's enable this feature on some boards that feature eMMC storage.
That covers the boards where I could test this on, and allows the same build to be written to an SD card, eMMC user partition, eMMC boot partition, or into SPI NOR flash.
Signed-off-by: Andre Przywara andre.przywara@arm.com ---
(resent to also include forgotten U-Boot list)
configs/bananapi_m64_defconfig | 1 + configs/emlid_neutis_n5_devboard_defconfig | 1 + configs/pine64-lts_defconfig | 1 + configs/pine_h64_defconfig | 1 + 4 files changed, 4 insertions(+)
diff --git a/configs/bananapi_m64_defconfig b/configs/bananapi_m64_defconfig index 292044d7b8b..0c0117e3c14 100644 --- a/configs/bananapi_m64_defconfig +++ b/configs/bananapi_m64_defconfig @@ -6,6 +6,7 @@ CONFIG_MACH_SUN50I=y CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y CONFIG_MMC0_CD_PIN="PH13" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SUPPORT_EMMC_BOOT=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y CONFIG_USB_EHCI_HCD=y diff --git a/configs/emlid_neutis_n5_devboard_defconfig b/configs/emlid_neutis_n5_devboard_defconfig index e2d3b1397e2..586f0f031aa 100644 --- a/configs/emlid_neutis_n5_devboard_defconfig +++ b/configs/emlid_neutis_n5_devboard_defconfig @@ -7,4 +7,5 @@ CONFIG_DRAM_CLK=408 CONFIG_DRAM_ZQ=3881977 # CONFIG_DRAM_ODT_EN is not set CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SUPPORT_EMMC_BOOT=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set diff --git a/configs/pine64-lts_defconfig b/configs/pine64-lts_defconfig index 6209e68e2d1..b3727009d73 100644 --- a/configs/pine64-lts_defconfig +++ b/configs/pine64-lts_defconfig @@ -8,6 +8,7 @@ CONFIG_DRAM_CLK=552 CONFIG_DRAM_ZQ=3881949 CONFIG_MMC0_CD_PIN="" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_SPL_SPI_SUNXI=y # CONFIG_SYS_MALLOC_CLEAR_ON_INIT is not set CONFIG_SUN8I_EMAC=y diff --git a/configs/pine_h64_defconfig b/configs/pine_h64_defconfig index 4e621db0c03..75f49fc81d1 100644 --- a/configs/pine_h64_defconfig +++ b/configs/pine_h64_defconfig @@ -7,6 +7,7 @@ CONFIG_SUNXI_DRAM_H6_LPDDR3=y CONFIG_MACPWR="PC16" CONFIG_MMC0_CD_PIN="PF6" CONFIG_MMC_SUNXI_SLOT_EXTRA=2 +CONFIG_SUPPORT_EMMC_BOOT=y CONFIG_USB3_VBUS_PIN="PL5" CONFIG_SPL_SPI_SUNXI=y # CONFIG_PSCI_RESET is not set

On Mon, 12 Jul 2021 11:06:48 +0100 Andre Przywara andre.przywara@arm.com wrote:
(resent to also include forgotten U-Boot list)
The Allwinner BootROM supports loading the SPL from eMMC boot partitions, but so far the SPL support for this case was a bit lacking, as it was a compile time decision, and even required a manual config change. This actually got accidentally fixed in v2021.04 (c0b417b2f1a3 "sunxi: support loading with SPL > 32KB"), but still some gaps remain: When just enabling an eMMC boot partition, but having no bootable code there, the BROM will just continue with booting from the user data partition, but U-Boot does not detect this and will try to load U-Boot proper from that boot partition.
This series fixes this, by replicating the BROMs decisions in the SPL, to decide on the partition to continue loading from.
Patch 1/3 prepares a generic function to take an extra argument, while patch 2/3 implements the actual algorithm. As this now allows to always have eMMC boot support on, patch 3/3 just activates this for a number of boards I could test it on.
I'd like to encourage other people to test this on boards with eMMC, to enable this in the respective defconfig, maybe even centrally.
Some more details on eMMC boot partition support and how to install U-Boot in there can be found in the linux-sunxi Wiki: http://linux-sunxi.org/Bootable_eMMC
Rebased and updated patch 1/3, then merged into sunxi/master, which landed in mainline already.
Thanks, Andre
Cheers, Andre
Changelog v1 .. v2:
- drop patch 1/5 (bugfix): already merged
- drop patch 3/5, as it was obsoleted by c0b417b2f1a3
- remove check for 32KB SPL limit (not applicable for H6 and beyond)
- remove extra sector offset adjustment (obsoleted by c0b417b2f1a3)
Andre Przywara (3): spl: mmc: extend spl_mmc_boot_mode() to take mmc argument sunxi: eMMC: Improve automatic boot source detection sunxi: defconfig: enable eMMC boot partition support
arch/arm/mach-imx/spl.c | 2 +- arch/arm/mach-k3/am6_init.c | 2 +- arch/arm/mach-k3/j721e_init.c | 2 +- arch/arm/mach-omap2/boot-common.c | 2 +- arch/arm/mach-rockchip/spl.c | 2 +- arch/arm/mach-socfpga/spl_a10.c | 2 +- arch/arm/mach-socfpga/spl_gen5.c | 2 +- arch/arm/mach-stm32mp/spl.c | 2 +- arch/arm/mach-sunxi/board.c | 80 ++++++++++++++++++++++ arch/arm/mach-uniphier/mmc-boot-mode.c | 5 +- common/spl/spl_mmc.c | 4 +- configs/bananapi_m64_defconfig | 1 + configs/emlid_neutis_n5_devboard_defconfig | 1 + configs/pine64-lts_defconfig | 1 + configs/pine_h64_defconfig | 1 + include/spl.h | 3 +- 16 files changed, 97 insertions(+), 15 deletions(-)
participants (3)
-
Andre Przywara
-
Jaehoon Chung
-
Simon Glass