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 <[email protected]>
> ---
> 
> (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();
> 

Reply via email to