Tutorial: CRC32 Checksum with the KBOOT Bootloader

In “Flash-Resident USB-HID Bootloader with the NXP Kinetis K22 Microcontroller” I presented how I’m using the tinyK22 (or FRDM-K22F) with a flash resident USB HID bootloader. To make sure that the loaded application is not corrupted somehow, it is important to verify it with a Cyclic redundancy Checksum (CRC). The NXP KBOOT Bootloader can verify such a CRC, but how to generate one and how to use it is not really obvious (at least to me), so this article explains how to generate that CRC.

CRC Values for KBOOT

CRC Values for KBOOT

Outline

This article explains how to calculate the CRC32 for KBOOT, both from binary and S-Record files and how to insert the values into the BCA (Bootloader Configuration Area). Additionally it gives tips about debugging the bootloaded application.

Bootloader Configuration Area (BCA)

The bootloader is configured with a BCA (Bootloader Configuration Area). As explained in “Getting Started: ROM Bootloader on the NXP FRDM-KL03Z Board“, it configures the ROM bootloader. That ROM bootloader for the KL03Z does *not* implement the checksum feature :-(, so I would have to build a flash-flash resident bootloader as explained in “Flash-Resident USB-HID Bootloader with the NXP Kinetis K22 Microcontroller“.

For the flash-resident bootloader, the BCA has part of the application to be loaded as well, and located at offset 0x3C0 right after the vector table located at offset 0x0000.

So if the application gets loaded at 0xC000 (as used in this tutorial), the BCA is located at 0xC3C0. That BCA can be implemented as a struct in C as in “Getting Started: ROM Bootloader on the NXP FRDM-KL03Z Board” or it could be part of the assembly code e.g. in the startup file as in this tutorial:

Bootloader Configuration Area

Bootloader Configuration Area

The CRC start address, size in bytes and the expected CRC values are just behind the tag bytes:

  • 0x3c0 + 0x4: [04:07] crcStartAddress
  • 0x3c0 + 0x8: [08:0b] crcByteCount
  • 0x3c0 + 0xC: [0c:0f] crcExpectedValue

The question is: how to calculate that expected CRC value?

CRC Value with KinetisFlashTool

On possibility to calculate the expected CRC values is to use the Kinetis Flash Tool. In the tool, browse for the binary (.bin) file (which is the only supported format 😦 ) and press the Config button:

Config in KinetisFlashTool

Config in KinetisFlashTool

In the dialog, enable the CRC Check with the image address:

Crc Check Enabled

Crc Check Enabled

Press OK, and the values will be shown in the BCA HEX field:

CRC value

CRC value

But this is a very painful process, and only works with .bin (Binary) files.

CRC with S-Record Files

A better approach is using the srec_cat utility (CRC Checksum Generation with ‘SRecord’ Tools for GNU and Eclipse):

srec_cat tinyK22_KBOOT_led_demo.srec -fill 0xff 0xc000 0xd000 -crop 0xc400 0xD000 -Bit_Reverse -CRC32LE 0x1000 -Bit_Reverse -XOR 0xff -crop 0x1000 0x1004 -Output - -hex_dump

💡 Kudos go to Robert Poor (see https://community.nxp.com/thread/462397) who has found out the correct command line to generate the CRC32 needed by KBOOT.

  • -fill 0xFF 0xC000 0xD000: fill memory from 0xC400..0xD000 with 0xff
  • -crop 0xc400 0xD000: just keep the area for the CRC calculation. This does not include the vector table and BCA itself
  • -Bit_Reverse -CRC32LE 0x1000 -Bit_Reverse -XOR 0xff: used to generate the correct CRC32 as expected by KBOOT and store it the given address
  • -crop 0x1000 0x1004 -Output -HEX_DUMP: Crop everything around the generated CRC32 and dump the output to the console.

💡 Ideally the vector table at 0xC000 and the BCA at 0xC3C0 would be included into the CRC, but KBOOT does not support this, so for simplicity I keep it excluded from the CRC calculation.

To find out the size, use the linker map file or use srec_info:

srec_info inputfile.srec

Which gives something like

Format: Motorola S-Record
Header: "tinyK22_KBOOT_led_demo.srec"
Execution Start Address: 0000C4D9
Data: C000 - CA2B

This then produces something like this:

srec_cat tinyK22_KBOOT_led_demo.srec -fill 0xff 0xc000 0xd000 -crop 0xc400 0xd000 -Bit_Reverse -CRC32LE 0x1000 -Bit_Reverse -XOR 0xff -crop 0x1000 0x1004 -Output - -hex_dump
00001000: D6 88 C6 B4             #V.F4

Means the CRC32 is 0xD688C6B4.

That value with the number of bytes and start address then can be entered into the sources like this:

Application CRC Values

Application CRC Values

However, this would require a recompilation of the application. So an easier way is to directly add the CRC32 to the .srec file:

srec_cat -generate 0xc3cc 0xc3d0 -constant-l-e 0xD688C6B4 4 tinyK22_KBOOT_led_demo.srec -exclude 0xc3cc 0xc3d0 -Output_Block_Size 16 -output newWithCRC32.srec

Then load the new file with the KinetisFlashTool:

Updating with SRecord File

Updating with SRecord File

The CRC check can be debugged in the Bootloader inside the function is_application_crc_check_pass() inside bl_app_crc_check.c:

Code in Bootloader to Check CRC

Code in Bootloader to Check CRC

CRC32 with Binary Files

Here is how to show the CRC fields of the BCA inside a binary file:

srec_cat tinyK22_KBOOT_led_demo.bin -binary -crop 0x3c0 0x3d0 -output - -hex_dump

This gives:

000003C0: 6B 63 66 67 00 C4 00 00 00 0C 00 00 B4 C6 88 D6 #kcfg.D......4F.V
Inspecting values in binary file

Inspecting values in binary file

To calculate the CRC32 value from a Binary, I use the following command line:

srec_cat tinyK22_KBOOT_led_demo.bin -binary -fill 0xff 0x0000 0x1000 -crop 0x0400 0x1000 -Bit_Reverse -CRC32LE 0x1000 -Bit_Reverse -XOR 0xff -crop 0x1000 0x1004 -Output - -hex_dump
00001000: 52 04 06 B8 #.c^B

It first fills the memory from 0x0000 to 0x1000 with the 0xff filler, then cuts out the area between 0x400 to 0x1000, calculates the checksum and issues it on the console.

Then add it to a binary. Below is the command line to insert that CRC32 value into the binary file at offset 0x3C4:

srec_cat -generate 0x3cc 0x3d0 -constant-b-e 0x520406B8 4 tinyK22_KBOOT_led_demo.bin -binary -exclude 0x3cc 0x3d0 -output newWithCRC32.bin -Binary

CRC Checks and Debugging with ‘Software’ Breakpoints

There is one potential problem with the CRC calculation done by the bootloader: If your debugger probe modifies the flash memory for setting breakpoints (e.g. Segger J-Link can do this to get ‘unlimited’ breakpoints (see “Software and Hardware Breakpoints“, then this is changing the code, and as such invalidates the CRC checksum/check. So if loading and CRC-checking application code, make sure you don’t have any breakpoints set in that area.

Automating

This article describes the manual steps to determine the CRC value and than add it to the application. For automating things with a Python script, see the work of Robert Poor at https://github.com/rdpoor/srec-crc.

Summary

Adding a CRC32 check in the bootloader makes the process more reliable, as it can detect bit in the loaded image. Knowing the correct polynomial and CRC calculation way is not always straight forward. Kudos to Robert Poor who has found out how to create the CRC32 for KBOOT. I’m now able to generate the checksum both from S19 and binary files. I’m doing things semi-automated (calculate the CRC and insert it into the file), and if I find time I plan to automate it further. Until then, have a look how Robert is automating it (see previous section).

The projects used in this tutorial are available on GitHub (see the links at the end of this article).

Happy Checking 🙂

Links

 

20 thoughts on “Tutorial: CRC32 Checksum with the KBOOT Bootloader

  1. Pingback: Converting Binary Files to Intel Hex Format with the SRecord Tool | MCU on Eclipse

  2. Hi Erich,
    I have been following your articles on KBOOT and and the KL03.
    I am using the KL82, which supports CRC32 check in ROM bootloader. I have managed to get the
    crcStartAddress
    crcByteCount
    crcExpectedValue
    of the application in the BCA (0x03C0).
    I am also using the ROM bootloader to reflash my application.

    Question:
    Is there a way to invoke a KBOOT CRC check other than by issuing a RESET to the microcontroller (via the reset pin; at this point the KL82 will enter KBOOT again) and querying via “get-property 8” command the status of the CRC32 integrity check.

    Thanks!
    Radu

    Like

    • The KL03 (and I think as well the KL82) ROM bootloader is closed source (no sources available).
      I did not see a specific bootloader entry point for the CRC itself, but you can enter the bootloader anytime you want from the application (e.g. see how this is done for the ROM bootloader: https://mcuoneclipse.com/2017/07/12/getting-started-rom-bootloader-on-the-nxp-frdm-kl03z-board/), see RunRomBootloader().
      The other way would be to read the CRC info from the image itself and run the same function as in the bootloader. The polynom should be the same I think. You would have to copy that code.
      I hope this helps,
      Erich

      Like

      • Hi Erich,
        I think there is a misunderstanding. Let me explain what I am doing. The KL82 comes with the flash erased and unsecured from factory. So upon power up it gets into KBOOT. (PS: blhost has been ported to Linux, and I’m talking via UART)

        root@mx6:~# blhost -p /dev/ttymxc2,56700 — get-property 1
        Ping responded in 1 attempt(s)
        Inject command ‘get-property’
        Response status = 0 (0x0) Success.
        Response word 1 = 1258358017 (0x4b010501)
        Current Version = K1.5.1
        root@mx6:~# blhost -p /dev/ttymxc2,57600 -n — read-memory 0x3c0 64
        Inject command ‘read-memory’
        Successful response to command ‘read-memory’
        ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
        ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
        (1/1)100% Completed!
        Successful generic response to command ‘read-memory’
        Response status = 0 (0x0) Success.
        Response word 1 = 64 (0x40)
        Read 64 of 64 bytes.

        I have added the CRC32 to the BCA area of my firmware, using the Kinetis Flash Tool, and uploaded the firmware via KBOOT.

        root@mx6:/opt/firmware# blhost -p /dev/ttymxc2,56700 — write-memory 0x0000 FW_release.bin
        Ping responded in 1 attempt(s)
        Inject command ‘write-memory’
        Preparing to send 28784 (0x7070) bytes to the target.
        Successful generic response to command ‘write-memory’
        (1/1)100% Completed!
        Successful generic response to command ‘write-memory’
        Response status = 0 (0x0) Success.
        Wrote 28784 of 28784 bytes.

        One can ask for the status code of the last CRC check operation:

        root@mx6:/opt/firmware# blhost -p /dev/ttymxc2,56700 — get-property 8
        Ping responded in 1 attempt(s)
        Inject command ‘get-property’
        Response status = 0 (0x0) Success.
        Response word 1 = 10403 (0x28a3)
        CRC Check Status = kStatus_AppCrcCheckInvalid

        Which is the correct answer, because there was no CRC32 the BCA sector when the KL82 was powered on.

        However, now, when I reset the chip, via the RESET pin (not issuing a RUN command) the chip will go into KBOOT, and will remian there, as I do not have a timeout set. Running the same –get-property command as above I get:

        root@mx6:/opt/firmware# blhost -p /dev/ttymxc2,56700 — get-property 8
        Ping responded in 1 attempt(s)
        Inject command ‘get-property’
        Response status = 0 (0x0) Success.
        Response word 1 = 10400 (0x28a0)
        CRC Check Status = kStatus_AppCrcCheckPassed

        Which is what I was expecting. My question is, if there is a better way to “restart” the KBOOT?

        Thanks!

        Like

        • Hi Radu,
          maybe I’m still confused. If you don’t have a timeout set for the bootloader, then it will check the CRC32 and if it is valid directly start the application? I’m not sure why it keeps ‘hanging’ in your case.

          Like

  3. Hi Erich,
    Thanks for the detailed explanation. 2 Questions:

    1. When writing:
    -crop 0xc400 0xD000
    How do you know what is the last index of data? (in this case: 0xD000)?
    I can’t seem to get the same CRC value as I get via the KinetisFlashTool, and I’m guessing the problem is in the crop data for the crc calculation.

    2. I’m working with bin file (using blhost). You did not mention it, but in such case, the byteCount and the startAddress should be hardcoded to the startup_MK64F12.S file, right? because the srec_cat you showed only referes to the crc value.
    Thanks,
    Roy

    Like

    • Hi Roy,
      1. The address range is from the start address (0xc400, inclusive) up to 0xCFFF (0xD000 is *not* included). So 0xCFFF is the last byte.
      2. For loading bin (binary files), as the binary file format is just a stream of data, you need to tell the bootloader where to load that data/code with an offset. But this should not have anything to do with the startup code itself?
      I hope this helps,
      Erich

      Like

  4. Pingback: Generating Intel Hex Files with a given Length using srec_cat | MCU on Eclipse

    • Wow, a script! And I had used some trial-and-error. I ended up to check the srec_cat source code to get some ideas what parameters to use.
      I noticed some threads here on a similar topic:
      https://www.avrfreaks.net/forum/project-has-both-app-and-bootloader
      https://www.mikrocontroller.net/topic/270001
      There is an effect in which bit order the data is handled. Additionally there the thing with ‘augementing’ (adding a stuff byte, see -No-AUG), not sure if you have tried that already?

      Like

      • My little bash script tests the following parameter combinations:

        (-CRC16_Big_Endian|-CRC16_Little_Endian) (|-xor 0xff) (|-Bit_Reverse) (-poly ibm|-poly ansi|-poly cciit|-poly t10-dif|-poly dnp|-poly dect|0x8005|0xA001|0x4003|0xC002) (-Most_To_Least|-Least_To_Most) (|-CCITT) (|-XMODEM) (|-BROKEN) (-AUGment|-No-AUGment) (|-Bit_Reverse) (|-xor 0xff)

        I also tried to append two 0x00’s as suggested, but with no luck.

        Liked by 1 person

      • I see… Modbus is mentioned but it produce the wrong number for input data ‘0x03’. The most obvious command line for me is:

        srec_cat -generate 0x0002 0x0003 -const-b-e 0x03 1 -o – | srec_cat – -crc16_big_endian 0x1002 -poly ansi -ccitt

        return 0xFCFF whereas Lammert’s site gives 0x41FF

        Anyhow, I can now reduce the number of combination to 3840: -CCITT, -XMODEM and -BROKEN are mutually exclusive:

        (|-xor 0xff) (|-Bit_Reverse) (-CRC16_Big_Endian|-CRC16_Little_Endian) (-poly ibm|-poly ansi|-poly cciit|-poly t10-dif|-poly dnp|-poly dect|0x8005|0xA001|0x4003|0xC002) (-Most_To_Least|-Least_To_Most) (-CCITT|-XMODEM|-BROKEN) (-AUGment|-No-AUGment) (|-Bit_Reverse) (|-xor 0xff)

        Like

  5. Why doesn’t the CRC calculated using srec_cat match what was calculated with the Flash Tool? It looks likes the addresses are different. Shouldn’t they match. Very confusing.

    Liked by 1 person

    • The reason is the different range used for the CRC calculation: the flash tool uses the exact bytes of the application, while with the S-Record tool I do padding up to the next block.

      Like

  6. Also, the version of the NXP bootloader I have does skip the expected value of the CRC in its CRC calculation if the expected value memory address falls within in the memory region [CRC start address, CRC start address + byte count] so it looks like it does support the vector table and the BCA in the CRC calculation. One would, however, have to include the start address and byte count in the CRC calculation. The code in the bootloader looks like this:

    // Run CRC, Considering skip crcExpectedValue address
    uint32_t bypassStartAddress = crcHeaderStart + ((uint32_t)&header->crcExpectedValue – (uint32_t)&header->tag);
    uint32_t bypassEndAddress = bypassStartAddress + sizeof(header->crcExpectedValue);

    if ((header->crcStartAddress >= bypassEndAddress) ||
    (header->crcStartAddress + header->crcByteCount crcStartAddress, header->crcByteCount);
    }
    else
    {
    // Assume that crcExpectedValue address (4 byte) resides in crc addresses completely
    crc32_update(&crcInfo, (uint8_t *)header->crcStartAddress, bypassStartAddress – header->crcStartAddress);
    crc32_update(&crcInfo, (uint8_t *)bypassEndAddress,
    header->crcStartAddress + header->crcByteCount – bypassEndAddress);
    }

    Liked by 1 person

      • It’s the bootloader downloaded with the SDK for both the FRDM-K22F and the FRDM-K64F. The CRC checking code that does the address manipulation is in

        source/bootloader/src/bl_app_crc_check.c

        I tested it this morning and it works. It allows me to CRC check everything (interrupt vector table, BCA, code) except the expected CRC value in the BCA. It automatically skips the four bytes with the expected value.

        Liked by 1 person

        • I should have mentioned that the SDK version was 2.x (SDK_2.x_FRDM-K22F and SDK_2.x_FRDM-K64F). Manifest version says 3.5.0 and SDK version says 2.6.0 (228 2019-06-14).

          Liked by 1 person

        • Hi James,
          thanks, I have to check what actual version I have currently. That would be indeed good news, thanks for letting me know about this!

          Like

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.