Search code examples
u-bootimx6

U-boot Script Bad Header CRC


I have a "flashing" script being loaded into a Uboot, on an iMX6, from a host PC via sdp. The script has been run through mkimage, so it has an image header. Here's the mkimage command:

mkimage -A arm -O linux -T script -C none -a 0 -e 0 -n "U-Boot script" -d $files_dir$flash_txt $files_dir$flash_scr

I can parse the header with binwalk:

$ binwalk -B flash.scr

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             uImage header, header size: 64 bytes, header CRC: 0x315E128A, created: 2021-09-13 14:52:46, image size: 2406 bytes, Data Address: 0x0, Entry Point: 0x0, data CRC: 0x7B909BAE, OS: Linux, CPU: ARM, image type: Script file, compression type: none, image name: "U-Boot script"

It fails to execute, with the uboot saying "Bad Header CRC". So, I'm trying to at least force it to work, while on the device, by correcting the CRC on the spot, using the built-in crc32 tool. It still won't work.

Here are uboot commands I'm trying (after the script is already in memory at address 0x10100000):

# display the script bytes
md 0x10100000

# reset the crc
mw 0x10100004 0x0 1

# verify the change
md 0x10100000

# recalc the crc
crc32 0x10100000 64

# write the crc
#mw 0x10100004 <VALUE> 1

# example:
mw 0x10100004 0x3a833d99 1

# run the script
source 0x10100000

When I do this, I still get the dreaded "Bad Header CRC" error!

I've found numerous sources which reveal / indicate which bytes contain the "header CRC". I've seen multiple sources saying that one needs to "reset" the checksum prior to attempting to compute it. This all looks to be valid logic. What am I doing wrong?


Update

The CORRECT uboot input / results

Per help from sawdust

...
Bad header crc
CTRL+C - Operation aborted.
SDP ended
uboot# md 0x10100000
10100000: 56190527 229a2e76 f4f64161 66090000    '..Vv.."aA.....f
10100010: 00000000 00000000 0533a380 00060205    ..........3.....
10100020: 6f422d55 7320746f 70697263 00000074    U-Boot script...
10100030: 00000000 00000000 00000000 00000000    ................
10100040: 5e090000 00000000 20766e65 61666564    ...^....env defa
10100050: 20746c75 2d20662d 65730a61 766e6574    ult -f -a.setenv
10100060: 7a697320 79625f65 685f6574 725f7865     size_byte_hex_r
10100070: 66746f6f 33352073 35453937 65730a41    ootfs 5379E5A.se
10100080: 766e6574 7a697320 6c625f65 5f6b636f    tenv size_block_
10100090: 5f786568 746f6f72 32207366 30444239    hex_rootfs 29BD0
101000a0: 7465730a 20766e65 657a6973 7479625f    .setenv size_byt
101000b0: 65685f65 62755f78 20746f6f 30433337    e_hex_uboot 73C0
101000c0: 65730a30 766e6574 7a697320 6c625f65    0.setenv size_bl
101000d0: 5f6b636f 5f786568 6f6f6275 39332074    ock_hex_uboot 39
101000e0: 730a0a45 6e657465 6f722076 7366746f    E..setenv rootfs
101000f0: 6c5f615f 6c656261 6f722220 7366746f    _a_label "rootfs
uboot# mw 0x10100004 0x0 1
uboot# crc32 0x10100000 0x40 0x10100004
crc32 for 10100000 ... 1010003f ==> 94926f83
uboot# md 0x10100000                   
10100000: 56190527 836f9294 f4f64161 66090000    '..V..o.aA.....f
10100010: 00000000 00000000 0533a380 00060205    ..........3.....
10100020: 6f422d55 7320746f 70697263 00000074    U-Boot script...
10100030: 00000000 00000000 00000000 00000000    ................
10100040: 5e090000 00000000 20766e65 61666564    ...^....env defa
10100050: 20746c75 2d20662d 65730a61 766e6574    ult -f -a.setenv
10100060: 7a697320 79625f65 685f6574 725f7865     size_byte_hex_r
10100070: 66746f6f 33352073 35453937 65730a41    ootfs 5379E5A.se
10100080: 766e6574 7a697320 6c625f65 5f6b636f    tenv size_block_
10100090: 5f786568 746f6f72 32207366 30444239    hex_rootfs 29BD0
101000a0: 7465730a 20766e65 657a6973 7479625f    .setenv size_byt
101000b0: 65685f65 62755f78 20746f6f 30433337    e_hex_uboot 73C0
101000c0: 65730a30 766e6574 7a697320 6c625f65    0.setenv size_bl
101000d0: 5f6b636f 5f786568 6f6f6275 39332074    ock_hex_uboot 39
101000e0: 730a0a45 6e657465 6f722076 7366746f    E..setenv rootfs
101000f0: 6c5f615f 6c656261 6f722220 7366746f    _a_label "rootfs
uboot# source 0x10100000    
## Executing script at 10100000

... SUCCESS!!!!!

Pre-upload PC Patch

