Dead Beef and DFU
A few months ago, I wrote about a combination demo board/oscilloscope/logic analyzer called LabTool . I recently wanted to fire it up and was unable to get it to work. The problem turned out to be a bad cable, but it did make me investigate something I was only slightly aware of previously: USB bootloaders with the DFU (Device Firmware Update) standard.
The old saying is that the nice thing about standards is there are so many of them. That seems to be the case with DFU, as well. The idea is that a device with a USB connector can be started in an update mode where it will present itself as a "DFU" device. Then a host computer can transfer new firmware to the device. While in DFU mode, the device doesn't perform its usual function.
This seems like a reasonable thing, and there's a USB specification that defines the whole thing. Assuming everyone follows it.
On the Linux side, I've used dfutool and dfu-util to program devices that have a DFU mode. On Windows, Atmel has Flip. Since the operation is governed by a standard, you'd think you'd be set if you had a program that knew how to download via DFU, right? Apparently not.
Several vendors provide chips that have DFU bootloaders built in. For security reasons, you usually have to erase the flash memory before you can do anything else (which is alarming since a bad flash would ruin your day). But not all of these bootloaders are created equal. Atmel and ST use DFU. NXP, the maker of the chip inside LabTool, also uses DFU.
At least some NXP chips, though, don't implement the whole thing. I was reading a blog post where someone had to reverse engineer the protocol used on the NXP devices (using the traditional hex number 0xDEADBEEF). In particular, the NXP bootloader only recognized a small subset of commands sufficient to load a more advanced bootloader that — in theory — could handle all the DFU commands.
This actually illustrates another way you can use DFU. The usual use case is to have the user reboot the device with a jumper in place (or removed) or while holding down some keys to enter DFU mode. However, the LabTool device has no software on it outside of the bootloader. Every time it starts, it waits for the host computer to send it the firmware to execute.
Of course, one of the things you could potentially load into a device like that is a more sophisticated bootloader.
If you examine the LabTool source code, turns out it doesn't really know anything about DFU either. It calls out to a private copy of dfu-util to boot the board. It is simple to figure out when that works, because the DFU device has one USB ID and the operating device has a different ID.
The file that the program sends to dfu-util has to have a DFU header on it. From the source code:
qint16 hashSize = (size+511)/512; quint8 header; header[ 0] = 0x1a & 0x3f; // AES_ACTIVE: 0x1a = AES Encryption not active header[ 0] |= 0x00 & 0xc0; // HASH_ACTIVE: 0x00 = CMAC Hash is used, value is in HASH_VALUE header[ 1] = 0x3f & 0x3f; // RESERVED header[ 1] |= 0x00 & 0xc0; // AES_CONTROL: 0x00 = not used here header[ 2] = ((hashSize & 0x00ff) >> 0); // HASH_SIZE lsb header[ 3] = ((hashSize & 0xff00) >> 8); // HASH_SIZE msb header[ 4] = 0x00; //HASH_VALUE header[ 5] = 0x00; //HASH_VALUE header[ 6] = 0x00; //HASH_VALUE header[ 7] = 0x00; //HASH_VALUE header[ 8] = 0x00; //HASH_VALUE header[ 9] = 0x00; //HASH_VALUE header = 0x00; //HASH_VALUE header = 0x00; //HASH_VALUE header = 0xff; //RESERVED header = 0xff; //RESERVED header = 0xff; //RESERVED header = 0xff; //RESERVED
As more things migrate to USB connections, I expect this sort of thing will become even more common. On the other hand, I might be tempted to build a similar function into my own software so I could to things like fail safe upgrades Still, the fact that DFU is built into many different CPUs means it is here for awhile.