]> mj.ucw.cz Git - home-hw.git/commitdiff
Bootloader: Verify signatures
authorMartin Mares <mj@ucw.cz>
Tue, 25 Feb 2020 16:35:36 +0000 (17:35 +0100)
committerMartin Mares <mj@ucw.cz>
Tue, 25 Feb 2020 16:35:36 +0000 (17:35 +0100)
lib/dfu-bootloader.c

index bec2c33d0ffeca957809474c9e59748616ffbd83..cb314d8673c2dee5b89d04235960d27328d5ca64 100644 (file)
@@ -16,6 +16,7 @@
 #include <libopencm3/cm3/scb.h>
 #include <libopencm3/cm3/systick.h>
 #include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/crc.h>
 #include <libopencm3/stm32/gpio.h>
 #include <libopencm3/stm32/flash.h>
 #include <libopencm3/stm32/usart.h>
 #define DEBUG(x...) do { } while (0)
 #endif
 
+// Offsets to firmware header fields (see tools/dfu-sign.c)
+#define HDR_LENGTH 0x1c
+#define HDR_FLASH_IN_PROGRESS 0x20
+
 byte usbd_control_buffer[1024];
 
 static enum dfu_state usbdfu_state = STATE_DFU_IDLE;
 
-struct prog_info {
+static struct {
        byte buf[sizeof(usbd_control_buffer)];
        u16 blocknum;
        u16 len;
-};
-
-static struct prog_info prog_page;
-
-// The first page of application code is programmed last,
-// so that we are able to detect incompletely uploaded firmware.
-static struct prog_info prog_header;
+} prog;
 
 static char usb_serial_number[13];
 
@@ -117,20 +116,7 @@ const struct usb_config_descriptor config = {
        .interface = ifaces,
 };
 
-#if 0 // FIXME
-static u32 adhoc_checksum(u32 start, uint len)
-{
-       u32 end = start + len;
-       u32 sum = 0;
-       while (start < end) {
-               sum *= 259309;
-               sum += *(volatile u32 *)start;
-               start += 4;
-       }
-       return sum;
-}
-#endif
-
+// FIXME: Rename usbdfu_ to dfu_
 static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
 {
        switch (usbdfu_state) {
@@ -147,46 +133,31 @@ static byte usbdfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
        }
 }
 
-static void program_page(struct prog_info *prog, bool need_erase)
-{
-       flash_unlock();
-       u32 baseaddr = BOOTLOADER_APP_START + prog->blocknum * dfu_function.wTransferSize;
-       DEBUG("DFU: Block %u -> %08x + %u\n", prog->blocknum, (uint) baseaddr, prog->len);
-       if (need_erase)
-               flash_erase_page(baseaddr);
-       for (uint i = 0; i < prog->len; i += 2) {
-               u16 data = *(u16 *)(prog->buf + i);
-               flash_program_half_word(baseaddr + i, data);
-       }
-       flash_lock();
-#if 0
-       u32 s1 = adhoc_checksum((u32) prog->buf, prog->len);
-       u32 s2 = adhoc_checksum(baseaddr, prog->len);
-       DEBUG("\t%08x %08x\n", (uint) s1, (uint) s2);
-       if (s1 != s2)
-               DEBUG("\t!!!!!!!!!!!!!!!!!!!!!!!!!\n");
-#endif
-}
-
 static void usbdfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED)
 {
        switch (usbdfu_state) {
        case STATE_DFU_DNBUSY:
-               if (prog_page.blocknum == 0) {
-                       // The first block will be programmed last
-                       prog_header = prog_page;
-                       flash_unlock();
-                       flash_erase_page(BOOTLOADER_APP_START);
-                       flash_lock();
-               } else
-                       program_page(&prog_page, true);
+               if (prog.blocknum == 0)
+                       *(u16*)(prog.buf + HDR_FLASH_IN_PROGRESS) = 0xffff;
+               flash_unlock();
+               u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * dfu_function.wTransferSize;
+               DEBUG("DFU: Block %u -> %08x + %u\n", prog.blocknum, (uint) baseaddr, prog.len);
+               flash_erase_page(baseaddr);
+               for (uint i = 0; i < prog.len; i += 2) {
+                       u16 data = *(u16 *)(prog.buf + i);
+                       flash_program_half_word(baseaddr + i, data);
+               }
+               flash_lock();
                usbdfu_state = STATE_DFU_DNLOAD_IDLE;
                return;
        case STATE_DFU_MANIFEST:
                // At the very end, program the first page
-               program_page(&prog_header, false);
+               // FIXME
+               flash_unlock();
+               flash_program_half_word(BOOTLOADER_APP_START + 0x20, 0);
+               flash_lock();
                usbdfu_state = STATE_DFU_MANIFEST_WAIT_RESET;
-               return; /* Will never return. */
+               return;
        default:
                return;
        }
@@ -209,9 +180,9 @@ static enum usbd_request_return_codes usbdfu_control_request(usbd_device *usbd_d
                        usbdfu_state = STATE_DFU_MANIFEST_SYNC;
                } else {
                        /* Copy download data for use on GET_STATUS. */
-                       prog_page.blocknum = req->wValue;
-                       prog_page.len = *len;
-                       memcpy(prog_page.buf, *buf, *len);
+                       prog.blocknum = req->wValue;
+                       prog.len = *len;
+                       memcpy(prog.buf, *buf, *len);
                        usbdfu_state = STATE_DFU_DNLOAD_SYNC;
                }
                return USBD_REQ_HANDLED;
@@ -335,10 +306,8 @@ static void clock_plain_hsi(void)
        rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_VCO_CLK_DIV3);
 }
 
