From fe42dbdcca4ced335e880f3627fe684a8893d922 Mon Sep 17 00:00:00 2001 From: d00616 Date: Wed, 28 Feb 2018 20:03:34 +0100 Subject: [PATCH] Nvm fota (#1018) * New Firmware OTA for NVM/mcuboot (nRF5) - Support for mcuboot (nRF5) - Support for run length encoded data - Support for smaller FIRMWARE_BLOCK_SIZE, if required * Update NVM * New MY_LOCK_MCU for NRF5 * Test with Sensebender GW + Sensebender Micro was successful --- MyConfig.h | 18 ++ MySensors.h | 2 + core/MyMessage.h | 4 +- core/MyOTAFirmwareUpdate.cpp | 210 ++++++++++++------ core/MyOTAFirmwareUpdate.h | 47 ++++ drivers/NRF5/Flash.cpp | 2 + drivers/NVM/VirtualPage.cpp | 86 +++---- .../SecurityPersonalizer.ino | 4 + hal/architecture/NRF5/MyHwNRF5.cpp | 25 +++ 9 files changed, 292 insertions(+), 106 deletions(-) diff --git a/MyConfig.h b/MyConfig.h index 6c72ba97..387d0c69 100644 --- a/MyConfig.h +++ b/MyConfig.h @@ -1654,6 +1654,23 @@ #define MY_SIGNING_SOFT_RANDOMSEED_PIN (7) #endif +/** + * @def MY_LOCK_DEVICE + * @brief Enable read back protection + * + * Enable read back protection feature. Currently only supported by NRF51+NRF52. + * Use this flag to protect signing and encryption keys stored in the MCU. + * + * Set this flag, when you use softsigning in MySensors. Don't set this + * in SecurityPersonalizer. + * + * @warning YOU CAN BRICK YOUR DEVICE!!! + * Don't set this flag without having an boot loader, OTA firmware update and + * an Gateway connection. To reset an device, you can try >> + * openocd -f interface/cmsis-dap.cfg -f target/nrf52.cfg -c "program dap apreg 1 0x04 0x01" + */ +//#define MY_LOCK_DEVICE + /** * @def MY_SIGNING_FEATURE * @ingroup internals @@ -2023,6 +2040,7 @@ #define MY_INDICATION_HANDLER #define MY_DISABLE_REMOTE_RESET #define MY_DISABLE_RAM_ROUTING_TABLE_FEATURE +#define MY_LOCK_DEVICE // core #define MY_CORE_ONLY // GW diff --git a/MySensors.h b/MySensors.h index 295888a7..dd233e12 100644 --- a/MySensors.h +++ b/MySensors.h @@ -126,11 +126,13 @@ MY_DEFAULT_RX_LED_PIN in your sketch instead to enable LEDs // FLASH #if defined(MY_OTA_FIRMWARE_FEATURE) +#ifndef MCUBOOT_PRESENT #if defined(MY_OTA_USE_I2C_EEPROM) #include "drivers/I2CEeprom/I2CEeprom.cpp" #else #include "drivers/SPIFlash/SPIFlash.cpp" #endif +#endif #include "core/MyOTAFirmwareUpdate.cpp" #endif diff --git a/core/MyMessage.h b/core/MyMessage.h index ad10e343..bdd46542 100644 --- a/core/MyMessage.h +++ b/core/MyMessage.h @@ -208,7 +208,9 @@ typedef enum { ST_FIRMWARE_REQUEST = 2, //!< Request FW block ST_FIRMWARE_RESPONSE = 3, //!< Response FW block ST_SOUND = 4, //!< Sound - ST_IMAGE = 5 //!< Image + ST_IMAGE = 5, //!< Image + ST_FIRMWARE_CONFIRM = 6, //!< Mark running firmware as valid (MyOTAFirmwareUpdateNVM + mcuboot) + ST_FIRMWARE_RESPONSE_RLE = 7, //!< Response FW block with run length encoded data } mysensor_stream; /// @brief Type of payload diff --git a/core/MyOTAFirmwareUpdate.cpp b/core/MyOTAFirmwareUpdate.cpp index 1b2aca88..35e3ce7c 100644 --- a/core/MyOTAFirmwareUpdate.cpp +++ b/core/MyOTAFirmwareUpdate.cpp @@ -26,15 +26,30 @@ extern MyMessage _msgTmp; // local variables #ifdef MY_OTA_USE_I2C_EEPROM I2CEeprom _flash(MY_OTA_I2C_ADDR); -#else +#elif !defined(MCUBOOT_PRESENT) SPIFlash _flash(MY_OTA_FLASH_SS, MY_OTA_FLASH_JDECID); #endif +// Map flash functions +#ifndef MCUBOOT_PRESENT +#define _flash_initialize() _flash.initialize() +#define _flash_readByte(addr) _flash.readByte(addr) +#define _flash_writeBytes( dstaddr, data, size) _flash.writeBytes( dstaddr, data, size) +#define _flash_blockErase32K(num) _flash.blockErase32K(num) +#define _flash_busy() _flash.busy() +#else +#define _flash_initialize() true +#define _flash_readByte(addr) (*((uint8_t *)(addr))) +#define _flash_blockErase32K(num) Flash.erase((uint32_t *)FLASH_AREA_IMAGE_1_OFFSET_0, FLASH_AREA_IMAGE_1_SIZE_0) +#define _flash_busy() false +#endif + LOCAL nodeFirmwareConfig_t _nodeFirmwareConfig; LOCAL bool _firmwareUpdateOngoing = false; LOCAL uint32_t _firmwareLastRequest; LOCAL uint16_t _firmwareBlock; LOCAL uint8_t _firmwareRetry; +LOCAL bool _firmwareResponse(uint16_t block, uint8_t *data); LOCAL void readFirmwareSettings(void) { @@ -83,15 +98,15 @@ LOCAL bool firmwareOTAUpdateProcess(void) // copy new FW config (void)memcpy(&_nodeFirmwareConfig, firmwareConfigResponse, sizeof(nodeFirmwareConfig_t)); // Init flash - if (!_flash.initialize()) { + if (!_flash_initialize()) { setIndication(INDICATION_ERR_FW_FLASH_INIT); OTA_DEBUG(PSTR("!OTA:FWP:FLASH INIT FAIL\n")); // failed to initialise flash _firmwareUpdateOngoing = false; } else { // erase lower 32K -> max flash size for ATMEGA328 - _flash.blockErase32K(0); + _flash_blockErase32K(0); // wait until flash erased - while ( _flash.busy() ) {} + while ( _flash_busy() ) {} _firmwareBlock = _nodeFirmwareConfig.blocks; _firmwareUpdateOngoing = true; // reset flags @@ -102,68 +117,46 @@ LOCAL bool firmwareOTAUpdateProcess(void) } OTA_DEBUG(PSTR("OTA:FWP:UPDATE SKIPPED\n")); // FW update skipped, no newer version available } else if (_msg.type == ST_FIRMWARE_RESPONSE) { - if (_firmwareUpdateOngoing) { - // extract FW block - replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data; - - OTA_DEBUG(PSTR("OTA:FWP:RECV B=%04" PRIX16 "\n"), firmwareResponse->block); // received FW block - if (firmwareResponse->block != _firmwareBlock - 1) { - OTA_DEBUG(PSTR("!OTA:FWP:WRONG FWB\n")); // received FW block - // wrong firmware block received - setIndication(INDICATION_FW_UPDATE_RX_ERR); - // no further processing required - return true; - } - setIndication(INDICATION_FW_UPDATE_RX); - // Save block to flash - _flash.writeBytes( ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, - firmwareResponse->data, FIRMWARE_BLOCK_SIZE); - // wait until flash written - while (_flash.busy()) {} -#ifdef OTA_EXTRA_FLASH_DEBUG - { - char prbuf[8]; - uint32_t addr = ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET; - OTA_DEBUG(PSTR("OTA:FWP:FL DUMP ")); - sprintf_P(prbuf,PSTR("%04" PRIX16 ":"), (uint16_t)addr); - MY_SERIALDEVICE.print(prbuf); - for(uint8_t i=0; i> 8), (uint8_t)(firmwareSize & 0xff),':'}; - _flash.writeBytes(0, OTAbuffer, FIRMWARE_START_OFFSET); - // wait until flash ready - while (_flash.busy()) {} - hwReboot(); - } else { - setIndication(INDICATION_ERR_FW_CHECKSUM); - OTA_DEBUG(PSTR("!OTA:FWP:CRC FAIL\n")); - } - } - // reset flags - _firmwareRetry = MY_OTA_RETRY + 1; - _firmwareLastRequest = 0; - } else { - OTA_DEBUG(PSTR("!OTA:FWP:NO UPDATE\n")); + // extract FW block + replyFirmwareBlock_t *firmwareResponse = (replyFirmwareBlock_t *)_msg.data; + // Proceed firmware data + return _firmwareResponse(firmwareResponse->block, firmwareResponse->data); +#ifdef FIRMWARE_PROTOCOL_31 + } else if (_msg.type == ST_FIRMWARE_RESPONSE_RLE) { + // RLE encoded block + // extract FW block + replyFirmwareBlockRLE_t *firmwareResponse = (replyFirmwareBlockRLE_t *)_msg.data; + uint8_t data[FIRMWARE_BLOCK_SIZE]; + for (uint8_t i=0; idata; + } + while ((_firmwareBlock) && (firmwareResponse->number_of_blocks)) { + _firmwareResponse(firmwareResponse->block, data); + firmwareResponse->number_of_blocks--; + firmwareResponse->block--; } return true; +#endif + } else { +#ifdef MCUBOOT_PRESENT + if (_msg.type == ST_FIRMWARE_CONFIRM) { + if (*(uint16_t *)MCUBOOT_IMAGE_0_MAGIC_ADDR == ((uint16_t)MCUBOOT_IMAGE_MAGIC)) { + if (*(uint8_t*)(MCUBOOT_IMAGE_0_IMG_OK_ADDR)!=MCUBOOT_IMAGE_0_IMG_OK_BYTE) { + // Calculate data word to write + uint32_t *img_ok_base_addr = (uint32_t*)(MCUBOOT_IMAGE_0_IMG_OK_ADDR & ~3); // align word wise + uint32_t img_ok_data = *img_ok_base_addr; + // Set copy of MCUBOOT_IMAGE_0_IMG_OK_ADDR to MCUBOOT_IMAGE_0_IMG_OK_BYTE (0x01) + uint8_t * img_ok_array = (uint8_t*)(&img_ok_data); + img_ok_array[MCUBOOT_IMAGE_0_IMG_OK_ADDR % 4] = MCUBOOT_IMAGE_0_IMG_OK_BYTE; + // Write word back + Flash.write(img_ok_base_addr, img_ok_data); + } + OTA_DEBUG(PSTR("!OTA:FWP:IMAGE CONFIRMED\n")); + } else { + OTA_DEBUG(PSTR("!OTA:FWP:INVALID MCUBOOT MAGIC\n")); + } + } +#endif } return false; } @@ -178,6 +171,18 @@ LOCAL void presentBootloaderInformation(void) (void)memcpy(requestFirmwareConfig, &_nodeFirmwareConfig, sizeof(nodeFirmwareConfig_t)); // add bootloader information requestFirmwareConfig->BLVersion = MY_OTA_BOOTLOADER_VERSION; +#ifdef FIRMWARE_PROTOCOL_31 + requestFirmwareConfig->blockSize = FIRMWARE_BLOCK_SIZE; +#ifndef MCUBOOT_PRESENT + requestFirmwareConfig->img_commited = 0x2; + requestFirmwareConfig->img_revision = 0x00; + requestFirmwareConfig->img_build_num = 0x00; +#else + requestFirmwareConfig->img_commited = *((uint8_t*)(MCUBOOT_IMAGE_0_IMG_OK_ADDR)); + requestFirmwareConfig->img_revision = *((uint16_t*)(MCUBOOT_IMAGE_0_IMG_REVISION_ADDR)); + requestFirmwareConfig->img_build_num = *((uint16_t*)(MCUBOOT_IMAGE_0_IMG_BUILD_NUM_ADDR)); +#endif +#endif _firmwareUpdateOngoing = false; (void)_sendRoute(build(_msgTmp, GATEWAY_ADDRESS, NODE_SENSOR_ID, C_STREAM, ST_FIRMWARE_CONFIG_REQUEST, false)); @@ -192,8 +197,8 @@ LOCAL bool transportIsValidFirmware(void) { // init crc uint16_t crc = ~0; - for (uint16_t i = 0; i < _nodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE; ++i) { - crc ^= _flash.readByte(i + FIRMWARE_START_OFFSET); + for (uint32_t i = 0; i < _nodeFirmwareConfig.blocks * FIRMWARE_BLOCK_SIZE; ++i) { + crc ^= _flash_readByte(i + FIRMWARE_START_OFFSET); for (int8_t j = 0; j < 8; ++j) { if (crc & 1) { crc = (crc >> 1) ^ 0xA001; @@ -207,3 +212,76 @@ LOCAL bool transportIsValidFirmware(void) _nodeFirmwareConfig.crc); return crc == _nodeFirmwareConfig.crc; } + +LOCAL bool _firmwareResponse(uint16_t block, uint8_t *data) +{ + if (_firmwareUpdateOngoing) { + OTA_DEBUG(PSTR("OTA:FWP:RECV B=%04" PRIX16 "\n"), block); // received FW block + if (block != _firmwareBlock - 1) { + OTA_DEBUG(PSTR("!OTA:FWP:WRONG FWB\n")); // received FW block + // wrong firmware block received + setIndication(INDICATION_FW_UPDATE_RX_ERR); + // no further processing required + return true; + } + setIndication(INDICATION_FW_UPDATE_RX); + // Save block to flash +#ifdef MCUBOOT_PRESENT + uint32_t addr = ((size_t)(((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE)) + (size_t)( + FIRMWARE_START_OFFSET)); + if (addr>2); + } +#else + _flash_writeBytes( ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET, + data, FIRMWARE_BLOCK_SIZE); +#endif + // wait until flash written + while (_flash_busy()) {} +#ifdef OTA_EXTRA_FLASH_DEBUG + { + char prbuf[8]; + uint32_t addr = ((_firmwareBlock - 1) * FIRMWARE_BLOCK_SIZE) + FIRMWARE_START_OFFSET; + OTA_DEBUG(PSTR("OTA:FWP:FL DUMP ")); + sprintf_P(prbuf,PSTR("%04" PRIX16 ":"), (uint16_t)addr); + MY_SERIALDEVICE.print(prbuf); + for(uint8_t i=0; i> 8), (uint8_t)(firmwareSize & 0xff),':'}; + _flash_writeBytes(0, OTAbuffer, FIRMWARE_START_OFFSET); + // wait until flash ready + while (_flash_busy()) {} +#endif + hwReboot(); + } else { + setIndication(INDICATION_ERR_FW_CHECKSUM); + OTA_DEBUG(PSTR("!OTA:FWP:CRC FAIL\n")); + } + } + // reset flags + _firmwareRetry = MY_OTA_RETRY + 1; + _firmwareLastRequest = 0; + } else { + OTA_DEBUG(PSTR("!OTA:FWP:NO UPDATE\n")); + } + return true; +} diff --git a/core/MyOTAFirmwareUpdate.h b/core/MyOTAFirmwareUpdate.h index 3e011df0..321ffd34 100644 --- a/core/MyOTAFirmwareUpdate.h +++ b/core/MyOTAFirmwareUpdate.h @@ -58,17 +58,36 @@ #define MyOTAFirmwareUpdate_h #include "MySensorsCore.h" +#ifdef MCUBOOT_PRESENT +#include "generated_dts_board.h" +#define FIRMWARE_PROTOCOL_31 +#endif #define LOCAL static //!< static +#if MAX_PAYLOAD >= 22 #define FIRMWARE_BLOCK_SIZE (16u) //!< Size of each firmware block +#else +#define FIRMWARE_BLOCK_SIZE (8u) //!< Size of each firmware block +#ifndef FIRMWARE_PROTOCOL_31 +#define FIRMWARE_PROTOCOL_31 +#endif +#endif #define FIRMWARE_MAX_REQUESTS (5u) //!< Number of times a firmware block should be requested before giving up #define MY_OTA_RETRY (5u) //!< Number of times to request a fw block before giving up #define MY_OTA_RETRY_DELAY (500u) //!< Number of milliseconds before re-requesting a FW block +#ifndef MCUBOOT_PRESENT #define FIRMWARE_START_OFFSET (10u) //!< Start offset for firmware in flash (DualOptiboot wants to keeps a signature first) +#else +#define FIRMWARE_START_OFFSET (FLASH_AREA_IMAGE_1_OFFSET_0) //!< Use offset from generated_dts_board.h (mcuboot) +#endif #define MY_OTA_BOOTLOADER_MAJOR_VERSION (3u) //!< Bootloader version major +#ifdef FIRMWARE_PROTOCOL_31 +#define MY_OTA_BOOTLOADER_MINOR_VERSION (1u) //!< Bootloader version minor +#else #define MY_OTA_BOOTLOADER_MINOR_VERSION (0u) //!< Bootloader version minor +#endif #define MY_OTA_BOOTLOADER_VERSION (MY_OTA_BOOTLOADER_MINOR_VERSION * 256 + MY_OTA_BOOTLOADER_MAJOR_VERSION) //!< Bootloader version #if defined(MY_DEBUG_VERBOSE_OTA_UPDATE) @@ -77,6 +96,18 @@ #else #define OTA_DEBUG(x,...) //!< debug NULL #endif + +#if defined(DOXYGEN) && !defined(FIRMWARE_PROTOCOL_31) +/** + * @brief Enabled FOTA 3.1 protocol extensions + * + * Supports smaller FIRMWARE_BLOCK_SIZE, RLE and NVM for nRF5 with mcuboot. The + * extension is enabled per default when mcuboot is present or full FIRMWARE_BLOCK_SIZE + * exeeds MAX_PAYLOAD. + */ +#define FIRMWARE_PROTOCOL_31 +#endif + /** * @brief FW config structure, stored in eeprom */ @@ -96,6 +127,12 @@ typedef struct { uint16_t blocks; //!< Number of blocks uint16_t crc; //!< CRC of block data uint16_t BLVersion; //!< Bootloader version +#ifdef FIRMWARE_PROTOCOL_31 + uint8_t blockSize; //!< Blocksize, when protocol version >= 3.1 is reported. Otherwhise the blocksize is 16 + uint8_t img_commited; //!< mcuboot image_ok attribute commited firmware=0x01(mcuboot)|0x02(DualOptiboot), when protocol version >= 3.1 is reported + uint16_t img_revision; //!< mcuboot revision attribute, when protocol version >= 3.1 is reported + uint32_t img_build_num; //!< mcuboot build_num attribute, when protocol version >= 3.1 is reported +#endif } __attribute__((packed)) requestFirmwareConfig_t; /** @@ -117,6 +154,16 @@ typedef struct { uint8_t data[FIRMWARE_BLOCK_SIZE]; //!< Block data } __attribute__((packed)) replyFirmwareBlock_t; +/** +* @brief FW block reply structure (RLE) +*/ +typedef struct { + uint16_t type; //!< Type of config + uint16_t version; //!< Version of config + uint16_t block; //!< Block index + uint16_t number_of_blocks; //!< Number of blocks to fill with data + uint8_t data; //!< Block data +} __attribute__((packed)) replyFirmwareBlockRLE_t; /** * @brief Read firmware settings from EEPROM diff --git a/drivers/NRF5/Flash.cpp b/drivers/NRF5/Flash.cpp index 298f0f18..bc7720ed 100644 --- a/drivers/NRF5/Flash.cpp +++ b/drivers/NRF5/Flash.cpp @@ -52,12 +52,14 @@ uint32_t *FlashClass::page_address(size_t page) uint32_t *FlashClass::top_app_page_address() { +#if !defined(MCUBOOT_PRESENT) // Bootcode at the top of the flash memory? // https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.sdk5.v12.0.0%2Flib_bootloader.html if (NRF_UICR->NRFFW[0]<0xFFFFFFFF) { // Return pointer calculated by SoftDevice/bootloader return (uint32_t *)NRF_UICR->NRFFW[0]; } +#endif // Return flash length return (uint32_t *)(Flash.page_count() << Flash.page_size_bits()); diff --git a/drivers/NVM/VirtualPage.cpp b/drivers/NVM/VirtualPage.cpp index 760120c7..6eaeb7e3 100644 --- a/drivers/NVM/VirtualPage.cpp +++ b/drivers/NVM/VirtualPage.cpp @@ -21,37 +21,45 @@ VirtualPageClass VirtualPage; -#ifndef VNM_VIRTUAL_PAGE_SIZE_BITS -#define VNM_VIRTUAL_PAGE_SIZE_BITS 12 -#elif VNM_VIRTUAL_PAGE_SIZE_BITS < 12 -#error "VNM_VIRTUAL_PAGE_SIZE_BITS must be >= 12" +#ifndef NVM_VIRTUAL_PAGE_SIZE_BITS +#define NVM_VIRTUAL_PAGE_SIZE_BITS 12 +#elif NVM_VIRTUAL_PAGE_SIZE_BITS < 12 +#error "NVM_VIRTUAL_PAGE_SIZE_BITS must be >= 12" +#endif + +// Calculate virtual page count, when mcuboot is present +#if defined(MCUBOOT_PRESENT) && !defined(NVM_VIRTUAL_PAGE_COUNT) +// mcuboot zephyr build via generated_dts_board.h +#include "generated_dts_board.h" +// Calculate number of free pages after scratch area +#define NVM_VIRTUAL_PAGE_COUNT (((CONFIG_FLASH_SIZE_0<<10)-(FLASH_AREA_IMAGE_SCRATCH_OFFSET_0+FLASH_AREA_IMAGE_SCRATCH_SIZE_0)) >> NVM_VIRTUAL_PAGE_SIZE_BITS) #endif // check page size -#ifndef VNM_VIRTUAL_PAGE_COUNT +#ifndef NVM_VIRTUAL_PAGE_COUNT #if FLASH_ERASE_CYCLES >= 20000 // use 16k of flash memory -#define VNM_VIRTUAL_PAGE_COUNT 4 +#define NVM_VIRTUAL_PAGE_COUNT 4 #else // use 32k of flash memory -#define VNM_VIRTUAL_PAGE_COUNT 8 +#define NVM_VIRTUAL_PAGE_COUNT 8 #endif #endif /* * How many virtual pages are skipped from top of flash */ -#ifndef VNM_VIRTUAL_PAGE_SKIP_FROM_TOP -#define VNM_VIRTUAL_PAGE_SKIP_FROM_TOP 0 +#ifndef NVM_VIRTUAL_PAGE_SKIP_FROM_TOP +#define NVM_VIRTUAL_PAGE_SKIP_FROM_TOP 0 #endif /* - * Calculate things around VNM_VIRTUAL_PAGE_SIZE + * Calculate things around NVM_VIRTUAL_PAGE_SIZE */ -#define VNM_VIRTUAL_PAGE_SIZE (1 << (VNM_VIRTUAL_PAGE_SIZE_BITS)) -#define VNM_VIRTUAL_PAGE_ADDRESS_MASK (~(VNM_VIRTUAL_PAGE_SIZE - 1)) -#define VNM_VIRTUAL_PAGE_ALIGN(address) \ - { address = (uint32_t *)((uint32_t)address & VNM_VIRTUAL_PAGE_ADDRESS_MASK); } +#define NVM_VIRTUAL_PAGE_SIZE (1 << (NVM_VIRTUAL_PAGE_SIZE_BITS)) +#define NVM_VIRTUAL_PAGE_ADDRESS_MASK (~(NVM_VIRTUAL_PAGE_SIZE - 1)) +#define NVM_VIRTUAL_PAGE_ALIGN(address) \ + { address = (uint32_t *)((uint32_t)address & NVM_VIRTUAL_PAGE_ADDRESS_MASK); } /* * Defines the position of status words in a page. @@ -79,8 +87,8 @@ VirtualPageClass VirtualPage; #define OFFSET_MAGIC 1 #define OFFSET_COUNTER 0 #define MASK_ERASE_COUNTER 0x00FFFFFF -#define OFFSET_STATUS_RELEASE_PREPARE VNM_VIRTUAL_PAGE_SIZE - 8 -#define OFFSET_STATUS_RELEASE_END VNM_VIRTUAL_PAGE_SIZE - 4 +#define OFFSET_STATUS_RELEASE_PREPARE NVM_VIRTUAL_PAGE_SIZE - 8 +#define OFFSET_STATUS_RELEASE_END NVM_VIRTUAL_PAGE_SIZE - 4 #define METADATA_SIZE 16 #define OFFSET_DATA 4 #endif @@ -88,45 +96,45 @@ VirtualPageClass VirtualPage; #define BIT_STATUS_RELEASE_PREPARE (1 << 30) #define BIT_STATUS_RELEASE_END (1 << 31) -#define VNM_VIRTUAL_PAGE_DATA_SIZE (VNM_VIRTUAL_PAGE_SIZE - METADATA_SIZE) +#define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - METADATA_SIZE) #else // use first 8 byte for magic and erase counter and last 8 byte for page release #define OFFSET_MAGIC 1 #define OFFSET_ERASE_COUNTER 0 #define OFFSET_DATA 2 #define OFFSET_STATUS_RELEASE_PREPARE \ - ((VNM_VIRTUAL_PAGE_SIZE - 8) / sizeof(uint32_t)) + ((NVM_VIRTUAL_PAGE_SIZE - 8) / sizeof(uint32_t)) #define OFFSET_STATUS_RELEASE_END \ - ((VNM_VIRTUAL_PAGE_SIZE - 4) / sizeof(uint32_t)) + ((NVM_VIRTUAL_PAGE_SIZE - 4) / sizeof(uint32_t)) #define MASK_ERASE_COUNTER 0xFFFFFFFF #define BIT_STATUS_RELEASE_PREPARE 1 #define BIT_STATUS_RELEASE_END 1 -#define VNM_VIRTUAL_PAGE_DATA_SIZE (VNM_VIRTUAL_PAGE_SIZE - 16) +#define NVM_VIRTUAL_PAGE_DATA_SIZE (NVM_VIRTUAL_PAGE_SIZE - 16) #endif uint16_t VirtualPageClass::size() const { - return (VNM_VIRTUAL_PAGE_DATA_SIZE); + return (NVM_VIRTUAL_PAGE_DATA_SIZE); } uint16_t VirtualPageClass::length() const { - return (VNM_VIRTUAL_PAGE_DATA_SIZE / 4); + return (NVM_VIRTUAL_PAGE_DATA_SIZE / 4); } uint16_t VirtualPageClass::page_count() const { - return (VNM_VIRTUAL_PAGE_COUNT - 1); + return (NVM_VIRTUAL_PAGE_COUNT - 1); } uint32_t VirtualPageClass::wear_level() { uint32_t max_erase_cycles = 0; - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t erase_cycles = get_page_erase_cycles(get_page_address(i)); if (erase_cycles > max_erase_cycles) { max_erase_cycles = erase_cycles; @@ -140,7 +148,7 @@ uint32_t *VirtualPageClass::get(uint32_t magic) { // Give back a page prepared for release and not closed - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t *page = get_page_address(i); if ( // correct magic is set @@ -156,7 +164,7 @@ uint32_t *VirtualPageClass::get(uint32_t magic) } // check if a unreleased page is available - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t *page = get_page_address(i); if ( // correct magic is set @@ -177,7 +185,7 @@ uint32_t *VirtualPageClass::allocate(uint32_t magic) uint32_t max_erase_cycles = (uint32_t)~0; // Avoid duplicate allocation of pages, look for the less used page - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t *page = get_page_address(i); // Delete duplicated pages @@ -225,7 +233,7 @@ uint32_t *VirtualPageClass::allocate(uint32_t magic, uint32_t max_writes) void VirtualPageClass::release_prepare(uint32_t *address) { // move pointer to beginning of the page - VNM_VIRTUAL_PAGE_ALIGN(address); + NVM_VIRTUAL_PAGE_ALIGN(address); // Nothing to do at a empty page if (address[OFFSET_MAGIC] == (uint32_t)~0) { @@ -244,7 +252,7 @@ void VirtualPageClass::release_prepare(uint32_t *address) void VirtualPageClass::release(uint32_t *address) { // move pointer to beginning of the page - VNM_VIRTUAL_PAGE_ALIGN(address); + NVM_VIRTUAL_PAGE_ALIGN(address); // Nothing to do at a empty page if (address[OFFSET_MAGIC] == (uint32_t)~0) { @@ -263,7 +271,7 @@ void VirtualPageClass::release(uint32_t *address) bool VirtualPageClass::release_started(uint32_t *address) { // move pointer to beginning of the page - VNM_VIRTUAL_PAGE_ALIGN(address); + NVM_VIRTUAL_PAGE_ALIGN(address); return (address[OFFSET_STATUS_RELEASE_PREPARE] & BIT_STATUS_RELEASE_PREPARE) == 0; @@ -272,7 +280,7 @@ bool VirtualPageClass::release_started(uint32_t *address) void VirtualPageClass::fail(uint32_t *address) { // move pointer to beginning of the page - VNM_VIRTUAL_PAGE_ALIGN(address); + NVM_VIRTUAL_PAGE_ALIGN(address); build_page(address, 0x00000000); return; @@ -281,7 +289,7 @@ void VirtualPageClass::fail(uint32_t *address) void VirtualPageClass::clean_up() { // No page found -> try to give back a page prepared for release - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t *page = get_page_address(i); if ((page[OFFSET_STATUS_RELEASE_END] & BIT_STATUS_RELEASE_END) == 0) { build_page(get_page_address(i), ~0); @@ -292,7 +300,7 @@ void VirtualPageClass::clean_up() void VirtualPageClass::format() { - for (int i = 1; i <= VNM_VIRTUAL_PAGE_COUNT; i++) { + for (int i = 1; i <= NVM_VIRTUAL_PAGE_COUNT; i++) { uint32_t *address = get_page_address(i); build_page(address, (uint32_t)~0); } @@ -301,25 +309,25 @@ void VirtualPageClass::format() uint32_t *VirtualPageClass::get_page_address(uint16_t page) { return (uint32_t *)(Flash.top_app_page_address() - - ((page + VNM_VIRTUAL_PAGE_SKIP_FROM_TOP) - << VNM_VIRTUAL_PAGE_SIZE_BITS)); + ((page + NVM_VIRTUAL_PAGE_SKIP_FROM_TOP) + << NVM_VIRTUAL_PAGE_SIZE_BITS)); } void VirtualPageClass::build_page(uint32_t *address, uint32_t magic) { // move pointer to beginning of the page - VNM_VIRTUAL_PAGE_ALIGN(address); + NVM_VIRTUAL_PAGE_ALIGN(address); // get erase counter uint32_t erase_counter = get_page_erase_cycles(address); // Check if a magic is set if (address[OFFSET_MAGIC] != (uint32_t)~0) { - Flash.erase(address, VNM_VIRTUAL_PAGE_SIZE); + Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE); } else { // check if page is empty - for (int i = OFFSET_DATA; i < (VNM_VIRTUAL_PAGE_SIZE / 4); i++) { + for (int i = OFFSET_DATA; i < (NVM_VIRTUAL_PAGE_SIZE / 4); i++) { if (address[i] != (uint32_t)~0) { - Flash.erase(address, VNM_VIRTUAL_PAGE_SIZE); + Flash.erase(address, NVM_VIRTUAL_PAGE_SIZE); break; } } diff --git a/examples/SecurityPersonalizer/SecurityPersonalizer.ino b/examples/SecurityPersonalizer/SecurityPersonalizer.ino index 58ae7593..17eeac54 100644 --- a/examples/SecurityPersonalizer/SecurityPersonalizer.ino +++ b/examples/SecurityPersonalizer/SecurityPersonalizer.ino @@ -234,6 +234,10 @@ #define GENERATE_SOMETHING #endif +#if defined(MY_LOCK_MCU) +#undefine MY_LOCK_MCU // The Sketch after SecurityPersonaliter should lock the MCU +#endif + /********************************** Preprocessor sanitychecks *************************************/ #if defined(GENERATE_SOFT_SERIAL) && !defined(USE_SOFT_SIGNING) #error Cannot generate soft serial using ATSHA204A, use USE_SOFT_SINGING for this diff --git a/hal/architecture/NRF5/MyHwNRF5.cpp b/hal/architecture/NRF5/MyHwNRF5.cpp index b1b7dc7f..921bbf8a 100644 --- a/hal/architecture/NRF5/MyHwNRF5.cpp +++ b/hal/architecture/NRF5/MyHwNRF5.cpp @@ -66,6 +66,31 @@ void hwWriteConfig(int adr, uint8_t value) bool hwInit() { +#ifdef MY_LOCK_MCU +#ifdef NRF51 + // Lock MCU + if((uint32_t)((NRF_UICR->RBPCONF & UICR_RBPCONF_PALL_Msk) >> UICR_RBPCONF_PALL_Pos) != + UICR_RBPCONF_PALL_Enabled) { + Flash.write((uint32_t *)&NRF_UICR->RBPCONF, (NRF_UICR->RBPCONF & ~UICR_RBPCONF_PALL_Msk)); + hwReboot(); + } +#else + // Lock MCU + if((uint32_t)((NRF_UICR->APPROTECT & UICR_APPROTECT_PALL_Msk) >> UICR_APPROTECT_PALL_Pos) != + UICR_APPROTECT_PALL_Enabled) { + Flash.write((uint32_t *)&NRF_UICR->APPROTECT, (NRF_UICR->APPROTECT & ~UICR_APPROTECT_PALL_Msk)); + hwReboot(); + } +#endif +#endif + +#if defined(NRF51) && defined(CONFIG_ENABLE_PINRESET) + // Enabling reset for NRF51 isn't handled by arduino-nrf5. Enable it, if requested. + NRF_POWER->RESET = POWER_RESET_RESET_Enabled; + NRF_POWER->RAMON |= (POWER_RAMON_ONRAM0_RAM0On << POWER_RAMON_ONRAM0_Pos) | + (POWER_RAMON_ONRAM1_RAM1On << POWER_RAMON_ONRAM1_Pos); +#endif + // Clock is manged by sleep modes. Radio depends on HFCLK. // Force to start HFCLK NRF_CLOCK->EVENTS_HFCLKSTARTED = 0;