#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];
.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) {
}
}
-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;
}
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;
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;
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();
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
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. */