mirror of
https://github.com/rfivet/stm32bringup.git
synced 2025-05-18 13:18:32 -04:00
343 lines
9.5 KiB
HTML
343 lines
9.5 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>3.8 CRC-32 Code Validation</title>
|
|
<link type="text/css" rel="stylesheet" href="style.css">
|
|
</head>
|
|
<body>
|
|
<h1>3.8 CRC-32 Code Validation</h1
|
|
|
|
The STM32F030 family comes with a CRC calculation unit. It can be used
|
|
during startup to validate the integrity of the code in memory.
|
|
<p>
|
|
Cyclic Redundancy Check is a way to do error detection and correction. I
|
|
have already met CRC when dealing with the DS18B20 sensor where CRC-8 is
|
|
used during the scratchpad 9 bytes transmission.
|
|
<p>
|
|
The STM32 CRC calculation unit has the following default characteristic:
|
|
<ul>
|
|
<li> POLY32 is 0x04C11DB7.
|
|
|
|
<li> Initialisation 0xFFFFFFFF.
|
|
|
|
<li> High bit first (left shift).
|
|
|
|
<li> 32 bit word input, little endian.
|
|
</ul>
|
|
I don't plan to write a self-signing executable, so on top of the STM32
|
|
startup code validation, I will also write a sign32 command to sign
|
|
binary files during build.
|
|
|
|
<h2>Implementation Steps</h2>
|
|
<ol>
|
|
<li> Update <b>stm32f030xx.h</b> with the CRC calculation unit definitions.
|
|
|
|
<li> Update startup with `check_flash()` to be tested before `init()` is
|
|
called.
|
|
|
|
<li> Update <b>generic.ld</b> with a new section used as placeholder for the CRC
|
|
sum at the end of the flashable content.
|
|
|
|
<li> Write `sign32` command to sign a binary file.
|
|
|
|
<li> Update <b>Makefile</b> to sign the binary executable and create an intel
|
|
hex version out of it.
|
|
</ol>
|
|
<h3>1. stm32f030xx.h</h3>
|
|
|
|
The CRC calculation unit is on the AHB bus and its clock need to be
|
|
enabled before use.
|
|
|
|
<pre>
|
|
#define RCC_AHBENR_CRCEN (1 << 6) /* 6: CRC clock enable */
|
|
</pre>
|
|
|
|
I will make use of the default setup so I only need to refer to the Data
|
|
Register and the Control Register. I create all register definitions as
|
|
there is a gap in the memory layout.
|
|
|
|
<pre>
|
|
#define CRC ((volatile unsigned *) 0x40023000)
|
|
#define CRC_DR CRC[ 0]
|
|
#define CRC_IDR CRC[ 1]
|
|
#define CRC_CR CRC[ 2]
|
|
#define CRC_INIT CRC[ 4]
|
|
</pre>
|
|
|
|
<h3>2. startup.crc.c</h3>
|
|
|
|
I make a copy of <b>startup.ram.c</b> into <b>startup.crc.c</b>.
|
|
<p>
|
|
I use conditional compilation, the build option `CRC32SIGN` will be
|
|
defined in the Makefile.
|
|
<p>
|
|
The constant variable `crcsum` is a placeholder with the hexadecimal value
|
|
<code>DEADC0DE</code> in byte order. This value will be overriden by the
|
|
computed CRC value during build. The linker will put `crcsum` at the end of the
|
|
used FLASH.
|
|
<p>
|
|
`check_flash()` use the CRC calculation unit to compute the CRC value
|
|
from beginning of FLASH `isr_vector` to end of FLASH `crcsum`. If
|
|
`crcsum` value is the correct CRC, the computed result will be 0.
|
|
|
|
<pre>
|
|
#ifdef CRC32SIGN
|
|
const unsigned crcsum __attribute__((section(".crc_chk"))) = 0xDEC0ADDE ;
|
|
|
|
static int check_flash( void) {
|
|
int ret = 0 ;
|
|
|
|
/* Flash CRC validation */
|
|
RCC_AHBENR |= RCC_AHBENR_CRCEN ; /* Enable CRC periph */
|
|
CRC_CR = 1 ; /* Reset */
|
|
if( CRC_DR == 0xFFFFFFFF) { /* CRC periph is alive and resetted */
|
|
const unsigned *wp = (const unsigned *) isr_vector ;
|
|
while( wp <= &crcsum)
|
|
CRC_DR = *wp++ ;
|
|
|
|
ret = CRC_DR == 0 ;
|
|
}
|
|
|
|
RCC_AHBENR &= ~RCC_AHBENR_CRCEN ; /* Disable CRC periph */
|
|
return ret ;
|
|
}
|
|
#endif
|
|
</pre>
|
|
|
|
Flash content is checked before calling `init()`. This means the check
|
|
is done using the default clock setup of HSI 8 MHz.
|
|
|
|
<pre>
|
|
if(
|
|
#ifdef CRC32SIGN
|
|
check_flash() &&
|
|
#endif
|
|
init() == 0)
|
|
main() ;
|
|
</pre>
|
|
|
|
<h3>3. generic.ld</h3>
|
|
|
|
I add a new section to hold the CRC value placeholder. This needs to be
|
|
DWORD aligned and at the end of the used FLASH area.
|
|
|
|
<pre>
|
|
.crc __etext + SIZEOF(.data) :
|
|
{
|
|
KEEP(*(.crc_chk))
|
|
} > FLASH
|
|
</pre>
|
|
|
|
<h3>4. sign32</h3>
|
|
|
|
The command `sign32` creates a <b>signed.bin</b> file from it's input file
|
|
specified as parameter.
|
|
|
|
<pre>
|
|
$ touch empty.bin
|
|
|
|
$ ./sign32 empty.bin
|
|
FFFFFFFF empty.bin: 0, signed.bin: 4
|
|
</pre>
|
|
|
|
If the input file is already signed, the output signed.bin is identical
|
|
to the input.
|
|
|
|
<pre>
|
|
$ mv signed.bin FFFFFFFF.bin
|
|
|
|
$ ./sign32 FFFFFFFF.bin
|
|
00000000 FFFFFFFF.bin: 4, signed.bin: 4
|
|
</pre>
|
|
|
|
Padding with null is done on the input to insure the calculation is
|
|
DWORD aligned.
|
|
|
|
<pre>
|
|
$ echo > nl.bin
|
|
|
|
$ ./sign32 nl.bin
|
|
E88E0BAD nl.bin: 1, signed.bin: 8
|
|
|
|
$ hexdump -C signed.bin
|
|
00000000 0a 00 00 00 ad 0b 8e e8 |........|
|
|
00000008
|
|
|
|
</pre>
|
|
|
|
Calculation stops when the placeholder DEADC0DE is found or the end of
|
|
the input file is reached.
|
|
<p>
|
|
I create a folder <b>crc32/</b> for <b>sign32.c</b> and its <b>Makefile</b>.
|
|
<p>
|
|
The core of sign32.c is customizable to do CRC calculation bitwise,
|
|
unrolled bitwise, tablewise or to generate the CRC table.
|
|
|
|
<h3>5. Makefile</h3>
|
|
|
|
The build option `CRC32SIGN` controls the signature of the binary file
|
|
and the generation of the intel hex version from the signed binary using
|
|
`objcopy`.
|
|
|
|
<pre>
|
|
# build options
|
|
CRC32SIGN := 1
|
|
|
|
ifdef CRC32SIGN
|
|
CDEFINES += -DCRC32SIGN=$(CRC32SIGN)
|
|
endif
|
|
|
|
%.$(BINLOC).bin: %.elf
|
|
@echo $@
|
|
$(OBJCOPY) -O binary $< $@
|
|
ifdef CRC32SIGN
|
|
crc32/sign32 $@
|
|
mv signed.bin $@
|
|
|
|
%.hex: %.$(BINLOC).bin
|
|
@echo $@ from $<
|
|
$(OBJCOPY) --change-address=$(BINLOC) -I binary -O ihex $< $@
|
|
else
|
|
|
|
%.hex: %.elf
|
|
@echo $@ from $<
|
|
$(OBJCOPY) -O ihex $< $@
|
|
endif
|
|
|
|
|
|
</pre>
|
|
|
|
<h2>Building and testing</h2>
|
|
|
|
If I build an executable, I can see that the binary file is CRC-32
|
|
signed. In the example below, the CRC-32 signature is 0xDEAD68BE and the
|
|
total binary image is 1728 bytes long.
|
|
|
|
<pre>
|
|
$ make
|
|
f030f4.elf from startup.crc.o txeie.o uptime.o libstm32.a
|
|
Memory region Used Size Region Size %age Used
|
|
FLASH: 1728 B 16 KB 10.55%
|
|
RAM: 16 B 4 KB 0.39%
|
|
text data bss dec hex filename
|
|
1725 0 16 1741 6cd f030f4.elf
|
|
f030f4.0x08000000.bin
|
|
crc32/sign32 f030f4.0x08000000.bin
|
|
DEAD68BE f030f4.0x08000000.bin: 1724, signed.bin: 1728
|
|
mv signed.bin f030f4.0x08000000.bin
|
|
f030f4.hex from f030f4.0x08000000.bin
|
|
</pre>
|
|
|
|
I can double check that the value at the end of the binary file matches.
|
|
|
|
<pre>
|
|
$ hexdump -C f030f4.0x08000000.bin | tail
|
|
00000630 8b 42 01 d3 cb 00 c0 1a 52 41 83 08 8b 42 01 d3 |.B......RA...B..|
|
|
00000640 8b 00 c0 1a 52 41 43 08 8b 42 01 d3 4b 00 c0 1a |....RAC..B..K...|
|
|
00000650 52 41 41 1a 00 d2 01 46 52 41 10 46 70 47 ff e7 |RAA....FRA.FpG..|
|
|
00000660 01 b5 00 20 00 f0 06 f8 02 bd c0 46 00 29 f7 d0 |... .......F.)..|
|
|
00000670 76 e7 70 47 70 47 c0 46 50 4c 4c 48 53 45 0a 00 |v.pGpG.FPLLHSE..|
|
|
00000680 20 25 64 20 25 73 25 73 00 75 70 00 77 65 65 6b | %d %s%s.up.week|
|
|
00000690 00 64 61 79 00 68 6f 75 72 00 6d 69 6e 75 74 65 |.day.hour.minute|
|
|
000006a0 00 73 65 63 6f 6e 64 00 30 31 32 33 34 35 36 37 |.second.01234567|
|
|
000006b0 38 39 41 42 43 44 45 46 00 00 00 00 be 68 ad de |89ABCDEF.....h..|
|
|
000006c0
|
|
</pre>
|
|
|
|
I can flash the resulting intel hex file and see that it executes.
|
|
|
|
<pre>
|
|
$ stm32flash -x f030f4.hex COM6
|
|
stm32flash 0.7-patch-exe
|
|
|
|
http://stm32flash.sourceforge.net/
|
|
|
|
Using Parser : Intel HEX
|
|
Location : 0x8000000
|
|
Size : 1728
|
|
Interface serial_w32: 57600 8E1
|
|
Version : 0x31
|
|
Option 1 : 0x00
|
|
Option 2 : 0x00
|
|
Device ID : 0x0444 (STM32F03xx4/6)
|
|
- RAM : Up to 4KiB (2048b reserved by bootloader)
|
|
- Flash : Up to 32KiB (size first sector: 4x1024)
|
|
- Option RAM : 16b
|
|
- System RAM : 3KiB
|
|
Write to memory
|
|
Erasing memory
|
|
Wrote address 0x080006c0 (100.00%) Done.
|
|
|
|
Starting execution at address 0x08000000... done.
|
|
</pre>
|
|
|
|
I can use <b><i>stm32flash</i></b> to compute the CRC-32 checksum on the first
|
|
1728 bytes of FLASH, the result is 0 as this covers both the payload AND
|
|
the CRC-32 checksum value.
|
|
|
|
<pre>
|
|
$ stm32flash -C -S 0x08000000:1728 COM6
|
|
stm32flash 0.7-patch-exe
|
|
|
|
http://stm32flash.sourceforge.net/
|
|
|
|
Interface serial_w32: 57600 8E1
|
|
Version : 0x31
|
|
Option 1 : 0x00
|
|
Option 2 : 0x00
|
|
Device ID : 0x0444 (STM32F03xx4/6)
|
|
- RAM : Up to 4KiB (2048b reserved by bootloader)
|
|
- Flash : Up to 32KiB (size first sector: 4x1024)
|
|
- Option RAM : 16b
|
|
- System RAM : 3KiB
|
|
CRC computation
|
|
CRC address 0x080006c0 (100.00%) Done.
|
|
CRC(0x08000000-0x080006c0) = 0x00000000
|
|
</pre>
|
|
|
|
If I ask <b><i>stm32flash</i></b> to compute the CRC-32 checksum on the first
|
|
1724 bytes (payload excluding CRC-32 checksum value), it returns 0xdead68be,
|
|
which is the value computed at build time.
|
|
|
|
<pre>
|
|
$ stm32flash -C -S 0x08000000:1724 COM6
|
|
stm32flash 0.7-patch-exe
|
|
|
|
http://stm32flash.sourceforge.net/
|
|
|
|
Interface serial_w32: 57600 8E1
|
|
Version : 0x31
|
|
Option 1 : 0x00
|
|
Option 2 : 0x00
|
|
Device ID : 0x0444 (STM32F03xx4/6)
|
|
- RAM : Up to 4KiB (2048b reserved by bootloader)
|
|
- Flash : Up to 32KiB (size first sector: 4x1024)
|
|
- Option RAM : 16b
|
|
- System RAM : 3KiB
|
|
CRC computation
|
|
CRC address 0x080006bc (100.00%) Done.
|
|
CRC(0x08000000-0x080006bc) = 0xdead68be
|
|
</pre>
|
|
|
|
Because STM32F030 USART bootloader is v3.1, it doesn't implement the CRC
|
|
checksum command included in v3.3. This means that in this case
|
|
<b><i>stm32flash</i></b> computes the CRC checksum value on its own. You can
|
|
check the sources of <b><i>stm32flash</i></b> for its implementation of the
|
|
CRC-32 calculation.
|
|
|
|
<h2>Checkpoint</h2>
|
|
|
|
There is variations in the functionality of the CRC calculation unit
|
|
among different STM32 chipset family. The <code>check_flash()</code>
|
|
implementation I just made relies on the default settings for polynomial,
|
|
initial value, polynomial length and shift direction. This should be common to
|
|
most chipset.
|
|
<p>
|
|
<a href="39_resistor.html">Next</a>, I will use the ADC to read a resistor
|
|
value.
|
|
|
|
<hr>© 2020-2025 Renaud Fivet
|
|
</body>
|
|
</html>
|