Generate a checksum for the code?

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd like to have a checksum of the code embedded to the binary. How would I do this?

My application has a custom bootloader. I'd like to get protection from a failed software update to avoid expensive fiend trips. The idea is to have the (protected) bootloader to check that the code in the application area is valid before jumping into it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I do exactly that here:

http://spaces.atmel.com/gf/proje...

Notice that in the post build steps of the app code I have:

srec_cat $(MSBuildProjectDirectory)\$(Configuration)\$(OutputFileName).hex -intel -fill 0xFF 0x0000 0x6FFE --l-e-crc16 0x6FFE -o foo.bin -binary

That is using srec_cat (comes with WinAVR) to pad the .hex file to a "full" app space and then embed a 16bit CRC at the end of it.

Meanwhile in the bootloader code I have:

#ifdef CRC_FLASH
static uint16_t updcrc(uint8_t c, uint16_t crc)
{
	uint8_t flag;
	for (uint8_t i = 0; i < 8; ++i)
	{
		flag = !!(crc & 0x8000);
		crc <<= 1;
		if (c & 0x80)
			crc |= 1;
		if (flag)
			crc ^= 0x1021;
		c <<= 1;
	}
	return crc;
}

uint8_t crc_app_ok(void) {
	uint16_t crc = 0xFFFF;
	for (uint16_t i=0; i < CODE_LEN; i++) {
		crc = updcrc(pgm_read_byte(i), crc);
	}
	// augment
	crc = updcrc(0, updcrc(0, crc));
#ifdef UART_DEBUG
	UART_putsP(PSTR("App CRC= "), crc);
	UART_putsP(PSTR("Flash CRC= "), pgm_read_word(CODE_LEN));
#endif
	return (pgm_read_word(CODE_LEN) == crc); 
}
#endif

and:

	if ((eeprom_read_word((const uint16_t *)E2END - 1) != 0xFFFF) &&		/* Start application if exists */
#ifdef CRC_FLASH	
		(crc_app_ok())) {
#else
		(pgm_read_word(0) != (uint16_t)0xFFFF)) {
#endif
		f_ptr reset = (f_ptr)0;
		reset();
	}		

So when built with CRC_FLASH defined the code "costs" a bit more but it does a full CRC check on the app space. Without this my only sanity check (apart from a flag I keep in EEPROM) is that the first 16bits are not 0xFFFF which is (only just!) better than nothing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An arithmetic checksum is pretty trivial. It is not very good at detecting errors.

A CRC (cyclic redundancy check) is far more effective.

Unfortunately there are several different implementations of CRC16, CRC32, ...
In practice, it does not matter which CRC 'version' you use. Just make sure that you use the same one at each end.

Google "CRC16" and you should get plenty of example code.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Just make sure that you use the same one at each end.

What David says is so true. I even knew from reading the SRecord manual that --l-e-crc16 would implement a CRC with the 0x1021 polynomial which is effectively the Xmodem-CRC polynomial but when I came to run some test data through an implementation of it I made on the PC (much easier to develop there) first I could not get it to match the CRC results that Srecord was producing. One thing is whether the initial value passed in is 0xFFFF or 0x0000. That can scupper things before you get started. But the real killer (that I finally determined by reading the Srecord source code) is that in there case they use an "augmented" CRC. That basically means you start with the known initial value (0xFFFF in this case) then you feed that and the bytes of data to be CRCd into the calculating engine. But the key thing is that you must then end by feeding two further 0x00 bytes into the engine which is why you see:

   // augment
   crc = updcrc(0, updcrc(0, crc)); 

above. That was the "magic" that made the CRC I was using on the PC (built into srec_cat) match what I was (eventually) trying to do on the AVR. Once I had the code matched on the PC I just ported the "decoder" to the AVR and it worked.

So the above code may not look like much but it must have taken me 8 hours or more to get a matched pair!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:
An arithmetic checksum is pretty trivial. It is not very good at detecting errors.

Well, in this use it does not matter much. There is either a good software or half of new version and half of the previous one - pretty much any check would see a difference.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

So the above code may not look like much but it must have taken me 8 hours or more to get a matched pair!

Thank you SO much for sharing! I'm sure your post saved me much more than 8 hours. Thank you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you're interested in doing the check while the system that's providing the "to be loaded by bootloader" file is still connected (as a routine operation after programming), why not just compare EVERY SINGLE BYTE of the FLASH against the file you just programmed data? Way less code to do that than to generate and analyze a checksum, and lends itself to this overall structure in the system that's operating the bootloader:

if (verifyProgram()) {
    traceMsg("FLASH already matches file; no action needed");
} else {
    if (loadNewProgram()) {
        if (!verifyProgram()) {
           traceMsg("FLASH fails to verify after programming");
        }
    } else {
        traceMsg("Failure detected during programming operation");
    }
}

Compared to the time you're already spending to burn the FLASH, even reading every byte back again is insignificant. You can use a simple additive checksum on the individual pages sent if you're worried about communications errors.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But the CRC thing is more for the occasion when a bootloader starts up not knowing that a previous session was interrupted somehow and it now needs to know whether the thing it might launch looks "intact" or not. CRCing it is a good way to find out.

As it happens, in my SD bootloader I go a bit over the top with belts and braces (in part this was to show the various techniques that might be employed). Not only do a I have provision to do a start up CRC (oh and one at end of download too) but I also maintain an EEPROM flag to say whether there's an app in flash or not. This is cleared when the bootloading process starts the erasing and is finally set when the app delivery is complete. It also serves doubly duty in that it holds a version number for the app code. Then at next start up the bootloader looks for an SD card and if found it scans for an AVRAPnnn file and begins the process if it finds an "nnn" that is greater than the EEPROM stored number/flag.