mirror of https://github.com/wolfSSL/wolfBoot.git
Add wolfBoot target for NXP i.MX95 Cortex-M7
parent
8015a1fa4f
commit
724ca34677
3
arch.mk
3
arch.mk
|
|
@ -284,6 +284,9 @@ ifeq ($(ARCH),ARM)
|
|||
ifeq ($(TARGET),imx_rt)
|
||||
CORTEX_M7=1
|
||||
endif
|
||||
ifeq ($(TARGET),imx95_m7)
|
||||
CORTEX_M7=1
|
||||
endif
|
||||
ifeq ($(TARGET),stm32l0)
|
||||
CORTEX_M0=1
|
||||
SPI_TARGET=stm32
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
ARCH?=ARM
|
||||
TARGET?=imx95_m7
|
||||
SIGN?=ECC256
|
||||
HASH?=SHA256
|
||||
DEBUG?=0
|
||||
VTOR?=1
|
||||
CORTEX_M0?=0
|
||||
NO_ASM?=0
|
||||
NO_MPU?=1
|
||||
EXT_FLASH?=0
|
||||
SPI_FLASH?=0
|
||||
ALLOW_DOWNGRADE?=0
|
||||
NVM_FLASH_WRITEONCE?=0
|
||||
WOLFBOOT_VERSION?=0
|
||||
V?=0
|
||||
SPMATH?=1
|
||||
RAM_CODE?=1
|
||||
DUALBANK_SWAP?=0
|
||||
PKA?=0
|
||||
|
||||
# wolfBoot runs from ITCM; the partitions live in the M7's reserved DDR window
|
||||
# (memory@80000000, 16 MiB) where Linux stages images before restarting the
|
||||
# core. See hal/imx95_m7.c for why "flash" here is ordinary DDR.
|
||||
WOLFBOOT_PARTITION_SIZE?=0x400000
|
||||
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x80100000
|
||||
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x80500000
|
||||
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x80900000
|
||||
|
||||
# Sized for ML-DSA-87 from the start, even though bring-up signs with ECC256,
|
||||
# so switching to SIGN=ML_DSA is a config change rather than a re-layout.
|
||||
# ML-DSA-87: 4627-byte signature, 2592-byte public key.
|
||||
WOLFBOOT_SECTOR_SIZE?=0x4000
|
||||
IMAGE_HEADER_SIZE?=12288
|
||||
|
||||
WOLFBOOT_SMALL_STACK?=0
|
||||
|
|
@ -0,0 +1,290 @@
|
|||
/* imx95_m7.c
|
||||
*
|
||||
* HAL for the Cortex-M7 on the NXP i.MX95.
|
||||
*
|
||||
* Copyright (C) 2026 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
|
||||
*/
|
||||
|
||||
/* The M7 on this part has no dedicated flash. wolfBoot is loaded into TCM by
|
||||
* the Linux remoteproc driver on the A55 cluster, and the images it verifies
|
||||
* live in the DDR region reserved for the M7 (memory@80000000, 16 MiB). The
|
||||
* "flash" the partitions sit in is therefore ordinary DDR: writes are copies
|
||||
* and erases are fills. Updates are staged by Linux writing the update
|
||||
* partition before restarting the core.
|
||||
*
|
||||
* Addresses come from the i.MX 95 Reference Manual rev 5 ch. 123 and from the
|
||||
* board's own device tree; see BOARD-FINDINGS.md in the demo tree.
|
||||
*
|
||||
* ITCM core view 0x00000000 system view 0x203C0000 256 KiB
|
||||
* DTCM core view 0x20000000 system view 0x20400000 256 KiB
|
||||
* DDR 0x80000000 16 MiB
|
||||
*
|
||||
* The core/system distinction matters: wolfBoot is linked for the core view,
|
||||
* while remoteproc loads through the system view and the imx_rproc driver
|
||||
* translates between them.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <target.h>
|
||||
#include "image.h"
|
||||
|
||||
/* ---------------------------------------------------------------------------
|
||||
* remoteproc resource table
|
||||
*
|
||||
* Linux finds the resource table by looking for a .resource_table section in
|
||||
* the ELF *it* loads. On this target that ELF is wolfBoot, not the payload, so
|
||||
* the vdev/vring declaration has to live here even though the RPMsg endpoint is
|
||||
* serviced later by whatever wolfBoot boots. Without it the kernel logs
|
||||
* "No resource table in elf" and never creates the virtio device.
|
||||
*
|
||||
* The addresses are the ones the board's device tree already reserves, so the
|
||||
* two sides agree by construction rather than by convention:
|
||||
*
|
||||
* vdev0vring0 0x88000000 32 KiB
|
||||
* vdev0vring1 0x88008000 32 KiB
|
||||
* vdevbuffer 0x88020000 1 MiB
|
||||
*
|
||||
* A vring of 256 buffers at 4 KiB alignment needs about 10 KiB, so each fits
|
||||
* its 32 KiB reservation with room to spare.
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
#define RSC_VDEV 3U
|
||||
#define VIRTIO_ID_RPMSG 7U
|
||||
#define VIRTIO_RPMSG_F_NS (1U << 0) /* name service announcements */
|
||||
|
||||
#define RSC_VRING0_DA 0x88000000U
|
||||
#define RSC_VRING1_DA 0x88008000U
|
||||
#define RSC_VRING_ALIGN 0x1000U
|
||||
#define RSC_VRING_NUM 256U
|
||||
|
||||
struct fw_rsc_vdev_vring {
|
||||
uint32_t da;
|
||||
uint32_t align;
|
||||
uint32_t num;
|
||||
uint32_t notifyid;
|
||||
uint32_t reserved;
|
||||
};
|
||||
|
||||
struct fw_rsc_vdev {
|
||||
uint32_t type;
|
||||
uint32_t id;
|
||||
uint32_t notifyid;
|
||||
uint32_t dfeatures;
|
||||
uint32_t gfeatures;
|
||||
uint32_t config_len;
|
||||
uint8_t status;
|
||||
uint8_t num_of_vrings;
|
||||
uint8_t reserved[2];
|
||||
struct fw_rsc_vdev_vring vring[2];
|
||||
};
|
||||
|
||||
struct imx95_rsc_table {
|
||||
uint32_t ver;
|
||||
uint32_t num;
|
||||
uint32_t reserved[2];
|
||||
uint32_t offset[1];
|
||||
struct fw_rsc_vdev vdev;
|
||||
};
|
||||
|
||||
const struct imx95_rsc_table resource_table
|
||||
__attribute__((section(".resource_table"), used)) = {
|
||||
1, /* ver */
|
||||
1, /* num: one entry */
|
||||
{ 0, 0 }, /* reserved */
|
||||
{ offsetof(struct imx95_rsc_table, vdev) },
|
||||
{
|
||||
RSC_VDEV,
|
||||
VIRTIO_ID_RPMSG,
|
||||
0, /* notifyid, assigned by the host */
|
||||
VIRTIO_RPMSG_F_NS, /* dfeatures */
|
||||
0, /* gfeatures, filled by the host */
|
||||
0, /* config_len */
|
||||
0, /* status, driven by the host */
|
||||
2, /* num_of_vrings */
|
||||
{ 0, 0 },
|
||||
{
|
||||
{ RSC_VRING0_DA, RSC_VRING_ALIGN, RSC_VRING_NUM, 0, 0 },
|
||||
{ RSC_VRING1_DA, RSC_VRING_ALIGN, RSC_VRING_NUM, 1, 0 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Erased state of the DDR-backed pseudo-flash. */
|
||||
#define FLASH_ERASED_BYTE 0xFFU
|
||||
|
||||
/* Bounds of the M7's reserved DDR window. Anything outside it is rejected
|
||||
* rather than silently corrupting memory that belongs to Linux. */
|
||||
#define M7_DDR_BASE 0x80000000UL
|
||||
#define M7_DDR_SIZE 0x01000000UL
|
||||
|
||||
/* Liveness marker. The M7 has no confirmed UART route on this carrier, so
|
||||
* early bring-up reports progress through shared memory that Linux can read
|
||||
* with devmem instead of over a serial port.
|
||||
*
|
||||
* This lives inside the M7's own DDR window, immediately above the console
|
||||
* ring at 0x80F00000, and deliberately NOT in the 0x88000000 block: the device
|
||||
* tree reserves that for RPMsg (vdev0/vdev1 vrings, and a 1 MiB vdevbuffer at
|
||||
* 0x88020000), so a status word there would be scribbling on buffers Linux
|
||||
* owns as soon as RPMsg is brought up. */
|
||||
#define WOLFBOOT_STATUS_ADDR 0x80F10000UL
|
||||
#define WOLFBOOT_STATUS_MAGIC 0x57424F54UL /* "WBOT" */
|
||||
|
||||
/* Cortex-M7 DWT cycle counter. With no usable console on this carrier the
|
||||
* cycle counter is how the signature-verification cost gets measured: it is
|
||||
* exact, free to read, and needs no peripheral. Timestamps are published
|
||||
* alongside the status word for the A55 to read. */
|
||||
#define CORE_DEMCR (*(volatile uint32_t *)0xE000EDFCUL)
|
||||
#define DEMCR_TRCENA (1UL << 24)
|
||||
#define DWT_CTRL (*(volatile uint32_t *)0xE0001000UL)
|
||||
#define DWT_CYCCNTENA (1UL << 0)
|
||||
#define DWT_CYCCNT (*(volatile uint32_t *)0xE0001004UL)
|
||||
|
||||
/* Cortex-M7 cache maintenance. Both caches are OFF out of reset, and this
|
||||
* image runs its text from DDR, so leaving them off means every instruction
|
||||
* fetch and data access goes to external memory - measured at ~664 cycles per
|
||||
* byte for SHA-256, roughly fifty times slower than the core is capable of.
|
||||
*
|
||||
* The ARMv7-M default memory map already marks 0x80000000-0x9FFFFFFF as normal
|
||||
* cacheable memory, so no MPU region is needed to make this take effect. */
|
||||
#define SCB_CCR (*(volatile uint32_t *)0xE000ED14UL)
|
||||
#define SCB_CCSIDR (*(volatile uint32_t *)0xE000ED80UL)
|
||||
#define SCB_CSSELR (*(volatile uint32_t *)0xE000ED84UL)
|
||||
#define SCB_ICIALLU (*(volatile uint32_t *)0xE000EF50UL)
|
||||
#define SCB_DCISW (*(volatile uint32_t *)0xE000EF60UL)
|
||||
#define CCR_IC (1UL << 17)
|
||||
#define CCR_DC (1UL << 16)
|
||||
|
||||
#define DSB() __asm__ volatile ("dsb 0xF" ::: "memory")
|
||||
#define ISB() __asm__ volatile ("isb 0xF" ::: "memory")
|
||||
|
||||
static void hal_cache_enable(void)
|
||||
{
|
||||
uint32_t ccsidr, sets, ways, s, w;
|
||||
|
||||
/* I-cache: invalidate, then enable. */
|
||||
DSB(); ISB();
|
||||
SCB_ICIALLU = 0UL;
|
||||
DSB(); ISB();
|
||||
SCB_CCR |= CCR_IC;
|
||||
DSB(); ISB();
|
||||
|
||||
/* D-cache: invalidate every set/way before enabling, otherwise stale
|
||||
* lines from reset can be written back over live DDR. */
|
||||
SCB_CSSELR = 0UL; /* select L1 data cache */
|
||||
DSB();
|
||||
ccsidr = SCB_CCSIDR;
|
||||
sets = (ccsidr >> 13) & 0x7FFFUL;
|
||||
ways = (ccsidr >> 3) & 0x3FFUL;
|
||||
for (s = 0; s <= sets; s++) {
|
||||
for (w = 0; w <= ways; w++) {
|
||||
/* DCISW: way in [31:30], set in [13:5] for a 32-byte line. */
|
||||
SCB_DCISW = ((w << 30) | (s << 5));
|
||||
}
|
||||
}
|
||||
DSB();
|
||||
SCB_CCR |= CCR_DC;
|
||||
DSB(); ISB();
|
||||
}
|
||||
|
||||
static void hal_cycle_counter_start(void)
|
||||
{
|
||||
CORE_DEMCR |= DEMCR_TRCENA;
|
||||
DWT_CYCCNT = 0;
|
||||
DWT_CTRL |= DWT_CYCCNTENA;
|
||||
}
|
||||
|
||||
static int hal_addr_is_valid(uint32_t address, int len)
|
||||
{
|
||||
uint64_t start = (uint64_t)address;
|
||||
uint64_t end;
|
||||
|
||||
if (len < 0)
|
||||
return 0;
|
||||
end = start + (uint64_t)len;
|
||||
if (start < (uint64_t)M7_DDR_BASE)
|
||||
return 0;
|
||||
if (end > (uint64_t)M7_DDR_BASE + (uint64_t)M7_DDR_SIZE)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef __WOLFBOOT
|
||||
|
||||
/* Record a progress code where the A55 side can see it. Kept deliberately
|
||||
* trivial: no locking, no ordering guarantees beyond the write itself. */
|
||||
void hal_status(uint32_t code)
|
||||
{
|
||||
volatile uint32_t *status = (volatile uint32_t *)WOLFBOOT_STATUS_ADDR;
|
||||
|
||||
status[0] = WOLFBOOT_STATUS_MAGIC;
|
||||
status[1] = code;
|
||||
/* status[2] is stamped at hal_init, status[3] at hal_prepare_boot; the
|
||||
* difference is the cost of everything wolfBoot does in between, which is
|
||||
* dominated by the signature verification. */
|
||||
status[code + 1] = DWT_CYCCNT;
|
||||
}
|
||||
|
||||
void hal_init(void)
|
||||
{
|
||||
/* Clocks, DDR and the TCM split are configured before this core is
|
||||
* released: DDR by the OEI/SPL stage, the TCM split by the System Manager
|
||||
* on the M33 via M7_CFG[TCM_SIZE] in BLK_CTRL_Secure_AON. There is
|
||||
* nothing for wolfBoot to bring up here except the caches, which are off
|
||||
* out of reset and which this image needs because it runs from DDR. */
|
||||
hal_cache_enable();
|
||||
hal_cycle_counter_start();
|
||||
hal_status(1);
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
hal_status(2);
|
||||
}
|
||||
|
||||
#endif /* __WOLFBOOT */
|
||||
|
||||
int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
||||
{
|
||||
if (data == NULL)
|
||||
return -1;
|
||||
if (!hal_addr_is_valid(address, len))
|
||||
return -1;
|
||||
|
||||
memcpy((void *)(uintptr_t)address, data, (size_t)len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_unlock(void)
|
||||
{
|
||||
}
|
||||
|
||||
void RAMFUNCTION hal_flash_lock(void)
|
||||
{
|
||||
}
|
||||
|
||||
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
||||
{
|
||||
if (!hal_addr_is_valid(address, len))
|
||||
return -1;
|
||||
|
||||
memset((void *)(uintptr_t)address, FLASH_ERASED_BYTE, (size_t)len);
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
/* Linker script for wolfBoot on the i.MX95 Cortex-M7.
|
||||
*
|
||||
* Linked for the *core* view of TCM. The Linux remoteproc driver loads through
|
||||
* the system view (ITCM 0x203C0000, DTCM 0x20400000 at the default 256K/256K
|
||||
* TCM split) and imx_rproc translates between the two. Linking for the system
|
||||
* view produces an image that loads cleanly and faults on the first fetch.
|
||||
*
|
||||
* Sizes assume M7_CFG[TCM_SIZE] = 000b (256 KiB ITCM + 256 KiB DTCM), which is
|
||||
* the reset default. The System Manager on the M33 owns that field.
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ITCM (rx) : ORIGIN = 0x00000000, LENGTH = 256K
|
||||
DTCM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.isr_vector))
|
||||
. = ALIGN(8);
|
||||
*(.text*)
|
||||
*(.rodata*)
|
||||
*(.init*)
|
||||
*(.fini*)
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > ITCM
|
||||
|
||||
/* Consumed by the remoteproc ELF loader. Not required to start the core,
|
||||
* but RPMsg needs it, so keep it even when empty. */
|
||||
.resource_table :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
KEEP(*(.resource_table*))
|
||||
. = ALIGN(8);
|
||||
} > ITCM
|
||||
|
||||
.edidx :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.ARM.exidx*)
|
||||
} > ITCM
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > DTCM
|
||||
|
||||
.bss (NOLOAD) :
|
||||
{
|
||||
_start_bss = .;
|
||||
__bss_start__ = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
__bss_end__ = .;
|
||||
_end = .;
|
||||
} > DTCM
|
||||
. = ALIGN(4);
|
||||
}
|
||||
|
||||
END_STACK = ORIGIN(DTCM) + LENGTH(DTCM);
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
/* uart_drv_imx95_m7.c
|
||||
*
|
||||
* Console for the Cortex-M7 on the NXP i.MX95.
|
||||
*
|
||||
* Copyright (C) 2026 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
|
||||
*/
|
||||
|
||||
/* This is a shared-memory console, not a peripheral driver.
|
||||
*
|
||||
* The M7's LPUART is not routed to an accessible header on the Toradex SMARC
|
||||
* Development Board: markers written to /dev/ttyLP1..3 from Linux appear on no
|
||||
* host serial channel, and Linux owns all four LPUART instances anyway, so an
|
||||
* M7 writing to one would contend with it. Rather than depend on unproven pin
|
||||
* routing, the console is a ring buffer in the M7's own reserved DDR window,
|
||||
* which the A55 cluster reads with the memtool helper.
|
||||
*
|
||||
* The buffer sits at the top of the M7's 16 MiB carveout (memory@80000000),
|
||||
* clear of the wolfBoot partitions lower down (BOOT 0x80100000, UPDATE
|
||||
* 0x80500000, SWAP 0x80900000). It deliberately does NOT use the RPMsg
|
||||
* vdevbuffer carveout, which belongs to the RPMsg transport.
|
||||
*
|
||||
* Reader contract: `wr` counts bytes ever written and never wraps within a run,
|
||||
* so a reader tracks its own position and copies (wr - pos) bytes from
|
||||
* data[pos % size]. Overrun is possible and expected for very chatty output;
|
||||
* the reader detects it when (wr - pos) > size and reports the gap rather than
|
||||
* silently printing corrupt text.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CONSOLE_BASE 0x80F00000UL
|
||||
#define CONSOLE_MAGIC 0x4E4F4357UL /* "WCON" little-endian */
|
||||
#define CONSOLE_SIZE 0x10000UL /* 64 KiB of text */
|
||||
|
||||
struct console_hdr {
|
||||
volatile uint32_t magic;
|
||||
volatile uint32_t wr; /* total bytes written, monotonic */
|
||||
volatile uint32_t size; /* == CONSOLE_SIZE */
|
||||
volatile uint32_t rsvd;
|
||||
};
|
||||
|
||||
#define CONSOLE_HDR ((struct console_hdr *)CONSOLE_BASE)
|
||||
#define CONSOLE_DATA ((volatile uint8_t *)(CONSOLE_BASE + sizeof(struct console_hdr)))
|
||||
|
||||
int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop)
|
||||
{
|
||||
struct console_hdr *h = CONSOLE_HDR;
|
||||
|
||||
(void)bitrate;
|
||||
(void)data;
|
||||
(void)parity;
|
||||
(void)stop;
|
||||
|
||||
h->wr = 0;
|
||||
h->size = CONSOLE_SIZE;
|
||||
/* Magic last: a reader that samples mid-init sees no valid console rather
|
||||
* than a valid magic with a stale size. */
|
||||
h->magic = CONSOLE_MAGIC;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uart_tx(const uint8_t c)
|
||||
{
|
||||
struct console_hdr *h = CONSOLE_HDR;
|
||||
uint32_t w;
|
||||
|
||||
if (h->magic != CONSOLE_MAGIC)
|
||||
(void)uart_init(0, 0, 'N', 0);
|
||||
|
||||
w = h->wr;
|
||||
CONSOLE_DATA[w % CONSOLE_SIZE] = c;
|
||||
/* Publish the byte before advancing the counter, so a reader never sees a
|
||||
* count covering a byte that has not been stored yet. */
|
||||
h->wr = w + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uart_rx(uint8_t *c)
|
||||
{
|
||||
(void)c;
|
||||
return -1; /* console is write-only */
|
||||
}
|
||||
|
||||
void uart_write(const char *buf, unsigned int len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
(void)uart_tx((const uint8_t)buf[i]);
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/* Linker script for the wolfBoot test app on the i.MX95 Cortex-M7.
|
||||
*
|
||||
* The generic ARM.ld is not usable here: it gives RAM only 16 KiB and defines
|
||||
* no heap symbols, and wolfCrypt's test/benchmark need both a real heap (RSA
|
||||
* and ECC key material) and syscalls.c's _sbrk, which references
|
||||
* _Min_Heap_Size.
|
||||
*
|
||||
* Code runs from the BOOT partition in the M7's reserved DDR window; data and
|
||||
* heap live in DTCM (core view 0x20000000, 256 KiB at the reset-default
|
||||
* M7_CFG[TCM_SIZE] split). Keeping the working set in TCM is the point of the
|
||||
* exercise -- these numbers are meant to characterise the real-time core, not
|
||||
* DDR latency.
|
||||
*/
|
||||
|
||||
_Min_Heap_Size = 0x00018000; /* 96 KiB - RSA/ECC test vectors and key structs */
|
||||
_Min_Stack_Size = 0x00008000; /* 32 KiB */
|
||||
|
||||
MEMORY
|
||||
{
|
||||
FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@
|
||||
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K /* DTCM, core view */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
_start_text = .;
|
||||
KEEP(*(.isr_vector))
|
||||
*(.init)
|
||||
*(.fini)
|
||||
*(.text*)
|
||||
KEEP(*(.rodata*))
|
||||
. = ALIGN(4);
|
||||
_end_text = .;
|
||||
} > FLASH
|
||||
|
||||
.ARM :
|
||||
{
|
||||
__exidx_start = .;
|
||||
*(.ARM.exidx*)
|
||||
__exidx_end = .;
|
||||
} > FLASH
|
||||
|
||||
_stored_data = .;
|
||||
|
||||
.data : AT (_stored_data)
|
||||
{
|
||||
_start_data = .;
|
||||
KEEP(*(.data*))
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.ramcode))
|
||||
. = ALIGN(4);
|
||||
_end_data = .;
|
||||
} > RAM
|
||||
|
||||
.bss :
|
||||
{
|
||||
_start_bss = .;
|
||||
*(.bss*)
|
||||
*(COMMON)
|
||||
. = ALIGN(4);
|
||||
_end_bss = .;
|
||||
_end = .;
|
||||
} > RAM
|
||||
|
||||
/* Reserve heap and stack explicitly so the link fails loudly if DTCM is
|
||||
* over-committed, rather than overflowing at run time. */
|
||||
.heap (NOLOAD) :
|
||||
{
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( _start_heap = . );
|
||||
. = . + _Min_Heap_Size;
|
||||
. = ALIGN(8);
|
||||
} > RAM
|
||||
|
||||
.stack (NOLOAD) :
|
||||
{
|
||||
. = . + _Min_Stack_Size;
|
||||
. = ALIGN(8);
|
||||
PROVIDE ( END_STACK = . );
|
||||
PROVIDE ( _end_stack = . );
|
||||
} > RAM
|
||||
}
|
||||
|
||||
_wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@;
|
||||
_wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@;
|
||||
_wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@;
|
||||
_wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@;
|
||||
|
|
@ -616,6 +616,16 @@ ifeq ($(TARGET),stm32l5)
|
|||
APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET).o
|
||||
endif
|
||||
|
||||
# i.MX95 Cortex-M7. The console is a shared-memory ring buffer rather than an
|
||||
# LPUART: no M7 UART is routed to a host-accessible channel on the Toradex SMARC
|
||||
# carrier, and Linux owns all four LPUART instances. -mcpu/-mfpu come from
|
||||
# arch.mk via CORTEX_M7.
|
||||
ifeq ($(TARGET),imx95_m7)
|
||||
CFLAGS+=-I..
|
||||
APP_OBJS+=../hal/uart/uart_drv_$(UART_TARGET).o
|
||||
LSCRIPT_TEMPLATE=ARM-imx95_m7.ld
|
||||
endif
|
||||
|
||||
ifeq ($(TARGET),stm32h5)
|
||||
ifeq ($(TZEN),1)
|
||||
LSCRIPT_TEMPLATE=ARM-stm32h5-ns.ld
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
/* app_imx95_m7.c
|
||||
*
|
||||
* Test application for the Cortex-M7 on the NXP i.MX95.
|
||||
*
|
||||
* Copyright (C) 2026 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
|
||||
*/
|
||||
|
||||
/* Output goes to the shared-memory console in hal/uart/uart_drv_imx95_m7.c,
|
||||
* because this part has no M7 UART routed to a host-accessible channel on the
|
||||
* Toradex SMARC carrier. wolfCrypt's test and benchmark call printf(), which
|
||||
* test-app/syscalls.c routes to uart_write(), so their output lands there too.
|
||||
*
|
||||
* Liveness markers stay in a shared status block so the demo's right pane can
|
||||
* poll them cheaply without parsing console text:
|
||||
*
|
||||
* devmem 0x80F10010 -> 0x41505031 ("APP1") once the app is running
|
||||
* devmem 0x80F10014 -> heartbeat, increments continuously
|
||||
*
|
||||
* wolfBoot writes its own progress codes at 0x80F10000, so a reader can tell
|
||||
* "bootloader ran" from "payload ran" with no peripheral wired up. The block
|
||||
* sits just above the console ring in the M7's DDR window, clear of the
|
||||
* RPMsg reservations at 0x88000000.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "wolfboot/wolfboot.h"
|
||||
|
||||
#if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK)
|
||||
#include <wolfssl/wolfcrypt/settings.h>
|
||||
#include <wolfssl/wolfcrypt/wc_port.h>
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_TEST
|
||||
int wolfcrypt_test(void *args);
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_BENCHMARK
|
||||
int benchmark_test(void *args);
|
||||
#endif
|
||||
|
||||
#define APP_STATUS_ADDR 0x80F10010UL
|
||||
#define APP_STATUS_MAGIC 0x41505031UL /* "APP1" */
|
||||
|
||||
extern int uart_init(uint32_t bitrate, uint8_t data, char parity, uint8_t stop);
|
||||
extern void uart_write(const char *buf, unsigned int len);
|
||||
|
||||
/* Local, so the banner works in builds that do not link ../src/string.o. */
|
||||
static void say(const char *s)
|
||||
{
|
||||
unsigned int n = 0;
|
||||
|
||||
while (s[n] != '\0')
|
||||
n++;
|
||||
uart_write(s, n);
|
||||
}
|
||||
|
||||
void main(void)
|
||||
{
|
||||
volatile uint32_t *status = (volatile uint32_t *)APP_STATUS_ADDR;
|
||||
uint32_t heartbeat = 0;
|
||||
/* remoteproc loads only PT_LOAD segments, and .bss is NOBITS - nothing
|
||||
* zeroes it before entry, so do it here before any static is read. */
|
||||
extern char _start_bss[], _end_bss[];
|
||||
char *p;
|
||||
|
||||
for (p = _start_bss; p < _end_bss; p++)
|
||||
*p = 0;
|
||||
|
||||
(void)uart_init(115200, 8, 'N', 1);
|
||||
|
||||
status[0] = APP_STATUS_MAGIC;
|
||||
status[1] = heartbeat;
|
||||
|
||||
say("\r\nwolfBoot test app: i.MX95 Cortex-M7\r\n");
|
||||
|
||||
#if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK)
|
||||
wolfCrypt_Init();
|
||||
#ifdef WOLFCRYPT_TEST
|
||||
say("\r\nRunning wolfCrypt tests...\r\n");
|
||||
wolfcrypt_test(NULL);
|
||||
say("Tests complete.\r\n");
|
||||
#endif
|
||||
#ifdef WOLFCRYPT_BENCHMARK
|
||||
say("\r\nRunning wolfCrypt benchmarks...\r\n");
|
||||
benchmark_test(NULL);
|
||||
say("Benchmarks complete.\r\n");
|
||||
#endif
|
||||
wolfCrypt_Cleanup();
|
||||
#endif
|
||||
|
||||
say("Idle.\r\n");
|
||||
|
||||
/* Confirm this image booted so wolfBoot does not roll it back. */
|
||||
wolfBoot_success();
|
||||
|
||||
while (1) {
|
||||
status[1] = ++heartbeat;
|
||||
}
|
||||
}
|
||||
|
|
@ -44,6 +44,47 @@
|
|||
extern volatile uint64_t HAL_time_ms;
|
||||
#elif defined(TARGET_lpc55s69)
|
||||
extern volatile uint64_t SysTick_time_ms;
|
||||
#elif defined(TARGET_imx95_m7)
|
||||
/* Cortex-M7 DWT cycle counter. Exact, free to read, and needs no
|
||||
* peripheral - which matters here because the M7 has no console UART
|
||||
* routed on this carrier. The core runs at 800 MHz (read from the board's
|
||||
* clk_summary: "m7 800000000"), not assumed. CYCCNT is 32-bit and wraps
|
||||
* every ~5.4 s at that rate, so accumulate into 64 bits on each read;
|
||||
* benchmark intervals are shorter than one wrap, so sampling on every call
|
||||
* is sufficient to catch it. */
|
||||
#define IMX95_M7_HZ 800000000ULL
|
||||
static uint64_t m7_cyc_hi = 0;
|
||||
static uint32_t m7_cyc_last = 0;
|
||||
static int m7_cyc_inited = 0;
|
||||
|
||||
static void m7_cyc_init(void);
|
||||
|
||||
static uint64_t m7_get_ticks(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (!m7_cyc_inited) {
|
||||
m7_cyc_init();
|
||||
m7_cyc_inited = 1;
|
||||
}
|
||||
now = *(volatile uint32_t *)0xE0001004UL; /* DWT_CYCCNT */
|
||||
|
||||
if (now < m7_cyc_last)
|
||||
m7_cyc_hi += 0x100000000ULL; /* wrapped since last read */
|
||||
m7_cyc_last = now;
|
||||
return m7_cyc_hi + now;
|
||||
}
|
||||
|
||||
static void m7_cyc_init(void)
|
||||
{
|
||||
*(volatile uint32_t *)0xE000EDFCUL |= (1UL << 24); /* DEMCR.TRCENA */
|
||||
*(volatile uint32_t *)0xE0001004UL = 0; /* DWT_CYCCNT = 0 */
|
||||
*(volatile uint32_t *)0xE0001000UL |= 1UL; /* DWT_CTRL.CYCCNTENA */
|
||||
m7_cyc_hi = 0;
|
||||
m7_cyc_last = 0;
|
||||
}
|
||||
|
||||
static uint64_t m7_start_ticks = 0;
|
||||
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
|
||||
/* PPC time base register for accurate timing (e6500). */
|
||||
static uint32_t ppc_tb_hz = 0;
|
||||
|
|
@ -147,6 +188,12 @@ unsigned long my_time(unsigned long* timer)
|
|||
unsigned long t = (unsigned long)(SysTick_time_ms / 1000);
|
||||
if (timer) *timer = t;
|
||||
return t;
|
||||
#elif defined(TARGET_imx95_m7)
|
||||
{
|
||||
unsigned long t = (unsigned long)(m7_get_ticks() / IMX95_M7_HZ);
|
||||
if (timer) *timer = t;
|
||||
return t;
|
||||
}
|
||||
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
|
||||
if (ppc_tb_hz == 0)
|
||||
ppc_tb_hz = ppc_get_timebase_hz();
|
||||
|
|
@ -191,6 +238,13 @@ double current_time(int reset)
|
|||
#elif defined(TARGET_lpc55s69)
|
||||
(void)reset;
|
||||
return (double)SysTick_time_ms / 1000.0;
|
||||
#elif defined(TARGET_imx95_m7)
|
||||
if (reset) {
|
||||
m7_cyc_init();
|
||||
m7_cyc_inited = 1;
|
||||
m7_start_ticks = 0;
|
||||
}
|
||||
return (double)(m7_get_ticks() - m7_start_ticks) / (double)IMX95_M7_HZ;
|
||||
#elif defined(TARGET_nxp_t2080) || defined(TARGET_nxp_t1024)
|
||||
if (ppc_tb_hz == 0)
|
||||
ppc_tb_hz = ppc_get_timebase_hz();
|
||||
|
|
|
|||
Loading…
Reference in New Issue