/* * * Copyright (c) 2020 Project CHIP Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "mw320_ota.h" #include "fsl_debug_console.h" #include "task.h" #if (MW320_OTA_TEST == 1) #include "gpio_fw.h" unsigned char * mw320_ota_mcufw = gpio_fw_bin; unsigned int mw320_ota_len = gpio_fw_bin_len; #endif // MW320_OTA_TEST extern "C" { #include "crc32.h" #include "mflash_drv.h" #include "partition.h" typedef int (*data_fetch)(unsigned char * buf, size_t len); struct partition_entry * part_get_passive_partition(struct partition_entry * p1, struct partition_entry * p2); } #define FIRMWARE_UPDATE_BUF_SIZE 256 // Max buffer size can be written in 1 transaction #define SEC_FW_SIG_LEN 284 #define FW_MAGIC_STR (('M' << 0) | ('R' << 8) | ('V' << 16) | ('L' << 24)) #define FW_MAGIC_SIG ((0x7BUL << 0) | (0xF1UL << 8) | (0x9CUL << 16) | (0x2EUL << 24)) #define SEC_FW_MAGIC_SIG (('S' << 0) | ('B' << 8) | ('F' << 16) | ('H' << 24)) /* * Firmware magic signature * * First word is the string "MRVL" and is endian invariant. * Second word = magic value 0x2e9cf17b. * Third word = time stamp (seconds since 00:00:00 UTC, January 1, 1970). */ struct img_hdr { uint32_t magic_str; uint32_t magic_sig; uint32_t time; uint32_t seg_cnt; uint32_t entry; }; struct seg_hdr { uint32_t type; uint32_t offset; uint32_t len; uint32_t laddr; uint32_t crc; }; struct tlv_hdr { uint32_t magic; uint32_t len; uint32_t crc; }; typedef struct tlv_hdr img_sec_hdr; static uint32_t calculate_image_crc(uint32_t flash_addr, uint32_t size) { int32_t result; uint32_t buf[32]; uint32_t addr = flash_addr; uint32_t crc = 0U; PRINTF("Calculate image CRC\r\n"); for (addr = flash_addr; addr < flash_addr + size - sizeof(buf); addr += sizeof(buf)) { result = mflash_drv_read(addr, buf, sizeof(buf)); if (result != WM_SUCCESS) { assert(false); } crc = soft_crc32(buf, sizeof(buf), crc); } /* Remaining data */ result = mflash_drv_read(addr, buf, flash_addr + size - addr); if (result != WM_SUCCESS) { assert(false); } crc = soft_crc32(buf, flash_addr + size - addr, crc); return crc; } static struct partition_entry * get_passive_firmware(void) { short history = 0; struct partition_entry *f1, *f2; f1 = part_get_layout_by_id(FC_COMP_FW, &history); f2 = part_get_layout_by_id(FC_COMP_FW, &history); if (f1 == NULL || f2 == NULL) { PRINTF("Unable to retrieve flash layout\r\n"); return NULL; } return (struct partition_entry *) part_get_passive_partition(f1, f2); } static int fw_update_begin(struct partition_entry * p) { /* Erase the passive partition before updating it */ if (mflash_drv_erase(p->start, p->size)) { PRINTF("Failed to erase partition\r\n"); return -WM_FAIL; } return WM_SUCCESS; } static int fw_update_data(struct partition_entry * p, const char * data, size_t datalen) { int32_t result; result = mflash_drv_write(p->start, (uint32_t *) data, datalen); if (result != WM_SUCCESS) { PRINTF("Failed to write partition\r\n"); return -WM_FAIL; } p->start += datalen; return WM_SUCCESS; } static int verify_load_firmware(uint32_t flash_addr, uint32_t size) { struct img_hdr ih; struct seg_hdr sh; int32_t result; if (size < (sizeof(ih) + sizeof(sh))) { return -WM_FAIL; } result = mflash_drv_read(flash_addr, (uint32_t *) &ih, sizeof(ih)); if (result != WM_SUCCESS) { assert(false); } /* MCUXpresso SDK image has only 1 segment */ if ((ih.magic_str != FW_MAGIC_STR) || (ih.magic_sig != FW_MAGIC_SIG) || ih.seg_cnt != 1U) { return -WM_FAIL; } result = mflash_drv_read(flash_addr + sizeof(ih), (uint32_t *) &sh, sizeof(sh)); if (result != kStatus_Success) { assert(false); } /* Image size should just cover segment end. */ if (sh.len + sh.offset != size) { return -WM_FAIL; } if (calculate_image_crc(flash_addr + sh.offset, sh.len) != sh.crc) { return -WM_FAIL; } return WM_SUCCESS; } static void mw320_dev_reset(unsigned int delay_ms) { vTaskDelay(1000 / portTICK_PERIOD_MS); NVIC_SystemReset(); return; } // ============================================================================ typedef struct _fw_update_param { // Accumulated size uint32_t pump_size; // How many bytes has been written uint32_t p_start; // Start address of the partition img_sec_hdr ish; // ish from downloaded firmware buffer // int hdr_size; // Pointer to passive firmware struct partition_entry part_arg; // Callback function to write the data // data_fetch data_fetch_cb; } fw_update_param_t; static fw_update_param_t g_fmup_param; /* Initialize the ota function by getting an id */ fw_update_id_t mw320_fw_update_begin(void) { int error; struct partition_entry * p; if (g_fmup_param.pump_size != 0) { PRINTF("Warning, wt_addr is not 0. last fw_abort may be missing \r\n"); } memset(&g_fmup_param, 0, sizeof(g_fmup_param)); // ---------------------------------------------------- // Initialize the firmware update parameters p = get_passive_firmware(); memcpy(&g_fmup_param.part_arg, p, sizeof(struct partition_entry)); g_fmup_param.p_start = p->start; // ---------------------------------------------------- // Erase the partition error = fw_update_begin(&g_fmup_param.part_arg); if (error != WM_SUCCESS) { return NULL; } return &g_fmup_param; } /* Write the received chunk to the partition */ int mw320_fw_update_wrblock(fw_update_id_t fwup_id, unsigned char * pblock, unsigned int blksize) { fw_update_param_t * pfwup_parm = (fw_update_param_t *) fwup_id; unsigned int wr_size = 0; // How many bytes has been written unsigned char * buf = pblock; int error; if (pfwup_parm != &g_fmup_param) { PRINTF("Incorrect fw update id \r\n"); return -WM_FAIL; } // Check if the size is available if ((pfwup_parm->pump_size + blksize) > pfwup_parm->part_arg.size) { PRINTF("[Error], Firmware size is too big. (limit, current, new)=(%u, %u, %u) \r\n", pfwup_parm->part_arg.size, pfwup_parm->pump_size, blksize); // purge_stream_bytes(pfwup_parm->data_fetch_cb, blksize, pblock, FIRMWARE_UPDATE_BUF_SIZE); return -WM_FAIL; } // If it's the 1st block if (pfwup_parm->p_start == pfwup_parm->part_arg.start) { memcpy(&pfwup_parm->ish, buf, sizeof(img_sec_hdr)); } /* Write the data chunk to firmware partition */ while (wr_size < blksize) { unsigned int blk_left = blksize - wr_size; unsigned int chk_size = (blk_left < FIRMWARE_UPDATE_BUF_SIZE) ? blk_left : FIRMWARE_UPDATE_BUF_SIZE; error = fw_update_data(&pfwup_parm->part_arg, (char *) buf, chk_size); if (error) { PRINTF("Failed to write firmware data \r\n"); return -WM_FAIL; } wr_size += chk_size; pfwup_parm->pump_size += chk_size; buf += chk_size; } return WM_SUCCESS; } /* Finalize the firmware update. - rst_delay_sec: Delay to reset the device >= 0: Will reset the device after rst_delay_sec seconds <0: No reset */ int mw320_fw_update_end(fw_update_id_t fwup_id, int rst_delay_sec) { fw_update_param_t * pfwup_parm = (fw_update_param_t *) fwup_id; int error; if (pfwup_parm != &g_fmup_param) { PRINTF("Incorrect fw update id \r\n"); return -WM_FAIL; } // Validate the firmware data in flash PRINTF("Firmware verification start ... filesize = %d\r\n", pfwup_parm->pump_size); if (pfwup_parm->ish.magic == SEC_FW_MAGIC_SIG) { error = verify_load_firmware((pfwup_parm->p_start + SEC_FW_SIG_LEN), (pfwup_parm->pump_size - SEC_FW_SIG_LEN)); } else { error = verify_load_firmware(pfwup_parm->p_start, pfwup_parm->pump_size); } if (error != WM_SUCCESS) { PRINTF("FW verification fail! Keep the same firmware \r\n"); return -WM_FAIL; } PRINTF("FW verification pass, switch the firmware partition \r\n"); // Restore the start pfwup_parm->part_arg.start = pfwup_parm->p_start; // Switch the active partition to the new one part_set_active_partition(&pfwup_parm->part_arg); // Reset the device if requested if (rst_delay_sec > 0) { mw320_dev_reset(rst_delay_sec * 1000); } return WM_SUCCESS; } /* Abort the ota function */ int mw320_fw_update_abort(fw_update_id_t fwup_id) { fw_update_param_t * pfwup_parm = (fw_update_param_t *) fwup_id; if (pfwup_parm != &g_fmup_param) { PRINTF("Incorrect fw update id \r\n"); return -WM_FAIL; } memcpy(&g_fmup_param, 0, sizeof(g_fmup_param)); return WM_SUCCESS; } #if (MW320_OTA_TEST == 1) // Module Test function // This is also an example that how to use the interface void mw320_fw_update_test(void) { unsigned int wr_size = 0; int error; fw_update_id_t fwupid = mw320_fw_update_begin(); while (wr_size < mw320_ota_len) { unsigned int chk_size = ((mw320_ota_len - wr_size) < 1024) ? (mw320_ota_len - wr_size) : 1024; PRINTF("===> Wt (%d, %d)\r\n", wr_size, chk_size); error = mw320_fw_update_wrblock(fwupid, &mw320_ota_mcufw[wr_size], chk_size); if (error != WM_SUCCESS) { break; } wr_size += chk_size; } if (error == WM_SUCCESS) { mw320_fw_update_end(fwupid, -1); } return; } #endif // MW320_OTA_TEST