]> mj.ucw.cz Git - home-hw.git/blob - lib/dfu-bootloader.c
Bootloader: Verification
[home-hw.git] / lib / dfu-bootloader.c
1 /*
2  *      Generic DFU Bootloader
3  *
4  *      (c) 2020 Martin Mareš <mj@ucw.cz>
5  *
6  *      Based on example code from the libopencm3 project, which is
7  *      Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
8  *
9  *      Licensed under the GNU LGPL v3 or any later version.
10  */
11
12 #include "util.h"
13
14 #include <libopencm3/cm3/cortex.h>
15 #include <libopencm3/cm3/nvic.h>
16 #include <libopencm3/cm3/scb.h>
17 #include <libopencm3/cm3/systick.h>
18 #include <libopencm3/stm32/rcc.h>
19 #include <libopencm3/stm32/crc.h>
20 #include <libopencm3/stm32/gpio.h>
21 #include <libopencm3/stm32/flash.h>
22 #include <libopencm3/stm32/usart.h>
23 #include <libopencm3/stm32/desig.h>
24 #include <libopencm3/usb/usbd.h>
25 #include <libopencm3/usb/dfu.h>
26
27 #include <string.h>
28
29 #ifdef BOOTLOADER_DEBUG
30 #define DEBUG(x...) debug_printf(x)
31 #else
32 #define DEBUG(x...) do { } while (0)
33 #endif
34
35 // Offsets to firmware header fields (see tools/dfu-sign.c)
36 #define HDR_LENGTH 0x1c
37 #define HDR_FLASH_IN_PROGRESS 0x20
38
39 // DFU blocks should be equal to erase blocks of the flash
40 #define BLOCK_SIZE 1024
41 byte usbd_control_buffer[BLOCK_SIZE];
42
43 static enum dfu_state dfu_state = STATE_DFU_IDLE;
44
45 static struct {
46         byte buf[sizeof(usbd_control_buffer)];
47         u16 blocknum;
48         u16 len;
49 } prog;
50
51 static char usb_serial_number[13];
52
53 enum usb_string {
54         STR_MANUFACTURER = 1,
55         STR_PRODUCT,
56         STR_SERIAL,
57 };
58
59 static const char *usb_strings[] = {
60         BOOTLOADER_MFG_NAME,
61         BOOTLOADER_PROD_NAME,
62         usb_serial_number,
63 };
64
65 const struct usb_device_descriptor dev = {
66         .bLength = USB_DT_DEVICE_SIZE,
67         .bDescriptorType = USB_DT_DEVICE,
68         .bcdUSB = 0x0200,
69         .bDeviceClass = 0,
70         .bDeviceSubClass = 0,
71         .bDeviceProtocol = 0,
72         .bMaxPacketSize0 = 64,
73         .idVendor = BOOTLOADER_MFG_ID,
74         .idProduct = BOOTLOADER_PROD_ID,
75         .bcdDevice = BOOTLOADER_PROD_VERSION,
76         .iManufacturer = STR_MANUFACTURER,
77         .iProduct = STR_PRODUCT,
78         .iSerialNumber = STR_SERIAL,
79         .bNumConfigurations = 1,
80 };
81
82 const struct usb_dfu_descriptor dfu_function = {
83         .bLength = sizeof(struct usb_dfu_descriptor),
84         .bDescriptorType = DFU_FUNCTIONAL,
85         .bmAttributes = USB_DFU_CAN_DOWNLOAD | USB_DFU_WILL_DETACH,
86         .wDetachTimeout = 255,
87         .wTransferSize = BLOCK_SIZE,
88         .bcdDFUVersion = 0x0100,
89 };
90
91 const struct usb_interface_descriptor iface = {
92         .bLength = USB_DT_INTERFACE_SIZE,
93         .bDescriptorType = USB_DT_INTERFACE,
94         .bInterfaceNumber = 0,
95         .bAlternateSetting = 0,
96         .bNumEndpoints = 0,
97         .bInterfaceClass = 0xFE, /* Device Firmware Upgrade */
98         .bInterfaceSubClass = 1,
99         .bInterfaceProtocol = 2,
100         .extra = &dfu_function,
101         .extralen = sizeof(dfu_function),
102 };
103
104 const struct usb_interface ifaces[] = {{
105         .num_altsetting = 1,
106         .altsetting = &iface,
107 }};
108
109 const struct usb_config_descriptor config = {
110         .bLength = USB_DT_CONFIGURATION_SIZE,
111         .bDescriptorType = USB_DT_CONFIGURATION,
112         .wTotalLength = 0,
113         .bNumInterfaces = 1,
114         .bConfigurationValue = 1,
115         .iConfiguration = 0,
116         .bmAttributes = USB_CONFIG_ATTR_DEFAULT,        // bus-powered
117         .bMaxPower = 50,                                // multiplied by 2 mA
118         .interface = ifaces,
119 };
120
121 static byte dfu_getstatus(usbd_device *usbd_dev UNUSED, u32 *bwPollTimeout)
122 {
123         switch (dfu_state) {
124         case STATE_DFU_DNLOAD_SYNC:
125                 dfu_state = STATE_DFU_DNBUSY;
126                 *bwPollTimeout = 100;
127                 return DFU_STATUS_OK;
128         case STATE_DFU_MANIFEST_SYNC:
129                 /* Device will reset when read is complete. */
130                 dfu_state = STATE_DFU_MANIFEST;
131                 return DFU_STATUS_OK;
132         case STATE_DFU_ERROR:
133                 return DFU_STATUS_ERR_VERIFY;
134         default:
135                 return DFU_STATUS_OK;
136         }
137 }
138
139 static void dfu_getstatus_complete(usbd_device *usbd_dev UNUSED, struct usb_setup_data *req UNUSED)
140 {
141         switch (dfu_state) {
142         case STATE_DFU_DNBUSY:
143                 if (prog.blocknum == 0) {
144                         // The "flash in progress" word is programmed as 0xffff first and reset later
145                         *(u16*)(prog.buf + HDR_FLASH_IN_PROGRESS) = 0xffff;
146                 }
147                 u32 baseaddr = BOOTLOADER_APP_START + prog.blocknum * BLOCK_SIZE;
148                 DEBUG("DFU: Block %u -> %08x + %u\n", prog.blocknum, (uint) baseaddr, prog.len);
149                 flash_unlock();
150                 flash_erase_page(baseaddr);
151                 for (uint i = 0; i < prog.len; i += 2) {
152                         u16 data = *(u16 *)(prog.buf + i);
153                         flash_program_half_word(baseaddr + i, data);
154                 }
155                 flash_lock();
156                 for (uint i = 0; i < prog.len; i++) {
157                         if (*(byte *)(baseaddr + i) != prog.buf[i]) {
158                                 DEBUG("DFU: Verification failed\n");
159                                 dfu_state = STATE_DFU_ERROR;
160                         }
161                 }
162                 dfu_state = STATE_DFU_DNLOAD_IDLE;
163                 return;
164         case STATE_DFU_MANIFEST:
165                 // At the very end, re-flash the "flash in progress" word
166                 flash_unlock();
167                 flash_program_half_word(BOOTLOADER_APP_START + 0x20, 0);
168                 flash_lock();
169                 dfu_state = STATE_DFU_MANIFEST_WAIT_RESET;
170                 return;
171         default:
172                 return;
173         }
174 }
175
176 static enum usbd_request_return_codes dfu_control_request(usbd_device *usbd_dev,
177         struct usb_setup_data *req,
178         byte **buf,
179         u16 *len,
180         void (**complete)(usbd_device *usbd_dev, struct usb_setup_data *req))
181 {
182         if ((req->bmRequestType & 0x7F) != 0x21)
183                 return USBD_REQ_NOTSUPP; /* Only accept class request. */
184
185         DEBUG("DFU: Request %02x in state %d\n", req->bRequest, dfu_state);
186
187         switch (req->bRequest) {
188         case DFU_DNLOAD:
189                 if (len == NULL || *len == 0) {
190                         dfu_state = STATE_DFU_MANIFEST_SYNC;
191                 } else {
192                         /* Copy download data for use on GET_STATUS. */
193                         prog.blocknum = req->wValue;
194                         prog.len = *len;
195                         memcpy(prog.buf, *buf, *len);
196                         dfu_state = STATE_DFU_DNLOAD_SYNC;
197                 }
198                 return USBD_REQ_HANDLED;
199         case DFU_CLRSTATUS:
200                 /* Clear error and return to dfuIDLE. */
201                 if (dfu_state == STATE_DFU_ERROR)
202                         dfu_state = STATE_DFU_IDLE;
203                 return USBD_REQ_HANDLED;
204         case DFU_ABORT:
205                 /* Abort returns to dfuIDLE state. */
206                 dfu_state = STATE_DFU_IDLE;
207                 return USBD_REQ_HANDLED;
208         case DFU_UPLOAD:
209                 /* Upload not supported for now. */
210                 return USBD_REQ_NOTSUPP;
211         case DFU_GETSTATUS: {
212                 u32 bwPollTimeout = 0; /* 24-bit number of milliseconds */
213                 (*buf)[0] = dfu_getstatus(usbd_dev, &bwPollTimeout);
214                 (*buf)[1] = bwPollTimeout & 0xFF;
215                 (*buf)[2] = (bwPollTimeout >> 8) & 0xFF;
216                 (*buf)[3] = (bwPollTimeout >> 16) & 0xFF;
217                 (*buf)[4] = dfu_state;
218                 (*buf)[5] = 0; /* iString not used here */
219                 *len = 6;
220                 *complete = dfu_getstatus_complete;
221                 return USBD_REQ_HANDLED;
222                 }
223         case DFU_GETSTATE:
224                 /* Return state with no state transition. */
225                 *buf[0] = dfu_state;
226                 *len = 1;
227                 return USBD_REQ_HANDLED;
228         }
229
230         return USBD_REQ_NOTSUPP;
231 }
232
233 static void dfu_set_config(usbd_device *usbd_dev, u16 wValue UNUSED)
234 {
235         usbd_register_control_callback(
236                                 usbd_dev,
237                                 USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE,
238                                 USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT,
239                                 dfu_control_request);
240 }
241
242 static void dfu_reset(void)
243 {
244         dfu_state = STATE_DFU_IDLE;
245 }
246
247 /*
248  *  This is a modified version of rcc_clock_setup_in_hsi_out_48mhz(),
249  *  which properly turns off the PLL before setting its parameters.
250  */
251 static void my_rcc_clock_setup_in_hsi_out_48mhz(void)
252 {
253         /* Enable internal high-speed oscillator. */
254         rcc_osc_on(RCC_HSI);
255         rcc_wait_for_osc_ready(RCC_HSI);
256
257         /* Select HSI as SYSCLK source. */
258         rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
259
260         // XXX: Disable PLL
261         rcc_osc_off(RCC_PLL);
262
263         /*
264          * Set prescalers for AHB, ADC, ABP1, ABP2.
265          * Do this before touching the PLL (TODO: why?).
266          */
267         rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV);       /*Set.48MHz Max.72MHz */
268         rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8);     /*Set. 6MHz Max.14MHz */
269         rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2);        /*Set.24MHz Max.36MHz */
270         rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV);       /*Set.48MHz Max.72MHz */
271         rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_CLK_NODIV);  /*Set.48MHz Max.48MHz */
272
273         /*
274          * Sysclk runs with 48MHz -> 1 waitstates.
275          * 0WS from 0-24MHz
276          * 1WS from 24-48MHz
277          * 2WS from 48-72MHz
278          */
279         flash_set_ws(FLASH_ACR_LATENCY_1WS);
280
281         /*
282          * Set the PLL multiplication factor to 12.
283          * 8MHz (internal) * 12 (multiplier) / 2 (PLLSRC_HSI_CLK_DIV2) = 48MHz
284          */
285         rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL12);
286
287         /* Select HSI/2 as PLL source. */
288         rcc_set_pll_source(RCC_CFGR_PLLSRC_HSI_CLK_DIV2);
289
290         /* Enable PLL oscillator and wait for it to stabilize. */
291         rcc_osc_on(RCC_PLL);
292         rcc_wait_for_osc_ready(RCC_PLL);
293
294         /* Select PLL as SYSCLK source. */
295         rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);
296
297         /* Set the peripheral clock frequencies used */
298         rcc_ahb_frequency = 48000000;
299         rcc_apb1_frequency = 24000000;
300         rcc_apb2_frequency = 48000000;
301 }
302
303 static void clock_plain_hsi(void)
304 {
305         // Select HSI as SYSCLK source
306         rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);
307
308         // Disable PLL
309         rcc_osc_off(RCC_PLL);
310
311         // Set prescalers for AHB, ADC, ABP1, ABP2, USB to defaults
312         rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV);
313         rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV2);
314         rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_NODIV);
315         rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV);
316         rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_VCO_CLK_DIV3);
317 }
318
319 static void reset_peripherals(void)
320 {
321         // Turn off clock to all peripherals and reset them
322         RCC_AHBENR = 0x00000014;
323         RCC_APB1ENR = 0;
324         RCC_APB2ENR = 0;
325         RCC_APB1RSTR = 0x22fec9ff;
326         RCC_APB2RSTR = 0x0038fffd;
327         RCC_APB1RSTR = 0;
328         RCC_APB2RSTR = 0;
329 }
330
331 int main(void)
332 {
333         usbd_device *usbd_dev;
334
335         reset_peripherals();
336
337         // Flash programming requires running on the internal oscillator
338         my_rcc_clock_setup_in_hsi_out_48mhz();
339
340         rcc_periph_clock_enable(RCC_GPIOA);
341         rcc_periph_clock_enable(RCC_GPIOC);
342         rcc_periph_clock_enable(RCC_USB);
343         rcc_periph_clock_enable(RCC_CRC);
344
345 #ifdef DEBUG_USART
346         // Currently, only USART1 is supported
347         rcc_periph_clock_enable(RCC_USART1);
348         rcc_periph_reset_pulse(RST_USART1);
349         gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO9);
350
351         usart_set_baudrate(USART1, 115200);
352         usart_set_databits(USART1, 8);
353         usart_set_stopbits(USART1, USART_STOPBITS_1);
354         usart_set_mode(USART1, USART_MODE_TX);
355         usart_set_parity(USART1, USART_PARITY_NONE);
356         usart_set_flow_control(USART1, USART_FLOWCONTROL_NONE);
357
358         usart_enable(USART1);
359 #endif
360
361 #ifdef DEBUG_LED_BLUEPILL
362         // BluePill LED
363         gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
364         debug_led(1);
365 #endif
366
367         // Systick: set to overflow in 1 ms, will use only the overflow flag, no interrupts
368         systick_set_frequency(1000, CPU_CLOCK_MHZ * 1000000);
369         systick_clear();
370         systick_counter_enable();
371
372         desig_get_unique_id_as_dfu(usb_serial_number);
373
374         DEBUG("DFU: Entered boot-loader (SN %s)\n", usb_serial_number);
375
376         // Simulate USB disconnect
377         gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO11 | GPIO12);
378         gpio_clear(GPIOA, GPIO11 | GPIO12);
379         for (uint i=0; i<100; i++) {
380                 while (!systick_get_countflag())
381                         ;
382         }
383
384         DEBUG("DFU: Ready\n");
385         debug_led(0);
386
387         usbd_dev = usbd_init(&st_usbfs_v1_usb_driver, &dev, &config, usb_strings, ARRAY_SIZE(usb_strings), usbd_control_buffer, sizeof(usbd_control_buffer));
388         usbd_register_reset_callback(usbd_dev, dfu_reset);
389         usbd_register_set_config_callback(usbd_dev, dfu_set_config);
390
391 restart: ;
392
393         uint timeout = 5000;
394         while (timeout || (dfu_state != STATE_DFU_IDLE && dfu_state != STATE_DFU_MANIFEST_WAIT_RESET)) {
395                 usbd_poll(usbd_dev);
396                 if (timeout && systick_get_countflag()) {
397                         timeout--;
398                         if (!(timeout & 0x3f))
399                                 debug_led_toggle();
400                 }
401         }
402
403         u32 *app = (u32 *) BOOTLOADER_APP_START;
404         u32 sp = app[0];
405         u32 pc = app[1];
406         u32 len = app[HDR_LENGTH/4];
407         u16 flash_in_progress = *(u16 *)(BOOTLOADER_APP_START + HDR_FLASH_IN_PROGRESS);
408
409         crc_reset();
410         u32 crc = crc_calculate_block(app, len/4);
411         u32 want_crc = *(u32 *)(BOOTLOADER_APP_START + len);
412         DEBUG("DFU: fip=%04x, crc=%08x, want=%08x, len=%u\n", (uint) flash_in_progress, (uint) crc, (uint) want_crc, (uint) len);
413         if (flash_in_progress || crc != want_crc) {
414                 DEBUG("DFU: Bad firmware\n");
415                 goto restart;
416         }
417
418         DEBUG("DFU: Starting application (sp=%08x, pc=%08x)\n", (uint) sp, (uint) pc);
419
420 #ifdef DEBUG_USART
421         while (!usart_get_flag(DEBUG_USART, USART_FLAG_TC))
422                 ;
423 #endif
424         debug_led(0);
425
426         reset_peripherals();
427         clock_plain_hsi();
428
429         /* Set vector table base address. */
430         SCB_VTOR = BOOTLOADER_APP_START;
431
432         /* Initialize master stack pointer. */
433         asm volatile("msr msp, %0"::"g" (sp));
434
435         /* Jump to application. */
436         ((void (*)(void)) pc)();
437 }