/* nrf5340.c * * Copyright (C) 2024 wolfSSL Inc. * * This file is part of wolfBoot. * * wolfBoot is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfBoot is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ /* Note: Also used by TARGET_nrf5340_net */ #ifdef TARGET_nrf5340 #include #include "image.h" #include "string.h" #include "printf.h" #include "nrf5340.h" #include "spi_flash.h" /* TODO: * Key Storage: See 7.1.18.4.2 Key storage: * The key storage region of the UICR can contain multiple keys of different type, including symmetrical keys, hashes, public/private key pairs and other device secrets * Key headers are allocated an address range of 0x400 in the UICR memory map, allowing a total of 128 keys to be addressable inside the key storage region. * The key storage region contains multiple key slots, where each slot consists of a key header and an associated key value. The key value is limited to 128 bits. * Any key size greater than 128 bits must be divided and distributed over multiple key slot instances. */ #ifndef USE_RTC #define USE_RTC 0 /* Use RTC0 for sleep */ #endif /* Network updates can be signed with "--id 2" and placed into the normal update partition, * or they can be placed into the external flash at offset 0x100000 */ /* Set Partition ID in .config using WOLFBOOT_PART_ID=2 */ #ifndef PART_NET_ID #define PART_NET_ID 2 /* default */ #endif /* Offset in external QSPI flash for network update * Comes from nrf5340_net.config WOLFBOOT_PARTITION_UPDATE_ADDRESS) */ #ifndef PART_NET_ADDR #define PART_NET_ADDR 0x100000UL #endif /* IPC Channels 0/1 */ /* Channel 0: APP Send -> NET Recv * Channel 1: NET Send -> APP Recv */ #ifdef TARGET_nrf5340_app #define USE_IPC_SEND 0 #define USE_IPC_RECV 1 #else #define USE_IPC_SEND 1 #define USE_IPC_RECV 0 #endif /* SHM: Shared Memory between network and application cores */ /* first 64KB (0x10000) is used by wolfBoot and limited in nrf5340.ld */ #ifndef SHARED_MEM_ADDR #define SHARED_MEM_ADDR (0x20000000UL + (64 * 1024)) #endif /* Shared memory states (mask, easier to check) */ #define SHARED_STATUS_UNKNOWN 0x00 #define SHARED_STATUS_READY 0x01 #define SHARED_STATUS_VERSION 0x02 #define SHARED_STATUS_UPDATE_START 0x04 #define SHARED_STATUS_UPDATE_DONE 0x08 #define SHARED_STATUS_DO_BOOT 0x10 #define SHARED_STATUS_TIMEOUT 0x80 #define SHAREM_MEM_MAGIC 0x5753484D /* WSHM */ typedef struct { uint32_t magic; uint32_t status; uint32_t version; /* always refers to network core version */ uint32_t size; } ShmInfo_t; typedef struct { ShmInfo_t net; /* network core write location */ ShmInfo_t app; /* application core write location */ } SharedCores_t; typedef struct { SharedCores_t core; /* Note: If enableShm=0 do not access below */ /* application places firmware here */ uint8_t data[FLASH_SIZE_NET]; /* used as "swap" */ uint8_t swap[FLASH_PAGESZ_NET]; } SharedMem_t; static int enableShm = 0; static int doUpdateNet = 0; #ifdef TARGET_nrf5340_app static SharedMem_t* shm = (SharedMem_t*)SHARED_MEM_ADDR; #else static SharedCores_t shm_shadow; static SharedMem_t* shm = (SharedMem_t*)&shm_shadow; #endif /* UART */ #ifdef DEBUG_UART #ifndef UART_SEL #define UART_SEL 0 /* select UART 0 or 1 */ #endif #if !defined(UART_PORT) && !defined(UART_PIN) #if defined(TARGET_nrf5340_app) #define UART_PORT 0 #define UART_PIN 20 #define UART_NET_PORT 1 #define UART_NET_PIN 1 #else #define UART_PORT 1 #define UART_PIN 1 #endif #endif void uart_init(void) { /* nRF5340-DK: (P0.20 or P1.01) * App: UART0=P0.20 * Net: UART0=P1.01 */ UART_ENABLE(UART_SEL) = 0; GPIO_PIN_CNF(UART_PORT, UART_PIN) = (GPIO_CNF_OUT #ifdef TARGET_nrf5340_net | GPIO_CNF_MCUSEL(1) #endif ); UART_PSEL_TXD(UART_SEL) = (PSEL_PORT(UART_PORT) | UART_PIN); UART_BAUDRATE(UART_SEL) = BAUD_115200; UART_CONFIG(UART_SEL) = 0; /* Flow=Diabled, Stop=1-bit, Parity exclude */ UART_ENABLE(UART_SEL) = 8; /* allow network core access to UART pin - must be set from app core */ #if defined(TARGET_nrf5340_app) && \ defined(UART_NET_PORT) && defined(UART_NET_PIN) GPIO_PIN_CNF(UART_NET_PORT, UART_NET_PIN) = (GPIO_CNF_OUT | GPIO_CNF_MCUSEL(1)); #endif } #ifndef UART_TX_MAX_SZ #define UART_TX_MAX_SZ 128 #endif void uart_write_sz(const char* c, unsigned int sz) { /* EasyDMA must be a RAM buffer */ static uint8_t uartTxBuf[UART_TX_MAX_SZ]; while (sz > 0) { unsigned int xfer = sz; if (xfer > sizeof(uartTxBuf)) xfer = sizeof(uartTxBuf); memcpy(uartTxBuf, c, xfer); UART_EVENT_ENDTX(UART_SEL) = 0; UART_TXD_PTR(UART_SEL) = (uint32_t)uartTxBuf; UART_TXD_MAXCOUNT(UART_SEL) = xfer; UART_TASK_STARTTX(UART_SEL) = 1; while (UART_EVENT_ENDTX(UART_SEL) == 0); sz -= xfer; c += xfer; } } void uart_write(const char* buf, unsigned int sz) { const char* line; unsigned int lineSz; do { /* find `\n` */ line = memchr(buf, '\n', sz); if (line == NULL) { uart_write_sz(buf, sz); break; } lineSz = line - buf; if (lineSz > sz-1) lineSz = sz-1; uart_write_sz(buf, lineSz); uart_write_sz("\r\n", 2); /* handle CRLF */ buf = line; sz -= lineSz + 1; /* skip \n, already sent */ } while ((int)sz > 0); } #endif /* DEBUG_UART */ /* Non-volatile memory controller - use actual flash address */ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { int i = 0; uint32_t *src, *dst; #ifdef DEBUG_FLASH wolfBoot_printf("Internal Flash Write: addr 0x%x, len %d\n", address, len); #endif while (i < len) { if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) { src = (uint32_t *)data; dst = (uint32_t *)address; /* set both secure and non-secure registers */ NVMC_CONFIG = NVMC_CONFIG_WEN; NVMC_CONFIGNS = NVMC_CONFIG_WEN; while (NVMC_READY == 0); dst[i >> 2] = src[i >> 2]; while (NVMC_READY == 0); i+=4; } else { uint32_t val; uint8_t *vbytes = (uint8_t *)(&val); int off = (address + i) - (((address + i) >> 2) << 2); dst = (uint32_t *)(address - off); val = dst[i >> 2]; vbytes[off] = data[i]; /* set both secure and non-secure registers */ NVMC_CONFIG = NVMC_CONFIG_WEN; NVMC_CONFIGNS = NVMC_CONFIG_WEN; while (NVMC_READY == 0); dst[i >> 2] = val; while (NVMC_READY == 0); i++; } } return 0; } int RAMFUNCTION hal_flash_erase(uint32_t address, int len) { uint32_t end = address + len - 1; uint32_t p; #if defined(DEBUG_FLASH) && DEBUG_FLASH > 1 wolfBoot_printf("Internal Flash Erase: addr 0x%x, len %d\n", address, len); #endif /* mask to page start address */ address &= ~(FLASH_PAGE_SIZE-1); for (p = address; p <= end; p += FLASH_PAGE_SIZE) { /* set both secure and non-secure registers */ NVMC_CONFIG = NVMC_CONFIG_EEN; NVMC_CONFIGNS = NVMC_CONFIG_EEN; while (NVMC_READY == 0); *(volatile uint32_t *)p = 0xFFFFFFFF; while (NVMC_READY == 0); #ifdef DEBUG_FLASH wolfBoot_printf("Internal Flash Erase: page 0x%x\n", p); #endif } return 0; } void RAMFUNCTION hal_flash_unlock(void) { } void RAMFUNCTION hal_flash_lock(void) { } #ifdef TARGET_nrf5340_net /* external flash is access application core shared memory directly */ /* calculates location in shared memory */ static uintptr_t ext_flash_addr_calc(uintptr_t address) { /* offset external flash addresses by the update partition address */ address -= WOLFBOOT_PARTITION_UPDATE_ADDRESS; /* check address */ if (address >= (FLASH_SIZE_NET + FLASH_PAGESZ_NET)) { address = 0; } return address; } int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { uintptr_t addr = ext_flash_addr_calc(address); #ifdef DEBUG_FLASH wolfBoot_printf("Ext Write: Len %d, Addr 0x%x (off 0x%x) -> 0x%x\n", len, address, addr, data); #endif if (enableShm) memcpy(shm->data + addr, data, len); return 0; } int ext_flash_read(uintptr_t address, uint8_t *data, int len) { uintptr_t addr = ext_flash_addr_calc(address); #ifdef DEBUG_FLASH wolfBoot_printf("Ext Read: Len %d, Addr 0x%x (off 0x%x) -> %p\n", len, address, addr, data); #endif if (enableShm) memcpy(data, shm->data + addr, len); else memset(data, FLASH_BYTE_ERASED, len); return len; } int ext_flash_erase(uintptr_t address, int len) { uintptr_t addr = ext_flash_addr_calc(address); #ifdef DEBUG_FLASH wolfBoot_printf("Ext Erase: Len %d, Addr 0x%x (off 0x%x)\n", len, address, addr); #endif if (enableShm) memset(shm->data + addr, FLASH_BYTE_ERASED, len); return 0; } void ext_flash_lock(void) { /* no op */ } void ext_flash_unlock(void) { /* no op */ } #endif /* TARGET_nrf5340_net */ static void clock_init(void) { #ifdef TARGET_nrf5340_app CLOCK_HFCLKSRC = 1; /* use external high frequency clock */ CLOCK_HFCLKSTART = 1; /* wait for high frequency clock startup */ while (CLOCK_HFCLKSTARTED == 0); #endif /* Start low frequency clock - used by RTC */ CLOCK_LFCLKSRC = 0; /* internal low power */ CLOCK_LFCLKSTART = 1; /* wait for high frequency clock startup */ while (CLOCK_LFCLKSTARTED == 0); RTC_PRESCALER(USE_RTC) = 0; /* 32768 per second */ } void sleep_us(uint32_t usec) { /* Calculate number ticks to wait */ uint32_t comp = ((usec * 32768UL) / 1000000); if (comp == 0) comp = 1; /* wait at least 1 tick */ if (comp > RTC_OVERFLOW) comp = RTC_OVERFLOW; /* max wait (512 seconds with prescaler=0) */ RTC_CLEAR(USE_RTC) = 1; RTC_EVTENSET(USE_RTC) = RTC_EVTENSET_CC0; RTC_EVENT_CC(USE_RTC, 0) = 0; /* clear compare event */ RTC_CC(USE_RTC, 0) = comp; RTC_START(USE_RTC) = 1; /* wait for compare event */ while (RTC_EVENT_CC(USE_RTC, 0) == 0); RTC_STOP(USE_RTC) = 1; } #ifdef TARGET_nrf5340_app void hal_net_core(int hold) /* 1=hold, 0=release */ { if (hold) { /* stop the network core from booting */ NETWORK_FORCEOFF = NETWORK_FORCEOFF_HOLD; } else { /* release network core - errata 161 network core release */ NETWORK_ERRATA_161 = 1; NETWORK_FORCEOFF = NETWORK_FORCEOFF_RELEASE; sleep_us(5); NETWORK_FORCEOFF = NETWORK_FORCEOFF_HOLD; sleep_us(1); NETWORK_FORCEOFF = NETWORK_FORCEOFF_RELEASE; NETWORK_ERRATA_161 = 0; } } #endif static uint8_t* get_image_hdr(struct wolfBoot_image* img) { #ifdef EXT_FLASH return img->hdr_cache; #else return img->hdr; #endif } static uint16_t get_image_partition_id(struct wolfBoot_image* img) { return wolfBoot_get_blob_type(get_image_hdr(img)) & HDR_IMG_TYPE_PART_MASK; } #define IMAGE_IS_NET_CORE(img) ( \ (get_image_partition_id(img) == PART_NET_ID) && \ (img->fw_size < (FLASH_SIZE_NET - IMAGE_HEADER_SIZE))) static int hal_net_get_image(struct wolfBoot_image* img, ShmInfo_t* info) { int ret; #ifdef TARGET_nrf5340_app /* check the update partition for a network core update */ ret = wolfBoot_open_image(img, PART_UPDATE); if (ret == 0 && !IMAGE_IS_NET_CORE(img)) { ret = -1; } /* if external flash is enabled, try an alternate location */ #ifdef EXT_FLASH if (ret != 0) { ret = wolfBoot_open_image_external(img, PART_UPDATE, (uint8_t*)PART_NET_ADDR); if (ret == 0 && !IMAGE_IS_NET_CORE(img)) { ret = -1; } } #endif #else /* TARGET_nrf5340_net */ ret = wolfBoot_open_image(img, PART_BOOT); #endif /* TARGET_nrf5340_* */ if (ret == 0) { uint32_t ver = wolfBoot_get_blob_version(get_image_hdr(img)); /* Note: network core fault writing to shared memory means application * core did not enable access at run-time yet */ info->version = ver; info->size = IMAGE_HEADER_SIZE + img->fw_size; wolfBoot_printf("Network Image: Ver 0x%x, Size %d\n", ver, img->fw_size); } else { info->version = 0; /* not known */ wolfBoot_printf("Network Image: Update not found\n"); } return ret; } static void hal_shm_init(void) { IPC_SEND_CNF(USE_IPC_SEND) = (1 << USE_IPC_SEND); IPC_RECEIVE_CNF(USE_IPC_RECV) = (1 << USE_IPC_RECV); IPC_EVENTS_RECEIVE(USE_IPC_SEND) = 0; IPC_EVENTS_RECEIVE(USE_IPC_RECV) = 0; #ifdef TARGET_nrf5340_app /* Allow the network core to access shared SDRAM at 0x2000_0000 */ SPU_EXTDOMAIN_PERM(0) = (SPU_EXTDOMAIN_PERM_SECATTR_SECURE | SPU_EXTDOMAIN_PERM_UNLOCK); #endif } static void hal_shm_status_set(ShmInfo_t* info, uint32_t status) { IPC_TASKS_SEND(USE_IPC_SEND) = 1; if (info != NULL) { info->magic = SHAREM_MEM_MAGIC; info->status = status; } } static uint32_t hal_shm_status_wait(ShmInfo_t* info, uint32_t status, uint32_t timeout_ms) { uint32_t status_ret = SHARED_STATUS_UNKNOWN; int ret = 0; do { /* see if status shared already */ if (info != NULL && (info->magic == SHAREM_MEM_MAGIC && (info->status & status) != 0)) { status_ret = info->status; break; } /* Wait for event */ while (IPC_EVENTS_RECEIVE(USE_IPC_RECV) == 0 && --timeout_ms > 0) { sleep_us(1000); } if (timeout_ms == 0) { status_ret = SHARED_STATUS_TIMEOUT; break; } /* clear event */ IPC_EVENTS_RECEIVE(USE_IPC_RECV) = 0; /* if we got an event and "info" not provided, just return status to * signal event occurred */ if (info == NULL) { status_ret = status; break; } } while (1); return status_ret; } static void hal_shm_cleanup(void) { #ifdef TARGET_nrf5340_app /* Restore defaults preventing network core from accessing shared SDRAM */ SPU_EXTDOMAIN_PERM(0) = (SPU_EXTDOMAIN_PERM_SECATTR_NONSECURE | SPU_EXTDOMAIN_PERM_UNLOCK); #endif } static const char* hal_shm_status_string(uint32_t status) { switch (status) { case SHARED_STATUS_READY: return "Ready"; case SHARED_STATUS_VERSION: return "Version"; case SHARED_STATUS_UPDATE_START: return "Update Start"; case SHARED_STATUS_UPDATE_DONE: return "Update Done"; case SHARED_STATUS_DO_BOOT: return "Do boot"; case SHARED_STATUS_TIMEOUT: return "Timeout"; default: break; } return "Unknown"; } static int hal_net_signal_wait_ready(uint32_t timeout_ms) { int ret = 0; uint32_t status; /* wait for network core ready */ do { hal_shm_status_set(NULL, SHARED_STATUS_READY); status = hal_shm_status_wait(NULL, SHARED_STATUS_READY, 1); } while (status == SHARED_STATUS_TIMEOUT && --timeout_ms > 0); if (timeout_ms == 0 && status == SHARED_STATUS_TIMEOUT) { ret = -1; } return ret; } /* Handles network core updates */ static void hal_net_check_version(void) { int ret; struct wolfBoot_image img; uint32_t timeout, status = 0; #ifdef TARGET_nrf5340_app /* check the network core version */ hal_net_get_image(&img, &shm->core.app); /* release network core - issue boot command */ hal_net_core(0); wolfBoot_printf("Waiting for ready from net core...\n"); /* wait for ready status from network core */ ret = hal_net_signal_wait_ready(500); if (ret == 0) { enableShm = 1; wolfBoot_printf("Net core ready\n"); /* wait for version */ status = hal_shm_status_wait(&shm->core.net, SHARED_STATUS_VERSION, 2*1000); } else { wolfBoot_printf("Net core timeout, disable shared mem\n"); } /* check if network core can continue booting or needs to wait for update */ if (ret != 0 || shm->core.app.version <= shm->core.net.version) { wolfBoot_printf("Network Core: Releasing for boot\n"); } else { wolfBoot_printf("Found Network Core update: Ver %d->%d, Size %d->%d\n", shm->core.net.version, shm->core.app.version, shm->core.net.size, shm->core.app.size); /* validate the update is valid */ if (wolfBoot_verify_integrity(&img) == 0 && wolfBoot_verify_authenticity(&img) == 0) { wolfBoot_printf("Network image valid, loading into shared mem\n"); /* relocate image to shared ram */ #ifdef EXT_FLASH /* must be multiple of 4 (READ.CNT length must be multiple of 4) */ ret = ext_flash_read(PART_NET_ADDR, shm->data, (shm->core.app.size + 3) & ~3); #else memcpy(shm->data, img.hdr, shm->core.app.size); #endif /* initialize remainder of shared memory with 0xFF (erased) */ memset(shm->data + shm->core.app.size, FLASH_BYTE_ERASED, sizeof(shm->data) - shm->core.app.size); if (ret >= 0) { doUpdateNet = 1; /* signal network core to do update */ hal_shm_status_set(&shm->core.app, SHARED_STATUS_UPDATE_START); #ifndef NRF_SYNC_CORES wolfBoot_printf("Waiting for net core update to finish...\n"); /* wait for update_done - 30 seconds */ status = hal_shm_status_wait(&shm->core.net, SHARED_STATUS_UPDATE_DONE, 30*1000); if (status == SHARED_STATUS_UPDATE_DONE) { wolfBoot_printf("Network core firmware update done\n"); } #else wolfBoot_printf("Continuing boot while network core updates\n"); #endif } } else { wolfBoot_printf("Network image failed: Hdr %d, Hash %d, Sig %d\n", img.hdr_ok, img.sha_ok, img.signature_ok); } } /* inform network core to boot */ hal_shm_status_set(&shm->core.app, SHARED_STATUS_DO_BOOT); #else /* TARGET_nrf5340_net */ /* wait for IPC event indicating application core exists */ ret = hal_net_signal_wait_ready(500); if (ret == 0) { /* enable use of shared memory */ shm = (SharedMem_t*)SHARED_MEM_ADDR; enableShm = 1; } else { wolfBoot_printf("App core timeout, disable shared mem\n"); } /* inform application core we are ready */ hal_shm_status_set(&shm->core.net, SHARED_STATUS_READY); if (enableShm) { wolfBoot_printf("App core ready\n"); hal_net_get_image(&img, &shm->core.net); hal_shm_status_set(&shm->core.net, SHARED_STATUS_VERSION); /* wait for do_boot or update from app core - 2 seconds */ wolfBoot_printf("Waiting for update or boot from app core...\n"); status = hal_shm_status_wait(&shm->core.app, (SHARED_STATUS_UPDATE_START | SHARED_STATUS_DO_BOOT), 2*1000); /* are we updating? */ if (status == SHARED_STATUS_UPDATE_START) { wolfBoot_printf("Starting update: Ver %d->%d, Size %d->%d\n", shm->core.net.version, shm->core.app.version, shm->core.net.size, shm->core.app.size); doUpdateNet = 1; /* trigger update */ wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); } } /* proceed to update_flash routines */ #endif /* TARGET_nrf5340_* */ exit: wolfBoot_printf("Status: App %s (ver %d), Net %s (ver %d)\n", hal_shm_status_string(shm->core.app.status), shm->core.app.version, hal_shm_status_string(shm->core.net.status), shm->core.net.version); } void hal_init(void) { #ifdef DEBUG_UART const char* bootStr = "wolfBoot HAL Init (" CORE_STR " core)\n"; #endif clock_init(); #ifdef DEBUG_UART uart_init(); uart_write(bootStr, strlen(bootStr)); #endif hal_shm_init(); /* need early init of external flash to support checking network core */ spi_flash_probe(); hal_net_check_version(); } /* enable write protection for the region of flash specified */ int hal_flash_protect(uint32_t start, uint32_t len) { /* only application core supports SPU */ #ifdef TARGET_nrf5340_app uint32_t region, n, i; /* limit check */ if (start > FLASH_SIZE) return -1; /* truncate if exceeds flash size */ if (start + len > FLASH_SIZE) len = FLASH_SIZE - start; region = (start / SPU_BLOCK_SIZE); n = (len / SPU_BLOCK_SIZE); for (i = 0; i < n; i++) { /* do not allow write to this region and lock till next reset */ SPU_FLASHREGION_PERM(region+i) = ( SPU_FLASHREGION_PERM_EXEC | SPU_FLASHREGION_PERM_READ | SPU_FLASHREGION_PERM_SECATTR | SPU_FLASHREGION_PERM_LOCK ); } #endif return 0; } void hal_prepare_boot(void) { /* Write protect bootloader region of flash */ hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE); if (enableShm) { #ifdef TARGET_nrf5340_net if (doUpdateNet) { /* signal application core update done */ struct wolfBoot_image img; /* Reopen image and refresh information */ hal_net_get_image(&img, &shm->core.net); wolfBoot_printf("Network version (after update): 0x%x\n", shm->core.net.version); hal_shm_status_set(&shm->core.net, SHARED_STATUS_UPDATE_DONE); } else { hal_shm_status_set(&shm->core.net, SHARED_STATUS_DO_BOOT); } #endif #if defined(TARGET_nrf5340_app) && defined(NRF_SYNC_CORES) /* if core synchronization enabled, * then wait for update_done or do_boot (5 seconds, 30 for update) */ wolfBoot_printf("Waiting for network core...\n"); (void)hal_shm_status_wait(&shm->core.net, (SHARED_STATUS_UPDATE_DONE | SHARED_STATUS_DO_BOOT), doUpdateNet ? 30*1000 : 5*1000); #endif } hal_shm_cleanup(); } #endif /* TARGET_* */