This is an automated email from the ASF dual-hosted git repository.
acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 44a38732227 arch/arm/imxrt: add a CAAM-backed /dev/random driver
44a38732227 is described below
commit 44a38732227125f0d367763270eb5a070e4442d3
Author: Royyan Zahir <[email protected]>
AuthorDate: Sun Sep 20 19:03:38 2026 +0400
arch/arm/imxrt: add a CAAM-backed /dev/random driver
The part has a hardware random number generator and nothing registers
it, so up_randompool_initialize() is never seeded from hardware. There
is no CAAM, TRNG or RNG driver anywhere in arch/arm/src/imxrt, and the
RT117x headers describe the block only as an address-map comment.
imxrt_caam.c brings up job ring zero and instantiates the RNG state
handle when the boot ROM has not, retrying with a longer entropy sample
until the self test passes. imxrt_rng.c registers /dev/random and
/dev/urandom on top, and is the i.MX9 driver's sibling: same health
checks, same FIPS 140-2 continuous test, same refusal to return a short
read and call it entropy.
The instantiation descriptor posts no job ring completion, so the state
handle is what reports it, and the ring is taken back to a known state
to latch it. Job ring zero is started and the cache and watchdog bits
set first: RDSTA and JRSTART both read zero out of reset on this part.
Scoped to RT117x, which is the family that carries CAAM.
Built for imxrt1170-evk:nsh with the driver on, and for imxrt1060-evk:nsh
to confirm the shared clock-gate header still builds without it.
Run on an FMU-v6X-RT (i.MX RT1176): /dev/random and /dev/urandom both
return, the first read after a cold boot included, and five consecutive
reads are distinct.
Signed-off-by: Royyan Zahir <[email protected]>
---
arch/arm/src/imxrt/CMakeLists.txt | 8 +
arch/arm/src/imxrt/Kconfig | 19 +
arch/arm/src/imxrt/Make.defs | 8 +
.../arm/src/imxrt/hardware/rt117x/imxrt117x_caam.h | 133 +++++++
arch/arm/src/imxrt/imxrt_caam.c | 434 +++++++++++++++++++++
arch/arm/src/imxrt/imxrt_caam.h | 85 ++++
arch/arm/src/imxrt/imxrt_periphclks.h | 9 +
arch/arm/src/imxrt/imxrt_rng.c | 286 ++++++++++++++
8 files changed, 982 insertions(+)
diff --git a/arch/arm/src/imxrt/CMakeLists.txt
b/arch/arm/src/imxrt/CMakeLists.txt
index f16722d8def..9a2a9aa4293 100644
--- a/arch/arm/src/imxrt/CMakeLists.txt
+++ b/arch/arm/src/imxrt/CMakeLists.txt
@@ -177,4 +177,12 @@ if(CONFIG_IMXRT_ELE)
list(APPEND SRCS imxrt118x_ele.c)
endif()
+if(CONFIG_IMXRT_CAAM)
+ list(APPEND SRCS imxrt_caam.c)
+endif()
+
+if(CONFIG_IMXRT_RNG)
+ list(APPEND SRCS imxrt_rng.c)
+endif()
+
target_sources(arch PRIVATE ${SRCS})
diff --git a/arch/arm/src/imxrt/Kconfig b/arch/arm/src/imxrt/Kconfig
index 4221c9782bb..a3138542394 100644
--- a/arch/arm/src/imxrt/Kconfig
+++ b/arch/arm/src/imxrt/Kconfig
@@ -500,6 +500,14 @@ config IMXRT_ELE_FW_PATH
Path (relative to the NuttX top-level directory, or absolute) to
the NXP EdgeLock Enclave firmware AHAB container.
+config IMXRT_CAAM
+ bool
+ default n
+ ---help---
+ Enable the CAAM job ring driver
+ (arch/arm/src/imxrt/imxrt_caam.c). Present on RT117x-class
+ SoCs.
+
config IMXRT_CM7_BOOT
bool
default n
@@ -507,6 +515,17 @@ config IMXRT_CM7_BOOT
menu "i.MX RT Peripheral Selection"
+config IMXRT_RNG
+ bool "CAAM true random number generator"
+ default n
+ depends on ARCH_FAMILY_IMXRT117x
+ select IMXRT_CAAM
+ select ARCH_HAVE_RNG
+ ---help---
+ Register /dev/random and /dev/urandom, both backed by the CAAM
+ true random number generator. Without this the entropy pool is
+ never seeded from hardware.
+
config IMXRT_EDMA
bool "eDMA"
default n
diff --git a/arch/arm/src/imxrt/Make.defs b/arch/arm/src/imxrt/Make.defs
index 999f756efb8..e7f037dbdf8 100644
--- a/arch/arm/src/imxrt/Make.defs
+++ b/arch/arm/src/imxrt/Make.defs
@@ -134,6 +134,14 @@ ifeq ($(CONFIG_IMXRT_ENET),y)
CHIP_CSRCS += imxrt_enet.c
endif
+ifeq ($(CONFIG_IMXRT_CAAM),y)
+CHIP_CSRCS += imxrt_caam.c
+endif
+
+ifeq ($(CONFIG_IMXRT_RNG),y)
+CHIP_CSRCS += imxrt_rng.c
+endif
+
ifeq ($(CONFIG_IMXRT_LPI2C),y)
CHIP_CSRCS += imxrt_lpi2c.c
endif
diff --git a/arch/arm/src/imxrt/hardware/rt117x/imxrt117x_caam.h
b/arch/arm/src/imxrt/hardware/rt117x/imxrt117x_caam.h
new file mode 100644
index 00000000000..f34aa85c138
--- /dev/null
+++ b/arch/arm/src/imxrt/hardware/rt117x/imxrt117x_caam.h
@@ -0,0 +1,133 @@
+/*****************************************************************************
+ * arch/arm/src/imxrt/hardware/rt117x/imxrt117x_caam.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ *****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_IMXRT_HARDWARE_RT117X_IMXRT117X_CAAM_H
+#define __ARCH_ARM_SRC_IMXRT_HARDWARE_RT117X_IMXRT117X_CAAM_H
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "hardware/imxrt_memorymap.h"
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+/* The job rings are separate 16 KB pages above the general block. Only ring
+ * zero is used here; one ring answers one request at a time, which is all a
+ * random number generator needs.
+ */
+
+#define IMXRT_CAAM_GEN_BASE 0x40440000
+#define IMXRT_CAAM_JR0_BASE 0x40450000
+
+/* General block */
+
+#define IMXRT_CAAM_MCFGR_OFFSET 0x0004
+#define IMXRT_CAAM_SCFGR_OFFSET 0x000c
+#define IMXRT_CAAM_JRSTART_OFFSET 0x005c
+
+/* RNG4 block, inside the general block */
+
+#define IMXRT_CAAM_RTMCTL_OFFSET 0x0600
+#define IMXRT_CAAM_RTSDCTL_OFFSET 0x0610
+#define IMXRT_CAAM_RTFRQMIN_OFFSET 0x0618
+#define IMXRT_CAAM_RTFRQMAX_OFFSET 0x061c
+#define IMXRT_CAAM_RDSTA_OFFSET 0x06c0
+
+#define IMXRT_CAAM_MCFGR (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_MCFGR_OFFSET)
+#define IMXRT_CAAM_SCFGR (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_SCFGR_OFFSET)
+#define IMXRT_CAAM_JRSTART (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_JRSTART_OFFSET)
+#define IMXRT_CAAM_RTMCTL (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_RTMCTL_OFFSET)
+#define IMXRT_CAAM_RTSDCTL (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_RTSDCTL_OFFSET)
+#define IMXRT_CAAM_RTFRQMIN (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_RTFRQMIN_OFFSET)
+#define IMXRT_CAAM_RTFRQMAX (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_RTFRQMAX_OFFSET)
+#define IMXRT_CAAM_RDSTA (IMXRT_CAAM_GEN_BASE +
IMXRT_CAAM_RDSTA_OFFSET)
+
+/* Job ring zero */
+
+#define IMXRT_CAAM_IRBA_H_OFFSET 0x0000
+#define IMXRT_CAAM_IRBA_L_OFFSET 0x0004
+#define IMXRT_CAAM_IRS_OFFSET 0x000c
+#define IMXRT_CAAM_IRSA_OFFSET 0x0014
+#define IMXRT_CAAM_IRJA_OFFSET 0x001c
+#define IMXRT_CAAM_ORBA_H_OFFSET 0x0020
+#define IMXRT_CAAM_ORBA_L_OFFSET 0x0024
+#define IMXRT_CAAM_ORS_OFFSET 0x002c
+#define IMXRT_CAAM_ORJR_OFFSET 0x0034
+#define IMXRT_CAAM_ORSF_OFFSET 0x003c
+#define IMXRT_CAAM_JRSTA_OFFSET 0x0044
+#define IMXRT_CAAM_JRINT_OFFSET 0x004c
+#define IMXRT_CAAM_JRCFG1_OFFSET 0x0054
+#define IMXRT_CAAM_JRCR_OFFSET 0x006c
+
+#define IMXRT_CAAM_IRBA_H (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_IRBA_H_OFFSET)
+#define IMXRT_CAAM_IRBA_L (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_IRBA_L_OFFSET)
+#define IMXRT_CAAM_IRS (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_IRS_OFFSET)
+#define IMXRT_CAAM_IRSA (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_IRSA_OFFSET)
+#define IMXRT_CAAM_IRJA (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_IRJA_OFFSET)
+#define IMXRT_CAAM_ORBA_H (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_ORBA_H_OFFSET)
+#define IMXRT_CAAM_ORBA_L (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_ORBA_L_OFFSET)
+#define IMXRT_CAAM_ORS (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_ORS_OFFSET)
+#define IMXRT_CAAM_ORJR (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_ORJR_OFFSET)
+#define IMXRT_CAAM_ORSF (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_ORSF_OFFSET)
+#define IMXRT_CAAM_JRSTA (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_JRSTA_OFFSET)
+#define IMXRT_CAAM_JRINT (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_JRINT_OFFSET)
+#define IMXRT_CAAM_JRCFG1 (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_JRCFG1_OFFSET)
+#define IMXRT_CAAM_JRCR (IMXRT_CAAM_JR0_BASE +
IMXRT_CAAM_JRCR_OFFSET)
+
+/* MCFGR */
+
+#define CAAM_MCFGR_SWRST (1 << 31) /* Software reset */
+#define CAAM_MCFGR_DMA_RST (1 << 28) /* DMA reset */
+#define CAAM_MCFGR_WDE (1 << 30) /* DECO watchdog enable */
+#define CAAM_MCFGR_LARGE_BURST (1 << 2) /* 128/256-byte bursts */
+#define CAAM_MCFGR_AWCACHE_MASK (0xf << 8)
+#define CAAM_MCFGR_AWCACHE_BUFF (0x1 << 8)
+#define CAAM_MCFGR_AWCACHE_CACH (0x2 << 8)
+
+#define CAAM_JRSTART_JR0 (1 << 0) /* Start job ring zero */
+
+/* JRCR, JRINT, JRCFG1 */
+
+#define CAAM_JRCR_RESET (1 << 0)
+#define CAAM_JRINT_ERR_HALT_MASK (3 << 2)
+#define CAAM_JRINT_ERR_HALT_INPROG (1 << 2)
+#define CAAM_JRCFG1_IMSK (1 << 0) /* Mask the ring interrupt */
+
+/* RTMCTL, RTSDCTL, RDSTA */
+
+#define CAAM_RTMCTL_PRGM (1 << 16) /* Program, not run, mode */
+#define CAAM_RTMCTL_ERR (1 << 12)
+#define CAAM_RTSDCTL_ENT_DLY_SHIFT (16)
+#define CAAM_RTSDCTL_ENT_DLY_MASK (0xffff << CAAM_RTSDCTL_ENT_DLY_SHIFT)
+#define CAAM_RTSDCTL_SAMP_SIZE_MASK (0xffff)
+
+#define CAAM_RDSTA_IF0 (1 << 0) /* State handle 0
instantiated */
+#define CAAM_RDSTA_PR0 (1 << 4) /* With prediction resistance
*/
+#define CAAM_RDSTA_SKVN (1 << 30) /* Secure keys already made */
+#define CAAM_RDSTA_ERRMASK (3 << 16)
+
+#endif /* __ARCH_ARM_SRC_IMXRT_HARDWARE_RT117X_IMXRT117X_CAAM_H */
diff --git a/arch/arm/src/imxrt/imxrt_caam.c b/arch/arm/src/imxrt/imxrt_caam.c
new file mode 100644
index 00000000000..dba90f0fbff
--- /dev/null
+++ b/arch/arm/src/imxrt/imxrt_caam.c
@@ -0,0 +1,434 @@
+/*****************************************************************************
+ * arch/arm/src/imxrt/imxrt_caam.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <debug.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/arch.h>
+
+#include "arm_internal.h"
+#include "hardware/rt117x/imxrt117x_caam.h"
+#include "imxrt_caam.h"
+#include "imxrt_periphclks.h"
+
+#ifdef CONFIG_IMXRT_CAAM
+
+/*****************************************************************************
+ * Pre-processor Definitions
+ *****************************************************************************/
+
+#ifndef ARMV7M_DCACHE_LINESIZE
+# define ARMV7M_DCACHE_LINESIZE 32
+#endif
+
+/* One entry each way. A ring deeper than that would only let a second
+ * request queue behind a caller that is already waiting for the first.
+ */
+
+#define CAAM_RING_ENTRIES 1
+
+/* CAAM writes the result by DMA, so the landing buffer owns whole cache
+ * lines and shares them with nothing.
+ */
+
+#define CAAM_RNG_BLOCKLEN ARMV7M_DCACHE_LINESIZE
+
+#define CAAM_DESC_WORDS 8
+
+/* Descriptor words, from the SEC reference descriptor encoding. */
+
+#define CAAM_DESC_HDR(len) (0xb0800000 | (len))
+#define CAAM_OP_RNG_GENERATE 0x82500002
+#define CAAM_OP_RNG_INIT_SH0 0x82500006
+#define CAAM_OP_RNG_GEN_SK 0x82501000
+#define CAAM_JUMP_WAIT_CLASS1 0xa2000001
+#define CAAM_LOAD_CLRW 0x10880004
+#define CAAM_FIFO_STORE_RNG 0x60340000
+
+/* Entropy sample length, in system clocks. A self test that fails is
+ * retried with a longer one, which is how NXP's own code finds a value
+ * that passes across voltage and temperature.
+ */
+
+#define CAAM_INSTANTIATE_SETTLE 20000
+
+#define CAAM_ENT_DELAY_MIN 3200
+#define CAAM_ENT_DELAY_MAX 12800
+#define CAAM_ENT_DELAY_STEP 400
+
+#define CAAM_TIMEOUT 100000
+
+/*****************************************************************************
+ * Private Data
+ *****************************************************************************/
+
+/* The rings and the descriptor are read by CAAM over DMA, and the result is
+ * written back the same way, so each one owns its cache lines outright.
+ */
+
+static uint32_t g_input_ring[CAAM_RING_ENTRIES]
+ aligned_data(ARMV7M_DCACHE_LINESIZE);
+
+static uint32_t g_output_ring[CAAM_RING_ENTRIES * 2]
+ aligned_data(ARMV7M_DCACHE_LINESIZE);
+
+static uint32_t g_desc[CAAM_DESC_WORDS]
+ aligned_data(ARMV7M_DCACHE_LINESIZE);
+
+static uint8_t g_rngbuf[CAAM_RNG_BLOCKLEN]
+ aligned_data(ARMV7M_DCACHE_LINESIZE);
+
+static bool g_initialized;
+
+/*****************************************************************************
+ * Private Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: imxrt_caam_clean
+ *****************************************************************************/
+
+static void imxrt_caam_clean(void *addr, size_t len)
+{
+ up_clean_dcache((uintptr_t)addr, (uintptr_t)addr + len);
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_invalidate
+ *****************************************************************************/
+
+static void imxrt_caam_invalidate(void *addr, size_t len)
+{
+ up_invalidate_dcache((uintptr_t)addr, (uintptr_t)addr + len);
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_run
+ *
+ * Description:
+ * Submit the descriptor in g_desc to job ring zero and wait for it.
+ *
+ * Returned Value:
+ * Zero on success, -EIO if the ring reported an error, -ETIMEDOUT if it
+ * never answered.
+ *
+ *****************************************************************************/
+
+static int imxrt_caam_ring_init(void);
+
+static int imxrt_caam_run(void)
+{
+ uint32_t status;
+ int timeout;
+
+ imxrt_caam_clean(g_desc, sizeof(g_desc));
+
+ g_input_ring[0] = (uint32_t)(uintptr_t)g_desc;
+ imxrt_caam_clean(g_input_ring, sizeof(g_input_ring));
+
+ putreg32(1, IMXRT_CAAM_IRJA);
+
+ for (timeout = CAAM_TIMEOUT; timeout > 0; timeout--)
+ {
+ if (getreg32(IMXRT_CAAM_ORSF) != 0)
+ {
+ break;
+ }
+ }
+
+ if (timeout == 0)
+ {
+ _err("ERROR: job ring did not answer\n");
+ return -ETIMEDOUT;
+ }
+
+ imxrt_caam_invalidate(g_output_ring, sizeof(g_output_ring));
+ status = g_output_ring[1];
+
+ /* Tell the ring the slot is free again whatever the outcome, or the next
+ * request finds it still occupied.
+ */
+
+ putreg32(1, IMXRT_CAAM_ORJR);
+
+ if (status != 0)
+ {
+ _err("ERROR: job failed, status 0x%08" PRIx32 "\n", status);
+ return -EIO;
+ }
+
+ return OK;
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_kick_trng
+ *
+ * Description:
+ * Set the entropy sample length and the frequency limits derived from it,
+ * which is what the self test in the state handle instantiation checks
+ * against.
+ *
+ *****************************************************************************/
+
+static void imxrt_caam_kick_trng(uint32_t ent_delay)
+{
+ uint32_t val;
+
+ modifyreg32(IMXRT_CAAM_RTMCTL, 0, CAAM_RTMCTL_PRGM);
+
+ val = getreg32(IMXRT_CAAM_RTSDCTL) & ~CAAM_RTSDCTL_ENT_DLY_MASK;
+ putreg32(val | (ent_delay << CAAM_RTSDCTL_ENT_DLY_SHIFT),
+ IMXRT_CAAM_RTSDCTL);
+
+ putreg32(ent_delay >> 2, IMXRT_CAAM_RTFRQMIN);
+ putreg32(ent_delay << 4, IMXRT_CAAM_RTFRQMAX);
+
+ modifyreg32(IMXRT_CAAM_RTMCTL, CAAM_RTMCTL_PRGM, 0);
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_instantiate
+ *
+ * Description:
+ * Instantiate RNG state handle zero, generating the secure keys with it if
+ * nothing has done so since power on.
+ *
+ *****************************************************************************/
+
+static int imxrt_caam_instantiate(bool gen_sk)
+{
+ int words = 2;
+
+ g_desc[1] = CAAM_OP_RNG_INIT_SH0;
+
+ if (gen_sk)
+ {
+ g_desc[2] = CAAM_JUMP_WAIT_CLASS1;
+ g_desc[3] = CAAM_LOAD_CLRW;
+ g_desc[4] = 1;
+ g_desc[5] = CAAM_OP_RNG_GEN_SK;
+ words = 6;
+ }
+
+ g_desc[0] = CAAM_DESC_HDR(words);
+
+ return imxrt_caam_run();
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_rng_init
+ *****************************************************************************/
+
+static int imxrt_caam_rng_init(void)
+{
+ uint32_t ent_delay;
+ bool gen_sk;
+ int ret = -EIO;
+
+ if ((getreg32(IMXRT_CAAM_RDSTA) & CAAM_RDSTA_IF0) != 0)
+ {
+ return OK;
+ }
+
+ gen_sk = (getreg32(IMXRT_CAAM_RDSTA) & CAAM_RDSTA_SKVN) == 0;
+
+ for (ent_delay = CAAM_ENT_DELAY_MIN;
+ ent_delay <= CAAM_ENT_DELAY_MAX;
+ ent_delay += CAAM_ENT_DELAY_STEP)
+ {
+ int settle;
+
+ imxrt_caam_kick_trng(ent_delay);
+
+ ret = imxrt_caam_instantiate(gen_sk);
+
+ for (settle = CAAM_INSTANTIATE_SETTLE; settle > 0; settle--)
+ {
+ if ((getreg32(IMXRT_CAAM_RDSTA) & CAAM_RDSTA_IF0) != 0)
+ {
+ return OK;
+ }
+
+ up_udelay(100);
+ }
+
+ imxrt_caam_ring_init();
+
+ if ((getreg32(IMXRT_CAAM_RDSTA) & CAAM_RDSTA_IF0) != 0)
+ {
+ return OK;
+ }
+ }
+
+ _err("ERROR: RNG would not instantiate\n");
+ return ret < 0 ? ret : -EIO;
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_ring_init
+ *****************************************************************************/
+
+static int imxrt_caam_ring_init(void)
+{
+ int timeout;
+
+ putreg32(CAAM_JRCR_RESET, IMXRT_CAAM_JRCR);
+
+ for (timeout = CAAM_TIMEOUT; timeout > 0; timeout--)
+ {
+ if ((getreg32(IMXRT_CAAM_JRCR) & CAAM_JRCR_RESET) == 0)
+ {
+ break;
+ }
+ }
+
+ if (timeout == 0)
+ {
+ _err("ERROR: job ring would not reset\n");
+ return -ETIMEDOUT;
+ }
+
+ memset(g_input_ring, 0, sizeof(g_input_ring));
+ memset(g_output_ring, 0, sizeof(g_output_ring));
+ imxrt_caam_clean(g_input_ring, sizeof(g_input_ring));
+ imxrt_caam_clean(g_output_ring, sizeof(g_output_ring));
+
+ putreg32(0, IMXRT_CAAM_IRBA_H);
+ putreg32((uint32_t)(uintptr_t)g_input_ring, IMXRT_CAAM_IRBA_L);
+ putreg32(0, IMXRT_CAAM_ORBA_H);
+ putreg32((uint32_t)(uintptr_t)g_output_ring, IMXRT_CAAM_ORBA_L);
+ putreg32(CAAM_RING_ENTRIES, IMXRT_CAAM_IRS);
+ putreg32(CAAM_RING_ENTRIES, IMXRT_CAAM_ORS);
+
+ /* Completion is polled, so the ring interrupt is never wanted. */
+
+ modifyreg32(IMXRT_CAAM_JRCFG1, 0, CAAM_JRCFG1_IMSK);
+
+ return OK;
+}
+
+/*****************************************************************************
+ * Public Functions
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Name: imxrt_caam_initialize
+ *****************************************************************************/
+
+int imxrt_caam_initialize(void)
+{
+ int ret;
+
+ if (g_initialized)
+ {
+ return OK;
+ }
+
+ imxrt_clockall_caam();
+
+ modifyreg32(IMXRT_CAAM_MCFGR, CAAM_MCFGR_AWCACHE_MASK,
+ CAAM_MCFGR_AWCACHE_CACH | CAAM_MCFGR_AWCACHE_BUFF |
+ CAAM_MCFGR_WDE | CAAM_MCFGR_LARGE_BURST);
+
+ modifyreg32(IMXRT_CAAM_JRSTART, 0, CAAM_JRSTART_JR0);
+
+ ret = imxrt_caam_ring_init();
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ ret = imxrt_caam_rng_init();
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ g_initialized = true;
+ return OK;
+}
+
+/*****************************************************************************
+ * Name: imxrt_caam_get_random
+ *****************************************************************************/
+
+int imxrt_caam_get_random(uint8_t *buffer, size_t buflen)
+{
+ size_t done = 0;
+ int ret;
+
+ if (buffer == NULL || buflen == 0)
+ {
+ return -EINVAL;
+ }
+
+ ret = imxrt_caam_initialize();
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ while (done < buflen)
+ {
+ size_t chunk = buflen - done;
+
+ if (chunk > sizeof(g_rngbuf))
+ {
+ chunk = sizeof(g_rngbuf);
+ }
+
+ memset(g_rngbuf, 0, sizeof(g_rngbuf));
+ imxrt_caam_clean(g_rngbuf, sizeof(g_rngbuf));
+
+ g_desc[0] = CAAM_DESC_HDR(4);
+ g_desc[1] = CAAM_OP_RNG_GENERATE;
+ g_desc[2] = CAAM_FIFO_STORE_RNG | sizeof(g_rngbuf);
+ g_desc[3] = (uint32_t)(uintptr_t)g_rngbuf;
+
+ ret = imxrt_caam_run();
+ if (ret < 0)
+ {
+ memset(buffer, 0, buflen);
+ return ret;
+ }
+
+ imxrt_caam_invalidate(g_rngbuf, sizeof(g_rngbuf));
+ memcpy(buffer + done, g_rngbuf, chunk);
+ done += chunk;
+ }
+
+ /* Leave nothing behind for the next caller to find. */
+
+ memset(g_rngbuf, 0, sizeof(g_rngbuf));
+ return OK;
+}
+
+#endif /* CONFIG_IMXRT_CAAM */
diff --git a/arch/arm/src/imxrt/imxrt_caam.h b/arch/arm/src/imxrt/imxrt_caam.h
new file mode 100644
index 00000000000..663e26ab9d7
--- /dev/null
+++ b/arch/arm/src/imxrt/imxrt_caam.h
@@ -0,0 +1,85 @@
+/*****************************************************************************
+ * arch/arm/src/imxrt/imxrt_caam.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ *****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_IMXRT_IMXRT_CAAM_H
+#define __ARCH_ARM_SRC_IMXRT_IMXRT_CAAM_H
+
+/*****************************************************************************
+ * Included Files
+ *****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <stddef.h>
+#include <stdint.h>
+
+/*****************************************************************************
+ * Public Function Prototypes
+ *****************************************************************************/
+
+#undef EXTERN
+#if defined(__cplusplus)
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/*****************************************************************************
+ * Name: imxrt_caam_initialize
+ *
+ * Description:
+ * Bring up job ring zero and, if the boot ROM has not already done so,
+ * instantiate the RNG state handle. Idempotent.
+ *
+ * Returned Value:
+ * Zero on success, a negated errno on failure.
+ *
+ *****************************************************************************/
+
+int imxrt_caam_initialize(void);
+
+/*****************************************************************************
+ * Name: imxrt_caam_get_random
+ *
+ * Description:
+ * Fill a buffer from the CAAM true random number generator.
+ *
+ * Input Parameters:
+ * buffer - Where to put the bytes
+ * buflen - How many to fetch
+ *
+ * Returned Value:
+ * Zero on success, a negated errno on failure. On failure the buffer
+ * holds nothing a caller may use.
+ *
+ *****************************************************************************/
+
+int imxrt_caam_get_random(uint8_t *buffer, size_t buflen);
+
+#undef EXTERN
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* __ARCH_ARM_SRC_IMXRT_IMXRT_CAAM_H */
diff --git a/arch/arm/src/imxrt/imxrt_periphclks.h
b/arch/arm/src/imxrt/imxrt_periphclks.h
index d8aaaeb354b..ccbec63274b 100644
--- a/arch/arm/src/imxrt/imxrt_periphclks.h
+++ b/arch/arm/src/imxrt/imxrt_periphclks.h
@@ -64,6 +64,9 @@
#define imxrt_clockoff_aoi1()
imxrt_periphclk_configure(CCM_CCGR_AOI1, CCM_CG_OFF)
#define imxrt_clockoff_aoi2()
imxrt_periphclk_configure(CCM_CCGR_AOI2, CCM_CG_OFF)
#define imxrt_clockoff_bee()
imxrt_periphclk_configure(CCM_CCGR_BEE, CCM_CG_OFF)
+#ifdef CONFIG_ARCH_FAMILY_IMXRT117x
+# define imxrt_clockoff_caam()
imxrt_periphclk_configure(CCM_CCGR_CAAM, CCM_CG_OFF)
+#endif
#define imxrt_clockoff_can1()
imxrt_periphclk_configure(CCM_CCGR_CAN1, CCM_CG_OFF)
#ifndef CONFIG_ARCH_FAMILY_IMXRT117x
# define imxrt_clockoff_can1_serial()
imxrt_periphclk_configure(CCM_CCGR_CAN1_SERIAL, CCM_CG_OFF)
@@ -230,6 +233,9 @@
#define imxrt_clockrun_aoi1()
imxrt_periphclk_configure(CCM_CCGR_AOI1, CCM_CG_RUN)
#define imxrt_clockrun_aoi2()
imxrt_periphclk_configure(CCM_CCGR_AOI2, CCM_CG_RUN)
#define imxrt_clockrun_bee()
imxrt_periphclk_configure(CCM_CCGR_BEE, CCM_CG_RUN)
+#ifdef CONFIG_ARCH_FAMILY_IMXRT117x
+# define imxrt_clockrun_caam()
imxrt_periphclk_configure(CCM_CCGR_CAAM, CCM_CG_RUN)
+#endif
#define imxrt_clockrun_can1()
imxrt_periphclk_configure(CCM_CCGR_CAN1, CCM_CG_RUN)
#ifndef CONFIG_ARCH_FAMILY_IMXRT117x
# define imxrt_clockrun_can1_serial()
imxrt_periphclk_configure(CCM_CCGR_CAN1_SERIAL, CCM_CG_RUN)
@@ -398,6 +404,9 @@
#define imxrt_clockall_aoi1()
imxrt_periphclk_configure(CCM_CCGR_AOI1, CCM_CG_ALL)
#define imxrt_clockall_aoi2()
imxrt_periphclk_configure(CCM_CCGR_AOI2, CCM_CG_ALL)
#define imxrt_clockall_bee()
imxrt_periphclk_configure(CCM_CCGR_BEE, CCM_CG_ALL)
+#ifdef CONFIG_ARCH_FAMILY_IMXRT117x
+# define imxrt_clockall_caam()
imxrt_periphclk_configure(CCM_CCGR_CAAM, CCM_CG_ALL)
+#endif
#define imxrt_clockall_can1()
imxrt_periphclk_configure(CCM_CCGR_CAN1, CCM_CG_ALL)
#ifndef CONFIG_ARCH_FAMILY_IMXRT117x
# define imxrt_clockall_can1_serial()
imxrt_periphclk_configure(CCM_CCGR_CAN1_SERIAL, CCM_CG_ALL)
diff --git a/arch/arm/src/imxrt/imxrt_rng.c b/arch/arm/src/imxrt/imxrt_rng.c
new file mode 100644
index 00000000000..f71c132f5fb
--- /dev/null
+++ b/arch/arm/src/imxrt/imxrt_rng.c
@@ -0,0 +1,286 @@
+/****************************************************************************
+ * arch/arm/src/imxrt/imxrt_rng.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <assert.h>
+#include <debug.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <nuttx/drivers/drivers.h>
+#include <nuttx/fs/fs.h>
+#include <nuttx/mutex.h>
+
+#include <chip.h>
+
+#include "arm_internal.h"
+#include "imxrt_caam.h"
+
+#if defined(CONFIG_IMXRT_RNG)
+#if defined(CONFIG_DEV_RANDOM) || defined(CONFIG_DEV_URANDOM_ARCH)
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+#if !defined(ARMV7M_DCACHE_LINESIZE) || ARMV7M_DCACHE_LINESIZE == 0
+# undef ARMV7M_DCACHE_LINESIZE
+# define ARMV7M_DCACHE_LINESIZE 32
+#endif
+
+/* CAAM writes the result by DMA, so the landing buffer is a whole number
+ * of cache lines and nothing else shares them.
+ */
+
+#define RNG_BLOCKLEN ARMV7M_DCACHE_LINESIZE
+
+/* Prefilled before every request. Zero could not be told apart from an
+ * a block of genuine zeros, so an untouched buffer reports separately.
+ */
+
+#define RNG_FILL 0xaa
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static ssize_t imxrt_rng_read(struct file *filep, char *buffer, size_t
+ buflen);
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+struct rng_dev_s
+{
+ mutex_t rd_devlock; /* Exclusive access to the ring */
+ uint8_t rd_lastval[RNG_BLOCKLEN]; /* Previous block, FIPS test */
+ bool rd_first; /* No previous block yet */
+};
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static struct rng_dev_s g_rngdev =
+{
+ .rd_devlock = NXMUTEX_INITIALIZER,
+ .rd_first = true,
+};
+
+static uint8_t g_rngbuf[RNG_BLOCKLEN]
+ aligned_data(ARMV7M_DCACHE_LINESIZE);
+
+static const struct file_operations g_rngops =
+{
+ NULL, /* open */
+ NULL, /* close */
+ imxrt_rng_read, /* read */
+};
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: imxrt_rng_all
+ *
+ * Description:
+ * Rejecting a constant block costs nothing, since a genuine one has
+ * probability 2^-512, and it covers the first block, which the continuous
+ * test below cannot.
+ *
+ ****************************************************************************/
+
+static bool imxrt_rng_all(const uint8_t *buf, size_t len, uint8_t val)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ {
+ if (buf[i] != val)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/****************************************************************************
+ * Name: imxrt_rng_block
+ *
+ * Description:
+ * Fetch one RNG_BLOCKLEN block from CAAM into g_rngbuf and check it.
+ *
+ * Returned Value:
+ * Zero on success, a negated errno on failure. Never returns a block
+ * that failed a health check.
+ *
+ ****************************************************************************/
+
+static int imxrt_rng_block(void)
+{
+ int ret;
+
+ memset(g_rngbuf, RNG_FILL, sizeof(g_rngbuf));
+
+ ret = imxrt_caam_get_random(g_rngbuf, sizeof(g_rngbuf));
+ if (ret < 0)
+ {
+ _err("ERROR: CAAM random request failed: %d\n", ret);
+ return ret;
+ }
+
+ if (imxrt_rng_all(g_rngbuf, sizeof(g_rngbuf), RNG_FILL))
+ {
+ _err("ERROR: buffer untouched; the CAAM write never reached here\n");
+ return -EIO;
+ }
+
+ if (imxrt_rng_all(g_rngbuf, sizeof(g_rngbuf), 0))
+ {
+ _err("ERROR: CAAM returned an all-zero block\n");
+ return -EIO;
+ }
+
+ /* FIPS 140-2 continuous test: a repeat means the source has stalled. */
+
+ if (g_rngdev.rd_first)
+ {
+ g_rngdev.rd_first = false;
+ }
+ else if (memcmp(g_rngdev.rd_lastval, g_rngbuf, sizeof(g_rngbuf)) == 0)
+ {
+ _err("ERROR: CAAM repeated a block\n");
+ return -EIO;
+ }
+
+ memcpy(g_rngdev.rd_lastval, g_rngbuf, sizeof(g_rngbuf));
+ return OK;
+}
+
+/****************************************************************************
+ * Name: imxrt_rng_read
+ ****************************************************************************/
+
+static ssize_t imxrt_rng_read(struct file *filep, char *buffer,
+ size_t buflen)
+{
+ size_t done = 0;
+ int ret;
+
+ ret = nxmutex_lock(&g_rngdev.rd_devlock);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ while (done < buflen)
+ {
+ size_t chunk = buflen - done;
+
+ ret = imxrt_rng_block();
+ if (ret < 0)
+ {
+ /* A short read is a lie about how much entropy the caller got, so
+ * report the failure unless some was already delivered.
+ */
+
+ nxmutex_unlock(&g_rngdev.rd_devlock);
+ return done > 0 ? (ssize_t)done : ret;
+ }
+
+ if (chunk > sizeof(g_rngbuf))
+ {
+ chunk = sizeof(g_rngbuf);
+ }
+
+ memcpy(buffer + done, g_rngbuf, chunk);
+ done += chunk;
+ }
+
+ /* Leave nothing behind for the next caller to find. */
+
+ memset(g_rngbuf, 0, sizeof(g_rngbuf));
+
+ nxmutex_unlock(&g_rngdev.rd_devlock);
+ return (ssize_t)done;
+}
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: devrandom_register
+ *
+ * Description:
+ * Register the /dev/random driver, backed by the CAAM true random
+ * number generator. Must be called BEFORE devurandom_register.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEV_RANDOM
+void devrandom_register(void)
+{
+ register_driver("/dev/random", &g_rngops, 0444, NULL);
+}
+#endif
+
+/****************************************************************************
+ * Name: devurandom_register
+ *
+ * Description:
+ * Register /dev/urandom. CAAM is the source for both nodes: it is a
+ * hardware generator, so there is nothing weaker to offer here.
+ *
+ * Input Parameters:
+ * None
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_DEV_URANDOM_ARCH
+void devurandom_register(void)
+{
+ register_driver("/dev/urandom", &g_rngops, 0444, NULL);
+}
+#endif
+
+#endif /* CONFIG_DEV_RANDOM || CONFIG_DEV_URANDOM_ARCH */
+#endif /* CONFIG_IMXRT_RNG */