The loader uses data organized into packets that have a header and a checksum to verify delivery. Once the packet is delivered to the microcontroller and its checksum is validated, the loader passes it to the decryption engine (if encrypted). Located internally in the encrypted data is an additional cipher checksum. This gives the decryption engine a means to verify the key information. It will apply the global key first and test the checksum. If it fails, it will apply the unique key from the serial number and try again. If that fails, the loader is notified that the firmware is invalid for this device. This also offers a way to protect the code from unauthorized tampering or accidental destruction.
If the decryption engine successfully decodes the data, it will pass it to a flash routine that will program the actual block of flash memory. These routines will vary from different manufacturers, though. The COP8 routines are shown in the design example that follows. This routine sets aside a block of 128 bytes of RAM to use for updating the flash. However, this RAM can be used for application data after the loader completes.
EXAMPLE DESIGN
Figure 3 shows a firmware loader flow chart with decryption. The loader uses a modified version of the industry-standard Intel hex format commonly implemented in device programmers and in-system emulators. This will provide an existing infrastructure of applications that support the standard and ease of use. The loader firmware will be written in COP8 assembler to minimize code size and exploit all of the device's flash features. For a copy of the code, go to www.electronicdesign.com. For further comments, e-mail the author at the address given at the end of the article.
Briefly, Intel hex format is an ASCII-coded representation of data records used to program object code or data into a device's memory. The format comprises a Start of Record field using an ASCII 0x3A or ':', a Data Length field (two ASCII characters) representing a length of 0x00-0xFF, an Offset field, a Record Type field, an Information or Data field, and a Checksum field. Standard printable ASCII characters represent each field. For instance, '10', which equals 0x10 in hexadecimal, or 16 decimal, might represent a length field. Actually, its coding is 0x31, 0x30, which are the ASCII codes for '1' and '0.' Because the content is in printable ASCII characters, the entire object code can be examined with a text editor, thus the need for encryption.
The table covers record types for normal Intel hex format. Note that the standard defines only six record types. To take advantage of the existing infrastructure, the loader will use the Intel hex format standard and denote encrypted data with a new record type. The record type 0x10 will be selected to represent encrypted data. The choice is arbitrary.
The code loader will also need some method of flow control to make sure that the flash write cycle completes before the next record is loaded. In most cases, applications create files that are broken into groups of 16 data bytes to ease readability. But flash memory usually comes in blocks of 128 or 256 bytes. So, there must be a command from the loader to request data from the host to allow for variable record sizes. The loader has to handle 256 bytes of data as this is the maximum record size allowed by the standard. The bottom of the table shows the new extended record type for this example. Because the COP8 device family has only a 16-bit address, record types 0x02 through 0x05 won't be implemented. For processors with larger flash memory, such as the CR16, which can directly address 16 Mbytes of memory, the extended linear-addressing record types will be required.
Tracing the flow chart of Figure 3, the first task following a Reset is verifying the flash-memory contents. This is accomplished by reading each page of 128 bytes, excluding the boot-verification code and loader code located at the top of flash memory. Only the application code is verified. This makes calculating a checksum much easier. However, it could include checking the loader components of the firmware if desired. If the checksum passes, control is vectored to the entry point of the application firmware. It's very important that this entry location remains the same. If it moves, the loader will vector to an incorrect location and possibly crash. Alternatively, the loader could read an entry vector from the code, push it onto the stack, and return, forcing execution to begin at that location. This allows for variable entry points.
If the code fails the checksum test, the loader firmware will take control and continuously signal the host, by whatever means is designed into the hardware, to begin loading. Optionally, a user interface can be controlled by the loader to signal this state. A simple LED that indicates "firmware load" can be extremely useful when debugging a system.
The loader firmware will request records from the host until the host signals, via an EOF record, that there are no more records. A detected record type of 0x10 will be vectored to the decryption engine, which will decrypt the data in place. This lets the code that actually writes the data to the flash operate on both encrypted and unencrypted data. It may be of value to provide an unencrypted datapath for development and internal testing. When the EOF record is received, all new firmware will be written to flash memory. At this point, the loader passes control back to the boot test to verify that the new code is intact.
As shown above, field-upgradable equipment can be implemented without exposing IP. With a simple cipher, the manufacturer's firmware can be protected and customers can take advantage of upgrading their equipment from anywhere in the world that they have Internet access.
To download a listing, click Download the Code below.
Download the Code