// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright 2018 NXP * Copyright (C) 2018 Pengutronix, Roland Hieber * * CAAM RNG self-test -- based on the vendor patch for U-Boot: * https://portland.source.codeaurora.org/patches/external/imxsupport/uboot-imx/imx_v2016.03_4.1.15_2.0.0_ga/HAB-238-Run-RNG-self-test-for-impacted-i.MX-chips.zip * * | From: Utkarsh Gupta * | Subject: [PATCH] HAB-238 Run RNG self test for impacted i.MX chips * | * | Patch is only applicable to imx_v2016.03_4.1.15_2.0.0_ga branch of u-boot. * | Please adapt the patch for your respective release version. * | * | Background: * | Few i.MX chips which have HAB 4.2.3 or beyond, have oberserved following * | warning message generated by HAB due to incorrect implementation of drng * | self test in boot ROM. * | * | Event |0xdb|0x0024|0x42| SRCE Field: 69 30 e1 1d * | | | | | STS = HAB_WARNING (0x69) * | | | | | RSN = HAB_ENG_FAIL (0x30) * | | | | | CTX = HAB_CTX_ENTRY (0xE1) * | | | | | ENG = HAB_ENG_CAAM (0x1D) * | | | | | Evt Data (hex): * | | | | | 00 08 00 02 40 00 36 06 55 55 00 03 00 00 00 00 * | | | | | 00 00 00 00 00 00 00 00 00 00 00 01 * | * | It is recommended to run this rng self test before any RNG related crypto * | implementations are done. * [...] * | * | Signed-off-by: Utkarsh Gupta * * Known impacted chips: * * - i.MX6DQ+ silicon revision 1.1 * - i.MX6DQ silicon revision 1.6 * - i.MX6DLS silicon revision 1.4 * - i.MX6SX silicon revision 1.4 * - i.MX6UL silicon revision 1.2 * - i.MX67SD silicon revision 1.3 */ #define pr_fmt(fmt) "rng_self_test: " fmt #include #include #include #include "error.h" #include "regs.h" #include "jr.h" #include "rng_self_test.h" static const u32 rng_dsc1[] = { 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_result1[] = { 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 }; static const u32 rng_dsc2[] = { 0xb080003a, 0x04800020, 0x27b73130, 0x30b4b10f, 0x7c62b1ad, 0x77abe899, 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0x02800020, 0x63f757cf, 0xb9165584, 0xc3c1b407, 0xcc4ce8ad, 0x1ffe8a58, 0xfb4fa893, 0xbb5f4af0, 0x3fb946a1, 0x12a00020, 0x56cbcaa5, 0xfff3adad, 0xe804dcbf, 0x9a900c71, 0xa42017e3, 0x826948e2, 0xd0cfeb3e, 0xaf1a136a, 0x82500405, 0xa2000001, 0x10880004, 0x00000005, 0x12820004, 0x00000020, 0x82500001, 0xa2000001, 0x10880004, 0x40000045, 0x02800020, 0x2e882f8a, 0xe929943e, 0x8132c0a8, 0x12037f90, 0x809fbd66, 0x8684ea04, 0x00cbafa7, 0x7b82d12a, 0x82500009, 0xa2000001, 0x10880004, 0x00000005, 0x82500001, 0x60340020, 0xFFFFFFFF, 0xa2000001, 0x10880004, 0x00000005, 0x8250000d }; static const u8 rng_result2[] = { 0x76, 0x87, 0x66, 0x4e, 0xd8, 0x1d, 0x1f, 0x43, 0x76, 0x50, 0x85, 0x5d, 0x1e, 0x1d, 0x9d, 0x0f, 0x93, 0x75, 0x83, 0xff, 0x9a, 0x9b, 0x61, 0xa9, 0xa5, 0xeb, 0xa3, 0x28, 0x2a, 0x15, 0xc1, 0x57 }; /* * construct_rng_self_test_jobdesc() - Implement destination address in RNG self test descriptors * Returns zero on success, and negative on error. */ static void construct_rng_self_test_jobdesc(u32 *desc, const u32 *rng_st_dsc, u8 *res_addr, int desc_size) { int result_addr_idx = desc_size - 5; int i; for (i = 0; i < desc_size; i++) { desc[i] = rng_st_dsc[i]; } /* Replace destination address in the descriptor */ desc[result_addr_idx] = (u32)res_addr; } /* rng_self_test_done() - callback for caam_jr_enqueue */ static void rng_self_test_done(struct device_d *dev, u32 *desc, u32 err, void *arg) { int * job_err = arg; *job_err = err; } /* * caam_rng_self_test() - Perform RNG self test * Returns zero on success, and negative on error. */ int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid, const u8 rngrev) { int ret, desc_size = 0, result_size = 0, job_err = 0; const u32 *rng_st_dsc; const u8 *exp_result; u32 *desc; u8 *result; pr_debug("got CAAM ERA %d, RNG Version ID %d, RNG revision %d\n", caam_era, rngvid, rngrev); if (caam_era < 8 && rngvid == 4 && rngrev < 3) { /* older affected i.MX chipsets have CAAM < 8 and have RNG4 < 4.3 */ rng_st_dsc = rng_dsc1; desc_size = ARRAY_SIZE(rng_dsc1); exp_result = rng_result1; result_size = ARRAY_SIZE(rng_result1); } else if (caam_era >= 8 || (rngvid >= 4 && rngrev >= 3)) { /* newer affected chipsets have CAAM >= 8 or RNG4 >= 4.3 */ rng_st_dsc = rng_dsc2; desc_size = ARRAY_SIZE(rng_dsc2); exp_result = rng_result2; result_size = ARRAY_SIZE(rng_result2); } else { pr_err("Invalid CAAM version: %d,%d,%d\n", caam_era, rngvid, rngrev); return -EINVAL; } result = dma_alloc(sizeof(*result) * result_size); desc = dma_alloc(sizeof(*desc) * desc_size); if (!result || !desc) { ret = -ENOMEM; goto err; } construct_rng_self_test_jobdesc(desc, rng_st_dsc, result, desc_size); dma_sync_single_for_device((unsigned long)desc, desc_size * sizeof(*desc), DMA_TO_DEVICE); dma_sync_single_for_device((unsigned long)result, result_size * sizeof(*result), DMA_FROM_DEVICE); /* wait for job completion */ ret = caam_jr_enqueue(dev, desc, rng_self_test_done, &job_err); if (ret) { pr_err("Running RNG self-test descriptor failed: %d %s\n", ret, strerror(ret)); goto err; } if (job_err) { ret = -EINVAL; pr_err("Job Error:\n"); caam_jr_strstatus(dev, job_err); goto err; } dma_sync_single_for_cpu((unsigned long)result, result_size * sizeof(*result), DMA_FROM_DEVICE); if (memcmp(result, exp_result, sizeof(*result) * result_size) != 0) { pr_err("RNG self-test failed with unexpected result\n"); ret = -ERANGE; goto err; } pr_info("RNG software self-test passed\n"); ret = 0; err: dma_free(desc); dma_free(result); return ret; }