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
This commit is contained in:
+18
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
+3
-1
@@ -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
|
||||
|
||||
+144
-66
@@ -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<FIRMWARE_BLOCK_SIZE; i++) {
|
||||
uint8_t data = _flash.readByte(addr + i);
|
||||
sprintf_P(prbuf,PSTR("%02" PRIX8 ""), (uint8_t)data);
|
||||
MY_SERIALDEVICE.print(prbuf);
|
||||
}
|
||||
OTA_DEBUG(PSTR("\n"));
|
||||
}
|
||||
#endif
|
||||
_firmwareBlock--;
|
||||
if (!_firmwareBlock) {
|
||||
// We're done! Do a checksum and reboot.
|
||||
OTA_DEBUG(PSTR("OTA:FWP:FW END\n")); // received FW block
|
||||
_firmwareUpdateOngoing = false;
|
||||
if (transportIsValidFirmware()) {
|
||||
OTA_DEBUG(PSTR("OTA:FWP:CRC OK\n")); // FW checksum ok
|
||||
// Write the new firmware config to eeprom
|
||||
hwWriteConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,
|
||||
sizeof(nodeFirmwareConfig_t));
|
||||
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
|
||||
const uint16_t firmwareSize = FIRMWARE_BLOCK_SIZE * _nodeFirmwareConfig.blocks;
|
||||
const uint8_t OTAbuffer[FIRMWARE_START_OFFSET] = {'F','L','X','I','M','G',':', (uint8_t)(firmwareSize >> 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; i<FIRMWARE_BLOCK_SIZE; i++) {
|
||||
data[i]=firmwareResponse->data;
|
||||
}
|
||||
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<FLASH_AREA_IMAGE_SCRATCH_OFFSET_0) {
|
||||
Flash.write_block( (uint32_t *)addr, (uint32_t *)data, FIRMWARE_BLOCK_SIZE>>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<FIRMWARE_BLOCK_SIZE; i++) {
|
||||
uint8_t data = _flash_readByte(addr + i);
|
||||
sprintf_P(prbuf,PSTR("%02" PRIX8 ""), (uint8_t)data);
|
||||
MY_SERIALDEVICE.print(prbuf);
|
||||
}
|
||||
OTA_DEBUG(PSTR("\n"));
|
||||
}
|
||||
#endif
|
||||
_firmwareBlock--;
|
||||
if (!_firmwareBlock) {
|
||||
// We're done! Do a checksum and reboot.
|
||||
OTA_DEBUG(PSTR("OTA:FWP:FW END\n")); // received FW block
|
||||
_firmwareUpdateOngoing = false;
|
||||
if (transportIsValidFirmware()) {
|
||||
OTA_DEBUG(PSTR("OTA:FWP:CRC OK\n")); // FW checksum ok
|
||||
// Write the new firmware config to eeprom
|
||||
hwWriteConfigBlock((void*)&_nodeFirmwareConfig, (void*)EEPROM_FIRMWARE_TYPE_ADDRESS,
|
||||
sizeof(nodeFirmwareConfig_t));
|
||||
#ifndef MCUBOOT_PRESENT
|
||||
// All seems ok, write size and signature to flash (DualOptiboot will pick this up and flash it)
|
||||
const uint16_t firmwareSize = FIRMWARE_BLOCK_SIZE * _nodeFirmwareConfig.blocks;
|
||||
const uint8_t OTAbuffer[FIRMWARE_START_OFFSET] = {'F','L','X','I','M','G',':', (uint8_t)(firmwareSize >> 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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
+47
-39
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user