After running mkimage, I fixed the CRC in this way. This could be written in a more elegant fashion I'm sure - but this proved functional at least:

# The mkimage "header CRC" is invalid for some reason, so it needs to be corrected!

$files_dir=files/
$flash_scr=flash.scr
$flash_hdr=flash.hdr

chmod 644 $files_dir$flash_scr

# zero the current crc, and copy the 64 byte header to a separate file
printf '\x00\x00\x00\x00' | dd of=$files_dir$flash_scr bs=1 seek=4 count=4 conv=notrunc
head -c 64 $files_dir$flash_scr > $files_dir$flash_hdr

# calculate the correct crc
crc=$(crc32 $files_dir$flash_hdr)
b1=$(echo $crc | cut -c 1,2)
b2=$(echo $crc | cut -c 3,4)
b3=$(echo $crc | cut -c 5,6)
b4=$(echo $crc | cut -c 7,8)

# update the script, with the correct value
printf "\x${b1}\x${b2}\x${b3}\x${b4}" | dd of=$files_dir$flash_scr bs=1 seek=4 count=4 conv=notrunc

# clean up 
rm $files_dir$flash_hdr

The fundamental flaw in mkimage

Ideally, mkimage would do its job correctly in the first place. Why it is malfunctioning is still a mystery...


Solution

  • What am I doing wrong?

    U-Boot almost always assumes hexadecimal values for command arguments, so using the 0x... prefix is actually superfluous. AFAIK there is no way to input decimal values.

    # recalc the crc
    crc32 0x10100000 64
    

    So when you specify the length of the header in the crc32 command as 64, you have specified an incorrect (header) length of 0x64 or 100 decimal.
    Reviewing the U-Boot command response confirms your mistake:

    => crc32 0x10100000 64                                                          
    CRC32 for 10100000 ... 10100063 ==> 9988c6ca
    

    The address range of 0x10100000 through 0x10100063 is a span of 100 bytes.


    Addendum (with revisions)

    When you install the calculated value, you are probably introducing an endian (byte order) issue.

    # write the crc
    #mw 0x10100004 <VALUE> 1
    

    The calculated CRC32 value probably needs to specified in reverse byte-order (assuming little-endian mode for the i.MX6) in the mw command.


    For example, the mkimage command installs an original header CRC32 value of d8bc0e3a 3a0ebcd8 in the second word (in little-endian order):

    => md 20000000                                                                  
    20000000: 56190527 3a0ebcd8 a0284161 1f000000    '..V...:aA(.....               
    20000010: ...
    
    => imi 20000000                                                                 
                                                                                    
    ## Checking Image at 20000000 ...                                               
       Legacy image found                                                           
       Image Name:   U-Boot script                                                  
       Image Type:   PowerPC Linux Script (uncompressed)                            
       Data Size:    ...                                            
    

    After zeroing the second word, the CRC32 command produces the (same) value (as expected) (but as a 4-byte string rather than an integer):

    => crc32 20000000 40                                                            
    crc32 for 20000000 ... 2000003f ==> d8bc0e3a                                    
    

    If you install this value literally using the mw command, then the byte order is not what is required for a 32-bit word value.

    => mw 20000004 d8bc0e3a                                                         
    => md 20000000                                                                  
    20000000: 56190527 d8bc0e3a a0284161 1f000000    '..V:...aA(.....               
    20000010: ...
    
    => imi 20000000                                                                 
                                                                            
    ## Checking Image at 20000000 ...                                               
       Legacy image found                                                           
       Bad Header Checksum                                                          
    => 
    

    The mw command treats the value to write as a byte string rather than an integer value an integer value, and therefore does not perform a byte reordering (even though this is a little-endian CPU) a byte reordering of the CRC32 value is required.


    Addendum 2

    Slight corrections to above in regards to which U-Boot commands are displaying results in little-endian mode.

    The md command is displaying word values assuming the stored value is in little-endian mode.
    To see the true byte order in memory, use the md.b command.

    => md 20000000 4                                                                
    20000000: 56190527 3a0ebcd8 a0284161 1f000000    '..V...:aA(.....               
    
    => md.b 20000000 10                                                             
    20000000: 27 05 19 56 d8 bc 0e 3a 61 41 28 a0 00 00 00 1f    '..V...:aA(.....   
    =>
    

    Therefore, the mkimage command installs an original header CRC32 value of 3a0ebcd8 in the second word (in little-endian order).

    The CRC32 command produces the 4-byte value as a byte string (i.e. already in little-endian order).

    The mw command is aware that this is a little-endian CPU, and does treats the value to write as an integer value.
    Since the CRC32 result is a byte string (rather than an integer value), these bytes must be reordered for input using the mw command.
    Got that?


    One possible way to avoid this endian confusion would be to use the automatic write feature of the CRC32 command.
    Append the address of the header CRC to your crc32 command, and the calculated value will be stored in the correct order for you, e.g.

    crc32 0x10100000 0x40 0x10100004
    

    Still wonder why you don't have a good CRC32 in the first place using mkimage, and why you had to resort to this hack.