The boot ROM of a few i.MX6 revisions runs the RNG self test incorrectly.
It leaves a HAB event behind on every boot:

    STS = HAB_WARNING (0x69)   RSN = HAB_ENG_FAIL (0x30)
    CTX = HAB_CTX_ENTRY (0xE1) ENG = HAB_ENG_CAAM (0x1D)

NXP recommends to run a rng self test before any RNG related
crypto implementations are done.

Affected are i.MX6Solo, i.MX6DL and i.MX6SX revision 1.4, i.MX6Q
revision 1.6 and i.MX6QP revision 1.1, and no other revision of those
chips. Both chip and revision are read at runtime and have to match.

Run this test on affected chips and stop if test fails.

Part of the code is taken from u-boot-imx NXP tree, from the commit
linked below, and adapted to mainline.

Link: 
https://github.com/nxp-imx/uboot-imx/commit/541284a3c55f90446ca179b26500f93ed201eb96
Signed-off-by: Utkarsh Gupta <[email protected]>
Signed-off-by: Gaurav Jain <[email protected]>
Signed-off-by: Stefan Bigler <[email protected]>
Signed-off-by: Heiko Schocher <[email protected]>
---
Rework against NXP base:
- adapt the chip detection
- checkpatch cleanup
- Kconfig symbol renamed and set a default

It may be worth discussing whether triggering a panic() and bringing the entire
system to a halt is appropriate in this case.

 arch/arm/include/asm/arch-imx/cpu.h |   2 +
 drivers/crypto/fsl/Kconfig          |  22 ++++
 drivers/crypto/fsl/Makefile         |   1 +
 drivers/crypto/fsl/jr.c             |   3 +
 drivers/crypto/fsl/jr.h             |   1 +
 drivers/crypto/fsl/rng_self_test.c  | 157 ++++++++++++++++++++++++++++
 6 files changed, 186 insertions(+)
 create mode 100644 drivers/crypto/fsl/rng_self_test.c

diff --git a/arch/arm/include/asm/arch-imx/cpu.h 
b/arch/arm/include/asm/arch-imx/cpu.h
index bbc4b421a02..f2225df9519 100644
--- a/arch/arm/include/asm/arch-imx/cpu.h
+++ b/arch/arm/include/asm/arch-imx/cpu.h
@@ -95,7 +95,9 @@
 #define CHIP_REV_1_1            0x11
 #define CHIP_REV_1_2            0x12
 #define CHIP_REV_1_3            0x13
+#define CHIP_REV_1_4            0x14
 #define CHIP_REV_1_5            0x15
+#define CHIP_REV_1_6            0x16
 #define CHIP_REV_2_0            0x20
 #define CHIP_REV_2_1            0x21
 #define CHIP_REV_2_2            0x22
diff --git a/drivers/crypto/fsl/Kconfig b/drivers/crypto/fsl/Kconfig
index 244a9bd905d..41de402c530 100644
--- a/drivers/crypto/fsl/Kconfig
+++ b/drivers/crypto/fsl/Kconfig
@@ -100,4 +100,26 @@ config FSL_DCP_RNG
          and a Pseudo-Random Number Generator (PRNG) to achieve a true
          randomness and cryptographic strength.
 
+config FSL_CAAM_RNG_SELF_TEST
+       bool "Run the CAAM RNG self test on affected i.MX6 chips"
+       depends on FSL_CAAM
+       default y if MX6S || MX6DL || MX6Q || MX6QDL || MX6SX
+       help
+         The boot ROM of a few i.MX6 revisions runs the RNG self test
+         incorrectly and leaves a HAB warning behind, which reads
+
+           STS = HAB_WARNING (0x69)   RSN = HAB_ENG_FAIL (0x30)
+           CTX = HAB_CTX_ENTRY (0xE1) ENG = HAB_ENG_CAAM (0x1D)
+
+         The warning itself is harmless and this option does not make it
+         go away. What it does is run the self test correctly, before
+         anything else uses the RNG, so that a genuinely broken RNG is
+         found rather than assumed to be the known warning. The board is
+         stopped if the test fails.
+
+         Affected are i.MX6Solo, i.MX6DL and i.MX6SX revision 1.4, i.MX6Q
+         revision 1.6 and i.MX6QP revision 1.1, and no other revision of
+         those chips. Chip and revision are read at runtime, so a build
+         covering several of them behaves correctly on each.
+
 endif