-int main(void)
+static void reset_peripherals(void)
 {
-       usbd_device *usbd_dev;
-
        // Turn off clock to all peripherals and reset them
        RCC_AHBENR = 0x00000014;
        RCC_APB1ENR = 0;
@@ -347,6 +316,13 @@ int main(void)
        RCC_APB2RSTR = 0x0038fffd;
        RCC_APB1RSTR = 0;
        RCC_APB2RSTR = 0;
+}
+
+int main(void)
+{
+       usbd_device *usbd_dev;
+
+       reset_peripherals();
 
        // Flash programming requires running on the internal oscillator
        my_rcc_clock_setup_in_hsi_out_48mhz();
@@ -354,6 +330,7 @@ int main(void)
        rcc_periph_clock_enable(RCC_GPIOA);
        rcc_periph_clock_enable(RCC_GPIOC);
        rcc_periph_clock_enable(RCC_USB);
+       rcc_periph_clock_enable(RCC_CRC);
 
 #ifdef DEBUG_USART
        // Currently, only USART1 is supported
@@ -404,47 +381,39 @@ int main(void)
 restart: ;
 
        uint timeout = 5000;
-       while (timeout) {
+       while (timeout || (usbdfu_state != STATE_DFU_IDLE && usbdfu_state != STATE_DFU_MANIFEST_WAIT_RESET)) {
                usbd_poll(usbd_dev);
-               if (systick_get_countflag()) {
+               if (timeout && systick_get_countflag()) {
                        timeout--;
                        if (!(timeout & 0x3f))
                                debug_led_toggle();
                }
        }
 
-       volatile u32 *app = (volatile u32 *) BOOTLOADER_APP_START;
+       u32 *app = (u32 *) BOOTLOADER_APP_START;
        u32 sp = app[0];
        u32 pc = app[1];
-
-#if 0
-       uint len = 7140;
-       u32 sum = adhoc_checksum(BOOTLOADER_APP_START, len);
-       DEBUG("DFU: Checksum=%08x, len=%u\n", (uint) sum, len);
-#endif
-
-       DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
-       if ((sp & 0x2ffe0000) != 0x20000000) {
-               DEBUG("DFU: Suspicious SP, refusing to start\n");
+       u32 len = app[HDR_LENGTH/4];
+       u16 flash_in_progress = *(u16 *)(BOOTLOADER_APP_START + HDR_FLASH_IN_PROGRESS);
+
+       crc_reset();
+       u32 crc = crc_calculate_block(app, len/4);
+       u32 want_crc = *(u32 *)(BOOTLOADER_APP_START + len);
+       DEBUG("DFU: fip=%04x, crc=%08x, want=%08x, len=%u\n", (uint) flash_in_progress, (uint) crc, (uint) want_crc, (uint) len);
+       if (flash_in_progress || crc != want_crc) {
+               DEBUG("DFU: Bad firmware\n");
                goto restart;
        }
 
+       DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
+
 #ifdef DEBUG_USART
        while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
                ;
 #endif
        debug_led(0);
 
-       rcc_periph_clock_disable(RCC_GPIOA);
-       rcc_periph_clock_disable(RCC_GPIOC);
-       rcc_periph_clock_disable(RCC_USB);
-       rcc_periph_clock_disable(RCC_USART1);
-
-       rcc_periph_reset_pulse(RST_GPIOA);
-       rcc_periph_reset_pulse(RST_GPIOC);
-       rcc_periph_reset_pulse(RST_USB);
-       rcc_periph_reset_pulse(RST_USART1);
-
+       reset_peripherals();
        clock_plain_hsi();
 
        /* Set vector table base address. */