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?
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.