diff --git a/drivers/crypto/fsl/Makefile b/drivers/crypto/fsl/Makefile
index 965c4938c8c..14b53db7844 100644
--- a/drivers/crypto/fsl/Makefile
+++ b/drivers/crypto/fsl/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_RSA_FREESCALE_EXP) += fsl_rsa.o
 obj-$(CONFIG_$(PHASE_)FSL_CAAM_RNG) += rng.o
 obj-$(CONFIG_FSL_DCP_RNG) += dcp_rng.o
 obj-$(CONFIG_FSL_MFGPROT) += fsl_mfgprot.o
+obj-$(CONFIG_FSL_CAAM_RNG_SELF_TEST) += rng_self_test.o
diff --git a/drivers/crypto/fsl/jr.c b/drivers/crypto/fsl/jr.c
index 07d336c074a..3a5032bcaf6 100644
--- a/drivers/crypto/fsl/jr.c
+++ b/drivers/crypto/fsl/jr.c
@@ -804,6 +804,9 @@ init:
        pamu_enable();
 #endif
 
+       if (IS_ENABLED(CONFIG_FSL_CAAM_RNG_SELF_TEST))
+               rng_self_test();
+
        if (get_rng_vid(caam->sec) >= 4) {
                if (rng_init(sec_idx, caam->sec) < 0) {
                        printf("SEC%u:  RNG instantiation failed\n", sec_idx);
diff --git a/drivers/crypto/fsl/jr.h b/drivers/crypto/fsl/jr.h
index b136cd8d05a..d8bd0797331 100644
--- a/drivers/crypto/fsl/jr.h
+++ b/drivers/crypto/fsl/jr.h
@@ -132,4 +132,5 @@ struct caam_regs {
 void caam_jr_strstatus(u32 status);
 int run_descriptor_jr(uint32_t *desc);
 
+void rng_self_test(void);
 #endif
diff --git a/drivers/crypto/fsl/rng_self_test.c 
b/drivers/crypto/fsl/rng_self_test.c
new file mode 100644
index 00000000000..7dabdc26551
--- /dev/null
+++ b/drivers/crypto/fsl/rng_self_test.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * CAAM RNG self test for the i.MX6 chips whose boot ROM gets it wrong
+ *
+ * Copyright 2018, 2021 NXP
+ * Copyright (C) 2026 Stefan Bigler, Securiton AG
+ * Copyright (C) 2026 Heiko Schocher, Nabla Software Engineering 
<[email protected]>
+ */
+
+#include <cpu_func.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <vsprintf.h>
+#include <asm/arch/sys_proto.h>
+#include <linux/kernel.h>
+#include "jr.h"
+
+/* Number of words in the descriptor below */
+#define RNG_DSC_SIZE           0x36
+
+/* Size of the known answer the descriptor produces */
+#define RNG_KAT_SIZE           32
+
+/*
+ * Descriptor of the self test, taken from the NXP tree. The word five
+ * from the end holds the address the result is written to and is filled
+ * in before the descriptor is submitted.
+ */
+#define RNG_DSC_RESULT_IDX     (RNG_DSC_SIZE - 5)
+
+static const u32 rng_dsc[RNG_DSC_SIZE] = {
+       0xb0800036, 0x04800010, 0x3c85a15b, 0x50a9d0b1,
+       0x71a09fee, 0x2eecf20b, 0x02800020, 0xb267292e,
+       0x85bf712d, 0xe85ff43a, 0xa716b7fb, 0xc40bb528,
+       0x27b6f564, 0x8821cb5d, 0x9b5f6c26, 0x12a00020,
+       0x0a20de17, 0x6529357e, 0x316277ab, 0x2846254e,
+       0x34d23ba5, 0x6f5e9c32, 0x7abdc1bb, 0x0197a385,
+       0x82500405, 0xa2000001, 0x10880004, 0x00000005,
+       0x12820004, 0x00000020, 0x82500001, 0xa2000001,
+       0x10880004, 0x40000045, 0x02800020, 0x8f389cc7,
+       0xe7f7cbb0, 0x6bf2073d, 0xfc380b6d, 0xb22e9d1a,
+       0xee64fcb7, 0xa2b48d49, 0xdf9bc3a4, 0x82500009,
+       0xa2000001, 0x10880004, 0x00000005, 0x82500001,
+       0x60340020, 0xFFFFFFFF, 0xa2000001, 0x10880004,
+       0x00000005, 0x8250000d
+};
+
+static const u8 rng_result[RNG_KAT_SIZE] = {
+       0x3a, 0xfe, 0x2c, 0x87, 0xcc, 0xb6, 0x44, 0x49,
+       0x19, 0x16, 0x9a, 0x74, 0xa1, 0x31, 0x8b, 0xef,
+       0xf4, 0x86, 0x0b, 0xb9, 0x5e, 0xee, 0xae, 0x91,
+       0x92, 0xf4, 0xa9, 0x8f, 0xb0, 0x37, 0x18, 0xa4
+};
+
+/**
+ * rng_self_test_jobdesc() - build the self test descriptor
+ *
+ * @desc:      descriptor to fill in, RNG_DSC_SIZE words
+ * @result:    where the CAAM is to deposit the generated data
+ */
+static void rng_self_test_jobdesc(u32 *desc, u8 *result)
+{
+       uint i;
+
+       for (i = 0; i < RNG_DSC_SIZE; i++)
+               desc[i] = rng_dsc[i];
+
+       desc[RNG_DSC_RESULT_IDX] = (uintptr_t)result;
+}
+
+/**
+ * rng_test() - run the self test once and compare against the known answer
+ *
+ * A failure means the RNG cannot be trusted, so the board is stopped
+ * rather than left to carry on with keys derived from it.
+ */
+static void rng_test(void)
+{
+       u8 *result;
+       u32 *desc;
+       uint size;
+       int ret, i;
+
+       result = memalign(ARCH_DMA_MINALIGN, RNG_KAT_SIZE);
+       if (!result) {
+               puts("RNG:   not enough memory for the result\n");
+               return;
+       }
+
+       desc = memalign(ARCH_DMA_MINALIGN, sizeof(u32) * RNG_DSC_SIZE);
+       if (!desc) {
+               puts("RNG:   not enough memory for the descriptor\n");
+               free(result);
+               return;
+       }
+
+       rng_self_test_jobdesc(desc, result);
+
+       size = roundup(sizeof(u32) * RNG_DSC_SIZE, ARCH_DMA_MINALIGN);
+       flush_dcache_range((ulong)desc, (ulong)desc + size);
+       size = roundup(RNG_KAT_SIZE, ARCH_DMA_MINALIGN);
+       flush_dcache_range((ulong)result, (ulong)result + size);
+
+       ret = run_descriptor_jr(desc);
+       if (ret) {
+               printf("RNG:   self test descriptor failed: %d\n", ret);
+               goto err;
+       }
+
+       invalidate_dcache_range((ulong)result, (ulong)result + size);
+
+       for (i = 0; i < RNG_KAT_SIZE; i++)
+               debug("%02x", result[i]);
+       debug("\n");
+
+       if (memcmp(result, rng_result, RNG_KAT_SIZE))
+               goto err;
+
+       free(desc);
+       free(result);
+       puts("RNG:   self test passed\n");
+
+       return;
+
+err:
+       free(desc);
+       free(result);
+       panic("RNG:   self test failed\n");
+}
+
+/**
+ * rng_self_test_needed() - is this chip one of the affected ones?
+ *
+ * Chip and revision both have to match one of the pairs that NXP names
+ * as impacted, and no other revision of those chips is on that list.
+ */
+static bool rng_self_test_needed(void)
+{
+       if (is_cpu_type(MXC_CPU_MX6QP))
+               return soc_rev() == CHIP_REV_1_1;
+
+       if (is_cpu_type(MXC_CPU_MX6Q))
+               return soc_rev() == CHIP_REV_1_6;
+
+       if (is_cpu_type(MXC_CPU_MX6SOLO) || is_cpu_type(MXC_CPU_MX6DL) ||
+           is_cpu_type(MXC_CPU_MX6SX))
+               return soc_rev() == CHIP_REV_1_4;
+
+       return false;
+}
+
+void rng_self_test(void)
+{
+       if (rng_self_test_needed())
+               rng_test();
+}
-- 
2.55.0

Reply via email to