/** * * \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/source/nmflash.h" #include "spi_flash/include/spi_flash.h" #include "nmdrv.h" //#include "main.h" /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* GLOBALS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static uint32 gu32LocationId = MEM_ID_NONE; static uint8 *gpu8Location = NULL; uint16 gu16LastAccessId = 0; uint8 gu8Success = 0; uint8 gu8Changed = 0; uint8 gu8Init = 0; uint8 gu8Reset = 0; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTIONS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static sint8 winc_flash_compare(uint8 *pu8Buf, uint32 u32Offset, uint32 u32Size) { sint8 ret = M2M_SUCCESS; uint8 buf[128]; uint32 offset = 0; while (offset < u32Size) { uint32 chunk_sz = sizeof(buf); if (chunk_sz > u32Size - offset) chunk_sz = u32Size - offset; ret = spi_flash_read(buf, u32Offset + offset, chunk_sz); if (ret != M2M_SUCCESS) break; ret = m2m_memcmp(buf, pu8Buf + offset, chunk_sz); if (ret != 0) break; offset += chunk_sz; } return ret; } sint8 winc_flash_write_verify(uint8 *pu8Buf, uint32 u32Offset, uint32 u32Size) { sint8 ret = M2M_ERR_FAIL; uint8 count = 20; while ((ret != M2M_SUCCESS) && (count-- > 0)) { ret = spi_flash_write(pu8Buf, u32Offset, u32Size); if (ret == M2M_SUCCESS) ret = winc_flash_compare(pu8Buf, u32Offset, u32Size); } return ret; } static sint8 set_changed_flag(tstrFlashAccessPersistent *pstrPersistentInfo) { sint8 ret = FLASH_RETURN_OK; if (pstrPersistentInfo->u8ModeFlags & FLASH_MODE_FLAGS_UNCHANGED) { pstrPersistentInfo->u8ModeFlags &= ~FLASH_MODE_FLAGS_UNCHANGED; if (winc_flash_write_verify((uint8*)pstrPersistentInfo, HOST_CONTROL_FLASH_OFFSET, sizeof(tstrFlashAccessPersistent)) == M2M_SUCCESS) gu8Changed = 1; else ret = FLASH_ERR_WINC_ACCESS; } return ret; } static uint8 crc7(uint8 crc, const uint8 *buff, uint16 len) { uint8 reg = crc; uint16 i; for(i = 0; i < len; i++) { uint16 g; for(g = 0; g < 8; g++) { uint8 inv = (((buff[i] << g) & 0x80) >> 7) ^ ((reg >> 6) & 1); reg = ((reg << 1) & 0x7f) ^ (9 * inv); } } return reg; } static sint8 read_control_sector(tstrOtaControlSec *pstrControlSec, uint32 u32Offset) { sint8 s8Ret = spi_flash_read((uint8*)pstrControlSec, u32Offset, sizeof(tstrOtaControlSec)); if (s8Ret == M2M_SUCCESS) { if (pstrControlSec->u32OtaMagicValue != OTA_MAGIC_VALUE) s8Ret = M2M_ERR_FAIL; if (pstrControlSec->u32OtaControlSecCrc != crc7(0x7f, (uint8*)pstrControlSec, sizeof(tstrOtaControlSec) - 4)) s8Ret = M2M_ERR_FAIL; } return s8Ret; } static sint8 update_control_sector(tstrOtaControlSec *pstrControlSec) { sint8 ret = M2M_ERR_FAIL; ret = spi_flash_erase(M2M_BACKUP_FLASH_OFFSET, M2M_BACKUP_FLASH_SZ); if (ret == M2M_SUCCESS) { pstrControlSec->u32OtaSequenceNumber++; pstrControlSec->u32OtaControlSecCrc = crc7(0x7f, (uint8*)pstrControlSec, sizeof(tstrOtaControlSec) - 4); ret = winc_flash_write_verify((uint8*)pstrControlSec, M2M_BACKUP_FLASH_OFFSET, sizeof(tstrOtaControlSec)); if (ret == M2M_SUCCESS) { ret = spi_flash_erase(M2M_CONTROL_FLASH_OFFSET, M2M_CONTROL_FLASH_SZ); if (ret == M2M_SUCCESS) { pstrControlSec->u32OtaSequenceNumber++; pstrControlSec->u32OtaControlSecCrc = crc7(0x7f, (uint8*)pstrControlSec, sizeof(tstrOtaControlSec) - 4); ret = winc_flash_write_verify((uint8*)pstrControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec)); } } } return ret; } static sint8 access_control_sector(tenuCSOp enuOp, uint32 *param) { static tstrOtaControlSec strControlSec = {0}; sint8 s8Ret = M2M_SUCCESS; uint8 bUpdate = false; if ((enuOp != CS_INITIALIZE) && (strControlSec.u32OtaMagicValue != OTA_MAGIC_VALUE)) { if (param != NULL) *param = 0; return M2M_ERR_FAIL; } switch (enuOp) { case CS_INITIALIZE: s8Ret = read_control_sector(&strControlSec, M2M_CONTROL_FLASH_OFFSET); if (s8Ret != M2M_SUCCESS) { s8Ret = read_control_sector(&strControlSec, M2M_BACKUP_FLASH_OFFSET); if (s8Ret == M2M_SUCCESS) { /* * Reinstate the control sector from backup. */ s8Ret = spi_flash_erase(M2M_CONTROL_FLASH_OFFSET, M2M_CONTROL_FLASH_SZ); if (s8Ret == M2M_SUCCESS) s8Ret = winc_flash_write_verify((uint8*)&strControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec)); } } break; case CS_INVALIDATE_RB: // Update trashes the backup sector, so we need to avoid unnecessary updates. if (strControlSec.u32OtaRollbackImageValidStatus != OTA_STATUS_INVALID) { strControlSec.u32OtaRollbackImageValidStatus = OTA_STATUS_INVALID; bUpdate = true; } break; case CS_VALIDATE_RB: // Update trashes the backup sector, so we need to avoid unnecessary updates. if (strControlSec.u32OtaRollbackImageValidStatus != OTA_STATUS_VALID) { strControlSec.u32OtaRollbackImageValidStatus = OTA_STATUS_VALID; bUpdate = true; } break; case CS_VALIDATE_SWITCH: strControlSec.u32OtaRollbackImageValidStatus = OTA_STATUS_VALID; // intentional fallthrough. case CS_SWITCH: if (strControlSec.u32OtaRollbackImageValidStatus == OTA_STATUS_VALID) { uint32 tmp = strControlSec.u32OtaCurrentworkingImagOffset; strControlSec.u32OtaCurrentworkingImagOffset = strControlSec.u32OtaRollbackImageOffset; strControlSec.u32OtaRollbackImageOffset = tmp; bUpdate = true; } else s8Ret = M2M_ERR_FAIL; break; case CS_GET_ACTIVE: if (param == NULL) s8Ret = M2M_ERR_FAIL; else *param = strControlSec.u32OtaCurrentworkingImagOffset; break; case CS_GET_INACTIVE: if (param == NULL) s8Ret = M2M_ERR_FAIL; else *param = strControlSec.u32OtaRollbackImageOffset; break; case CS_DEINITIALIZE: m2m_memset((uint8*)&strControlSec, 0, sizeof(tstrOtaControlSec)); break; default: s8Ret = M2M_ERR_FAIL; } if (bUpdate) { s8Ret = update_control_sector(&strControlSec); M2M_INFO("CS update:%d %s\n", enuOp, (s8Ret==M2M_SUCCESS)?"":"Failed"); } return s8Ret; } static sint8 local_access_ptr(tenuFlashDataFnCtl enuCtl, void *pvStr) { sint8 s8Ret = M2M_ERR_FAIL; static uint8 *pu8Location = NULL; static uint8 u8Flags = 0; switch (enuCtl) { case FLASH_DATA_FN_INITIALIZE: { tstrDataAccessInitParams *init_params = (tstrDataAccessInitParams*)pvStr; u8Flags = init_params->u8Flags; pu8Location = gpu8Location; s8Ret = M2M_SUCCESS; } break; case FLASH_DATA_FN_DATA: if (pu8Location != NULL) { tstrDataAccessParams *params = (tstrDataAccessParams*)pvStr; if (u8Flags & FLASH_FN_FLAGS_WRITE) m2m_memcpy(pu8Location, params->pu8Buf + params->u32DataOffset, params->u32DataSize); if (u8Flags & FLASH_FN_FLAGS_READ) m2m_memcpy(params->pu8Buf + params->u32DataOffset, pu8Location, params->u32DataSize); pu8Location += params->u32DataSize; s8Ret = M2M_SUCCESS; } break; case FLASH_DATA_FN_TERMINATE: case FLASH_DATA_FN_COMPLETE: u8Flags = 0; pu8Location = NULL; s8Ret = M2M_SUCCESS; break; } return s8Ret; } static sint8 winc_flash_access(tenuFlashDataFnCtl enuCtl, void *pvStr) { sint8 s8Ret = M2M_ERR_FAIL; static uint32 u32CurrentAddr = 0; static uint8 u8Flags = 0; switch (enuCtl) { case FLASH_DATA_FN_INITIALIZE: { tstrDataAccessInitParams *init_params = (tstrDataAccessInitParams*)pvStr; printf("FA Init: 0x%lx Fg 0x%02x Sz 0x%lx \n", gu32LocationId, init_params->u8Flags, init_params->u32TotalSize); u8Flags = init_params->u8Flags; switch (gu32LocationId) { case MEM_ID_WINC_FLASH: u32CurrentAddr = 0; break; case MEM_ID_WINC_ACTIVE: s8Ret = access_control_sector(CS_GET_ACTIVE, &u32CurrentAddr); break; case MEM_ID_WINC_INACTIVE: s8Ret = access_control_sector(CS_GET_INACTIVE, &u32CurrentAddr); /* If we're about to write to the inactive partition, mark it as invalid. */ if ((s8Ret == M2M_SUCCESS) && (u8Flags & (FLASH_FN_FLAGS_WRITE | FLASH_FN_FLAGS_ERASE))) s8Ret = access_control_sector(CS_INVALIDATE_RB, NULL); break; case MEM_ID_NONE: s8Ret = M2M_ERR_FAIL; break; default: u32CurrentAddr = gu32LocationId; break; } if (u8Flags & FLASH_FN_FLAGS_READ_SURROUNDING) { init_params->u32AlignmentSize = FLASH_SECTOR_SZ; init_params->u32StartAlignment = u32CurrentAddr & (FLASH_SECTOR_SZ - 1); } s8Ret = M2M_SUCCESS; } break; case FLASH_DATA_FN_DATA: { tstrDataAccessParams *params = (tstrDataAccessParams*)pvStr; if (u8Flags & FLASH_FN_FLAGS_COMPARE_BEFORE) { printf("c-"); // If contents match already, return success s8Ret = winc_flash_compare(params->pu8Buf + params->u32DataOffset, u32CurrentAddr, params->u32DataSize); if (s8Ret == M2M_SUCCESS) goto END; } if (u8Flags & FLASH_FN_FLAGS_READ_SURROUNDING) { uint32 prefill_sz = params->u32DataOffset; uint32 postfill_sz = params->u32BufSize - (params->u32DataOffset + params->u32DataSize); if (prefill_sz > 0) { printf("r-"); s8Ret = spi_flash_read(params->pu8Buf, u32CurrentAddr - prefill_sz, prefill_sz); if (s8Ret != M2M_SUCCESS) goto END; } if (postfill_sz > 0) { printf("-r"); s8Ret = spi_flash_read(params->pu8Buf + params->u32BufSize - postfill_sz, u32CurrentAddr + params->u32DataSize, postfill_sz); if (s8Ret != M2M_SUCCESS) goto END; } } if (u8Flags & FLASH_FN_FLAGS_BACKUP) { if (params->u32BufSize > params->u32DataSize) { printf("b"); s8Ret = winc_flash_write_verify(params->pu8Buf, M2M_BACKUP_FLASH_OFFSET, params->u32BufSize); if (s8Ret == M2M_SUCCESS) s8Ret = prepare_backup(u32CurrentAddr - params->u32DataOffset); if (s8Ret != M2M_SUCCESS) goto END; } } if (u8Flags & FLASH_FN_FLAGS_ERASE) { printf("e"); s8Ret = spi_flash_erase(u32CurrentAddr, params->u32DataSize); if (s8Ret != M2M_SUCCESS) goto END; } if (u8Flags & FLASH_FN_FLAGS_WRITE) { printf("W"); if (u8Flags & FLASH_FN_FLAGS_READ_SURROUNDING) s8Ret = spi_flash_write(params->pu8Buf, u32CurrentAddr - params->u32DataOffset, params->u32BufSize); else s8Ret = spi_flash_write(params->pu8Buf + params->u32DataOffset, u32CurrentAddr, params->u32DataSize); if (s8Ret != M2M_SUCCESS) goto END; } if (u8Flags & FLASH_FN_FLAGS_COMPARE_AFTER) { printf("-c"); // If contents do not match, return failure if (u8Flags & FLASH_FN_FLAGS_READ_SURROUNDING) s8Ret = winc_flash_compare(params->pu8Buf, u32CurrentAddr - params->u32DataOffset, params->u32BufSize); else s8Ret = winc_flash_compare(params->pu8Buf + params->u32DataOffset, u32CurrentAddr, params->u32DataSize); if (s8Ret != M2M_SUCCESS) goto END; } if (u8Flags & FLASH_FN_FLAGS_READ) { printf("R"); s8Ret = spi_flash_read(params->pu8Buf + params->u32DataOffset, u32CurrentAddr, params->u32DataSize); if (s8Ret != M2M_SUCCESS) goto END; } END: u32CurrentAddr += params->u32DataSize; } break; case FLASH_DATA_FN_TERMINATE: case FLASH_DATA_FN_COMPLETE: printf(" FA End 0x%lx\n", u32CurrentAddr); u8Flags = 0; u32CurrentAddr = 0; s8Ret = M2M_SUCCESS; break; } return s8Ret; } void set_internal_info_ptr(tpfDataAccessFn *ppfFn, uint8 *pu8Ptr) { if (ppfFn != NULL) *ppfFn = local_access_ptr; gpu8Location = pu8Ptr; } void set_internal_info(tpfDataAccessFn *ppfFn, uint32 u32LocationId) { if (ppfFn != NULL) *ppfFn = winc_flash_access; gu32LocationId = u32LocationId; } uint8 is_internal_info(tpfDataAccessFn pfFn) { if (pfFn == winc_flash_access) return true; return false; } sint8 recover_backup(void) { sint8 ret = FLASH_RETURN_OK; uint32 u32BackupAddr = FLASH_BACKUP_STORE_OFFSET; tstrBackup strBackup; while (u32BackupAddr < FLASH_BACKUP_STORE_OFFSET + FLASH_BACKUP_STORE_SZ) { sint8 status = spi_flash_read((uint8*)&strBackup, u32BackupAddr, sizeof(tstrBackup)); if ((status == M2M_SUCCESS) && (strBackup.enuTransferStatus == BACKUP_STATUS_ACTIVE)) { uint8 *pu8Buff = malloc(strBackup.u32Size); if (pu8Buff == NULL) { ret = FLASH_ERR_INTERNAL; goto ERR; } status = spi_flash_read(pu8Buff, strBackup.u32SourceAddr, strBackup.u32Size); if (status == M2M_SUCCESS) { status = spi_flash_erase(strBackup.u32DestinationAddr, strBackup.u32Size); if (status == M2M_SUCCESS) { status = winc_flash_write_verify(pu8Buff, strBackup.u32DestinationAddr, strBackup.u32Size); if (status == M2M_SUCCESS) { strBackup.enuTransferStatus = BACKUP_STATUS_DONE; status = winc_flash_write_verify((uint8*)&strBackup.enuTransferStatus, u32BackupAddr, FLASH_BACKUP_STA_SZ); } } } free(pu8Buff); } if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } u32BackupAddr += sizeof(tstrBackup); } ERR: return ret; } sint8 prepare_backup(uint32 u32Target) { sint8 s8Ret = M2M_ERR_FAIL; uint32 u32BackupAddr = FLASH_BACKUP_STORE_OFFSET; tenuBackupStatus enuStatus = BACKUP_STATUS_EMPTY; s8Ret = spi_flash_read((uint8*)&enuStatus, u32BackupAddr, sizeof(enuStatus)); if ((s8Ret != M2M_SUCCESS) || (enuStatus != BACKUP_STATUS_EMPTY)) { u32BackupAddr += sizeof(tstrBackup); s8Ret = spi_flash_read((uint8*)&enuStatus, u32BackupAddr, sizeof(enuStatus)); if ((s8Ret == M2M_SUCCESS) && (enuStatus != BACKUP_STATUS_EMPTY)) s8Ret = M2M_ERR_FAIL; } if (s8Ret == M2M_SUCCESS) { tstrBackup strBackup = {BACKUP_STATUS_NOT_ACTIVE, u32Target, M2M_BACKUP_FLASH_OFFSET, M2M_BACKUP_FLASH_SZ}; s8Ret = winc_flash_write_verify((uint8*)&strBackup.enuTransferStatus, u32BackupAddr, FLASH_BACKUP_STA_SZ); if (s8Ret == M2M_SUCCESS) { s8Ret = winc_flash_write_verify((uint8*)&strBackup, u32BackupAddr, sizeof(strBackup)); if (s8Ret == M2M_SUCCESS) { strBackup.enuTransferStatus = BACKUP_STATUS_ACTIVE; s8Ret = winc_flash_write_verify((uint8*)&strBackup.enuTransferStatus, u32BackupAddr, FLASH_BACKUP_STA_SZ); } } } return s8Ret; } sint8 image_get_target(uint8 *pu8Target) { sint8 s8Ret = M2M_ERR_FAIL; uint32 u32OffsetActive = 0; uint32 u32OffsetInactive = 0; s8Ret = access_control_sector(CS_INITIALIZE, NULL); if (s8Ret == M2M_SUCCESS) { s8Ret = access_control_sector(CS_GET_ACTIVE, &u32OffsetActive); if (s8Ret == M2M_SUCCESS) { s8Ret = access_control_sector(CS_GET_INACTIVE, &u32OffsetInactive); if (s8Ret == M2M_SUCCESS) *pu8Target = (u32OffsetInactive > u32OffsetActive) ? 1 : 0; } access_control_sector(CS_DEINITIALIZE, NULL); } return s8Ret; } sint8 rootcert_get_size(tstrRootCertEntryHeader *pstrHdr, uint16 *pu16Size) { sint8 s8Ret = M2M_ERR_FAIL; if ((pstrHdr == NULL) || (pu16Size == NULL)) goto ERR; /* Set default size out to maximum. */ *pu16Size = 0xFFFF; switch (pstrHdr->strPubKey.u32PubKeyType) { case ROOT_CERT_PUBKEY_RSA: if (pstrHdr->strPubKey.strRsaKeyInfo.u16NSz > M2M_TLS_ROOTCER_FLASH_SZ) goto ERR; if (pstrHdr->strPubKey.strRsaKeyInfo.u16ESz > M2M_TLS_ROOTCER_FLASH_SZ) goto ERR; *pu16Size = sizeof(tstrRootCertEntryHeader) + ((pstrHdr->strPubKey.strRsaKeyInfo.u16NSz + 0x3) & ~0x3) + ((pstrHdr->strPubKey.strRsaKeyInfo.u16ESz + 0x3) & ~0x3); s8Ret = M2M_SUCCESS; break; case ROOT_CERT_PUBKEY_ECDSA: if (pstrHdr->strPubKey.strEcsdaKeyInfo.u16KeySz > M2M_TLS_ROOTCER_FLASH_SZ) goto ERR; *pu16Size = sizeof(tstrRootCertEntryHeader) + ((pstrHdr->strPubKey.strEcsdaKeyInfo.u16KeySz + 0x3) & ~0x3) * 2; s8Ret = M2M_SUCCESS; break; case 0xFFFFFFFF: // Invalid. May indicate end of list. Fail with size set to 0. *pu16Size = 0; break; default: // Corrupt header. break; } ERR: return s8Ret; } sint8 rootcert_access(tenuFlashAccessItemMode enuMode, tstrRootCertEntryHeader *pstrReferenceHdr, uint16 *pu16EntrySize, uint8 *pu8Buff, uint32 *pu32Offset) { sint8 ret = FLASH_RETURN_OK; sint8 status = M2M_SUCCESS; uint8 au8RootCertSig[] = M2M_TLS_ROOTCER_FLASH_SIG; tstrRootCertEntryHeader strEntryHeader; /* Previous version used 0-identifiers to indicate removed entries. Use last 20 bytes of pu8Buff to help us check for them. */ uint8 *pu8Zero = pu8Buff + M2M_TLS_ROOTCER_FLASH_SZ - sizeof(pstrReferenceHdr->au8SHA1NameHash); uint32 u32StoreOffset = 0; uint32 u32Entries = 0; m2m_memset(pu8Zero, 0, sizeof(pstrReferenceHdr->au8SHA1NameHash)); /* Use pu8SectionBuffer to read signature. */ status = spi_flash_read(pu8Buff, M2M_TLS_ROOTCER_FLASH_OFFSET, sizeof(au8RootCertSig)); if ((status != M2M_SUCCESS) || m2m_memcmp(pu8Buff, au8RootCertSig, sizeof(au8RootCertSig))) { /* * Root certificate section is not initialized. We could try to initialize it * here, but for now just fail. */ ret = FLASH_ERR_WINC_ACCESS; goto ERR; } /* * By default assume we'll get to the end of the flash store without finding what we need * (matching entry or space to add new entry). If we break while loop before reaching end of * store then we'll change ret accordingly. */ if (enuMode == FLASH_ITEM_ADD) ret = FLASH_ERR_SIZE; else ret = FLASH_ERR_WINC_CONFLICT; u32StoreOffset = *pu32Offset = sizeof(tstrRootCertFlashHeader); while (u32StoreOffset + sizeof(tstrRootCertEntryHeader) < M2M_TLS_ROOTCER_FLASH_SZ) { uint16 u16EntrySize = 0; status = spi_flash_read((uint8*)&strEntryHeader, M2M_TLS_ROOTCER_FLASH_OFFSET + u32StoreOffset, sizeof(tstrRootCertEntryHeader)); if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; break; } status = rootcert_get_size(&strEntryHeader, &u16EntrySize); if (status != M2M_SUCCESS) { // Found the end of the list. We are done. If we are adding an entry, check the space here. if ((enuMode == FLASH_ITEM_ADD) && ((*pu32Offset + *pu16EntrySize) <= M2M_TLS_ROOTCER_FLASH_SZ)) { u32Entries++; ret = FLASH_RETURN_OK; } break; } // If we are here we know that u32EntrySize is sane. if (m2m_memcmp(pu8Zero, (uint8*)pstrReferenceHdr, sizeof(strEntryHeader.au8SHA1NameHash))) { // Entry is not empty. status = spi_flash_read(pu8Buff + *pu32Offset, M2M_TLS_ROOTCER_FLASH_OFFSET + u32StoreOffset, u16EntrySize); if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; break; } if (enuMode == FLASH_ITEM_READIDX) { if (u32Entries == *(uint32*)pstrReferenceHdr) { // Found entry. pu16EntrySize is used to output size. *pu16EntrySize = u16EntrySize; ret = FLASH_RETURN_OK; break; } } else if (!m2m_memcmp(strEntryHeader.au8SHA1NameHash, (uint8*)pstrReferenceHdr, sizeof(strEntryHeader.au8SHA1NameHash))) { if (enuMode == FLASH_ITEM_ADD) { // Found a match. Cannot add. ret = FLASH_ERR_WINC_CONFLICT; break; } if (enuMode == FLASH_ITEM_READ) { // Found a match. pu16EntrySize is used to output size. *pu16EntrySize = u16EntrySize; ret = FLASH_RETURN_OK; break; } if (enuMode == FLASH_ITEM_REMOVE) { // Found a match. Continue, to complete entry count. ret = FLASH_RETURN_OK; // Cancel out increment of u32BuffOffset. *pu32Offset -= u16EntrySize; } } *pu32Offset += u16EntrySize; u32Entries++; } u32StoreOffset += u16EntrySize; } if (ret == FLASH_RETURN_OK) ((tstrRootCertFlashHeader*)pu8Buff)->u32nCerts = u32Entries; ERR: return ret; } sint8 transfer_run(tstrFlashAccess *pstrFlashAccess) { /* * Errors before start of first transfer will be reported as parameter errors. * This means the information is insufficient to allow us to begin. */ sint8 ret = FLASH_RETURN_OK; sint8 status = M2M_ERR_FAIL; tpfDataAccessFn pfWriteFn = pstrFlashAccess->pfDestinationFn; tpfDataAccessFn pfReadFn = pstrFlashAccess->pfSourceFn; tstrDataAccessInitParams read_init_params = {pstrFlashAccess->u32Size, FLASH_FN_FLAGS_READ, 0, 0}; tstrDataAccessInitParams write_init_params = {pstrFlashAccess->u32Size, FLASH_FN_FLAGS_WRITE, 0, 0}; uint32 u32BytesTransferred = 0; uint32 u32BytesRemaining = pstrFlashAccess->u32Size; uint8 *pu8Buff = NULL; if (pstrFlashAccess->strPersistentInfo.u8AccessFlags & FLASH_ACCESS_OPTION_COMPARE_BEFORE) write_init_params.u8Flags |= FLASH_FN_FLAGS_COMPARE_BEFORE; if (pstrFlashAccess->strPersistentInfo.u8AccessFlags & FLASH_ACCESS_OPTION_ERASE_FIRST) { write_init_params.u8Flags |= FLASH_FN_FLAGS_ERASE; if (pstrFlashAccess->strPersistentInfo.u8AccessFlags & FLASH_ACCESS_OPTION_KEEP_SURROUNDING) { write_init_params.u8Flags |= FLASH_FN_FLAGS_READ_SURROUNDING; if (pstrFlashAccess->strPersistentInfo.u8AccessFlags & FLASH_ACCESS_OPTION_USE_BACKUP) write_init_params.u8Flags |= FLASH_FN_FLAGS_BACKUP; } } if (pstrFlashAccess->strPersistentInfo.u8AccessFlags & FLASH_ACCESS_OPTION_COMPARE_AFTER) write_init_params.u8Flags |= FLASH_FN_FLAGS_COMPARE_AFTER; if (pstrFlashAccess->strPersistentInfo.u8ModeFlags & FLASH_MODE_FLAGS_DATA_IN_BACKUP) { if (prepare_backup(gu32LocationId) != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } set_changed_flag(&pstrFlashAccess->strPersistentInfo); ret = recover_backup(); if (ret < 0) goto ERR; } /* * Initialize control sector. Even if we don't need to access it, this at * least ensures that the control sector is not relying on the flash backup sector. */ status = access_control_sector(CS_INITIALIZE, NULL); if (status != M2M_SUCCESS && (pstrFlashAccess->strPersistentInfo.u8ModeFlags & FLASH_MODE_FLAGS_CS)) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } if (pfReadFn != NULL) { /* Prepare for read. */ status = pfReadFn(FLASH_DATA_FN_INITIALIZE, &read_init_params); if (status != M2M_SUCCESS) { if (is_internal_info(pfReadFn)) ret = FLASH_ERR_WINC_ACCESS; else ret = FLASH_ERR_LOCAL_ACCESS; pfReadFn(FLASH_DATA_FN_TERMINATE, NULL); goto ERR; } } if (pfWriteFn != NULL) { /* Prepare for write. */ status = pfWriteFn(FLASH_DATA_FN_INITIALIZE, &write_init_params); if (status != M2M_SUCCESS) { if (is_internal_info(pfWriteFn)) ret = FLASH_ERR_WINC_ACCESS; else ret = FLASH_ERR_LOCAL_ACCESS; goto TERMINATE; } } if (u32BytesRemaining > 0) { if (u32BytesRemaining > FLASH_SECTOR_SIZE) pu8Buff = malloc(FLASH_SECTOR_SIZE); else if (write_init_params.u32AlignmentSize > 1) pu8Buff = malloc(write_init_params.u32AlignmentSize); else pu8Buff = malloc(u32BytesRemaining); if (pu8Buff == NULL) { ret = FLASH_ERR_INTERNAL; goto TERMINATE; } } while (u32BytesRemaining > 0) { tstrDataAccessParams params = {pu8Buff, FLASH_SECTOR_SIZE, 0, FLASH_SECTOR_SIZE}; if (u32BytesTransferred > 0) write_init_params.u32StartAlignment = 0; if (write_init_params.u32AlignmentSize > 1) { params.u32DataOffset = write_init_params.u32StartAlignment & (write_init_params.u32AlignmentSize-1); params.u32DataSize = write_init_params.u32AlignmentSize - params.u32DataOffset; } if (params.u32DataSize > u32BytesRemaining) params.u32DataSize = u32BytesRemaining; /* Read. */ if (pfReadFn != NULL) { status = pfReadFn(FLASH_DATA_FN_DATA, ¶ms); if (status != M2M_SUCCESS) { if (is_internal_info(pfReadFn)) ret = FLASH_ERR_WINC_ACCESS; else ret = FLASH_ERR_LOCAL_ACCESS; break; } } /* Write. */ if (pfWriteFn != NULL) { if (is_internal_info(pfWriteFn)) { ret = set_changed_flag(&pstrFlashAccess->strPersistentInfo); if (ret < 0) break; } status = pfWriteFn(FLASH_DATA_FN_DATA, ¶ms); if (status != M2M_SUCCESS) { if (is_internal_info(pfWriteFn)) ret = FLASH_ERR_WINC_ACCESS; else ret = FLASH_ERR_LOCAL_ACCESS; break; } } u32BytesTransferred += params.u32DataSize; u32BytesRemaining -= params.u32DataSize; } if (u32BytesRemaining > 0) { TERMINATE: if (pfReadFn != NULL) pfReadFn(FLASH_DATA_FN_TERMINATE, NULL); if (pfWriteFn != NULL) pfWriteFn(FLASH_DATA_FN_TERMINATE, NULL); goto ERR; } else { if (pfReadFn != NULL) pfReadFn(FLASH_DATA_FN_COMPLETE, NULL); if (pfWriteFn != NULL) pfWriteFn(FLASH_DATA_FN_COMPLETE, NULL); } if (pstrFlashAccess->strPersistentInfo.u8ModeFlags & (FLASH_MODE_FLAGS_CS_VALIDATE_IMAGE | FLASH_MODE_FLAGS_CS_SWITCH)) { tenuCSOp enuOp; ret = set_changed_flag(&pstrFlashAccess->strPersistentInfo); if (ret < 0) goto ERR; if (pstrFlashAccess->strPersistentInfo.u8ModeFlags & FLASH_MODE_FLAGS_CS_VALIDATE_IMAGE) { if (pstrFlashAccess->strPersistentInfo.u8ModeFlags & FLASH_MODE_FLAGS_CS_SWITCH) enuOp = CS_VALIDATE_SWITCH; else enuOp = CS_VALIDATE_RB; } else enuOp = CS_SWITCH; status = access_control_sector(enuOp, NULL); if (status != M2M_SUCCESS) { ret = FLASH_ERR_WINC_ACCESS; goto ERR; } } pstrFlashAccess->strPersistentInfo.enuTransferStatus = FLASH_STATUS_DONE; status = winc_flash_write_verify((uint8*)pstrFlashAccess, HOST_CONTROL_FLASH_OFFSET, sizeof(tstrFlashAccessPersistent)); gu8Success = 1; ERR: if (pu8Buff != NULL) free(pu8Buff); access_control_sector(CS_DEINITIALIZE, NULL); return ret; }