Monday, October 24, 2011

Cortex-M3 Exception Vector Checksum

The 7th entry of the Cortex-M3 exception vector is reserved for the 2's complement of the checksum of the exception vector. At run time the bootloader computes the checksum again and adds it to the one stored in the exception vector, if the result equals zero it starts executing the user code.

The checksum is usually computed by the software that flashes the binary like FashMagic or openocd, if you're using openocd like me, you may see this message every time you flash a binary:
Warn : Verification will fail since checksum in image (0x00000000) to be 
written to flash is different from calculated vector checksum (0xeffc59e6).
Warn : To remove this warning modify build tools on developer PC to inject 
correct LPC vector checksum.
The code will run normally, because openocd computes the checksum for you, but it's just too annoying, so I wrote the following small utility to compute the checksum and inject it into the binary, it's called from the Makefile before running openocd to flash the binary:
#include <stdio.h>
int main(int argc, char **argv)
{
    if (argc == 1) {
        printf("usage: cm3_checksum <bin>\n");
        return 1;
    }

    FILE *file;
    if ((file = fopen(argv[1], "r+")) == NULL) {    
        return 1;
    }

    /* The checksum of the exception vector */
    unsigned int i, n, checksum=0;
    for (i=0; i<7; i++) {
        fread(&n, 4, 1, file);
        checksum += n;
    }

    /* The 2's complement of the checksum */
    checksum = -checksum;
    printf("checksum: 0x%X\n", checksum);
    
    /* write back the checksum to location 7 */
    fseek(file, 0x1c, SEEK_SET);
    fwrite(&checksum, 4, 1, file);

    fclose(file);
    return 0;
}

12 comments:

  1. I've tested your program but it changes/writes nothing to the file?

    ReplyDelete
    Replies
    1. That's impossible, make sure you call the program and pass the binary file,
      $ ./cm3_checksum prog.bin

      Delete
  2. Hi, thx for answer

    I tried with makefile (all: ... checksum main.bin) and manually with console (cmd under win7 32bit):

    checksum main.bin
    checksum: 0xEFFF7598

    But the error still appears:

    Warn : Verification will fail since checksum in image (0x00000000) to be written to flash is different from calculated vector checksum (0xefff7598).
    Warn : To remove this warning modify build tools on developer PC to inject correct LPC vector checksum.

    I compared the normal file and the file with checksum injected (PN + HEX) but I cant see differences.

    ReplyDelete
    Replies
    1. I'm not sure, maybe it fails to open the file for some reason, I haven't tried it before on Windows, you should add some printf for debugging.

      Delete
  3. I works with almost empty files like test.txt <- empty. But if I fill this file with few charachters, your program dont write anything in it.

    ReplyDelete
  4. I think the problem people are seeing is that the checksum gets inserted at the end of the file.

    Add this before the fwrite and it should work:

    fseek(file, 0x1c, SEEK_SET);

    Cheers!

    ReplyDelete
    Replies
    1. That won't make any difference, the file cursor is already at the right place, if you add this before fwrite it prints 28 (because it reads 7 4-bytes words)

      printf("%ld\n", ftell(file));

      I don't think there's a problem with the code, of course it doesn't work on empty files (or files with a "few" characters ;) ) anyway it should be easy enough to fix.

      Delete
    2. No, the file cursor isn't properly placed. In fact, that change with the fseek is the right thing to do.

      Basically, opening the file with the "+" flag has two impacts:

      1) the fopen call will fail if the file doesn't exist
      2) the file will be opened for *update*. That's the part that's breaking. Depending on the libc, the *writing* file cursor (yes, there is such a thing) will either follow the read cursor (as it is the case under Linux), or will be placed at the end of the file (as it is the case under MacOS).

      You see, the definition of "update" is loose. Some POSIX implementation take it you're willing to append data at the end of the file.


      Anyway, fseek is the universal, portable solution here :-)

      Delete
    3. Thanks for taking the time to explain it.. I will update the post :)

      Delete
  5. Thank you for the code.
    I'd like you to know that there are several things that can still go wrong in this code.

    1: The size of an int is undefined. Please use uint32_t for the checksum.
    2: The endianness of a uint32_t is undefined. Please write to a uint8_t buf[4], and convert the values manually.
    3: Error checking could/should be improved.

    Here are some suggestions (they are not tested):

    #include <stdint.h>
    ...
    uint32_t n, i, checksum = 0;
    uint8_t buf[4];
    int result = 0;
    ...
    if(4 != fread(buf, 4, 1, file))
    {
    result = 1;
    break;
    }
    checksum += (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
    ...

    buf[0] = checksum >> 24;
    buf[1] = checksum >> 16;
    buf[2] = checksum >> 8;
    buf[3] = checksum;
    if(0 == result && 0 == fseek(file, 0x1c, SEEK_SET) && (4 != fwrite(buf, 4, 1, file))
    {
    result = 1;
    }
    fclose(file);
    ...
    return(result);
    ...

    ReplyDelete
  6. I was a little sloppy in previous post. Here's a short correction for the last part:


    if(0 == result)
    {
    result = (0 != fseek(file, 0x1c, SEEK_SET) || 4 != fwrite(buf, 4, 1, file)) ? 1 : 0;
    }

    ReplyDelete
    Replies
    1. Hi, thanks for the code, I will create a repo later and add your changes.

      Delete