X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=lib%2Fdfu-bootloader.c;h=3df8351aad66e50098d281f6f519eea1cc0a3737;hb=553dc382661879e33c4970b4fb5babfc630432c9;hp=f40676c925c52d23f98549aa6cb5a41c19bf43a8;hpb=3aa8ac3fb9c547154430c73f68cd2a64b2ac7730;p=home-hw.git diff --git a/lib/dfu-bootloader.c b/lib/dfu-bootloader.c index f40676c..3df8351 100644 --- a/lib/dfu-bootloader.c +++ b/lib/dfu-bootloader.c @@ -36,9 +36,14 @@ #define HDR_LENGTH 0x1c #define HDR_FLASH_IN_PROGRESS 0x20 -byte usbd_control_buffer[1024]; +// DFU blocks should be equal to erase blocks of the flash +#define BLOCK_SIZE 1024 +byte usbd_control_buffer[BLOCK_SIZE]; static enum dfu_state dfu_state = STATE_DFU_IDLE; +static uint timeout; +#define DEFAULT_TIMEOUT 5000 // ms +#define DFU_TIMEOUT 2000 static struct { byte buf[sizeof(usbd_control_buffer)]; @@ -82,7 +87,7 @@ const struct usb_dfu_descriptor dfu_function = { .bDescriptorType = DFU_FUNCTIONAL, .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH, .wDetachTimeout = 255, - .wTransferSize = 1024, + .wTransferSize = BLOCK_SIZE, .bcdDFUVersion = 0x0100, }; @@ -116,6 +121,35 @@ const struct usb_config_descriptor config = { .interface = ifaces, }; +static inline u32 get_u32(u32 addr) +{ + return *(u32*)addr; +} + +static inline u16 get_u16(u32 addr) +{ + return *(u16*)addr; +} + +static bool verify_firmware(void) +{ + u32 len = get_u32(BOOTLOADER_APP_START + HDR_LENGTH); + u16 flash_in_progress = get_u16(BOOTLOADER_APP_START + HDR_FLASH_IN_PROGRESS); + + // FIXME: Should check if len is reasonable + + crc_reset(); + u32 crc = crc_calculate_block((u32 *)BOOTLOADER_APP_START, len/4); + u32 want_crc = get_u32(BOOTLOADER_APP_START + len); + DEBUG("DFU: fip=%04x crc=%08x/%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"); + return 0; + } + + return 1; +} + static byte dfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout) { switch (dfu_state) { @@ -127,6 +161,8 @@ static byte dfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout) /* Device will reset when read is complete. */ dfu_state = STATE_DFU_MANIFEST; return DFU_STATUS_OK; + case STATE_DFU_ERROR: + return DFU_STATUS_ERR_VERIFY; default: return DFU_STATUS_OK; } @@ -140,15 +176,19 @@ static void dfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setu // The "flash in progress" word is programmed as 0xffff first and reset later *(u16*)(prog.buf + HDR_FLASH_IN_PROGRESS) = 0xffff; } - flash_unlock(); - u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * dfu_function.wTransferSize; + u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * BLOCK_SIZE; DEBUG("DFU: Block %u -> %08x + %u\n", prog.blocknum, (uint) baseaddr, prog.len); + flash_unlock(); 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); - } + for (uint i = 0; i < prog.len; i += 2) + flash_program_half_word(baseaddr + i, *(u16*)(prog.buf + i)); flash_lock(); + for (uint i = 0; i < prog.len; i++) { + if (*(byte *)(baseaddr + i) != prog.buf[i]) { + DEBUG("DFU: Verification failed\n"); + dfu_state = STATE_DFU_ERROR; + } + } dfu_state = STATE_DFU_DNLOAD_IDLE; return; case STATE_DFU_MANIFEST: @@ -156,7 +196,10 @@ static void dfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setu flash_unlock(); flash_program_half_word(BOOTLOADER_APP_START + 0x20, 0); flash_lock(); - dfu_state = STATE_DFU_MANIFEST_WAIT_RESET; + if (verify_firmware()) + dfu_state = STATE_DFU_MANIFEST_WAIT_RESET; + else + dfu_state = STATE_DFU_ERROR; return; default: return; @@ -173,6 +216,7 @@ static enum usbd_request_return_codes dfu_control_request(usbd_device *usbd_dev, return USBD_REQ_NOTSUPP; /* Only accept class request. */ DEBUG("DFU: Request %02x in state %d\n", req->bRequest, dfu_state); + timeout = DFU_TIMEOUT; switch (req->bRequest) { case DFU_DNLOAD: @@ -223,10 +267,10 @@ static enum usbd_request_return_codes dfu_control_request(usbd_device *usbd_dev, static void dfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED) { usbd_register_control_callback( - usbd_dev, - USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - dfu_control_request); + usbd_dev, + USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, + dfu_control_request); } static void dfu_reset(void) @@ -318,15 +362,8 @@ static void reset_peripherals(void) RCC_APB2RSTR = 0; } -int main(void) +static void configure_hardware(void) { - usbd_device *usbd_dev; - - reset_peripherals(); - - // Flash programming requires running on the internal oscillator - my_rcc_clock_setup_in_hsi_out_48mhz(); - rcc_periph_clock_enable(RCC_GPIOA); rcc_periph_clock_enable(RCC_GPIOC); rcc_periph_clock_enable(RCC_USB); @@ -358,18 +395,33 @@ int main(void) systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000); systick_clear(); systick_counter_enable(); +} - desig_get_unique_id_as_dfu(usb_serial_number); - - DEBUG("DFU: Entered boot-loader (SN %s)\n", usb_serial_number); - - // Simulate USB disconnect +static void usb_disconnect(void) +{ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12); gpio_clear(GPIOA, GPIO11 | GPIO12); for (uint i=0; i<100; i++) { while (!systick_get_countflag()) ; } +} + +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(); + + configure_hardware(); + desig_get_unique_id_as_dfu(usb_serial_number); + + DEBUG("DFU: Started (SN %s)\n", usb_serial_number); + + usb_disconnect(); DEBUG("DFU: Ready\n"); debug_led(0); @@ -380,36 +432,26 @@ int main(void) restart: ; - uint timeout = 5000; + timeout = DEFAULT_TIMEOUT; while (timeout || (dfu_state != STATE_DFU_IDLE && dfu_state != STATE_DFU_MANIFEST_WAIT_RESET)) { usbd_poll(usbd_dev); if (timeout && systick_get_countflag()) { timeout--; + // FIXME: Blink LED even after timeout if (!(timeout & 0x3f)) debug_led_toggle(); } } - u32 *app = (u32 *) BOOTLOADER_APP_START; - u32 sp = app[0]; - u32 pc = app[1]; - 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"); + if (!verify_firmware()) goto restart; - } - DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc); + u32 sp = get_u32(BOOTLOADER_APP_START); + u32 pc = get_u32(BOOTLOADER_APP_START + 4); + DEBUG("DFU: Boot (sp=%08x pc=%08x)\n", (uint) sp, (uint) pc); #ifdef DEBUG_USART - while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC)) - ; + debug_flush(); #endif debug_led(0);