diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig index 2ab509d..56b9070 100644 --- a/drivers/crypto/caam/Kconfig +++ b/drivers/crypto/caam/Kconfig @@ -33,3 +33,27 @@ default y help Selecting this will register the SEC4 hardware rng. + +if CRYPTO_DEV_FSL_CAAM_RNG + +config CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST + bool "Run RNG software self-test on impacted chips" + depends on ARCH_IMX6 + depends on HABV4 + default y + help + Some chips with HAB >= 4.2.3 have an incorrect implementation of the + RNG self-test in ROM code. In this case, a software self-test should + be run to ensure correctness of the RNG. By enabling this config + option, the software self-test is run automatically when this case + is detected. + + Currently 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 + +endif diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile index 74d32da..7bd6f3e 100644 --- a/drivers/crypto/caam/Makefile +++ b/drivers/crypto/caam/Makefile @@ -3,3 +3,4 @@ # obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += ctrl.o error.o jr.o obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG) += caamrng.o +obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) += rng_self_test.o diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c index 9e62bd6..3991013 100644 --- a/drivers/crypto/caam/ctrl.c +++ b/drivers/crypto/caam/ctrl.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,7 @@ #include "desc_constr.h" #include "error.h" #include "ctrl.h" +#include "rng_self_test.h" bool caam_little_end; EXPORT_SYMBOL(caam_little_end); @@ -570,6 +572,24 @@ cha_vid_ls = rd_reg32(&ctrl->perfmon.cha_id_ls); + /* habv4_need_rng_software_self_test is determined by habv4_get_status() */ + if (IS_ENABLED(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) && + habv4_need_rng_software_self_test) { + u8 caam_era; + u8 rngvid; + u8 rngrev; + + caam_era = (rd_reg32(&ctrl->perfmon.ccb_id) & CCBVID_ERA_MASK) >> CCBVID_ERA_SHIFT; + rngvid = (cha_vid_ls & CHAVID_LS_RNGVID_MASK) >> CHAVID_LS_RNGVID_SHIFT; + rngrev = (rd_reg32(&ctrl->perfmon.cha_rev_ls) & CRNR_LS_RNGRN_MASK) >> CRNR_LS_RNGRN_SHIFT; + + ret = caam_rng_self_test(ctrlpriv->jrpdev[0], caam_era, rngvid, rngrev); + if (ret != 0) { + caam_remove(dev); + return ret; + } + } + /* * If SEC has RNG version >= 4 and RNG state handle has not been * already instantiated, do RNG instantiation diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h index 6c9d6d7..19e7d6d 100644 --- a/drivers/crypto/caam/regs.h +++ b/drivers/crypto/caam/regs.h @@ -279,6 +279,8 @@ /* CAAM Hardware Instantiation Parameters fa0-fbf */ u32 cha_rev_ms; /* CRNR - CHA Rev No. Most significant half*/ +#define CRNR_LS_RNGRN_SHIFT 16 +#define CRNR_LS_RNGRN_MASK (0xfull << CRNR_LS_RNGRN_SHIFT) u32 cha_rev_ls; /* CRNR - CHA Rev No. Least significant half*/ #define CTPR_MS_QI_SHIFT 25 #define CTPR_MS_QI_MASK (0x1ull << CTPR_MS_QI_SHIFT) @@ -311,6 +313,8 @@ #define CCBVID_ERA_SHIFT 24 u32 ccb_id; /* CCBVID - CCB Version ID */ u32 cha_id_ms; /* CHAVID - CHA Version ID Most Significant*/ +#define CHAVID_LS_RNGVID_SHIFT 16 +#define CHAVID_LS_RNGVID_MASK (0xfull << CHAVID_LS_RNGVID_SHIFT) u32 cha_id_ls; /* CHAVID - CHA Version ID Least Significant*/ u32 cha_num_ms; /* CHANUM - CHA Number Most Significant */ u32 cha_num_ls; /* CHANUM - CHA Number Least Significant*/ diff --git a/drivers/crypto/caam/rng_self_test.c b/drivers/crypto/caam/rng_self_test.c new file mode 100644 index 0000000..aab4fa2 --- /dev/null +++ b/drivers/crypto/caam/rng_self_test.c @@ -0,0 +1,206 @@ +// 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" + +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; +} diff --git a/drivers/crypto/caam/rng_self_test.h b/drivers/crypto/caam/rng_self_test.h new file mode 100644 index 0000000..1f5bf32 --- /dev/null +++ b/drivers/crypto/caam/rng_self_test.h @@ -0,0 +1,24 @@ +/* + * CAAM RNG self test + * + * Copyright (C) 2018 Pengutronix, Roland Hieber + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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. + */ + +#ifndef RNG_SELF_TEST_H +#define RNG_SELF_TEST_H + +int caam_rng_self_test(struct device_d *dev, const u8 caam_era, const u8 rngvid, const u8 rngrev); + +#endif /* RNG_SELF_TEST_H */ diff --git a/drivers/hab/habv4.c b/drivers/hab/habv4.c index 28fd42e..aa9506c 100644 --- a/drivers/hab/habv4.c +++ b/drivers/hab/habv4.c @@ -387,6 +387,39 @@ habv4_display_event_record((struct hab_event_record *)data); } +/* Some chips with HAB >= 4.2.3 have an incorrect implementation of the RNG + * self-test in ROM code. In this case, an HAB event is generated, and a + * software self-test should be run. This variable is set to @c true by + * habv4_get_status() when this occurs. */ +bool habv4_need_rng_software_self_test = false; +EXPORT_SYMBOL(habv4_need_rng_software_self_test); + +#define RNG_FAIL_EVENT_SIZE 36 +static uint8_t habv4_known_rng_fail_events[][RNG_FAIL_EVENT_SIZE] = { + { 0xdb, 0x00, 0x24, 0x42, 0x69, 0x30, 0xe1, 0x1d, + 0x00, 0x80, 0x00, 0x02, 0x40, 0x00, 0x36, 0x06, + 0x55, 0x55, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 }, + { 0xdb, 0x00, 0x24, 0x42, 0x69, 0x30, 0xe1, 0x1d, + 0x00, 0x04, 0x00, 0x02, 0x40, 0x00, 0x36, 0x06, + 0x55, 0x55, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 }, +}; + +static bool is_known_rng_fail_event(const uint8_t *data, size_t len) +{ + int i; + for (i = 0; i < ARRAY_SIZE(habv4_known_rng_fail_events); i++) { + if (memcmp(data, habv4_known_rng_fail_events[i], + min(len, (uint32_t)RNG_FAIL_EVENT_SIZE)) == 0) { + return true; + } + } + return false; +} + static int habv4_get_status(const struct habv4_rvt *rvt) { uint8_t data[256]; @@ -413,10 +446,18 @@ len = sizeof(data); while (rvt->report_event(HAB_STATUS_WARNING, index, data, &len) == HAB_STATUS_SUCCESS) { - pr_err("-------- HAB warning Event %d --------\n", index); - pr_err("event data:\n"); - habv4_display_event(data, len); + /* suppress RNG self-test fail events if they can be handled in software */ + if (IS_ENABLED(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_SELF_TEST) && + is_known_rng_fail_event(data, len)) { + pr_debug("RNG self-test failure detected, will run software self-test\n"); + habv4_need_rng_software_self_test = true; + } else { + pr_err("-------- HAB warning Event %d --------\n", index); + pr_err("event data:\n"); + habv4_display_event(data, len); + } + len = sizeof(data); index++; } diff --git a/include/hab.h b/include/hab.h index 78c2b86..abfce18 100644 --- a/include/hab.h +++ b/include/hab.h @@ -21,6 +21,7 @@ #include #ifdef CONFIG_HABV4 +extern bool habv4_need_rng_software_self_test; int imx28_hab_get_status(void); int imx6_hab_get_status(void); #else