embeddedu-bootimx6

iMX6 get U-Boot to temporarily boot another U-Boot


First some background:

We have the following setup in our iMX6-based embedded system. There are two U-Boot partitions and two system (Linux) partitions. Currently we use only the first U-Boot partition and it uses a standard method for selecting, running and (if need be) rolling back the system partitions.

We are now looking into a similar scheme for upgrading U-Boot itself (this will happen very rarely but we do want the ability to do this without having to return the devices to base).

However, this is more fraught with danger because, once you tell the iMX6 device to boot from the alternate U-Boot partition, that's it - there's no U-Boot/watchdog combo that will revert to the previous one if boot fails, so a bad update runs a serious risk of bricking the device until we can return it to base for repair (a costly option which is why we're trying to mitigate it as much as possible).

The method chosen is a two-step U-Boot install procedure, consisting of 'write' and 'activate'. It relies on our ability to successfully figure out which U-Boot partition will be run if the device reboots (the selected one) and which is currently being run (the booted one). We've got this bit sorted out already.

But the bit that we're missing is the ability for UBoot to transfer control to the other UBoot partition under some circumstances. We got it doing different actions based solely on the UBoot environment as follows:

First, mmcboot has a prefix added so that it checks for the control transfer, specifically it's set to run ub_xfer_chk ; <original content of mmcboot>.

Secondly, we have a variable ub_xfer_flag normally set to 0.

Thirdly, we have the checking function ub_xfer_chk, defined as:

if test ${ub_xfer_flag} -eq 1 ; then
    echo Soft-booting other UBoot...
    setenv ub_xfer_flag 0
    saveenv
    weave_magic
fi

The weave_magic code is where we are having trouble :-) The idea is that this will load the other UBoot partition into memory (at our CONFIS_SYS_TEXT_BASE of 0x1780000) and execute it as if the actual device had done it.

We've tested the meat of this solution by using reset in place of weave_magic and it successfully restarts the device once, so we're certain we can make it safe.


My specific question then is :how can I convince U-Boot to load a second copy from another partition and run it?

The two UBoot partitions live in the /dev/mmcblk3boot0 and /dev/mmcblk3boot1 devices accessible from the system partition and are 2M files, including the 1K lead-in header and a fair bit of padding at the end.


Update:

We have actually had some success and managed to load an IMX image from the boot partition with the command:

ext4load mmc ${mmcdev}:${mmcpart} 0x17800000 ${bootdir}/u-boot.imx

but, when trying to execute it with:

go 0x17800000

we get an illegal instruction and immediate reboot:

pc : [<17800070>]          lr : [<4ff83c64>]
sp : 4f579ac0  ip : 00000030     fp : 4f57be58
r10: 00000002  r9 : 4f579efc     r8 : 4ffbe2b0
r7 : 4f57be68  r6 : 17800000     r5 : fffff200  r4 : 000002cc
r3 : 17800000  r2 : 4f57be6c     r1 : 4f57be6c  r0 : 00000000
Flags: nZCv  IRQs off  FIQs off  Mode SVC_32
Resetting CPU ...

So I'm guessing that's not executable code at the start of that file. Any ideas on where to go from here?


Solution

  • The actual code in the IMX file is not at the beginning. You can discover this fact by using the excellent on-line disassembler with armv5 big-endian no-thumb architecture to figure out that the bytes at the beginning frequently give you invalid and/or not-very-sensible code:

    ldtdmi  a1, [a1], -a2             ; <UNPREDICTABLE>
    strne   a1, [a1, a1]
    andeq   a1, a1, a1
    ldrbne  pc, [pc, -ip, lsr #8]!    ; <UNPREDICTABLE>
    

    In any case, the data at the start of the IMX file is known to be header information (the d1 at the start is a "magic" marker indicating the IVT header and there should also be a DCD block after that. However, even beyond the IVT and DCD blocks (based on their purported lengths in the header fields), the code is not sensible.

    However, there's viable information at offset 0xc00 following a large chunk of 0x00 bytes:

    00000be0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000bf0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
    00000c00: 0f00 00ea 14f0 9fe5 14f0 9fe5 14f0 9fe5  ................
    00000c10: 14f0 9fe5 14f0 9fe5 14f0 9fe5 14f0 9fe5  ................
    

    Putting the hex bytes at offset 0xc00 into the disassembler, and adjusting for areas that are skipped by branches, shows both valid and sane ARM code.

    And, indeed, stripping the IMX file with:

    dd if=u-boot.imx bs=1 skip=3072 of=ub-at-c00.imx
    

    should give you a file you can boot with:

    ext4load mmc ${mmcdev}:${mmcpart} 0x17800000 ${bootdir}/ub-at-c00.imx
    go 0x17800000
    

    When we do this, it outputs:

    U-Boot 2014.04 (Nov 07 2018 - 19:05:32)
    CPU:   Freescale i.MX6Q rev1.5 at 792 MHz
    CPU:   Temperature 32 C, calibration data: 0x5764e169
    Reset cause: unknown reset
    Board: DTI BRD0208 (Spitfire I) 05/01/2017
    I2C:   ready
    DRAM:  1 GiB
    

    We know this is the newer UBoot simply because the normal one we're using outputs an October date rather than a November one.

    Unfotunately, it hangs at that point, with the watchdog timer eventually kicking in and rebooting back to the original UBoot but I suspect that has to do with UBoot not liking the current state of the device (i.e., it doesn't like initialising it twice).

    So we'll have to figure out how to convince it to do so but at least we've gotten it booting another copy of itself, which is what the question was about.