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 */

Reply via email to