laforge has submitted this change. ( https://gerrit.osmocom.org/c/osmo-asf4-dfu/+/42273?usp=email )
Change subject: dfu: download: flash the first block in manifest phase ......................................................................
dfu: download: flash the first block in manifest phase
To prevent half flashed applications, erase the first page when dfu downloading starts and save the first block for later. In manifest stage, flash the first block. If the first 4 byte are 0xffffffff, the board won't boot into application and go into the dfu bootloader.
Change-Id: I894f3ee71587ccb287e92d7025039954991c631f --- M usb/class/dfu/device/dfudf.c M usb/class/dfu/device/dfudf.h M usb_start.c 3 files changed, 58 insertions(+), 10 deletions(-)
Approvals: Jenkins Builder: Verified laforge: Looks good to me, approved
diff --git a/usb/class/dfu/device/dfudf.c b/usb/class/dfu/device/dfudf.c index f931260..389f07f 100644 --- a/usb/class/dfu/device/dfudf.c +++ b/usb/class/dfu/device/dfudf.c @@ -45,6 +45,11 @@ uint8_t dfu_download_data[512]; uint16_t dfu_download_length = 0; size_t dfu_download_offset = 0; + +/* buffer the first block, to write it last */ +uint8_t dfu_download_data_first[512]; +volatile uint16_t dfu_download_length_first = 0; + bool dfu_manifestation_complete = false;
/** diff --git a/usb/class/dfu/device/dfudf.h b/usb/class/dfu/device/dfudf.h index 818252d..390aace 100644 --- a/usb/class/dfu/device/dfudf.h +++ b/usb/class/dfu/device/dfudf.h @@ -51,6 +51,10 @@ extern uint16_t dfu_download_length; /** Offset of where the downloaded data should be flashed in bytes */ extern size_t dfu_download_offset; + +extern uint8_t dfu_download_data_first[512]; +extern volatile uint16_t dfu_download_length_first; + /** If manifestation (firmware flash and check) is complete */ extern bool dfu_manifestation_complete;
diff --git a/usb_start.c b/usb_start.c index 0b99746..cb2bc51 100644 --- a/usb_start.c +++ b/usb_start.c @@ -157,6 +157,7 @@
uint32_t application_start_address = BL_SIZE_BYTE; ASSERT(application_start_address > 0); + int rc;
while (true) { // main DFU infinite loop enum usb_dfu_state last_dfu_state = dfu_state; @@ -167,7 +168,22 @@ case USB_DFU_STATE_DFU_DNBUSY: // there is some data to be flashed LED_SYSTEM_off(); // switch LED off to indicate we are flashing if (dfu_download_length > 0) { // there is some data to be flashed - int32_t rc = flash_write(&FLASH_0, application_start_address + dfu_download_offset, dfu_download_data, dfu_download_length); // write downloaded data chunk to flash + /* The first block will be only erased, to ensure the + * full firmware has been written. + * In case of a power loss, the bootloader won't boot applications + * if the first 4 byte are 0xffffffff + */ + if (dfu_download_offset == 0) { + dfu_download_length_first = dfu_download_length; + memcpy(dfu_download_data_first, dfu_download_data, dfu_download_length); + /* erasing the first page is enough, flash_write will keep care of the remaining ones */ + rc = flash_erase(&FLASH_0, application_start_address, 1); + } else { + // write downloaded data chunk to flash + rc = flash_write(&FLASH_0, application_start_address + dfu_download_offset, + dfu_download_data, dfu_download_length); + } + if (ERR_NONE == rc) { dfu_state = USB_DFU_STATE_DFU_DNLOAD_IDLE; // indicate flashing this block has been completed } else { // there has been a programming error @@ -186,16 +202,39 @@ } LED_SYSTEM_on(); // switch LED on to indicate USB DFU can resume break; - case USB_DFU_STATE_DFU_MANIFEST: // we can start manifestation (finish flashing) - // in theory every DFU files should have a suffix to with a CRC to check the data - // in practice most downloaded files are just the raw binary with DFU suffix - CRITICAL_SECTION_ENTER(); - dfu_manifestation_complete = true; // we completed flashing and all checks - if (usb_dfu_func_desc->bmAttributes & USB_DFU_ATTRIBUTES_MANIFEST_TOLERANT) { - dfu_state = USB_DFU_STATE_DFU_MANIFEST_SYNC; - } else { - dfu_state = USB_DFU_STATE_DFU_MANIFEST_WAIT_RESET; + case USB_DFU_STATE_DFU_MANIFEST: + /* finish flashing by writing the first block */ + rc = flash_write(&FLASH_0, application_start_address, dfu_download_data_first, + dfu_download_length_first); + /* try to clear it, to ensure it doesn't boot into a broken application */ + if (rc != ERR_NONE) { + flash_erase(&FLASH_0, application_start_address, 1); } + + CRITICAL_SECTION_ENTER(); + switch (rc) { + case ERR_NONE: + if (usb_dfu_func_desc->bmAttributes & USB_DFU_ATTRIBUTES_MANIFEST_TOLERANT) { + dfu_state = USB_DFU_STATE_DFU_MANIFEST_SYNC; + } else { + dfu_state = USB_DFU_STATE_DFU_MANIFEST_WAIT_RESET; + } + dfu_status = USB_DFU_STATUS_OK; + break; + case ERR_BAD_ADDRESS: + dfu_state = USB_DFU_STATE_DFU_ERROR; + dfu_status = USB_DFU_STATUS_ERR_ADDRESS; + break; + case ERR_DENIED: + dfu_state = USB_DFU_STATE_DFU_ERROR; + dfu_status = USB_DFU_STATUS_ERR_WRITE; + break; + default: + dfu_state = USB_DFU_STATE_DFU_ERROR; + dfu_status = USB_DFU_STATUS_ERR_PROG; + break; + } + dfu_manifestation_complete = true; // we completed flashing and all checks CRITICAL_SECTION_LEAVE(); break; case USB_DFU_STATE_DFU_MANIFEST_WAIT_RESET: