/** * * \file * * \brief WINC Flash Interface. * * Copyright (c) 2017-2018 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * * \page License * * Subject to your compliance with these terms, you may use Microchip * software and any derivatives exclusively with Microchip products. * It is your responsibility to comply with third party license terms applicable * to your use of third party software (including open source software) that * may accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * \asf_license_stop * */ /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ #include "driver/include/m2m_flash.h" #include "driver/include/m2m_wifi.h" #include "driver/source/nmflash.h" #include "driver/source/m2m_hif.h" #include "spi_flash/include/spi_flash.h" #include "nmdrv.h" /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* GLOBALS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static tpfDataAccessFn gpfAppFn = NULL; static uint8 gau8ItemIdentifier[20] = {0}; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTIONS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static sint8 transfer_init(void) { sint8 ret = FLASH_RETURN_OK; /* Check module was initialized. */ if (gu8Init == 0) ret = FLASH_ERR_UNINIT; return ret; } static sint8 init_access(void) { sint8 ret = FLASH_RETURN_OK; gu8Reset = 0; if (m2m_wifi_reinit_hold() != M2M_SUCCESS) ret = FLASH_ERR_WINC_ACCESS; return ret; } static sint8 commit_access(tstrFlashAccess *pstrFlashAccess) { sint8 ret = FLASH_RETURN_OK; sint8 status = M2M_ERR_FAIL; tstrFlashAccessPersistent *pstrPersistentInfo = &pstrFlashAccess->strPersistentInfo; /* * To begin with, flash is unchanged. Later, when first flash erase/write occurs, this flag * will be cleared. */ pstrPersistentInfo->u8ModeFlags |= FLASH_MODE_FLAGS_UNCHANGED; if (pstrPersistentInfo->u8ModeFlags & FLASH_MODE_FLAGS_CS_SWITCH) { uint8 target = 0; if (image_get_target(&target) != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } if (target > 0) pstrPersistentInfo->u8ModeFlags |= FLASH_MODE_FLAGS_CS_SWITCH_TARGET; } status = spi_flash_read((uint8*)pstrPersistentInfo, HOST_CONTROL_FLASH_OFFSET, FLASH_SIG_STA_SZ); if (status == M2M_SUCCESS) { if ((pstrPersistentInfo->u32Signature != FLASH_SIGNATURE) || (pstrPersistentInfo->enuTransferStatus != FLASH_STATUS_EMPTY)) status = spi_flash_erase(HOST_CONTROL_FLASH_OFFSET, HOST_CONTROL_FLASH_SZ); } if (status == M2M_SUCCESS) { pstrPersistentInfo->u32Signature = FLASH_SIGNATURE; pstrPersistentInfo->enuTransferStatus = FLASH_STATUS_NOT_ACTIVE; status = winc_flash_write_verify((uint8*)pstrPersistentInfo, HOST_CONTROL_FLASH_OFFSET, FLASH_SIG_STA_SZ); if (status == M2M_SUCCESS) { status = winc_flash_write_verify((uint8*)pstrPersistentInfo, HOST_CONTROL_FLASH_OFFSET, sizeof(tstrFlashAccessPersistent)); if (status == M2M_SUCCESS) { pstrPersistentInfo->enuTransferStatus = FLASH_STATUS_ACTIVE; status = winc_flash_write_verify((uint8*)pstrPersistentInfo, HOST_CONTROL_FLASH_OFFSET, FLASH_SIG_STA_SZ); gu16LastAccessId = pstrPersistentInfo->u16AppId; gu8Success = 0; gu8Changed = 0; } } } if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } ret = transfer_run(pstrFlashAccess); ERR: return ret; } static sint8 register_app_fn(tpfDataAccessFn pfFn) { sint8 ret = FLASH_RETURN_OK; if (pfFn == NULL) ret = FLASH_ERR_PARAM; gpfAppFn = pfFn; return ret; } static sint8 app_data_access(tenuFlashDataFnCtl enuCtl, void *pvStr) { tstrDataAccessInitParamsApp init_params_app; tstrDataAccessParamsApp params_app; switch (enuCtl) { case FLASH_DATA_FN_INITIALIZE: { tstrDataAccessInitParams *init_params = (tstrDataAccessInitParams*)pvStr; init_params_app.u32TotalSize = init_params->u32TotalSize; if (init_params->u8Flags & FLASH_FN_FLAGS_READ) init_params_app.enuRW = FLASH_DATA_FN_READ; else if (init_params->u8Flags & FLASH_FN_FLAGS_WRITE) init_params_app.enuRW = FLASH_DATA_FN_WRITE; pvStr = &init_params_app; } break; case FLASH_DATA_FN_DATA: { tstrDataAccessParams *params = (tstrDataAccessParams*)pvStr; params_app.pu8Data = params->pu8Buf + params->u32DataOffset; params_app.u32DataSize = params->u32DataSize; pvStr = ¶ms_app; } break; case FLASH_DATA_FN_COMPLETE: case FLASH_DATA_FN_TERMINATE: break; } return gpfAppFn(enuCtl, pvStr); } sint8 m2m_flash_readimage(uint8 enuImageId, uint16 u16Id, tpfDataAccessFn pfDestFn, uint32 u32DestSize) { sint8 ret = FLASH_RETURN_OK; M2M_INFO("FA RdImage %d\n", enuImageId); ret = transfer_init(); if (ret < 0) goto ERR; ret = register_app_fn(pfDestFn); if (ret < 0) goto ERR; if (u32DestSize < OTA_IMAGE_SIZE) { ret = FLASH_ERR_SIZE; goto ERR; } if (enuImageId > FLASH_IMAGE_INACTIVE) { ret = FLASH_ERR_PARAM; goto ERR; } ret = init_access(); if (ret == FLASH_RETURN_OK) { /* Set parameters for whole transfer. */ tstrFlashAccess strFlashAccess; m2m_memset((uint8*)&strFlashAccess, 0, sizeof(tstrFlashAccess)); strFlashAccess.strPersistentInfo.u16AppId = u16Id; strFlashAccess.pfDestinationFn = app_data_access; switch (enuImageId) { case FLASH_IMAGE_ACTIVE: set_internal_info(&strFlashAccess.pfSourceFn, MEM_ID_WINC_ACTIVE); break; case FLASH_IMAGE_INACTIVE: set_internal_info(&strFlashAccess.pfSourceFn, MEM_ID_WINC_INACTIVE); break; } strFlashAccess.u32Size = OTA_IMAGE_SIZE; ret = commit_access(&strFlashAccess); } ERR: M2M_INFO("FAState:%d\n", ret); return ret; } sint8 m2m_flash_updateimage(uint8 u8Options, uint16 u16Id, tpfDataAccessFn pfSourceFn, uint32 u32SourceSize) { sint8 ret = FLASH_RETURN_OK; M2M_INFO("FA Image %d\n", u8Options); ret = transfer_init(); if (ret < 0) goto ERR; if (u8Options & FLASH_UPDATEIMAGE_OPTION_UPDATE) { uint8 au8ImageStart[4]; uint8 au8ImageCheck[] = {'N','M','I','S'}; tstrDataAccessInitParams init_params = {sizeof(au8ImageStart), FLASH_FN_FLAGS_READ}; sint8 status = M2M_SUCCESS; /* Check input parameters. */ ret = register_app_fn(pfSourceFn); if (ret < 0) goto ERR; if (u32SourceSize != OTA_IMAGE_SIZE) { ret = FLASH_ERR_PARAM; goto ERR; } status = app_data_access(FLASH_DATA_FN_INITIALIZE, &init_params); if (status == M2M_SUCCESS) { tstrDataAccessParams params = {au8ImageStart, sizeof(au8ImageStart), 0, sizeof(au8ImageStart)}; status = app_data_access(FLASH_DATA_FN_DATA, ¶ms); } if (status != M2M_SUCCESS) { ret = FLASH_ERR_LOCAL_ACCESS; goto ERR; } if (m2m_memcmp(au8ImageStart, au8ImageCheck, sizeof(au8ImageStart))) { ret = FLASH_ERR_WINC_CONFLICT; goto ERR; } } ret = init_access(); if (ret == FLASH_RETURN_OK) { /* Set parameters for whole transfer. */ tstrFlashAccess strFlashAccess; m2m_memset((uint8*)&strFlashAccess, 0, sizeof(tstrFlashAccess)); strFlashAccess.strPersistentInfo.u16AppId = u16Id; if (u8Options & FLASH_UPDATEIMAGE_OPTION_UPDATE) { strFlashAccess.strPersistentInfo.u8AccessFlags = FLASH_ACCESS_WINC_MASK | FLASH_ACCESS_OPTION_ERASE_FIRST; strFlashAccess.pfSourceFn = app_data_access; set_internal_info(&strFlashAccess.pfDestinationFn, MEM_ID_WINC_INACTIVE); strFlashAccess.u32Size = OTA_IMAGE_SIZE; } else strFlashAccess.u32Size = 0; if (u8Options & FLASH_UPDATEIMAGE_OPTION_VALIDATE) strFlashAccess.strPersistentInfo.u8ModeFlags |= FLASH_MODE_FLAGS_CS_VALIDATE_IMAGE; if (u8Options & FLASH_UPDATEIMAGE_OPTION_SWITCH) strFlashAccess.strPersistentInfo.u8ModeFlags |= FLASH_MODE_FLAGS_CS_SWITCH; strFlashAccess.strPersistentInfo.u8ModeFlags |= FLASH_MODE_FLAGS_CS; ret = commit_access(&strFlashAccess); } ERR: M2M_INFO("FAState:%d\n", ret); return ret; } static sint8 m2m_flash_rootcert_access(tenuFlashAccessItemMode enuMode, uint8 u8ModeOptions, uint8 u8AccessOptions, uint16 u16Id, tpfDataAccessFn pfFn, uint32 u32Size) { sint8 ret = FLASH_RETURN_OK; tstrRootCertEntryHeader strRootCertEntry; uint16 u16EntrySz = 0; M2M_INFO("FA Rootcert %d\n", enuMode); ret = transfer_init(); if (ret < 0) goto ERR; switch (enuMode) { case FLASH_ITEM_ADD: { sint8 status = M2M_SUCCESS; tstrDataAccessInitParams init_params = {sizeof(strRootCertEntry), FLASH_FN_FLAGS_READ}; // Read the entry header if (u32Size < sizeof(strRootCertEntry)) { ret = FLASH_ERR_PARAM; goto ERR; } status = pfFn(FLASH_DATA_FN_INITIALIZE, &init_params); if (status == M2M_SUCCESS) { tstrDataAccessParams params = {(uint8*)&strRootCertEntry, sizeof(strRootCertEntry), 0, sizeof(strRootCertEntry)}; status = pfFn(FLASH_DATA_FN_DATA, ¶ms); } if (status != M2M_SUCCESS) { ret = FLASH_ERR_LOCAL_ACCESS; goto ERR; } // Check source size matches size calculated from entry header. status = rootcert_get_size(&strRootCertEntry, &u16EntrySz); if ((status != M2M_SUCCESS) || (u32Size != u16EntrySz)) { ret = FLASH_ERR_PARAM; goto ERR; } } break; case FLASH_ITEM_READ: case FLASH_ITEM_REMOVE: m2m_memcpy(strRootCertEntry.au8SHA1NameHash, gau8ItemIdentifier, sizeof(gau8ItemIdentifier)); m2m_memset(gau8ItemIdentifier, 0, sizeof(gau8ItemIdentifier)); break; case FLASH_ITEM_READIDX: // Hack strRootCertEntry to carry the index from u8ModeOptions. *(uint32*)&strRootCertEntry = u8ModeOptions; break; default: /* No other item modes supported. */ ret = FLASH_ERR_PARAM; goto ERR; break; } ret = init_access(); if (ret == FLASH_RETURN_OK) { /* Now we can access the items in flash. */ uint8 *pu8Buff = malloc(M2M_TLS_ROOTCER_FLASH_SZ); uint32 u32Offset = 0; if (pu8Buff == NULL) { ret = FLASH_ERR_INTERNAL; goto ERR; } ret = rootcert_access(enuMode, &strRootCertEntry, &u16EntrySz, pu8Buff, &u32Offset); if (ret == FLASH_RETURN_OK) { /* Set parameters for whole transfer, according to enuMode. */ sint8 status = M2M_SUCCESS; tstrDataAccessInitParams init_params = {u16EntrySz, 0}; tstrDataAccessParams data_params = {pu8Buff + u32Offset, u16EntrySz, 0, u16EntrySz}; tstrFlashAccess strFlashAccess; m2m_memset((uint8*)&strFlashAccess, 0, sizeof(tstrFlashAccess)); strFlashAccess.strPersistentInfo.u16AppId = u16Id; strFlashAccess.strPersistentInfo.u8AccessFlags = u8AccessOptions; switch (enuMode) { case FLASH_ITEM_ADD: init_params.u8Flags = FLASH_FN_FLAGS_READ; status = pfFn(FLASH_DATA_FN_INITIALIZE, &init_params); if (status == M2M_SUCCESS) status = pfFn(FLASH_DATA_FN_DATA, &data_params); if (status != M2M_SUCCESS) { ret = FLASH_ERR_LOCAL_ACCESS; pfFn(FLASH_DATA_FN_TERMINATE, NULL); break; } u32Offset += u16EntrySz; // intentional fallthrough. case FLASH_ITEM_REMOVE: status = spi_flash_erase(M2M_BACKUP_FLASH_OFFSET, M2M_BACKUP_FLASH_SZ); if (status == M2M_SUCCESS) status = winc_flash_write_verify(pu8Buff, M2M_BACKUP_FLASH_OFFSET, u32Offset); if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; break; } set_internal_info(NULL, M2M_TLS_ROOTCER_FLASH_OFFSET); strFlashAccess.strPersistentInfo.u8ModeFlags |= FLASH_MODE_FLAGS_DATA_IN_BACKUP; break; case FLASH_ITEM_READ: case FLASH_ITEM_READIDX: // Check source size is sufficient for reading entry. if (u32Size < u16EntrySz) { ret = FLASH_ERR_SIZE; break; } init_params.u8Flags = FLASH_FN_FLAGS_WRITE; status = pfFn(FLASH_DATA_FN_INITIALIZE, &init_params); if (status == M2M_SUCCESS) status = pfFn(FLASH_DATA_FN_DATA, &data_params); if (status != M2M_SUCCESS) { ret = FLASH_ERR_LOCAL_ACCESS; pfFn(FLASH_DATA_FN_TERMINATE, NULL); break; } break; } if (ret == 0) { ret = commit_access(&strFlashAccess); if (enuMode != FLASH_ITEM_REMOVE) pfFn(FLASH_DATA_FN_COMPLETE, NULL); } } free(pu8Buff); } ERR: M2M_INFO("FAState:%d\n", ret); return ret; } sint8 m2m_flash_rootcert_add(uint16 u16Id, tpfDataAccessFn pfSourceFn, uint32 u32SourceSize) { sint8 ret = FLASH_RETURN_OK; ret = register_app_fn(pfSourceFn); if (ret == FLASH_RETURN_OK) ret = m2m_flash_rootcert_access(FLASH_ITEM_ADD, 0, FLASH_ACCESS_OPTION_COMPARE_AFTER, u16Id, app_data_access, u32SourceSize); return ret; } sint8 m2m_flash_rootcert_remove(uint16 u16Id, uint8 *pu8Identifier, uint32 u32IdentifierSz) { sint8 ret = FLASH_ERR_PARAM; if ((pu8Identifier != NULL) && (u32IdentifierSz == 20)) { m2m_memcpy(gau8ItemIdentifier, pu8Identifier, u32IdentifierSz); ret = m2m_flash_rootcert_access(FLASH_ITEM_REMOVE, 0, FLASH_ACCESS_OPTION_COMPARE_AFTER, u16Id, NULL, 0); } return ret; } sint8 m2m_flash_rootcert_read(uint16 u16Id, tpfDataAccessFn pfDestFn, uint32 u32DestSize, uint8 *pu8Identifier, uint32 u32IdentifierSz) { sint8 ret = FLASH_RETURN_OK; ret = register_app_fn(pfDestFn); if (ret == FLASH_RETURN_OK) { ret = FLASH_ERR_PARAM; if ((pu8Identifier != NULL) && (u32IdentifierSz == 20)) { m2m_memcpy(gau8ItemIdentifier, pu8Identifier, u32IdentifierSz); ret = m2m_flash_rootcert_access(FLASH_ITEM_READ, 0, 0, u16Id, app_data_access, u32DestSize); } } return ret; } sint8 m2m_flash_rootcert_readidx(uint16 u16Id, tpfDataAccessFn pfDestFn, uint32 u32DestSize, uint8 u8Index) { sint8 ret = FLASH_RETURN_OK; ret = register_app_fn(pfDestFn); if (ret == FLASH_RETURN_OK) ret = m2m_flash_rootcert_access(FLASH_ITEM_READIDX, u8Index, 0, u16Id, app_data_access, u32DestSize); return ret; } void m2m_flash_get_state(tstrFlashState *pstrState) { if (gu8Reset == 0) { sint8 status = M2M_ERR_FAIL; tstrFlashAccessPersistent strSavedFlashAccess; status = spi_flash_read((uint8*)&strSavedFlashAccess, HOST_CONTROL_FLASH_OFFSET, sizeof(tstrFlashAccessPersistent)); if ((status == M2M_SUCCESS) && (strSavedFlashAccess.u32Signature == FLASH_SIGNATURE)) { switch (strSavedFlashAccess.enuTransferStatus) { case FLASH_STATUS_ACTIVE: if (strSavedFlashAccess.u8ModeFlags & FLASH_MODE_FLAGS_CS_SWITCH) { // Check to see if switch happened before we were interrupted. If so we had actually completed. uint8 target; if (image_get_target(&target) == M2M_SUCCESS) { if ((target == 0) && (strSavedFlashAccess.u8ModeFlags & FLASH_MODE_FLAGS_CS_SWITCH_TARGET)) gu8Success = 1; if ((target > 0) && !(strSavedFlashAccess.u8ModeFlags & FLASH_MODE_FLAGS_CS_SWITCH_TARGET)) gu8Success = 1; } } gu16LastAccessId = strSavedFlashAccess.u16AppId; gu8Changed = !(strSavedFlashAccess.u8ModeFlags & FLASH_MODE_FLAGS_UNCHANGED); if (gu8Success == 1) { strSavedFlashAccess.enuTransferStatus = FLASH_STATUS_DONE; winc_flash_write_verify((uint8*)&strSavedFlashAccess, HOST_CONTROL_FLASH_OFFSET, FLASH_SIG_STA_SZ); } break; case FLASH_STATUS_DONE: gu16LastAccessId = strSavedFlashAccess.u16AppId; gu8Changed = !(strSavedFlashAccess.u8ModeFlags & FLASH_MODE_FLAGS_UNCHANGED); gu8Success = 1; break; default: break; } } } m2m_memset((uint8*)pstrState, 0, sizeof(tstrFlashState)); if (gu16LastAccessId) { pstrState->u16LastAccessId = gu16LastAccessId; pstrState->u8Success = gu8Success; pstrState->u8Changed = gu8Changed; } pstrState->u8Init = gu8Init; pstrState->u8Reset = gu8Reset; } sint8 m2m_flash_init(void) { if (gu8Reset == 0) { // WINC backup recovery may be needed. if (recover_backup() == FLASH_RETURN_OK) { gu8Init = 1; gu8Reset = 1; return M2M_SUCCESS; } } return M2M_ERR_FAIL; }