/******************************************************************************* File Name: m2m_flash.c Summary: This module contains the WINC flash interface. Description: This module contains the WINC flash interface. *******************************************************************************/ //DOM-IGNORE-BEGIN /******************************************************************************* * Copyright (C) 2021 Microchip Technology Inc. and its subsidiaries. * * 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. *******************************************************************************/ /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* INCLUDES *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ #include "m2m_flash.h" #include "spi_flash.h" #include "spi_flash_map.h" #include "nmdrv.h" /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* TYPEDEFS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ typedef struct { uint32_t address; uint32_t size; } tstrFlashMapEntry; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* GLOBALS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static const tstrFlashMapEntry flashMap[] = { {0 , 0 }, // WINC_FLASH_REGION_RAW, {0 , OTA_IMAGE_SIZE }, // WINC_FLASH_REGION_FIRMWARE_ACTIVE, {0 , OTA_IMAGE_SIZE }, // WINC_FLASH_REGION_FIRMWARE_INACTIVE, {M2M_PLL_FLASH_OFFSET , M2M_PLL_FLASH_SZ }, // WINC_FLASH_REGION_PLL_TABLE, {M2M_GAIN_FLASH_OFFSET , M2M_GAIN_FLASH_SZ }, // WINC_FLASH_REGION_GAIN_TABLE, {M2M_PLL_FLASH_OFFSET , M2M_PLL_FLASH_SZ+M2M_GAIN_FLASH_SZ}, // WINC_FLASH_REGION_PLL_AND_GAIN_TABLES, {M2M_TLS_ROOTCER_FLASH_OFFSET , M2M_TLS_ROOTCER_FLASH_SZ }, // WINC_FLASH_REGION_ROOT_CERTS, {M2M_TLS_SERVER_FLASH_OFFSET , M2M_TLS_SERVER_FLASH_SZ }, // WINC_FLASH_REGION_LOCAL_CERTS, {M2M_CACHED_CONNS_FLASH_OFFSET , M2M_CACHED_CONNS_FLASH_SZ }, // WINC_FLASH_REGION_CONN_PARAM, {0 , M2M_HTTP_MEM_FLASH_SZ }, // WINC_FLASH_REGION_HTTP_FILES, }; /*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* FUNCTIONS *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*/ static int8_t winc_flash_compare(uint8_t *pu8Buf, uint32_t u32Offset, uint32_t u32Size) { int8_t ret = M2M_SUCCESS; uint8_t buf[128]; uint32_t offset = 0; while(offset < u32Size) { uint32_t 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 = memcmp(buf, pu8Buf + offset, chunk_sz); if(ret != 0) break; offset += chunk_sz; } return ret; } static int8_t winc_flash_write_verify(uint8_t *pu8Buf, uint32_t u32Offset, uint32_t u32Size) { int8_t ret = M2M_ERR_FAIL; uint8_t 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; } /* Some internal functions for accessing the control structure. */ static uint8_t crc7(uint8_t crc, const uint8_t *buff, uint16_t len) { uint8_t reg = crc; uint16_t i; for(i = 0; i < len; i++) { uint16_t g; for(g = 0; g < 8; g++) { uint8_t inv = (((buff[i] << g) & 0x80) >> 7) ^ ((reg >> 6) & 1); reg = ((reg << 1) & 0x7f) ^ (9 * inv); } } return reg; } static int8_t verify_control_structure(tstrOtaControlSec *pstrControlSec) { int8_t s8Ret = M2M_SUCCESS; if(pstrControlSec->u32OtaMagicValue != OTA_MAGIC_VALUE) s8Ret = M2M_ERR_FAIL; if(pstrControlSec->u32OtaControlSecCrc != crc7(0x7f, (uint8_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4)) s8Ret = M2M_ERR_FAIL; return s8Ret; } static int8_t read_control_structure(tstrOtaControlSec *pstrControlSec) { int8_t s8Ret; s8Ret = spi_flash_read((uint8_t *)pstrControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec)); if(s8Ret == M2M_SUCCESS) s8Ret = verify_control_structure(pstrControlSec); if(s8Ret != M2M_SUCCESS) { s8Ret = spi_flash_read((uint8_t *)pstrControlSec, M2M_BACKUP_FLASH_OFFSET, sizeof(tstrOtaControlSec)); if(s8Ret == M2M_SUCCESS) s8Ret = verify_control_structure(pstrControlSec); } return s8Ret; } static int8_t update_control_structure(tstrOtaControlSec *pstrControlSec) { int8_t 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_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4); ret = winc_flash_write_verify((uint8_t *)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_t *)pstrControlSec, sizeof(tstrOtaControlSec) - 4); ret = winc_flash_write_verify((uint8_t *)pstrControlSec, M2M_CONTROL_FLASH_OFFSET, sizeof(tstrOtaControlSec)); } } } return ret; } static bool find_flash_section(tenuWincFlashRegion enuRegion, uint32_t *pu32StartAddr, uint32_t *pu32Size) { /* Ensure the pointers and region are valid. */ if((NULL == pu32StartAddr) || (NULL == pu32Size) || (enuRegion >= WINC_FLASH_NUM_REGIONS)) return false; /* For the raw region resolve the full flash space, otherwise lookup the region location and size from the flexible flash map. */ switch(enuRegion) { case WINC_FLASH_REGION_RAW: *pu32StartAddr = 0; *pu32Size = spi_flash_get_size() << 17; break; case WINC_FLASH_REGION_FIRMWARE_ACTIVE: case WINC_FLASH_REGION_FIRMWARE_INACTIVE: case WINC_FLASH_REGION_HTTP_FILES: { /* In these cases we need to read the control structure to find the appropriate flash address. */ tstrOtaControlSec strControl; /* Check the WINC is initialised and not running. */ if (NM_STATE_INIT != nm_get_state()) return false; /* Read control structure from flash. */ if (M2M_SUCCESS != read_control_structure(&strControl)) return false; if (WINC_FLASH_REGION_FIRMWARE_INACTIVE == enuRegion) *pu32StartAddr = strControl.u32OtaRollbackImageOffset; else if (WINC_FLASH_REGION_FIRMWARE_ACTIVE == enuRegion) *pu32StartAddr = strControl.u32OtaCurrentWorkingImagOffset; else if (WINC_FLASH_REGION_HTTP_FILES == enuRegion) *pu32StartAddr = strControl.u32OtaCurrentWorkingImagOffset + (M2M_HTTP_MEM_FLASH_OFFSET - M2M_OTA_IMAGE1_OFFSET); *pu32Size = flashMap[enuRegion].size; break; } default: *pu32StartAddr = flashMap[enuRegion].address; *pu32Size = flashMap[enuRegion].size; break; } M2M_INFO("Flash lookup %2d: 0x%06" PRIx32 " %0" PRId32 "\r\n", enuRegion, *pu32StartAddr, *pu32Size); return true; } int8_t m2m_flash_erase_sector(tenuWincFlashRegion enuRegion, uint8_t u8StartSector, uint8_t u8NumSectors) { uint32_t flashAddress; uint32_t flashRegionSize; /* Check the WINC is initialised and not running. */ if(NM_STATE_INIT != nm_get_state()) return M2M_ERR_FAIL; /* Check the region is valid. */ if(enuRegion >= WINC_FLASH_NUM_REGIONS) return M2M_ERR_INVALID_ARG; /* Find region address and size. */ if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize)) return M2M_ERR_FAIL; /* Erase is only supported for regions which begin on a sector boundary. */ if(flashAddress & (FLASH_SECTOR_SZ-1)) return M2M_ERR_INVALID_ARG; /* Check requested size fits within region size. */ if((((uint32_t)u8StartSector + u8NumSectors) * FLASH_SECTOR_SZ) > flashRegionSize) return M2M_ERR_FAIL; /* Find start address of area within requested region. */ flashAddress += (u8StartSector * FLASH_SECTOR_SZ); /* Erase the requested sectors. */ if(M2M_SUCCESS != spi_flash_erase(flashAddress, u8NumSectors * FLASH_SECTOR_SZ)) return M2M_ERR_FAIL; return M2M_SUCCESS; } int8_t m2m_flash_write(tenuWincFlashRegion enuRegion, void *pvBuffer, uint32_t u32Offset, uint32_t u32Size) { uint32_t flashAddress; uint32_t flashRegionSize; /* Check the WINC is initialised and not running. */ if(NM_STATE_INIT != nm_get_state()) return M2M_ERR_FAIL; /* Check the buffer pointer and region are valid. */ if((NULL == pvBuffer) || (enuRegion >= WINC_FLASH_NUM_REGIONS)) return M2M_ERR_INVALID_ARG; /* Find region address and size. */ if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize)) return M2M_ERR_FAIL; /* Check requested size fits within region size. Also check for wraparound. */ if(((u32Offset + u32Size) > flashRegionSize) || ((uint32_t)(u32Offset + u32Size) < u32Offset)) return M2M_ERR_FAIL; /* Find start address of area within requested region. */ flashAddress += u32Offset; /* Write data to flash. */ if(M2M_SUCCESS != spi_flash_write(pvBuffer, flashAddress, u32Size)) return M2M_ERR_FAIL; return M2M_SUCCESS; } int8_t m2m_flash_read(tenuWincFlashRegion enuRegion, void *pvBuffer, uint32_t u32Offset, uint32_t u32Size) { uint32_t flashAddress; uint32_t flashRegionSize; /* Check the WINC is initialised and not running. */ if(NM_STATE_INIT != nm_get_state()) return M2M_ERR_FAIL; /* Check the buffer pointer and region are valid. */ if((NULL == pvBuffer) || (enuRegion >= WINC_FLASH_NUM_REGIONS)) return M2M_ERR_INVALID_ARG; /* Find region address and size. */ if(false == find_flash_section(enuRegion, &flashAddress, &flashRegionSize)) return M2M_ERR_FAIL; /* Check requested size fits within region size. Also check for wraparound. */ if(((u32Offset + u32Size) > flashRegionSize) || ((uint32_t)(u32Offset + u32Size) < u32Offset)) return M2M_ERR_FAIL; /* Find start address of area within requested region. */ flashAddress += u32Offset; /* Read data from flash. */ if(M2M_SUCCESS != spi_flash_read(pvBuffer, flashAddress, u32Size)) return M2M_ERR_FAIL; return M2M_SUCCESS; } int8_t m2m_flash_switch_firmware(void) { tstrOtaControlSec strControl; uint32_t u32Tmp; /* Check the WINC is initialised and not running. */ if(NM_STATE_INIT != nm_get_state()) return M2M_ERR_FAIL; /* Read control structure from flash. */ if(M2M_SUCCESS != read_control_structure(&strControl)) return M2M_ERR_FAIL; /* Switch active and inactive. */ u32Tmp = strControl.u32OtaRollbackImageOffset; strControl.u32OtaRollbackImageOffset = strControl.u32OtaCurrentWorkingImagOffset; strControl.u32OtaCurrentWorkingImagOffset = u32Tmp; /* Ensure the inactive image is marked as invalid. This protects m2m_ota_switch_firmware from switching to an image whose validity is unknown. Switching remains possible via this API. */ strControl.u32OtaRollbackImageValidStatus = OTA_STATUS_INVALID; if(M2M_SUCCESS != update_control_structure(&strControl)) return M2M_ERR_FAIL; return M2M_SUCCESS; } //DOM-IGNORE-END