From 1405100dff33d03a5faeedd602493ebb8ce3521c Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sat, 4 Mar 2017 19:17:27 +0100 Subject: [PATCH] DFU: Introduce board/app-specific override for booting in DFU mode Using the USBDFU_OverrideEnterDFU() function, a board/application can define extra conditions when the system should boot in DFU mode, even if it was not explicitly switched to DFU mode from the application. The app/dfu/main.c uses this mechanism to boot into DFU mode if the stack + reset vector addresses are not plausible (i.e. some random junk appears to be flashed in the application partition) or if the user places a jumper accross the RxD+TxD lines of the debug UART. The idea is that the system can be recovered by placing this jumper and then re-installing the application from DFU. --- firmware/apps/dfu/main.c | 51 +++++++++++++++++++ .../usb/device/dfu/dfu.h | 1 + .../usb/device/dfu/dfu_driver.c | 8 +++ .../common/source/board_cstartup_gnu.c | 2 +- 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/firmware/apps/dfu/main.c b/firmware/apps/dfu/main.c index 07441e52..32c855c6 100644 --- a/firmware/apps/dfu/main.c +++ b/firmware/apps/dfu/main.c @@ -103,6 +103,57 @@ int USBDFU_handle_upload(uint8_t altif, unsigned int offset, return req_len; } +static int uart_has_loopback_jumper(void) +{ + unsigned int i; + const Pin uart_loopback_pins[] = { + {PIO_PA9A_URXD0, PIOA, ID_PIOA, PIO_INPUT, PIO_DEFAULT}, + {PIO_PA10A_UTXD0, PIOA, ID_PIOA, PIO_OUTPUT_0, PIO_DEFAULT} + }; + + /* Configure UART pins as I/O */ + PIO_Configure(uart_loopback_pins, PIO_LISTSIZE(uart_loopback_pins)); + + for (i = 0; i < 10; i++) { + /* Set TxD high; abort if RxD doesn't go high either */ + PIO_Set(&uart_loopback_pins[1]); + if (!PIO_Get(&uart_loopback_pins[0])) + return 0; + /* Set TxD low, abort if RxD doesn't go low either */ + PIO_Clear(&uart_loopback_pins[1]); + if (PIO_Get(&uart_loopback_pins[0])) + return 0; + } + /* if we reached here, RxD always follows TxD and thus a + * loopback jumper has been placed on RxD/TxD, and we will boot + * into DFU unconditionally */ + return 1; +} + +/* using this function we can determine if we should enter DFU mode + * during boot, or if we should proceed towards the application/runtime */ +int USBDFU_OverrideEnterDFU(void) +{ + uint32_t *app_part = (uint32_t *)FLASH_ADDR(0); + + /* If the loopback jumper is set, we enter DFU mode */ + if (uart_has_loopback_jumper()) + return 1; + + /* if the first word of the application partition doesn't look + * like a stack pointer (i.e. point to RAM), enter DFU mode */ + if ((app_part[0] < IRAM_ADDR) || + ((uint8_t *)app_part[0] > IRAM_END)) + return 1; + + /* if the second word of the application partition doesn't look + * like a function from flash (reset vector), enter DFU mode */ + if (((uint32_t *)app_part[1] < app_part) || + ((uint8_t *)app_part[1] > IFLASH_END)) + return 1; + + return 0; +} /* returns '1' in case we should break any endless loop */ static void check_exec_dbg_cmd(void) diff --git a/firmware/atmel_softpack_libraries/usb/device/dfu/dfu.h b/firmware/atmel_softpack_libraries/usb/device/dfu/dfu.h index 4af3e3b6..87ac0605 100644 --- a/firmware/atmel_softpack_libraries/usb/device/dfu/dfu.h +++ b/firmware/atmel_softpack_libraries/usb/device/dfu/dfu.h @@ -110,6 +110,7 @@ extern int USBDFU_handle_dnload(uint8_t altif, unsigned int offset, uint8_t *data, unsigned int len); extern int USBDFU_handle_upload(uint8_t altif, unsigned int offset, uint8_t *data, unsigned int req_len); +extern int USBDFU_OverrideEnterDFU(void); /* function to be called at end of EP0 handler during runtime */ void USBDFU_Runtime_RequestHandler(const USBGenericRequest *request); diff --git a/firmware/atmel_softpack_libraries/usb/device/dfu/dfu_driver.c b/firmware/atmel_softpack_libraries/usb/device/dfu/dfu_driver.c index f9e2d8eb..10f015b6 100644 --- a/firmware/atmel_softpack_libraries/usb/device/dfu/dfu_driver.c +++ b/firmware/atmel_softpack_libraries/usb/device/dfu/dfu_driver.c @@ -472,6 +472,14 @@ void USBDFU_SwitchToApp(void) NVIC_SystemReset(); } +/* A board can provide a function overriding this, enabling a + * board-specific 'boot into DFU' override, like a specific GPIO that + * needs to be pulled a certain way. */ +WEAK int USBDFU_OverrideEnterDFU(void) +{ + return 0; +} + void USBDCallbacks_RequestReceived(const USBGenericRequest *request) { USBDFU_DFU_RequestHandler(request); diff --git a/firmware/libboard/common/source/board_cstartup_gnu.c b/firmware/libboard/common/source/board_cstartup_gnu.c index 3d92d2fb..ce60b091 100644 --- a/firmware/libboard/common/source/board_cstartup_gnu.c +++ b/firmware/libboard/common/source/board_cstartup_gnu.c @@ -157,7 +157,7 @@ void ResetException( void ) /* we are before the text segment has been relocated, so g_dfu is * not initialized yet */ g_dfu = &_g_dfu; - if (g_dfu->magic != USB_DFU_MAGIC) { + if ((g_dfu->magic != USB_DFU_MAGIC) && !USBDFU_OverrideEnterDFU()) { BootIntoApp(); /* Infinite loop */ while ( 1 ) ;