diff options
author | Fredrik Thulin <fredrik@thulin.net> | 2016-05-27 21:58:55 +0200 |
---|---|---|
committer | Fredrik Thulin <fredrik@thulin.net> | 2016-05-27 21:58:55 +0200 |
commit | 13d9bfb4bf1cfbb3d723c919763bc460c193ed3b (patch) | |
tree | 2454a8313689ce2ed92644bc4a449b7fa039f997 /projects/bootloader | |
parent | 0009ba29bbb3d7ff911b343bafb9a005114ea2d8 (diff) | |
parent | 854a8ba169d64a16f5466c0cac9e1aeeff50659d (diff) |
Merge branch 'ft-dfu-code-loading'
Diffstat (limited to 'projects/bootloader')
-rw-r--r-- | projects/bootloader/Makefile | 20 | ||||
-rw-r--r-- | projects/bootloader/bootloader.c | 126 | ||||
-rw-r--r-- | projects/bootloader/crc32.c | 62 | ||||
-rw-r--r-- | projects/bootloader/dfu.c | 106 | ||||
-rw-r--r-- | projects/bootloader/dfu.h | 62 |
5 files changed, 376 insertions, 0 deletions
diff --git a/projects/bootloader/Makefile b/projects/bootloader/Makefile new file mode 100644 index 0000000..4eef758 --- /dev/null +++ b/projects/bootloader/Makefile @@ -0,0 +1,20 @@ +PROG = bootloader + +OBJS = crc32.o dfu.o + +all: $(PROG:=.elf) + +%.elf: %.o $(BOARD_OBJS) $(OBJS) $(LIBS) + $(CC) $(CFLAGS) $^ -o $@ -T$(BOOTLOADER_LDSCRIPT) -g -Wl,-Map=$*.map + $(OBJCOPY) -O ihex $*.elf $*.hex + $(OBJCOPY) -O binary $*.elf $*.bin + $(OBJDUMP) -St $*.elf >$*.lst + $(SIZE) $*.elf + +clean: + rm -f *.o + rm -f *.elf + rm -f *.hex + rm -f *.bin + rm -f *.map + rm -f *.lst diff --git a/projects/bootloader/bootloader.c b/projects/bootloader/bootloader.c new file mode 100644 index 0000000..ab3c1d9 --- /dev/null +++ b/projects/bootloader/bootloader.c @@ -0,0 +1,126 @@ +/* + * bootloader.c + * ------------ + * Bootloader to either install new firmware received from the MGMT UART, + * or jump to previously installed firmware. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "stm-init.h" +#include "stm-led.h" +#include "stm-uart.h" +#include "dfu.h" + +/* Linker symbols are strange in C. Make regular pointers for sanity. */ +__IO uint32_t *dfu_control = &CRYPTECH_DFU_CONTROL; +__IO uint32_t *dfu_firmware = &CRYPTECH_FIRMWARE_START; +/* The first word in the firmware is an address to the stack (msp) */ +__IO uint32_t *dfu_msp_ptr = &CRYPTECH_FIRMWARE_START; +/* The second word in the firmware is a pointer to the code + * (points at the Reset_Handler from the linker script). + */ +__IO uint32_t *dfu_code_ptr = &CRYPTECH_FIRMWARE_START + 1; + +typedef void (*pFunction)(void); + +/* This is it's own function to make it more convenient to set a breakpoint at it in gdb */ +void do_early_dfu_jump(void) +{ + pFunction loaded_app = (pFunction) *dfu_code_ptr; + /* Set the stack pointer to the correct one for the firmware */ + __set_MSP(*dfu_msp_ptr); + /* Set the Vector Table Offset Register */ + SCB->VTOR = (uint32_t) dfu_firmware; + loaded_app(); + while (1); +} + +int should_dfu() +{ + int i; + uint8_t rx = 0; + + /* While blinking the blue LED for one second, see if we receive a CR on the MGMT UART. + * We've discussed also requiring one or both of the FPGA config jumpers installed + * before allowing DFU of the STM32 - that check could be done here. + */ + led_on(LED_BLUE); + for (i = 0; i < 10; i++) { + HAL_Delay(100); + led_toggle(LED_BLUE); + if (uart_recv_char2(STM_UART_MGMT, &rx, 0) == HAL_OK) { + if (rx == 13) return 1; + } + } + return 0; +} + +int +main() +{ + int status; + + /* Check if we've just rebooted in order to jump to the firmware. */ + if (*dfu_control == HARDWARE_EARLY_DFU_JUMP) { + *dfu_control = 0; + do_early_dfu_jump(); + } + + stm_init(); + + uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\nThis is the bootloader speaking..."); + + if (should_dfu()) { + led_off(LED_BLUE); + if ((status = dfu_receive_firmware()) != 0) { + /* Upload of new firmware failed, reboot after lighting the red LED + * for three seconds. + */ + led_off(LED_BLUE); + led_on(LED_RED); + uart_send_string2(STM_UART_MGMT, (char *) "dfu_receive_firmware failed: "); + uart_send_number2(STM_UART_MGMT, status, 3, 16); + uart_send_string2(STM_UART_MGMT, (char *) "\r\n\r\nRebooting in three seconds\r\n"); + HAL_Delay(3000); + HAL_NVIC_SystemReset(); + while (1) {}; + } + } + + /* Set dfu_control to the magic value that will cause the us to call do_early_dfu_jump + * after rebooting back into this main() function. + */ + *dfu_control = HARDWARE_EARLY_DFU_JUMP; + + uart_send_string2(STM_UART_MGMT, (char *) "loading firmware\r\n\r\n"); + + /* De-initialize hardware by rebooting */ + HAL_NVIC_SystemReset(); + while (1) {}; +} diff --git a/projects/bootloader/crc32.c b/projects/bootloader/crc32.c new file mode 100644 index 0000000..4d1a0bc --- /dev/null +++ b/projects/bootloader/crc32.c @@ -0,0 +1,62 @@ +/* Reference code from RFC1952. Not meant to be used outside test code. */ + +#include "stm32f4xx_hal.h" + + +/* Table of CRCs of all 8-bit messages. */ +unsigned long crc_table[256]; + +/* Flag: has the table been computed? Initially false. */ +int crc_table_computed = 0; + +/* Make the table for a fast CRC. */ +void make_crc_table(void) +{ + unsigned long c; + + int n, k; + for (n = 0; n < 256; n++) { + c = (unsigned long) n; + for (k = 0; k < 8; k++) { + if (c & 1) { + c = 0xedb88320L ^ (c >> 1); + } else { + c = c >> 1; + } + } + crc_table[n] = c; + } + crc_table_computed = 1; +} + +/* + Update a running crc with the bytes buf[0..len-1] and return + the updated crc. The crc should be initialized to zero. Pre- and + post-conditioning (one's complement) is performed within this + function so it shouldn't be done by the caller. Usage example: + + unsigned long crc = 0L; + + while (read_buffer(buffer, length) != EOF) { + crc = update_crc(crc, buffer, length); + } + if (crc != original_crc) error(); +*/ +uint32_t update_crc(uint32_t crc, uint8_t *buf, int len) +{ + unsigned long c = crc ^ 0xffffffffL; + int n; + + if (!crc_table_computed) + make_crc_table(); + for (n = 0; n < len; n++) { + c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + } + return c ^ 0xffffffffL; +} + +/* Return the CRC of the bytes buf[0..len-1]. */ +unsigned long crc(unsigned char *buf, int len) +{ + return update_crc(0L, buf, len); +} diff --git a/projects/bootloader/dfu.c b/projects/bootloader/dfu.c new file mode 100644 index 0000000..231e388 --- /dev/null +++ b/projects/bootloader/dfu.c @@ -0,0 +1,106 @@ +/* + * dfu.c + * ------------ + * Receive new firmware from MGMT UART and write it to STM32 internal flash. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "dfu.h" +#include "stm-led.h" +#include "stm-uart.h" +#include "stm-flash.h" + +#include <string.h> + +extern uint32_t update_crc(uint32_t crc, uint8_t *buf, int len); + + +int dfu_receive_firmware(void) +{ + uint32_t filesize = 0, crc = 0, my_crc = 0, counter = 0; + uint32_t offset = DFU_FIRMWARE_ADDR, n = DFU_UPLOAD_CHUNK_SIZE; + uint32_t buf[DFU_UPLOAD_CHUNK_SIZE / 4]; + + uart_send_string2(STM_UART_MGMT, (char *) "\r\nOK, bootloader waiting for new firmware\r\n"); + + /* Read file size (4 bytes) */ + uart_receive_bytes(STM_UART_MGMT, (void *) &filesize, 4, 1000); + if (filesize < 512 || filesize > DFU_FIRMWARE_END_ADDR - DFU_FIRMWARE_ADDR) { + return -1; + } + + HAL_FLASH_Unlock(); + + while (filesize) { + /* By initializing buf to the same value that erased flash has (0xff), we don't + * have to try and be smart when writing the last page of data to the memory. + */ + memset(buf, 0xffffffff, sizeof(buf)); + + if (filesize < n) { + n = filesize; + } + + if (uart_receive_bytes(STM_UART_MGMT, (void *) &buf, n, 1000) != HAL_OK) { + return -2; + } + filesize -= n; + + /* After reception of a chunk but before ACKing we have "all" the time in the world to + * calculate CRC and write it to flash. + */ + my_crc = update_crc(my_crc, (uint8_t *) buf, n); + stm_flash_write32(offset, buf, sizeof(buf) / 4); + offset += DFU_UPLOAD_CHUNK_SIZE; + + /* ACK this chunk by sending the current chunk counter (4 bytes) */ + counter++; + uart_send_bytes(STM_UART_MGMT, (void *) &counter, 4); + led_toggle(LED_BLUE); + } + + HAL_FLASH_Lock(); + + /* The sending side will now send it's calculated CRC-32 */ + uart_receive_bytes(STM_UART_MGMT, (void *) &crc, 4, 1000); + if (crc == my_crc) { + uart_send_string2(STM_UART_MGMT, (char *) "\r\nSuccess\r\n"); + return 0; + } + + led_on(LED_RED); + led_on(LED_YELLOW); + + /* Better to erase the known bad firmware */ + stm_flash_erase_sectors(DFU_FIRMWARE_ADDR, DFU_FIRMWARE_END_ADDR); + + led_off(LED_YELLOW); + + return 0; +} diff --git a/projects/bootloader/dfu.h b/projects/bootloader/dfu.h new file mode 100644 index 0000000..8dfed9d --- /dev/null +++ b/projects/bootloader/dfu.h @@ -0,0 +1,62 @@ +/* + * dfu.h + * --------- + * Device Firmware Upgrade defines and prototypes. + * + * Copyright (c) 2016, NORDUnet A/S All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of the NORDUnet nor the names of its contributors may + * be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __STM32_BOOTLOADER_DFU_H +#define __STM32_BOOTLOADER_DFU_H + +#include "stm-init.h" + +/* symbols defined in the linker script (STM32F429BI_bootloader.ld) */ +extern uint32_t CRYPTECH_FIRMWARE_START; +extern uint32_t CRYPTECH_FIRMWARE_END; +extern uint32_t CRYPTECH_DFU_CONTROL; + +#define DFU_FIRMWARE_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_START) +#define DFU_FIRMWARE_END_ADDR ((uint32_t) &CRYPTECH_FIRMWARE_END) +#define DFU_UPLOAD_CHUNK_SIZE 4096 + +/* Magic bytes to signal the bootloader it should jump to the firmware + * instead of trying to receive a new firmware using the MGMT UART. + */ +#define HARDWARE_EARLY_DFU_JUMP 0xBADABADA + +extern __IO uint32_t *dfu_control; +extern __IO uint32_t *dfu_firmware; +extern __IO uint32_t *dfu_msp_ptr; +extern __IO uint32_t *dfu_code_ptr; + +extern int dfu_receive_firmware(void); + + +#endif /* __STM32_BOOTLOADER_DFU_H */ |