wolfBoot/hal/stm32n6.c

887 lines
29 KiB
C

/* stm32n6.c
*
* Copyright (C) 2025 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
*/
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <image.h>
#include "hal.h"
#include "hal/stm32n6.h"
#include "printf.h"
#include "hal/armv8m_tz.h"
/* OCTOSPI register definitions come from hal/spi/spi_drv_stm32.h (included
* via stm32n6.h). STM32N6 XSPI2 uses the same IP block as OCTOSPI.
*
* Note: We cannot reuse qspi_transfer() from spi_drv_stm32.c because it
* disables/enables the peripheral on each transfer. The N6 boots via XIP
* (memory-mapped mode on XSPI2), so we need custom transfer code that:
* 1) Aborts memory-mapped mode before indirect access
* 2) Restores memory-mapped mode after each operation
* 3) Runs from SRAM (RAMFUNCTION) when called from XIP code
*/
/* SPI mode values for OCTOSPI CCR fields */
#define SPI_MODE_NONE 0
#define SPI_MODE_SINGLE 1
/* RAMFUNCTION override for test-app (XIP needs flash ops in SRAM) */
#if defined(RAM_CODE) && !defined(__WOLFBOOT)
#undef RAMFUNCTION
#define RAMFUNCTION __attribute__((used,section(".ramcode"),long_call))
#endif
/* OCTOSPI indirect-mode command helper.
* Handles memory-mapped mode abort/restore and TEF error detection. */
static int RAMFUNCTION octospi_cmd(uint8_t fmode, uint8_t cmd,
uint32_t addr, uint32_t addrMode,
uint8_t *data, uint32_t dataSz, uint32_t dataMode,
uint32_t dummyCycles)
{
uint32_t ccr;
/* Abort memory-mapped mode if active */
if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) {
OCTOSPI_CR |= OCTOSPI_CR_ABORT;
while (OCTOSPI_CR & OCTOSPI_CR_ABORT)
;
}
while (OCTOSPI_SR & OCTOSPI_SR_BUSY)
;
OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF;
OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) |
OCTOSPI_CR_FMODE(fmode);
if (dataSz > 0) {
OCTOSPI_DLR = dataSz - 1;
}
ccr = OCTOSPI_CCR_IMODE(SPI_MODE_SINGLE) | OCTOSPI_CCR_ISIZE(0);
if (addrMode != SPI_MODE_NONE) {
ccr |= OCTOSPI_CCR_ADMODE(addrMode) | OCTOSPI_CCR_ADSIZE(3);
}
if (dataMode != SPI_MODE_NONE) {
ccr |= OCTOSPI_CCR_DMODE(dataMode);
}
OCTOSPI_CCR = ccr;
OCTOSPI_TCR = OCTOSPI_TCR_DCYC(dummyCycles);
OCTOSPI_IR = cmd;
if (addrMode != SPI_MODE_NONE) {
OCTOSPI_AR = addr;
}
/* Byte-wide DR access driven by FLEVEL instead of FTF. DR32-with-FTHRES
* polling stalls on transfers > FIFO depth (32 B) because the assumed
* "FTF means 4 bytes are ready" can race with the FIFO state once we've
* drained/filled below the threshold and the controller doesn't reassert
* FTF for the trailing bytes. Polling FLEVEL directly is correct for any
* transfer size and any threshold. */
if (dataSz > 0 && data != NULL) {
uint32_t sr;
while (dataSz > 0) {
sr = OCTOSPI_SR;
if (sr & OCTOSPI_SR_TEF) goto octospi_err;
if (fmode == 0) {
/* indirect write: push a byte if FIFO has free space */
if (OCTOSPI_SR_FLEVEL(sr) < 32) {
OCTOSPI_DR = *data;
data++;
dataSz--;
}
} else {
/* indirect read: pop a byte if FIFO has data */
if (OCTOSPI_SR_FLEVEL(sr) > 0) {
*data = OCTOSPI_DR;
data++;
dataSz--;
} else if (sr & OCTOSPI_SR_TCF) {
/* Transfer complete and FIFO drained -- should not
* happen with dataSz > 0 unless DLR mismatch. */
goto octospi_err;
}
}
}
}
while (!(OCTOSPI_SR & (OCTOSPI_SR_TCF | OCTOSPI_SR_TEF)))
;
if (OCTOSPI_SR & OCTOSPI_SR_TEF) goto octospi_err;
OCTOSPI_FCR = OCTOSPI_FCR_CTCF;
return 0;
octospi_err:
OCTOSPI_FCR = OCTOSPI_FCR_CTEF;
OCTOSPI_CR |= OCTOSPI_CR_ABORT;
while (OCTOSPI_CR & OCTOSPI_CR_ABORT)
;
return -1;
}
static int RAMFUNCTION octospi_write_enable(void)
{
/* A failed WREN leaves the latch clear: the device then ignores the
* program/erase, never goes BUSY, and octospi_wait_ready() reports
* idle on its first poll. */
return octospi_cmd(0, WRITE_ENABLE_CMD, 0, SPI_MODE_NONE,
NULL, 0, SPI_MODE_NONE, 0);
}
static int RAMFUNCTION octospi_wait_ready(void)
{
uint8_t sr;
do {
sr = 0;
/* A failed status-register transfer must not read as "ready":
* sr stays zero and the loop would exit as if the flash were idle. */
if (octospi_cmd(1, READ_SR_CMD, 0, SPI_MODE_NONE,
&sr, 1, SPI_MODE_SINGLE, 0) < 0)
return -1;
} while (sr & FLASH_SR_BUSY);
return 0;
}
static void RAMFUNCTION octospi_enable_mmap(void)
{
if ((OCTOSPI_CR & OCTOSPI_CR_FMODE_MASK) == OCTOSPI_CR_FMODE_MMAP) {
OCTOSPI_CR |= OCTOSPI_CR_ABORT;
while (OCTOSPI_CR & OCTOSPI_CR_ABORT)
;
}
while (OCTOSPI_SR & OCTOSPI_SR_BUSY)
;
OCTOSPI_FCR = OCTOSPI_FCR_CTCF | OCTOSPI_FCR_CTEF | OCTOSPI_FCR_CSMF;
OCTOSPI_CR = (OCTOSPI_CR & ~OCTOSPI_CR_FMODE_MASK) |
OCTOSPI_CR_FMODE_MMAP;
/* Single-SPI FAST_READ_4B (0x0C), 4-byte address, 8 dummy cycles.
* The MX25UM51245G is in default single-SPI mode after Boot ROM
* hand-off. */
OCTOSPI_CCR = OCTOSPI_CCR_IMODE(SPI_MODE_SINGLE) |
OCTOSPI_CCR_ISIZE(0) |
OCTOSPI_CCR_ADMODE(SPI_MODE_SINGLE) |
OCTOSPI_CCR_ADSIZE(3) |
OCTOSPI_CCR_DMODE(SPI_MODE_SINGLE);
OCTOSPI_TCR = OCTOSPI_TCR_DCYC(8);
OCTOSPI_IR = FAST_READ_4B_CMD;
DSB();
ISB();
}
static void RAMFUNCTION dcache_clean_invalidate_by_addr(uint32_t addr,
uint32_t size)
{
uint32_t line;
for (line = addr & ~0x1FUL; line < addr + size; line += 32) {
SCB_DCCIMVAC = line;
}
DSB();
ISB();
}
static void icache_enable(void)
{
DSB();
ISB();
SCB_ICIALLU = 0;
DSB();
ISB();
SCB_CCR |= SCB_CCR_IC;
DSB();
ISB();
}
static void dcache_enable(void)
{
DSB();
SCB_CCR |= SCB_CCR_DC;
DSB();
ISB();
}
static void icache_disable(void)
{
DSB();
ISB();
SCB_CCR &= ~SCB_CCR_IC;
SCB_ICIALLU = 0;
DSB();
ISB();
}
static void dcache_disable(void)
{
/* Clean+invalidate all lines by set/way before disabling */
uint32_t sets, ways, set, way, way_shift, tmp;
uint32_t ccsidr;
CSSELR = 0; /* select L1 data cache */
DSB();
ccsidr = CCSIDR;
sets = ((ccsidr >> 13) & 0x7FFF) + 1;
ways = ((ccsidr >> 3) & 0x3FF) + 1;
/* Calculate way shift: 32 - log2(ways) */
way_shift = 32;
tmp = ways - 1;
while (tmp) {
way_shift--;
tmp >>= 1;
}
for (way = 0; way < ways; way++) {
for (set = 0; set < sets; set++) {
SCB_DCCISW = (way << way_shift) | (set << 5);
}
}
DSB();
SCB_CCR &= ~SCB_CCR_DC;
DSB();
ISB();
}
/* OCTOSPI GPIO: PN0-PN11 as AF9 (DQS, CLK, NCS, IO0-IO7) */
static void octospi_gpio_init(void)
{
uint32_t reg;
int pin;
RCC_AHB4ENR |= RCC_AHB4ENR_GPIONEN;
DMB();
/* AF mode, very high speed, no pull */
reg = GPIO_MODER(GPION_BASE);
for (pin = 0; pin <= 11; pin++) {
reg &= ~(0x3 << (pin * 2));
reg |= (GPIO_MODE_AF << (pin * 2));
}
GPIO_MODER(GPION_BASE) = reg;
reg = GPIO_OSPEEDR(GPION_BASE);
for (pin = 0; pin <= 11; pin++) {
reg &= ~(0x3 << (pin * 2));
reg |= (GPIO_SPEED_VERY_HIGH << (pin * 2));
}
GPIO_OSPEEDR(GPION_BASE) = reg;
reg = GPIO_PUPDR(GPION_BASE);
for (pin = 0; pin <= 11; pin++) {
reg &= ~(0x3 << (pin * 2));
}
GPIO_PUPDR(GPION_BASE) = reg;
/* AF9 for PN0-PN7 (AFRL) and PN8-PN11 (AFRH) */
reg = 0;
for (pin = 0; pin <= 7; pin++) {
reg |= (9 << (pin * 4));
}
GPIO_AFRL(GPION_BASE) = reg;
reg = 0;
for (pin = 0; pin <= 3; pin++) {
reg |= (9 << (pin * 4));
}
GPIO_AFRH(GPION_BASE) = reg;
}
static void octospi_init(void)
{
/* Bring up XSPI2 from the state Boot ROM leaves. Boot ROM:
* - loads the FSBL via XSPI2, then disables the peripheral (CR.EN=0)
* and clears DCR1 (DEVSIZE=0)
* - leaves XSPIM_CR with MODE=1, which on N6 SWAPS the XSPI<->Port
* routing (XSPI2->Port 1, XSPI1->Port 2). The MX25UM51245G is wired
* to Port 2, so XSPI2 must be re-routed there before we can talk
* to the flash
* - leaves the flash itself in single-SPI mode (NOT OPI as initially
* suspected; single SPI works fine with FAST_READ_4B)
*/
RCC_AHB5ENR |= RCC_AHB5ENR_XSPI2EN | RCC_AHB5ENR_XSPIMEN;
RCC_MISCENR |= RCC_MISCENR_XSPIPHYCOMPEN;
DMB();
/* VDDIO3 powers the XSPI2 IO bank. The MX25UM51245G runs at 1.8V on
* NUCLEO-N657X0-Q, so configure VDDIO3 voltage range = 1.8V (bit 26).
* Then mark VDDIO3 supply valid. */
RCC_AHB4ENR |= RCC_AHB4ENR_PWREN;
DMB();
PWR_SVMCR3 |= PWR_SVMCR3_VDDIO3VRSEL; /* 1.8V range */
PWR_SVMCR3 |= PWR_SVMCR3_VDDIO3SV; /* supply valid */
DMB();
/* XSPIM_CR is only writable while BOTH XSPI1 and XSPI2 are CR.EN=0.
* Force XSPI1 disabled (Boot ROM enabled its clock and may have left
* EN set), then RCC-reset XSPI2 to clear its Boot-ROM-leftover state. */
XSPI1_CR &= ~XSPI_CR_EN;
DMB();
RCC_AHB5RSTSR = RCC_AHB5RSTR_XSPI2RST;
DMB();
RCC_AHB5RSTCR = RCC_AHB5RSTR_XSPI2RST;
DMB();
/* Direct mapping: XSPI2 -> Port 2. Without this, XSPI2 signals don't
* reach the flash and all reads time out. */
XSPIM_CR = 0;
DMB();
/* Set DEVSIZE for the 64 MB MX25UM51245G (DEVSIZE = log2(size)-1 = 25);
* CSHT (CS high time) = 3; keep DLYBYP. Prescaler /4 (matches ST BSP). */
OCTOSPI_DCR1 = OCTOSPI_DCR1_DLYBYP |
OCTOSPI_DCR1_DEVSIZE(25) |
OCTOSPI_DCR1_CSHT(3);
OCTOSPI_DCR2 = OCTOSPI_DCR2_PRESCALER(4);
/* Enable peripheral. FTHRES=4 matches DR32 word access from mmap.
* XIP mmap requires D-cache enabled and cache-line burst access;
* single-word XIP reads or non-cacheable mapping stall OCTOSPI with
* BUSY=1 + full FIFO. */
OCTOSPI_CR = OCTOSPI_CR_FTHRES(4) | OCTOSPI_CR_EN;
octospi_enable_mmap();
}
/* Configure clocks: PLL1 → 600 MHz CPU (Voltage Scale 1).
* STM32N6 supports up to 800 MHz at Voltage Scale 0 (PWR_VOSCR_VOS=1).
*
* Clock tree:
* HSI 64 MHz → PLL1 (M=4, N=75) → VCO 1200 MHz → PDIV1=1 → 1200 MHz
* IC1 /2 = 600 MHz → CPU (CPUSW=IC1)
* IC2 /3 = 400 MHz → AXI bus (SYSSW=IC2)
* IC6 /4 = 300 MHz → system bus C (SYSSW=IC6)
* IC11/3 = 400 MHz → system bus D (SYSSW=IC11)
* AHB prescaler /2 → HCLK = 300 MHz
*/
static void clock_config(void)
{
uint32_t reg;
/* Enable HSI 64 MHz */
RCC_CSR = RCC_CR_HSION;
while (!(RCC_SR & RCC_SR_HSIRDY))
;
/* Switch CPU and system buses back to HSI BEFORE disabling PLL1.
* The Boot ROM has already moved CPU/SYS to PLL1-sourced IC1/IC2/IC6/IC11
* for cold boot; clearing PLL1ON while CPU runs on PLL1 stops the CPU
* clock and hangs the part. (When loaded via OpenOCD the CPU is on
* reset-state HSI, which masked this on the development path.) */
reg = RCC_CFGR1;
reg &= ~(RCC_CFGR1_CPUSW_MASK | RCC_CFGR1_SYSSW_MASK);
RCC_CFGR1 = reg;
while ((RCC_CFGR1 & RCC_CFGR1_CPUSWS_MASK) != 0)
;
while ((RCC_CFGR1 & RCC_CFGR1_SYSSWS_MASK) != 0)
;
/* Disable PLL1 before reconfiguring */
RCC_CCR = RCC_CR_PLL1ON;
while (RCC_SR & RCC_SR_PLL1RDY)
;
/* PLL1: HSI / 4 * 75 = 1200 MHz VCO.
* Clear BYP (bypass) — Boot ROM leaves it set, which routes HSI
* directly to PLL output, skipping the VCO entirely. */
reg = RCC_PLL1CFGR1;
reg &= ~(RCC_PLL1CFGR1_SEL_MASK | RCC_PLL1CFGR1_DIVM_MASK |
RCC_PLL1CFGR1_DIVN_MASK | RCC_PLL1CFGR1_BYP);
reg |= RCC_PLL1CFGR1_SEL_HSI |
(4 << RCC_PLL1CFGR1_DIVM_SHIFT) |
(75 << RCC_PLL1CFGR1_DIVN_SHIFT);
RCC_PLL1CFGR1 = reg;
RCC_PLL1CFGR2 = 0; /* no fractional */
/* PDIV1=1, PDIV2=1 → PLL output = VCO = 1200 MHz.
* MODSSDIS: disable spread spectrum. PDIVEN: enable PLL output. */
RCC_PLL1CFGR3 = (1 << RCC_PLL1CFGR3_PDIV1_SHIFT) |
(1 << RCC_PLL1CFGR3_PDIV2_SHIFT) |
RCC_PLL1CFGR3_MODSSDIS |
RCC_PLL1CFGR3_MODSSRST |
RCC_PLL1CFGR3_PDIVEN;
/* Enable PLL1, wait for lock */
RCC_CSR = RCC_CR_PLL1ON;
while (!(RCC_SR & RCC_SR_PLL1RDY))
;
/* Configure IC dividers: disable → configure → re-enable.
* IC divider = INT + 1, SEL: 0=PLL1 (per ST LL driver). */
RCC_DIVENCR = RCC_DIVENR_IC1EN;
RCC_IC1CFGR = RCC_ICCFGR_SEL_PLL1 | ((2 - 1) << RCC_ICCFGR_INT_SHIFT);
RCC_DIVENSR = RCC_DIVENR_IC1EN;
RCC_DIVENCR = RCC_DIVENR_IC2EN;
RCC_IC2CFGR = RCC_ICCFGR_SEL_PLL1 | ((3 - 1) << RCC_ICCFGR_INT_SHIFT);
RCC_DIVENSR = RCC_DIVENR_IC2EN;
RCC_DIVENCR = RCC_DIVENR_IC6EN;
RCC_IC6CFGR = RCC_ICCFGR_SEL_PLL1 | ((4 - 1) << RCC_ICCFGR_INT_SHIFT);
RCC_DIVENSR = RCC_DIVENR_IC6EN;
RCC_DIVENCR = RCC_DIVENR_IC11EN;
RCC_IC11CFGR = RCC_ICCFGR_SEL_PLL1 | ((3 - 1) << RCC_ICCFGR_INT_SHIFT);
RCC_DIVENSR = RCC_DIVENR_IC11EN;
/* AHB prescaler /2 → HCLK = 300 MHz */
reg = RCC_CFGR2;
reg &= ~RCC_CFGR2_HPRE_MASK;
reg |= (1 << RCC_CFGR2_HPRE_SHIFT);
RCC_CFGR2 = reg;
/* Switch CPU to IC1, system bus to IC2/IC6/IC11 */
reg = RCC_CFGR1;
reg &= ~(RCC_CFGR1_CPUSW_MASK | RCC_CFGR1_SYSSW_MASK);
reg |= (0x3 << RCC_CFGR1_CPUSW_SHIFT) |
(0x3 << RCC_CFGR1_SYSSW_SHIFT);
RCC_CFGR1 = reg;
while ((RCC_CFGR1 & RCC_CFGR1_CPUSWS_MASK) !=
(0x3 << RCC_CFGR1_CPUSWS_SHIFT))
;
while ((RCC_CFGR1 & RCC_CFGR1_SYSSWS_MASK) !=
(0x3 << RCC_CFGR1_SYSSWS_SHIFT))
;
}
#ifdef DEBUG_UART
/* USART1 on PE5 (TX) / PE6 (RX), AF7 */
#define UART_BASE USART1_BASE
#define UART_CLOCK_FREQ 200000000UL /* PCLK2 = IC2(400MHz) / AHB(2) / APB2(1) */
static void uart_init_baud(uint32_t baud)
{
uint32_t reg;
RCC_APB2ENR |= RCC_APB2ENR_USART1EN;
RCC_AHB4ENR |= RCC_AHB4ENR_GPIOEEN;
DMB();
/* PE5/PE6 AF mode */
reg = GPIO_MODER(GPIOE_BASE);
reg &= ~((0x3 << (5 * 2)) | (0x3 << (6 * 2)));
reg |= (GPIO_MODE_AF << (5 * 2)) | (GPIO_MODE_AF << (6 * 2));
GPIO_MODER(GPIOE_BASE) = reg;
/* AF7 */
reg = GPIO_AFRL(GPIOE_BASE);
reg &= ~((0xF << (5 * 4)) | (0xF << (6 * 4)));
reg |= (7 << (5 * 4)) | (7 << (6 * 4));
GPIO_AFRL(GPIOE_BASE) = reg;
reg = GPIO_OSPEEDR(GPIOE_BASE);
reg &= ~((0x3 << (5 * 2)) | (0x3 << (6 * 2)));
reg |= (GPIO_SPEED_HIGH << (5 * 2)) | (GPIO_SPEED_HIGH << (6 * 2));
GPIO_OSPEEDR(GPIOE_BASE) = reg;
/* 8N1 */
UART_CR1(UART_BASE) = 0;
UART_CR2(UART_BASE) = 0;
UART_CR3(UART_BASE) = 0;
UART_BRR(UART_BASE) = (UART_CLOCK_FREQ + baud / 2) / baud;
UART_CR1(UART_BASE) = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;
}
void uart_write(const char *buf, unsigned int len)
{
unsigned int i;
for (i = 0; i < len; i++) {
while (!(UART_ISR(UART_BASE) & USART_ISR_TXE))
;
UART_TDR(UART_BASE) = buf[i];
}
while (!(UART_ISR(UART_BASE) & USART_ISR_TC))
;
}
#endif
/* Mark VDDIO supplies valid (required for OCTOSPI GPIO) */
static void pwr_enable_io_supply(void)
{
RCC_AHB4ENR |= RCC_AHB4ENR_PWREN;
DMB();
PWR_SVMCR1 |= PWR_SVMCR1_VDDIO4SV;
PWR_SVMCR2 |= PWR_SVMCR2_VDDIO5SV;
PWR_SVMCR3 |= PWR_SVMCR3_VDDIO2SV | PWR_SVMCR3_VDDIO3SV;
DMB();
}
void hal_init(void)
{
#ifdef TZEN
/* TrustZone enabled: wolfBoot runs Secure, NS app runs Non-Secure.
*
* Per ST's Template_Isolation_XIP reference (NUCLEO-N657X0-Q,
* STM32CubeN6 v1.3.0): the N6 IDAU defers to SAU for the XSPI2
* address range, so a SAU NS region over the secure-alias address
* 0x70xxxxxx actually classifies the access as NS. This matches
* ST's pattern of running the NS app at 0x70180400 (secure alias)
* with SAU REG[3] NS 0x70180000-0x701FFFFF. We mirror that here.
*
* SAU regions (only where we override IDAU default):
* REG[0]: NS, 0x24000000..0x240FFFFF -- AXISRAM1 NS alias
* (NS app stack/data)
* REG[1]: NS, 0x40000000..0x4FFFFFFF -- peripherals NS alias
* (NS app UART/GPIO)
* REG[2]: NS, 0x70020000..0x7011FFFF -- XSPI2 NOR boot
* partition (NS XIP)
*
* Addresses outside these regions fall back to IDAU defaults --
* wolfBoot's own code/data at 0x34180400 (AXISRAM2 S alias) stays
* Secure, the XSPI2 update/swap regions stay Secure. */
{
SAU_CTRL = 0;
DSB();
sau_init_region(0, 0x24000000, 0x240FFFE0, 0); /* NS: AXISRAM1 */
sau_init_region(1, 0x40000000, 0x4FFFFFE0, 0); /* NS: peripherals*/
sau_init_region(2, 0x70180000, 0x7027FFE0, 0); /* NS: boot part. */
SAU_CTRL = SAU_INIT_CTRL_ENABLE;
DSB();
ISB();
/* Enable SecureFault handler so TZ violations surface rather
* than escalating silently. */
SCB_SHCSR |= SCB_SHCSR_SECUREFAULT_EN;
}
#else
/* No TrustZone: blanket NSC region allows secure CPU to access all
* memory regardless of IDAU attribution, so wolfBoot can read XIP
* flash and NS peripherals. */
{
SAU_CTRL = 0;
DSB();
sau_init_region(0, 0x00000000, 0xFFFFFFE0, 1); /* full range, NSC */
SAU_CTRL = SAU_INIT_CTRL_ENABLE;
DSB();
ISB();
}
#endif
clock_config();
pwr_enable_io_supply();
icache_enable();
octospi_gpio_init();
octospi_init();
dcache_enable();
#ifdef DEBUG_UART
uart_init_baud(115200);
uart_write("wolfBoot Init\n", 14);
wolfBoot_printf("TrustZone: %s\n",
#if TZ_SECURE()
"Secure"
#else
"Off"
#endif
);
#endif
#if TZ_SECURE()
/* RISAF12 secure phase: cover the full 64 MB XSPI2 NOR window
* with a base SEC region so wolfBoot's secure-side reads of the
* boot partition (via 0x70020000) and writes to swap/update
* succeed. The N6 bus matrix tags transactions by CPU state, not
* by SAU effective attribute, so a secure CPU cannot "downgrade"
* its access to NS via SAU alone -- the only way to give the NS
* application access to the same flash is to flip subregion A to
* NS just before BLXNS (see hal_prepare_boot()).
*
* After Boot ROM hand-off RISAF12 has all regions disabled,
* which lets secure reads work but blocks NS reads. We make that
* explicit here so the post-BLXNS NS-side XIP fetch doesn't
* silently return RAZ. */
*(volatile uint32_t *)(0x56028258) |= (1u << 14); /* RCC AHB3ENR.RISAFEN */
DSB();
/* REG[0] (base region 1): SEC for the 128 KB bootloader area
* (0..0x1FFFF, ends right before the boot partition). */
*(volatile uint32_t *)(0x54031044) = 0x00000000;
*(volatile uint32_t *)(0x54031048) = 0x0001FFFF;
*(volatile uint32_t *)(0x5403104C) = 0x00FF00FF;
*(volatile uint32_t *)(0x54031040) = 0x00FF0101; /* BREN|SEC|PRIV */
DSB();
#endif
/* Warmup the OCTOSPI mmap path with one read of the boot partition.
* Without this delay and warmup access the first XIP read from
* wolfBoot_start stalls (BUSY stays set indefinitely). */
{
volatile uint32_t i;
volatile uint32_t warmup;
for (i = 0; i < 100000; i++) {}
warmup = *(volatile const uint32_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
(void)warmup;
}
}
void hal_prepare_boot(void)
{
octospi_enable_mmap();
dcache_disable();
icache_disable();
/* The RISAF12 split (NS for boot partition, SEC elsewhere) is
* applied later by stm32n6_tz_handoff() in do_boot() so wolfBoot's
* own reads of the vector table from the boot partition still
* pass the SEC base region installed by hal_init(). */
}
#if TZ_SECURE()
/* Reconfigure RIF for the secure -> non-secure hand-off. Called from
* do_boot() right before BLXNS, after the secure side has finished
* all of its boot-partition reads.
*
* Address-to-RISAF mapping per N6 CMSIS header (RIF_AWARE_PERIPH_INDEX
* _RISAF*):
* - RISAF2 (0x54027000) -> AXISRAM1 (SRAM1_AXI at 0x34000000 /
* 0x24000000), 1 MB total
* - RISAF3 (0x54028000) -> AXISRAM2 (SRAM2_AXI at 0x34100000 /
* 0x24100000) -- wolfBoot lives
* here; leave at default SEC
* - RISAF12 (0x54031000) -> XSPI2 NOR XIP (64 MB at 0x70000000 /
* 0x90000000)
*
* RISAF12 (XSPI2 NOR) -- three non-overlapping base regions:
* REG[0]: SEC, 0..0x1FFFF (bootloader + swap below boot)
* REG[1]: NS, 0x20000..0x11FFFF (boot partition -- NS XIP)
* REG[2]: SEC, 0x120000..end (update partition + remainder)
*
* RISAF2 (AXISRAM1) -- one base region:
* REG[0]: NS, 0..0xFFFFF (full 1 MB)
*
* The NS application links its stack at 0x24020000 (top of a 64 KB
* RAM region at 0x24010000) and its .data/.bss at 0x24010000+, so
* AXISRAM1 needs an NS-attributed RIF view for the NS CPU to push
* registers and access globals.
*/
void RAMFUNCTION stm32n6_tz_handoff(void)
{
/* Disable the bootloader-area RISAF12 SEC region installed by
* hal_init(). Reason: RISAF filters by CPU security state (not
* by bus HNONSEC), so leaving REG[0] SEC active would still
* gate the BLXNS pre-attribution check the secure CPU performs
* on the NS entry point at 0x70020XXX -- which is inside the
* boot partition, not bootloader, but the secure-side
* verification path may also touch the swap sector below it.
*
* With all RISAF12 regions disabled the default is allow-all,
* so both the secure CPU (during the BLXNS target check) and
* the NS CPU (during post-BLXNS XIP fetches) can read the
* boot partition. We still configure RISAF2 for AXISRAM1 NS so
* the NS application's stack pushes succeed.
*
* If later we need stricter isolation, the right shape is a
* RISAF12 region that admits BOTH S and NS transactions via
* the CID whitelist (set SEC=0 with WRENC/RDENC=0xFF and rely
* on the bus matrix promoting accesses). The current N6 silicon
* does not appear to honor that for the secure CPU's pre-check
* on BLXNS targets, hence the all-default approach. */
*(volatile uint32_t *)(0x54031040) = 0x00000000; /* REG[0] CFGR: BREN=0 */
*(volatile uint32_t *)(0x54031080) = 0x00000000; /* REG[1] CFGR: BREN=0 */
*(volatile uint32_t *)(0x540310C0) = 0x00000000; /* REG[2] CFGR: BREN=0 */
DSB();
/* RISAF2 (AXISRAM1) NS region for NS app RAM. NS app's stack
* lives at 0x24010000-0x2401FFFF (NS alias of AXISRAM1 offset
* 0x10000-0x1FFFF); this region admits NS transactions for the
* full 1 MB bank. */
*(volatile uint32_t *)(0x54027044) = 0x00000000;
*(volatile uint32_t *)(0x54027048) = 0x000FFFFF;
*(volatile uint32_t *)(0x5402704C) = 0x00FF00FF;
*(volatile uint32_t *)(0x54027040) = 0x00FF0001; /* BREN|NS|PRIV */
DSB();
}
#endif
/* Shared NOR flash helpers used by both hal_flash_* and ext_flash_* */
static int RAMFUNCTION nor_flash_write(uint32_t offset, const uint8_t *data,
int len)
{
uint32_t page_off, write_sz;
int remaining = len;
int ret = 0;
/* Buffer for data that may reside in XIP flash (memory-mapped XSPI2).
* The source pointer becomes invalid once XSPI2 leaves mmap mode for
* the SPI page-program command, so we copy to RAM first. */
uint8_t page_buf[FLASH_PAGE_SIZE];
if (len <= 0)
return 0;
while (remaining > 0) {
page_off = offset & (FLASH_PAGE_SIZE - 1);
write_sz = FLASH_PAGE_SIZE - page_off;
if ((int)write_sz > remaining)
write_sz = remaining;
memcpy(page_buf, data, write_sz);
if (octospi_write_enable() < 0) {
ret = -1;
break;
}
ret = octospi_cmd(0, PAGE_PROG_4B_CMD,
offset, SPI_MODE_SINGLE,
page_buf, write_sz, SPI_MODE_SINGLE, 0);
if (ret < 0)
break;
if (octospi_wait_ready() < 0) {
ret = -1;
break;
}
offset += write_sz;
data += write_sz;
remaining -= write_sz;
}
octospi_enable_mmap();
return ret;
}
static int RAMFUNCTION nor_flash_erase(uint32_t offset, int len)
{
uint32_t end;
int ret = 0;
if (len <= 0)
return -1;
end = offset + len;
while (offset < end) {
if (octospi_write_enable() < 0) {
ret = -1;
break;
}
ret = octospi_cmd(0, SEC_ERASE_4B_CMD,
offset, SPI_MODE_SINGLE,
NULL, 0, SPI_MODE_NONE, 0);
if (ret < 0)
break;
if (octospi_wait_ready() < 0) {
ret = -1;
break;
}
offset += FLASH_SECTOR_SIZE;
}
octospi_enable_mmap();
return ret;
}
/* Forward declaration: ext_flash_addr is defined below alongside the
* ext_flash_* API but is shared with the hal_flash_* path. */
static uint32_t RAMFUNCTION ext_flash_addr(uintptr_t address);
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
{
uint32_t off = ext_flash_addr(address);
int ret = nor_flash_write(off, data, len);
if (ret == 0 && len > 0)
dcache_clean_invalidate_by_addr(OCTOSPI_MEM_BASE + off, len);
return ret;
}
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
uint32_t off = ext_flash_addr(address);
int ret = nor_flash_erase(off, len);
if (ret == 0 && len > 0)
dcache_clean_invalidate_by_addr(OCTOSPI_MEM_BASE + off, len);
return ret;
}
void RAMFUNCTION hal_flash_unlock(void)
{
}
void RAMFUNCTION hal_flash_lock(void)
{
}
/* ext_flash API: accepts both device-relative offsets (update/swap) and
* absolute memory-mapped addresses (boot partition with PART_BOOT_EXT). */
static uint32_t RAMFUNCTION ext_flash_addr(uintptr_t address)
{
/* Accept either alias of the 64 MB XSPI2 NOR region (0x70xxxxxx S,
* 0x90xxxxxx NS) and convert to a device-relative offset. Anything
* below both aliases is treated as already-offset (update/swap). */
if (address >= OCTOSPI_MEM_BASE_NS &&
address < (OCTOSPI_MEM_BASE_NS + OCTOSPI_MEM_SIZE))
return (uint32_t)(address - OCTOSPI_MEM_BASE_NS);
if (address >= OCTOSPI_MEM_BASE_S &&
address < (OCTOSPI_MEM_BASE_S + OCTOSPI_MEM_SIZE))
return (uint32_t)(address - OCTOSPI_MEM_BASE_S);
return (uint32_t)address;
}
int RAMFUNCTION ext_flash_read(uintptr_t address, uint8_t *data, int len)
{
/* Read via XIP memory-mapped alias. OCTOSPI_MEM_BASE is the alias
* (0x70xxxxxx for non-TZ, 0x90xxxxxx for TZ) that the controller
* is accessible through given current GTZC attribution. */
volatile const uint8_t *src;
uint32_t off = ext_flash_addr(address);
int i;
if (len <= 0)
return 0;
src = (volatile const uint8_t *)(OCTOSPI_MEM_BASE + off);
for (i = 0; i < len; i++)
data[i] = src[i];
return len;
}
int RAMFUNCTION ext_flash_write(uintptr_t address, const uint8_t *data,
int len)
{
uint32_t off = ext_flash_addr(address);
int ret = nor_flash_write(off, data, len);
/* Invalidate D-cache for the XIP alias, otherwise later reads of the
* just-written region return stale data from cache. */
if (ret == 0 && len > 0)
dcache_clean_invalidate_by_addr(OCTOSPI_MEM_BASE + off, len);
return ret;
}
int RAMFUNCTION ext_flash_erase(uintptr_t address, int len)
{
uint32_t off = ext_flash_addr(address);
int ret = nor_flash_erase(off, len);
if (ret == 0 && len > 0)
dcache_clean_invalidate_by_addr(OCTOSPI_MEM_BASE + off, len);
return ret;
}
void RAMFUNCTION ext_flash_lock(void)
{
}
void RAMFUNCTION ext_flash_unlock(void)
{
}