diff --git a/extras/tools/bin2ota.py b/extras/tools/bin2ota.py index 5ec788d95..57e3f47f2 100755 --- a/extras/tools/bin2ota.py +++ b/extras/tools/bin2ota.py @@ -5,7 +5,7 @@ if len(sys.argv) != 4: print ("Usage: bin2ota.py BOARD sketch.bin sketch.ota") - print (" BOARD = [ MKR_WIFI_1010 | NANO_33_IOT | PORTENTA_H7_M7 | NANO_RP2040_CONNECT | NICLA_VISION | OPTA | GIGA | NANO_ESP32 ]") + print (" BOARD = [ MKR_WIFI_1010 | NANO_33_IOT | PORTENTA_H7_M7 | NANO_RP2040_CONNECT | NICLA_VISION | OPTA | GIGA | NANO_ESP32 | ESP32 | UNOR4WIFI]") sys.exit() board = sys.argv[1] @@ -37,6 +37,8 @@ # Magic number for all ESP32 boards not related to (VID/PID) elif board == "ESP32": magic_number = 0x45535033.to_bytes(4,byteorder='little') +elif board == "UNOR4WIFI": + magic_number = 0x23411002.to_bytes(4,byteorder='little') else: print ("Error,", board, "is not a supported board type") sys.exit() diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index b6f5fff80..6e65e1be3 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -108,7 +108,7 @@ #define OTA_STORAGE_PORTENTA_QSPI (0) #endif -#if defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_UNOR4_WIFI) #define OTA_STORAGE_ESP (1) #endif diff --git a/src/tls/bearssl/dec32be.c b/src/tls/bearssl/dec32be.c index 0b8b05a5e..733381229 100644 --- a/src/tls/bearssl/dec32be.c +++ b/src/tls/bearssl/dec32be.c @@ -23,7 +23,9 @@ */ #include -#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \ + defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \ + defined(ARDUINO_UNOR4_WIFI) #include "inner.h" diff --git a/src/tls/bearssl/enc32be.c b/src/tls/bearssl/enc32be.c index a00d96123..26fa3cee1 100644 --- a/src/tls/bearssl/enc32be.c +++ b/src/tls/bearssl/enc32be.c @@ -23,7 +23,9 @@ */ #include -#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \ + defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \ + defined(ARDUINO_UNOR4_WIFI) #include "inner.h" diff --git a/src/tls/bearssl/sha2small.c b/src/tls/bearssl/sha2small.c index 02283f05c..04cc71f81 100644 --- a/src/tls/bearssl/sha2small.c +++ b/src/tls/bearssl/sha2small.c @@ -23,7 +23,9 @@ */ #include -#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) +#if defined(BOARD_HAS_ECCX08) || defined(BOARD_HAS_OFFLOADED_ECCX08) || \ + defined(BOARD_HAS_SE050) || defined(ARDUINO_ARCH_ESP32) || \ + defined(ARDUINO_UNOR4_WIFI) #include "inner.h" diff --git a/src/utility/ota/OTA-unor4.cpp b/src/utility/ota/OTA-unor4.cpp new file mode 100644 index 000000000..34c417b02 --- /dev/null +++ b/src/utility/ota/OTA-unor4.cpp @@ -0,0 +1,187 @@ +/* + This file is part of ArduinoIoTCloud. + + Copyright 2020 ARDUINO SA (http://www.arduino.cc/) + + This software is released under the GNU General Public License version 3, + which covers the main part of arduino-cli. + The terms of this license can be found at: + https://www.gnu.org/licenses/gpl-3.0.en.html + + You can be released from the requirements of the above licenses by purchasing + a commercial license. Buying such a license is mandatory if you want to modify or + otherwise use the software for commercial activities involving the Arduino + software without disclosing the source code of your own applications. To purchase + a commercial license, send an email to license@arduino.cc. +*/ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include + +#if defined ARDUINO_UNOR4_WIFI && OTA_ENABLED + +#include "OTAUpdate.h" +#include +#include "tls/utility/SHA256.h" +#include "fsp_common_api.h" +#include "r_flash_lp.h" +#include "WiFiS3.h" + +/****************************************************************************** + * DEFINES + ******************************************************************************/ + +/* Key code for writing PRCR register. */ +#define BSP_PRV_PRCR_KEY (0xA500U) +#define BSP_PRV_PRCR_PRC1_UNLOCK ((BSP_PRV_PRCR_KEY) | 0x2U) +#define BSP_PRV_PRCR_LOCK ((BSP_PRV_PRCR_KEY) | 0x0U) + +#define OTA_MAGIC (*((volatile uint16_t *) &R_SYSTEM->VBTBKR[4])) +#define OTA_SIZE (*((volatile uint32_t *) &R_SYSTEM->VBTBKR[6])) + +/****************************************************************************** + * FUNCTION DEFINITION + ******************************************************************************/ + +static void unor4_setOTASize(uint32_t size) +{ + R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_PRC1_UNLOCK; + OTA_MAGIC = 0x07AA; + OTA_SIZE = size; + R_SYSTEM->PRCR = (uint16_t) BSP_PRV_PRCR_LOCK; +} + +static uint32_t unor4_getOTASize() +{ + if (OTA_MAGIC == 0x07AA) + { + return OTA_SIZE; + } + return 0; +} + +static int unor4_codeFlashOpen(flash_lp_instance_ctrl_t * ctrl) +{ + flash_cfg_t cfg; + + cfg.data_flash_bgo = false; + cfg.p_callback = nullptr; + cfg.p_context = nullptr; + cfg.p_extend = nullptr; + cfg.ipl = (BSP_IRQ_DISABLED); + cfg.irq = FSP_INVALID_VECTOR; + cfg.err_ipl = (BSP_IRQ_DISABLED); + cfg.err_irq = FSP_INVALID_VECTOR; + + fsp_err_t rv = FSP_ERR_UNSUPPORTED; + + rv = R_FLASH_LP_Open(ctrl,&cfg); + return rv; +} + +static int unor4_codeFlashClose(flash_lp_instance_ctrl_t * ctrl) +{ + fsp_err_t rv = FSP_ERR_UNSUPPORTED; + + rv = R_FLASH_LP_Close(ctrl); + return rv; +} + +int unor4_onOTARequest(char const * ota_url) +{ + OTAUpdate::Error ota_err = OTAUpdate::Error::None; + OTAUpdate ota; + + /* Initialize the board for OTA handling. */ + if ((ota_err = ota.begin("/update.bin")) != OTAUpdate::Error::None) + { + DEBUG_ERROR("OTAUpdate::begin() failed with %d", static_cast(ota_err)); + return static_cast(ota_err); + } + + /* Download the OTA file from the web storage location. */ + int const ota_download = ota.download(ota_url,"/update.bin"); + if (ota_download <= 0) + { + DEBUG_ERROR("OTAUpdate::download() failed with %d", ota_download); + return ota_download; + } + DEBUG_VERBOSE("OTAUpdate::download() %d bytes downloaded", static_cast(ota_download)); + + /* Verify update integrity */ + if ((ota_err = ota.verify()) != OTAUpdate::Error::None) + { + DEBUG_ERROR("OTAUpdate::verify() failed with %d", static_cast(ota_err)); + return static_cast(ota_err); + } + + /* Store update size and write OTA magin number */ + unor4_setOTASize(ota_download); + + /* Flash new firmware */ + if ((ota_err = ota.update("/update.bin")) != OTAUpdate::Error::None) + { + DEBUG_ERROR("OTAUpdate::update() failed with %d", static_cast(ota_err)); + return static_cast(ota_err); + } + + return static_cast(OTAUpdate::Error::None); +} + +String unor4_getOTAImageSHA256() +{ + /* The length of the application can be retrieved the same way it was + * communicated to the bootloader, that is by writing to the non-volatile + * storage registers of the RTC. + */ + SHA256 sha256; + uint32_t const app_start = 0x4000; + uint32_t const app_size = unor4_getOTASize(); + + flash_lp_instance_ctrl_t ctrl; + unor4_codeFlashOpen(&ctrl); + + sha256.begin(); + uint32_t b = 0; + uint32_t bytes_read = 0; for(uint32_t a = app_start; + bytes_read < app_size; + bytes_read += sizeof(b), a += sizeof(b)) + { + /* Read the next chunk of memory. */ + memcpy(&b, reinterpret_cast(a), sizeof(b)); + /* Feed it to SHA256. */ + sha256.update(reinterpret_cast(&b), sizeof(b)); + } + + unor4_codeFlashClose(&ctrl); + + /* Retrieve the final hash string. */ + uint8_t sha256_hash[SHA256::HASH_SIZE] = {0}; + sha256.finalize(sha256_hash); + String sha256_str; + std::for_each(sha256_hash, + sha256_hash + SHA256::HASH_SIZE, + [&sha256_str](uint8_t const elem) + { + char buf[4]; + snprintf(buf, 4, "%02X", elem); + sha256_str += buf; + }); + DEBUG_ERROR("SHA256: %d bytes (of %d) read", bytes_read, app_size); + return sha256_str; +} + +bool unor4_isOTACapable() +{ + /* check firmware version */ + String const fv = WiFi.firmwareVersion(); + if (fv < String("0.3.0")) { + return false; + } + return true; +} + +#endif /* ARDUINO_UNOR4_WIFI */ diff --git a/src/utility/ota/OTA.cpp b/src/utility/ota/OTA.cpp index bac388b59..e71aac5a2 100644 --- a/src/utility/ota/OTA.cpp +++ b/src/utility/ota/OTA.cpp @@ -55,6 +55,12 @@ String esp32_getOTAImageSHA256(); bool esp32_isOTACapable(); #endif +#ifdef ARDUINO_UNOR4_WIFI +int unor4_onOTARequest(char const * url); +String unor4_getOTAImageSHA256(); +bool unor4_isOTACapable(); +#endif + /****************************************************************************** * PUBLIC MEMBER FUNCTIONS ******************************************************************************/ @@ -74,6 +80,9 @@ int OTA::onRequest(String url, NetworkAdapter iface) #elif defined (ARDUINO_ARCH_ESP32) (void)iface; return esp32_onOTARequest(url.c_str()); +#elif defined (ARDUINO_UNOR4_WIFI) + (void)iface; + return unor4_onOTARequest(url.c_str()); #else #error "OTA not supported for this architecture" #endif @@ -89,6 +98,8 @@ String OTA::getImageSHA256() return portenta_h7_getOTAImageSHA256(); #elif defined (ARDUINO_ARCH_ESP32) return esp32_getOTAImageSHA256(); +#elif defined (ARDUINO_UNOR4_WIFI) + return unor4_getOTAImageSHA256(); #else #error "No method for SHA256 checksum calculation over application image defined for this architecture." #endif @@ -104,6 +115,8 @@ bool OTA::isCapable() return portenta_h7_isOTACapable(); #elif defined (ARDUINO_ARCH_ESP32) return esp32_isOTACapable(); +#elif defined (ARDUINO_UNOR4_WIFI) + return unor4_isOTACapable(); #else #error "OTA not supported for this architecture" #endif