diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:13:47 +0000 |
commit | 102b0d2daa97dae68d3eed54d8fe37a9cc38a892 (patch) | |
tree | bcf648efac40ca6139842707f0eba5a4496a6dd2 /drivers/nxp/crypto/caam/src | |
parent | Initial commit. (diff) | |
download | arm-trusted-firmware-upstream.tar.xz arm-trusted-firmware-upstream.zip |
Adding upstream version 2.8.0+dfsg.upstream/2.8.0+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | drivers/nxp/crypto/caam/src/auth/auth.mk | 12 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/auth/hash.c | 155 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/auth/nxp_crypto.c | 123 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/auth/rsa.c | 179 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/caam.c | 339 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/hw_key_blob.c | 81 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/jobdesc.c | 236 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/rng.c | 251 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/sec_hw_specific.c | 635 | ||||
-rw-r--r-- | drivers/nxp/crypto/caam/src/sec_jr_driver.c | 241 |
10 files changed, 2252 insertions, 0 deletions
diff --git a/drivers/nxp/crypto/caam/src/auth/auth.mk b/drivers/nxp/crypto/caam/src/auth/auth.mk new file mode 100644 index 0000000..d1f8c75 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/auth/auth.mk @@ -0,0 +1,12 @@ +# +# Copyright 2018-2020 NXP +# +# SPDX-License-Identifier: BSD-3-Clause +# +# + +SEC_DRIVERS_PATH := drivers/nxp/crypto/caam + +ifeq (${TRUSTED_BOARD_BOOT},1) +AUTH_SOURCES += $(wildcard $(SEC_DRIVERS_PATH)/src/auth/*.c) +endif diff --git a/drivers/nxp/crypto/caam/src/auth/hash.c b/drivers/nxp/crypto/caam/src/auth/hash.c new file mode 100644 index 0000000..1665df1 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/auth/hash.c @@ -0,0 +1,155 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include <drivers/auth/crypto_mod.h> + +#include "hash.h" +#include "jobdesc.h" +#include "sec_hw_specific.h" + +/* Since no Allocator is available . Taking a global static ctx. + * This would mean that only one active ctx can be there at a time. + */ + +static struct hash_ctx glbl_ctx; + +static void hash_done(uint32_t *desc, uint32_t status, void *arg, + void *job_ring) +{ + INFO("Hash Desc SUCCESS with status %x\n", status); +} + +/*************************************************************************** + * Function : hash_init + * Arguments : ctx - SHA context + * Return : init, + * Description : This function initializes the context for SHA calculation + ***************************************************************************/ +int hash_init(enum hash_algo algo, void **ctx) +{ + if (glbl_ctx.active == false) { + memset(&glbl_ctx, 0, sizeof(struct hash_ctx)); + glbl_ctx.active = true; + glbl_ctx.algo = algo; + *ctx = &glbl_ctx; + return 0; + } else { + return -1; + } +} + +/*************************************************************************** + * Function : hash_update + * Arguments : ctx - SHA context + * buffer - Data + * length - Length + * Return : -1 on error + * 0 on SUCCESS + * Description : This function creates SG entry of the data provided + ***************************************************************************/ +int hash_update(enum hash_algo algo, void *context, void *data_ptr, + unsigned int data_len) +{ + struct hash_ctx *ctx = context; + /* MAX_SG would be MAX_SG_ENTRIES + key + hdr + sg table */ + if (ctx->sg_num >= MAX_SG) { + ERROR("Reached limit for calling %s\n", __func__); + ctx->active = false; + return -EINVAL; + + } + + if (ctx->algo != algo) { + ERROR("ctx for algo not correct\n"); + ctx->active = false; + return -EINVAL; + } + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)data_ptr, data_len); + dmbsy(); +#endif + +#ifdef CONFIG_PHYS_64BIT + sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_hi, + (uint32_t) ((uintptr_t) data_ptr >> 32)); +#else + sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_hi, 0x0); +#endif + sec_out32(&ctx->sg_tbl[ctx->sg_num].addr_lo, (uintptr_t) data_ptr); + + sec_out32(&ctx->sg_tbl[ctx->sg_num].len_flag, + (data_len & SG_ENTRY_LENGTH_MASK)); + + ctx->sg_num++; + + ctx->len += data_len; + + return 0; +} + +/*************************************************************************** + * Function : hash_final + * Arguments : ctx - SHA context + * Return : SUCCESS or FAILURE + * Description : This function sets the final bit and enqueues the decriptor + ***************************************************************************/ +int hash_final(enum hash_algo algo, void *context, void *hash_ptr, + unsigned int hash_len) +{ + int ret = 0; + struct hash_ctx *ctx = context; + uint32_t final = 0U; + + struct job_descriptor jobdesc __aligned(CACHE_WRITEBACK_GRANULE); + + jobdesc.arg = NULL; + jobdesc.callback = hash_done; + + if (ctx->algo != algo) { + ERROR("ctx for algo not correct\n"); + ctx->active = false; + return -EINVAL; + } + + final = sec_in32(&ctx->sg_tbl[ctx->sg_num - 1].len_flag) | + SG_ENTRY_FINAL_BIT; + sec_out32(&ctx->sg_tbl[ctx->sg_num - 1].len_flag, final); + + dsb(); + + /* create the hw_rng descriptor */ + cnstr_hash_jobdesc(jobdesc.desc, (uint8_t *) ctx->sg_tbl, + ctx->len, hash_ptr); + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)ctx->sg_tbl, + (sizeof(struct sg_entry) * MAX_SG)); + inv_dcache_range((uintptr_t)hash_ptr, hash_len); + + dmbsy(); +#endif + + /* Finally, generate the requested random data bytes */ + ret = run_descriptor_jr(&jobdesc); + if (ret != 0) { + ERROR("Error in running descriptor\n"); + ret = -1; + } + ctx->active = false; + return ret; +} diff --git a/drivers/nxp/crypto/caam/src/auth/nxp_crypto.c b/drivers/nxp/crypto/caam/src/auth/nxp_crypto.c new file mode 100644 index 0000000..646e981 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/auth/nxp_crypto.c @@ -0,0 +1,123 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <stddef.h> +#include <string.h> + +#include "caam.h" +#include <common/debug.h> +#include <drivers/auth/crypto_mod.h> + +#include "hash.h" +#include "rsa.h" + +#define LIB_NAME "NXP crypto" + +/* + * Initialize the library and export the descriptor + */ +static void init(void) +{ + /* Initialize NXP crypto library`:*/ + NOTICE("Initializing & configuring SEC block.\n"); + + if (config_sec_block() < 0) { + ERROR("Init & config failure for caam.\n"); + } +} + +/* + * Verify a signature. + * + * For IMG_PLAT - data points to a PKCS#1.5 encoded HASH + * sig_alg will be RSA or ECC + * Parameters are passed using the DER encoding format following the ASN.1 + * structures detailed above. + */ +static int verify_signature(void *data_ptr, unsigned int data_len, + void *sig_ptr, unsigned int sig_len, + void *sign_alg, unsigned int sig_alg_len, + void *pk_ptr, unsigned int pk_len) +{ + int ret = CRYPTO_SUCCESS; + + enum sig_alg alg = *(enum sig_alg *)sign_alg; + + switch (alg) { + case RSA: + NOTICE("Verifying RSA\n"); + ret = rsa_verify_signature(data_ptr, data_len, sig_ptr, sig_len, + pk_ptr, pk_len); + break; + case ECC: + default: + ret = CRYPTO_ERR_SIGNATURE; + break; + } + + if (ret != 0) { + ERROR("RSA verification Failed\n"); + } + return ret; + +} + +/* + * Match a hash + * + * Digest info is passed as a table of SHA-26 hashes and digest_info_len + * is number of entries in the table + * This implementation is very specific to the CSF header parser ROTPK + * comparison. + */ +static int verify_hash(void *data_ptr, unsigned int data_len, + void *digest_info_ptr, unsigned int digest_info_len) +{ + void *ctx = NULL; + int i = 0, ret = 0; + enum hash_algo algo = SHA256; + uint8_t hash[SHA256_BYTES] __aligned(CACHE_WRITEBACK_GRANULE) = {0}; + uint32_t digest_size = SHA256_BYTES; + uint8_t *hash_tbl = digest_info_ptr; + + NOTICE("Verifying hash\n"); + ret = hash_init(algo, &ctx); + if (ret != 0) { + return CRYPTO_ERR_HASH; + } + + /* Update hash with that of SRK table */ + ret = hash_update(algo, ctx, data_ptr, data_len); + if (ret != 0) { + return CRYPTO_ERR_HASH; + } + + /* Copy hash at destination buffer */ + ret = hash_final(algo, ctx, hash, digest_size); + if (ret != 0) { + return CRYPTO_ERR_HASH; + } + + VERBOSE("%s Calculated hash\n", __func__); + for (i = 0; i < SHA256_BYTES/4; i++) { + VERBOSE("%x\n", *((uint32_t *)hash + i)); + } + + for (i = 0; i < digest_info_len; i++) { + if (memcmp(hash, (hash_tbl + (i * digest_size)), + digest_size) == 0) { + return CRYPTO_SUCCESS; + } + } + + return CRYPTO_ERR_HASH; +} + +/* + * Register crypto library descriptor + */ +REGISTER_CRYPTO_LIB(LIB_NAME, init, verify_signature, verify_hash, NULL); diff --git a/drivers/nxp/crypto/caam/src/auth/rsa.c b/drivers/nxp/crypto/caam/src/auth/rsa.c new file mode 100644 index 0000000..0c44462 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/auth/rsa.c @@ -0,0 +1,179 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include <drivers/auth/crypto_mod.h> + +#include "jobdesc.h" +#include "rsa.h" +#include "sec_hw_specific.h" + +/* This array contains DER value for SHA-256 */ +static const uint8_t hash_identifier[] = { + 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, + 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, + 0x04, 0x20 +}; + +static void rsa_done(uint32_t *desc, uint32_t status, void *arg, + void *job_ring) +{ + INFO("RSA Desc SUCCESS with status %x\n", status); +} + +static int rsa_public_verif_sec(uint8_t *sign, uint8_t *to, + uint8_t *rsa_pub_key, uint32_t klen) +{ + int ret = 0; + struct rsa_context ctx __aligned(CACHE_WRITEBACK_GRANULE); + struct job_descriptor jobdesc __aligned(CACHE_WRITEBACK_GRANULE); + + jobdesc.arg = NULL; + jobdesc.callback = rsa_done; + + memset(&ctx, 0, sizeof(struct rsa_context)); + + ctx.pkin.a = sign; + ctx.pkin.a_siz = klen; + ctx.pkin.n = rsa_pub_key; + ctx.pkin.n_siz = klen; + ctx.pkin.e = rsa_pub_key + klen; + ctx.pkin.e_siz = klen; + + cnstr_jobdesc_pkha_rsaexp(jobdesc.desc, &ctx.pkin, to, klen); + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)sign, klen); + flush_dcache_range((uintptr_t)rsa_pub_key, 2 * klen); + flush_dcache_range((uintptr_t)&ctx.pkin, sizeof(ctx.pkin)); + inv_dcache_range((uintptr_t)to, klen); + + dmbsy(); + dsbsy(); + isb(); +#endif + + /* Finally, generate the requested random data bytes */ + ret = run_descriptor_jr(&jobdesc); + if (ret != 0) { + ERROR("Error in running descriptor\n"); + ret = -1; + } +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + inv_dcache_range((uintptr_t)to, klen); + dmbsy(); + dsbsy(); + isb(); +#endif + return ret; +} + +/* + * Construct encoded hash EM' wrt PKCSv1.5. This function calculates the + * pointers for padding, DER value and hash. And finally, constructs EM' + * which includes hash of complete CSF header and ESBC image. If SG flag + * is on, hash of SG table and entries is also included. + */ +static int construct_img_encoded_hash_second(uint8_t *hash, uint8_t hash_len, + uint8_t *encoded_hash_second, + unsigned int key_len) +{ + /* + * RSA PKCSv1.5 encoding format for encoded message is below + * EM = 0x0 || 0x1 || PS || 0x0 || DER || Hash + * PS is Padding String + * DER is DER value for SHA-256 + * Hash is SHA-256 hash + * ********************************************************* + * representative points to first byte of EM initially and is + * filled with 0x0 + * representative is incremented by 1 and second byte is filled + * with 0x1 + * padding points to third byte of EM + * digest points to full length of EM - 32 bytes + * hash_id (DER value) points to 19 bytes before pDigest + * separator is one byte which separates padding and DER + */ + + unsigned int len; + uint8_t *representative; + uint8_t *padding, *digest; + uint8_t *hash_id, *separator; + int i; + int ret = 0; + + if (hash_len != SHA256_BYTES) { + return -1; + } + + /* Key length = Modulus length */ + len = (key_len / 2U) - 1U; + representative = encoded_hash_second; + representative[0] = 0U; + representative[1] = 1U; /* block type 1 */ + + padding = &representative[2]; + digest = &representative[1] + len - 32; + hash_id = digest - sizeof(hash_identifier); + separator = hash_id - 1; + + /* fill padding area pointed by padding with 0xff */ + memset(padding, 0xff, separator - padding); + + /* fill byte pointed by separator */ + *separator = 0U; + + /* fill SHA-256 DER value pointed by HashId */ + memcpy(hash_id, hash_identifier, sizeof(hash_identifier)); + + /* fill hash pointed by Digest */ + for (i = 0; i < SHA256_BYTES; i++) { + digest[i] = hash[i]; + } + + return ret; +} + +int rsa_verify_signature(void *hash_ptr, unsigned int hash_len, + void *sig_ptr, unsigned int sig_len, + void *pk_ptr, unsigned int pk_len) +{ + uint8_t img_encoded_hash_second[RSA_4K_KEY_SZ_BYTES]; + uint8_t encoded_hash[RSA_4K_KEY_SZ_BYTES] __aligned(CACHE_WRITEBACK_GRANULE); + int ret = 0; + + ret = construct_img_encoded_hash_second(hash_ptr, hash_len, + img_encoded_hash_second, + pk_len); + if (ret != 0) { + ERROR("Encoded Hash Failure\n"); + return CRYPTO_ERR_SIGNATURE; + } + + ret = rsa_public_verif_sec(sig_ptr, encoded_hash, pk_ptr, pk_len / 2); + if (ret != 0) { + ERROR("RSA signature Failure\n"); + return CRYPTO_ERR_SIGNATURE; + } + + ret = memcmp(img_encoded_hash_second, encoded_hash, sig_len); + if (ret != 0) { + ERROR("Comparison Failure\n"); + return CRYPTO_ERR_SIGNATURE; + } + + return CRYPTO_SUCCESS; +} diff --git a/drivers/nxp/crypto/caam/src/caam.c b/drivers/nxp/crypto/caam/src/caam.c new file mode 100644 index 0000000..e594f7b --- /dev/null +++ b/drivers/nxp/crypto/caam/src/caam.c @@ -0,0 +1,339 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "sec_hw_specific.h" + +static uintptr_t g_nxp_caam_addr; +static void *job_ring; + +uintptr_t get_caam_addr(void) +{ + if (g_nxp_caam_addr == 0) { + ERROR("Sec Init is not done.\n"); + panic(); + } + return g_nxp_caam_addr; +} + +/* This function sets the TZ bit for the Job ring number passed as @num */ +static void config_tz(int num) +{ + uint32_t jricid; + + /* Setting TZ bit of job ring */ + switch (num) { + case 0: + jricid = sec_in32(g_nxp_caam_addr + SEC_REG_JR0ICIDR_MS_OFFSET); + sec_out32(g_nxp_caam_addr + SEC_REG_JR0ICIDR_MS_OFFSET, + jricid | JRICID_MS_TZ); + break; + case 1: + jricid = sec_in32(g_nxp_caam_addr + SEC_REG_JR1ICIDR_MS_OFFSET); + sec_out32(g_nxp_caam_addr + SEC_REG_JR1ICIDR_MS_OFFSET, + jricid | JRICID_MS_TZ); + break; + case 2: + jricid = sec_in32(g_nxp_caam_addr + SEC_REG_JR2ICIDR_MS_OFFSET); + sec_out32(g_nxp_caam_addr + SEC_REG_JR2ICIDR_MS_OFFSET, + jricid | JRICID_MS_TZ); + break; + case 3: + jricid = sec_in32(g_nxp_caam_addr + SEC_REG_JR3ICIDR_MS_OFFSET); + sec_out32(g_nxp_caam_addr + SEC_REG_JR3ICIDR_MS_OFFSET, + jricid | JRICID_MS_TZ); + break; + default: + break; + } +} + +/* This function checks if Virtualization is enabled for JR and + * accordingly sets the bot for starting JR<num> in JRSTARTR register + */ +static inline void start_jr(int num) +{ + uint32_t ctpr = sec_in32((g_nxp_caam_addr + SEC_REG_CTPR_MS_OFFSET)); + uint32_t tmp = sec_in32((g_nxp_caam_addr + SEC_REG_JRSTARTR_OFFSET)); + uint32_t scfgr = sec_in32((g_nxp_caam_addr + SEC_REG_SCFGR_OFFSET)); + bool start = false; + + if ((ctpr & CTPR_VIRT_EN_INC) != 0U) { + if (((ctpr & CTPR_VIRT_EN_POR) != 0U) || + ((scfgr & SCFGR_VIRT_EN) != 0U)) { + start = true; + } + } else { + if ((ctpr & CTPR_VIRT_EN_POR) != 0U) { + start = true; + } + } + + if (start == true) { + switch (num) { + case 0: + tmp |= JRSTARTR_STARTJR0; + break; + case 1: + tmp |= JRSTARTR_STARTJR1; + break; + case 2: + tmp |= JRSTARTR_STARTJR2; + break; + case 3: + tmp |= JRSTARTR_STARTJR3; + break; + default: + break; + } + } + sec_out32((g_nxp_caam_addr + SEC_REG_JRSTARTR_OFFSET), tmp); +} + +/* This functions configures the Job Ring + * JR3 is reserved for use by Secure world + */ +static int configure_jr(int num) +{ + int ret; + void *reg_base_addr; + + switch (num) { + case 0: + reg_base_addr = (void *)(g_nxp_caam_addr + CAAM_JR0_OFFSET); + break; + case 1: + reg_base_addr = (void *)(g_nxp_caam_addr + CAAM_JR1_OFFSET); + break; + case 2: + reg_base_addr = (void *)(g_nxp_caam_addr + CAAM_JR2_OFFSET); + break; + case 3: + reg_base_addr = (void *)(g_nxp_caam_addr + CAAM_JR3_OFFSET); + break; + default: + break; + } + + /* Initialize the JR library */ + ret = sec_jr_lib_init(); + if (ret != 0) { + ERROR("Error in sec_jr_lib_init"); + return -1; + } + + start_jr(num); + + /* Do HW configuration of the JR */ + job_ring = init_job_ring(SEC_NOTIFICATION_TYPE_POLL, 0, 0, + reg_base_addr, 0); + + if (job_ring == NULL) { + ERROR("Error in init_job_ring"); + return -1; + } + + return ret; +} + +/* TBD - Configures and locks the ICID values for various JR */ +static inline void configure_icid(void) +{ +} + +/* TBD configures the TZ settings of RTIC */ +static inline void configure_rtic(void) +{ +} + +int sec_init(uintptr_t nxp_caam_addr) +{ + g_nxp_caam_addr = nxp_caam_addr; + return config_sec_block(); +} + +/* This function configure SEC block: + * - It does basic parameter setting + * - Configures the default Job ring assigned to TZ /secure world + * - Instantiates the RNG + */ +int config_sec_block(void) +{ + int ret = 0; + uint32_t mcfgr; + + if (g_nxp_caam_addr == 0) { + ERROR("Sec Init is not done.\n"); + return -1; + } else if (job_ring != NULL) { + NOTICE("Sec is already initialized and configured.\n"); + return ret; + } + + mcfgr = sec_in32(g_nxp_caam_addr + SEC_REG_MCFGR_OFFSET); + + /* Modify CAAM Read/Write attributes + * AXI Write - Cacheable, WB and WA + * AXI Read - Cacheable, RA + */ +#if defined(CONFIG_ARCH_LS2080A) || defined(CONFIG_ARCH_LS2088A) + mcfgr = (mcfgr & ~MCFGR_AWCACHE_MASK) | (0xb << MCFGR_AWCACHE_SHIFT); + mcfgr = (mcfgr & ~MCFGR_ARCACHE_MASK) | (0x6 << MCFGR_ARCACHE_SHIFT); +#else + mcfgr = (mcfgr & ~MCFGR_AWCACHE_MASK) | (0x2 << MCFGR_AWCACHE_SHIFT); +#endif + + /* Set PS bit to 1 */ +#ifdef CONFIG_PHYS_64BIT + mcfgr |= (1 << MCFGR_PS_SHIFT); +#endif + sec_out32(g_nxp_caam_addr + SEC_REG_MCFGR_OFFSET, mcfgr); + + /* Asssign ICID to all Job rings and lock them for usage */ + configure_icid(); + + /* Configure the RTIC */ + configure_rtic(); + + /* Configure the default JR for usage */ + ret = configure_jr(DEFAULT_JR); + if (ret != 0) { + ERROR("\nFSL_JR: configuration failure\n"); + return -1; + } + /* Do TZ configuration of default JR for sec firmware */ + config_tz(DEFAULT_JR); + +#ifdef CONFIG_RNG_INIT + /* Instantiate the RNG */ + ret = hw_rng_instantiate(); + if (ret != 0) { + ERROR("\nRNG Instantiation failure\n"); + return -1; + } +#endif + + return ret; +} + +/* This function is used for sumbitting job to the Job Ring + * [param] [in] - jobdesc to be submitted + * Return - -1 in case of error and 0 in case of SUCCESS + */ +int run_descriptor_jr(struct job_descriptor *jobdesc) +{ + int i = 0, ret = 0; + uint32_t *desc_addr = jobdesc->desc; + uint32_t desc_len = desc_length(jobdesc->desc); + uint32_t desc_word; + + for (i = 0; i < desc_len; i++) { + desc_word = desc_addr[i]; + VERBOSE("%x\n", desc_word); + sec_out32((uint32_t *)&desc_addr[i], desc_word); + } + dsb(); + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)desc_addr, desc_len * 4); + dmbsy(); + dsbsy(); + isb(); +#endif + + ret = enq_jr_desc(job_ring, jobdesc); + if (ret == 0) { + VERBOSE("JR enqueue done...\n"); + } else { + ERROR("Error in Enqueue\n"); + return ret; + } + + VERBOSE("Dequeue in progress"); + + ret = dequeue_jr(job_ring, -1); + if (ret >= 0) { + VERBOSE("Dequeue of %x desc success\n", ret); + ret = 0; + } else { + ERROR("deq_ret %x\n", ret); + ret = -1; + } + + return ret; +} + +/* this function returns a random number using HW RNG Algo + * In case of failure, random number returned is 0 + * prngWidth = 0 - 32 bit random number + * prngWidth > 0 means 64 bit random number + */ +unsigned long long get_random(int rngWidth) +{ + unsigned long long result = 0; + uint8_t rand_byte[64] __aligned(CACHE_WRITEBACK_GRANULE); + uint8_t rand_byte_swp[8]; + int bytes = 0; + int i = 0; + int ret = 0; + +#ifdef CAAM_TEST + rand_byte[0] = U(0x12); + rand_byte[1] = U(0x34); + rand_byte[2] = U(0x56); + rand_byte[3] = U(0x78); + rand_byte[4] = U(0x9a); + rand_byte[5] = U(0xbc); + rand_byte[6] = U(0xde); + rand_byte[7] = U(0xf1); +#endif + + if (rngWidth == 0U) { + bytes = 4; + } else { + bytes = 8; + } + + memset(rand_byte, 0, 64); + + ret = get_rand_bytes_hw(rand_byte, bytes); + + for (i = 0; i < bytes; i++) { + if (ret != 0) { + /* Return 0 in case of failure */ + rand_byte_swp[i] = 0; + } else { + rand_byte_swp[i] = rand_byte[bytes - i - 1]; + result = (result << 8) | rand_byte_swp[i]; + } + } + + INFO("result %llx\n", result); + + return result; + +} /* _get_RNG() */ + +unsigned int _get_hw_unq_key(uint64_t hw_key_phy_addr, unsigned int size) +{ + int ret = 0; + uint8_t *hw_key = (uint8_t *) ptov((phys_addr_t *) hw_key_phy_addr); + + ret = get_hw_unq_key_blob_hw(hw_key, size); + + return ret; +} diff --git a/drivers/nxp/crypto/caam/src/hw_key_blob.c b/drivers/nxp/crypto/caam/src/hw_key_blob.c new file mode 100644 index 0000000..0720695 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/hw_key_blob.c @@ -0,0 +1,81 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "sec_hw_specific.h" + + +/* Callback function after Instantiation decsriptor is submitted to SEC + */ +static void blob_done(uint32_t *desc, uint32_t status, void *arg, + void *job_ring) +{ + INFO("Blob Desc SUCCESS with status %x\n", status); +} + +/* @brief Submit descriptor to create blob + * @retval 0 on success + * @retval -1 on error + */ +int get_hw_unq_key_blob_hw(uint8_t *hw_key, int size) +{ + int ret = 0; + int i = 0; + + uint32_t key_sz = KEY_IDNFR_SZ_BYTES; + uint8_t key_data[KEY_IDNFR_SZ_BYTES]; + uint8_t in_data[16]; + uint8_t out_data[16 + KEY_BLOB_SIZE + MAC_SIZE]; + struct job_descriptor desc __aligned(CACHE_WRITEBACK_GRANULE); + struct job_descriptor *jobdesc = &desc; + uint32_t in_sz = 16U; + + /* Output blob will have 32 bytes key blob in beginning and + * 16 byte HMAC identifier at end of data blob + */ + uint32_t out_sz = in_sz + KEY_BLOB_SIZE + MAC_SIZE; + + uint32_t operation = CMD_OPERATION | OP_TYPE_ENCAP_PROTOCOL | + OP_PCLID_BLOB | BLOB_PROTO_INFO; + + memset(key_data, 0xff, KEY_IDNFR_SZ_BYTES); + memset(in_data, 0x00, in_sz); + memset(out_data, 0x00, in_sz); + + jobdesc->arg = NULL; + jobdesc->callback = blob_done; + + INFO("\nGenerating Master Key Verification Blob.\n"); + + /* Create the hw_rng descriptor */ + ret = cnstr_hw_encap_blob_jobdesc(jobdesc->desc, key_data, key_sz, + CLASS_2, in_data, in_sz, out_data, + out_sz, operation); + + /* Finally, generate the blob. */ + ret = run_descriptor_jr(jobdesc); + if (ret != 0) { + ERROR("Error in running hw unq key blob descriptor\n"); + return -1; + } + /* Copying alternate bytes of the Master Key Verification Blob. + */ + for (i = 0; i < size; i++) { + hw_key[i] = out_data[2 * i]; + } + + return ret; +} diff --git a/drivers/nxp/crypto/caam/src/jobdesc.c b/drivers/nxp/crypto/caam/src/jobdesc.c new file mode 100644 index 0000000..f559c4b --- /dev/null +++ b/drivers/nxp/crypto/caam/src/jobdesc.c @@ -0,0 +1,236 @@ +/* + * Copyright 2017-2020 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "rsa.h" +#include "sec_hw_specific.h" + + +/* Return Length of desctiptr from first word */ +uint32_t desc_length(uint32_t *desc) +{ + return desc[0] & DESC_LEN_MASK; +} + +/*Update start index in first word of descriptor */ +void desc_update_start_index(uint32_t *desc, uint32_t index) +{ + desc[0] |= (index << DESC_START_SHIFT); +} + +/* Initialize the descriptor */ +void desc_init(uint32_t *desc) +{ + *desc = 0; +} + +/* Add word in the descriptor and increment the length */ +void desc_add_word(uint32_t *desc, uint32_t word) +{ + uint32_t len = desc_length(desc); + + /* Add Word at Last */ + uint32_t *last = desc + len; + *last = word; + + /* Increase the length */ + desc[0] += 1; +} + +/* Add Pointer to the descriptor */ +void desc_add_ptr(uint32_t *desc, phys_addr_t *ptr) +{ + uint32_t len = desc_length(desc); + + /* Add Word at Last */ + phys_addr_t *last = (phys_addr_t *) (desc + len); + +#ifdef CONFIG_PHYS_64BIT + ptr_addr_t *ptr_addr = (ptr_addr_t *) last; + + ptr_addr->high = PHYS_ADDR_HI(ptr); + ptr_addr->low = PHYS_ADDR_LO(ptr); +#else + *last = ptr; +#endif + + /* Increase the length */ + desc[0] += (uint32_t) (sizeof(phys_addr_t) / sizeof(uint32_t)); +} + +/* Descriptor to generate Random words */ +int cnstr_rng_jobdesc(uint32_t *desc, uint32_t state_handle, + uint32_t *add_inp, uint32_t add_ip_len, + uint8_t *out_data, uint32_t len) +{ + phys_addr_t *phys_addr_out = vtop(out_data); + + /* Current descriptor support only 64K length */ + if (len > U(0xffff)) + return -1; + /* Additional Input not supported by current descriptor */ + if (add_ip_len > 0U) + return -1; + + VERBOSE("Constructing descriptor\n"); + desc_init(desc); + /* Class1 Alg Operation,RNG Optype, Generate */ + desc_add_word(desc, U(0xb0800000)); + desc_add_word(desc, U(0x82500000) | (state_handle << ALG_AAI_SH_SHIFT)); + desc_add_word(desc, U(0x60340000) | len); + desc_add_ptr(desc, phys_addr_out); + + return 0; + +} + +/* Construct descriptor to instantiate RNG */ +int cnstr_rng_instantiate_jobdesc(uint32_t *desc) +{ + desc_init(desc); + desc_add_word(desc, U(0xb0800000)); + /* Class1 Alg Operation,RNG Optype, Instantiate */ + desc_add_word(desc, U(0x82500004)); + /* Wait for done */ + desc_add_word(desc, U(0xa2000001)); + /*Load to clear written */ + desc_add_word(desc, U(0x10880004)); + /*Pri Mode Reg clear */ + desc_add_word(desc, U(0x00000001)); + /* Generate secure keys */ + desc_add_word(desc, U(0x82501000)); + + return 0; +} + +/* Construct descriptor to generate hw key blob */ +int cnstr_hw_encap_blob_jobdesc(uint32_t *desc, + uint8_t *key_idnfr, uint32_t key_sz, + uint32_t key_class, uint8_t *plain_txt, + uint32_t in_sz, uint8_t *enc_blob, + uint32_t out_sz, uint32_t operation) +{ + phys_addr_t *phys_key_idnfr, *phys_addr_in, *phys_addr_out; + int i = 0; + + phys_key_idnfr = vtop((void *)key_idnfr); + phys_addr_in = vtop((void *)plain_txt); + phys_addr_out = vtop((void *)enc_blob); + + desc_init(desc); + + desc_add_word(desc, U(0xb0800000)); + + /* Key Identifier */ + desc_add_word(desc, (key_class | key_sz)); + desc_add_ptr(desc, phys_key_idnfr); + + /* Source Address */ + desc_add_word(desc, U(0xf0400000)); + desc_add_ptr(desc, phys_addr_in); + + /* In Size = 0x10 */ + desc_add_word(desc, in_sz); + + /* Out Address */ + desc_add_word(desc, U(0xf8400000)); + desc_add_ptr(desc, phys_addr_out); + + /* Out Size = 0x10 */ + desc_add_word(desc, out_sz); + + /* Operation */ + desc_add_word(desc, operation); + + for (i = 0; i < 15; i++) + VERBOSE("desc word %x\n", desc[i]); + + return 0; +} + +/*************************************************************************** + * Function : inline_cnstr_jobdesc_pkha_rsaexp + * Arguments : desc - Pointer to Descriptor + * pkin - Pointer to Input Params + * out - Pointer to Output + * out_siz - Output Size + * Return : Void + * Description : Creates the descriptor for PKHA RSA + ***************************************************************************/ +void cnstr_jobdesc_pkha_rsaexp(uint32_t *desc, + struct pk_in_params *pkin, uint8_t *out, + uint32_t out_siz) +{ + phys_addr_t *ptr_addr_e, *ptr_addr_a, *ptr_addr_n, *ptr_addr_out; + + ptr_addr_e = vtop((void *)(pkin->e)); + ptr_addr_a = vtop((void *)(pkin->a)); + ptr_addr_n = vtop((void *)(pkin->n)); + ptr_addr_out = vtop((void *)(out)); + + desc_init(desc); + desc_add_word(desc, U(0xb0800000)); + desc_add_word(desc, U(0x02010000) | pkin->e_siz); + desc_add_ptr(desc, ptr_addr_e); + desc_add_word(desc, U(0x220c0000) | pkin->a_siz); + desc_add_ptr(desc, ptr_addr_a); + desc_add_word(desc, U(0x22080000) | pkin->n_siz); + desc_add_ptr(desc, ptr_addr_n); + desc_add_word(desc, U(0x81800006)); + desc_add_word(desc, U(0x620d0000) | out_siz); + desc_add_ptr(desc, ptr_addr_out); +} + +/*************************************************************************** + * Function : inline_cnstr_jobdesc_sha256 + * Arguments : desc - Pointer to Descriptor + * msg - Pointer to SG Table + * msgsz - Size of SG Table + * digest - Pointer to Output Digest + * Return : Void + * Description : Creates the descriptor for SHA256 HASH calculation + ***************************************************************************/ +void cnstr_hash_jobdesc(uint32_t *desc, uint8_t *msg, uint32_t msgsz, + uint8_t *digest) +{ + /* SHA 256 , output is of length 32 words */ + phys_addr_t *ptr_addr_in, *ptr_addr_out; + + ptr_addr_in = (void *)vtop(msg); + ptr_addr_out = (void *)vtop(digest); + + desc_init(desc); + desc_add_word(desc, U(0xb0800000)); + + /* Operation Command + * OP_TYPE_CLASS2_ALG | OP_ALG_ALGSEL_SHA256 | OP_ALG_AAI_HASH | + * OP_ALG_AS_INITFINAL | OP_ALG_ENCRYPT | OP_ALG_ICV_OFF) + */ + desc_add_word(desc, U(0x8443000d)); + + if (msgsz > U(0xffff)) { + desc_add_word(desc, U(0x25540000)); /* FIFO Load */ + desc_add_ptr(desc, ptr_addr_in); /* Pointer to msg */ + desc_add_word(desc, msgsz); /* Size */ + desc_add_word(desc, U(0x54200020)); /* FIFO Store */ + desc_add_ptr(desc, ptr_addr_out); /* Pointer to Result */ + } else { + desc_add_word(desc, U(0x25140000) | msgsz); + desc_add_ptr(desc, ptr_addr_in); + desc_add_word(desc, U(0x54200020)); + desc_add_ptr(desc, ptr_addr_out); + } + +} diff --git a/drivers/nxp/crypto/caam/src/rng.c b/drivers/nxp/crypto/caam/src/rng.c new file mode 100644 index 0000000..0b9d87d --- /dev/null +++ b/drivers/nxp/crypto/caam/src/rng.c @@ -0,0 +1,251 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "sec_hw_specific.h" + + +/* Callback function after Instantiation decsriptor is submitted to SEC */ +static void rng_done(uint32_t *desc, uint32_t status, void *arg, + void *job_ring) +{ + INFO("RNG Desc SUCCESS with status %x\n", status); +} + +/* Is the HW RNG instantiated? + * Return code: + * 0 - Not in the instantiated state + * 1 - In the instantiated state + * state_handle - 0 for SH0, 1 for SH1 + */ +static int is_hw_rng_instantiated(uint32_t *state_handle) +{ + int ret_code = 0; + uint32_t rdsta; + + rdsta = sec_in32(get_caam_addr() + RNG_REG_RDSTA_OFFSET); + + /*Check if either of the two state handles has been instantiated */ + if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) { + *state_handle = 0; + ret_code = 1; + } else if (rdsta & RNG_STATE0_HANDLE_INSTANTIATED) { + *state_handle = 1; + ret_code = 1; + } + + return ret_code; +} + +/* @brief Kick the TRNG block of the RNG HW Engine + * @param [in] ent_delay Entropy delay to be used + * By default, the TRNG runs for 200 clocks per sample; + * 1200 clocks per sample generates better entropy. + * @retval 0 on success + * @retval -1 on error + */ +static void kick_trng(int ent_delay) +{ + uint32_t val; + + /* put RNG4 into program mode */ + val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET); + val = val | RTMCTL_PRGM; + sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val); + + /* rtsdctl bits 0-15 contain "Entropy Delay, which defines the + * length (in system clocks) of each Entropy sample taken + */ + val = sec_in32(get_caam_addr() + RNG_REG_RTSDCTL_OFFSET); + val = (val & ~RTSDCTL_ENT_DLY_MASK) | + (ent_delay << RTSDCTL_ENT_DLY_SHIFT); + sec_out32(get_caam_addr() + RNG_REG_RTSDCTL_OFFSET, val); + /* min. freq. count, equal to 1/4 of the entropy sample length */ + sec_out32(get_caam_addr() + RNG_REG_RTFRQMIN_OFFSET, ent_delay >> 2); + /* disable maximum frequency count */ + sec_out32(get_caam_addr() + RNG_REG_RTFRQMAX_OFFSET, RTFRQMAX_DISABLE); + + /* select raw sampling in both entropy shifter + * and statistical checker + */ + val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET); + val = val | RTMCTL_SAMP_MODE_RAW_ES_SC; + sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val); + + /* put RNG4 into run mode */ + val = sec_in32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET); + val = val & ~RTMCTL_PRGM; + sec_out32(get_caam_addr() + RNG_REG_RTMCTL_OFFSET, val); +} + +/* @brief Submit descriptor to instantiate the RNG + * @retval 0 on success + * @retval -1 on error + */ +static int instantiate_rng(void) +{ + int ret = 0; + struct job_descriptor desc __aligned(CACHE_WRITEBACK_GRANULE); + struct job_descriptor *jobdesc = &desc; + + jobdesc->arg = NULL; + jobdesc->callback = rng_done; + + /* create the hw_rng descriptor */ + cnstr_rng_instantiate_jobdesc(jobdesc->desc); + + /* Finally, generate the requested random data bytes */ + ret = run_descriptor_jr(jobdesc); + if (ret != 0) { + ERROR("Error in running descriptor\n"); + ret = -1; + } + return ret; +} + +/* Generate Random Data using HW RNG + * Parameters: + * uint8_t* add_input - user specified optional input byte array + * uint32_t add_input_len - number of bytes of additional input + * uint8_t* out - user specified output byte array + * uint32_t out_len - number of bytes to store in output byte array + * Return code: + * 0 - SUCCESS + * -1 - ERROR + */ +static int +hw_rng_generate(uint32_t *add_input, uint32_t add_input_len, + uint8_t *out, uint32_t out_len, uint32_t state_handle) +{ + int ret = 0; + struct job_descriptor desc __aligned(CACHE_WRITEBACK_GRANULE); + struct job_descriptor *jobdesc = &desc; + + jobdesc->arg = NULL; + jobdesc->callback = rng_done; + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + inv_dcache_range((uintptr_t)out, out_len); + dmbsy(); +#endif + + /* create the hw_rng descriptor */ + ret = cnstr_rng_jobdesc(jobdesc->desc, state_handle, + add_input, add_input_len, out, out_len); + if (ret != 0) { + ERROR("Descriptor construction failed\n"); + ret = -1; + goto out; + } + /* Finally, generate the requested random data bytes */ + ret = run_descriptor_jr(jobdesc); + if (ret != 0) { + ERROR("Error in running descriptor\n"); + ret = -1; + } + +out: + return ret; +} + +/* this function instantiates the rng + * + * Return code: + * 0 - All is well + * <0 - Error occurred somewhere + */ +int hw_rng_instantiate(void) +{ + int ret = 0; + int ent_delay = RTSDCTL_ENT_DLY_MIN; + uint32_t state_handle; + + ret = is_hw_rng_instantiated(&state_handle); + if (ret != 0) { + NOTICE("RNG already instantiated\n"); + return 0; + } + do { + kick_trng(ent_delay); + ent_delay += 400; + /*if instantiate_rng(...) fails, the loop will rerun + *and the kick_trng(...) function will modify the + *upper and lower limits of the entropy sampling + *interval, leading to a sucessful initialization of + */ + ret = instantiate_rng(); + } while ((ret == -1) && (ent_delay < RTSDCTL_ENT_DLY_MAX)); + if (ret != 0) { + ERROR("RNG: Failed to instantiate RNG\n"); + return ret; + } + + NOTICE("RNG: INSTANTIATED\n"); + + /* Enable RDB bit so that RNG works faster */ + // sec_setbits32(&sec->scfgr, SEC_SCFGR_RDBENABLE); + + return ret; +} + +/* Generate random bytes, and stuff them into the bytes buffer + * + * If the HW RNG has not already been instantiated, + * it will be instantiated before data is generated. + * + * Parameters: + * uint8_t* bytes - byte buffer large enough to hold the requested random date + * int byte_len - number of random bytes to generate + * + * Return code: + * 0 - All is well + * ~0 - Error occurred somewhere + */ +int get_rand_bytes_hw(uint8_t *bytes, int byte_len) +{ + int ret_code = 0; + uint32_t state_handle; + + /* If this is the first time this routine is called, + * then the hash_drbg will not already be instantiated. + * Therefore, before generating data, instantiate the hash_drbg + */ + ret_code = is_hw_rng_instantiated(&state_handle); + if (ret_code == 0) { + INFO("Instantiating the HW RNG\n"); + + /* Instantiate the hw RNG */ + ret_code = hw_rng_instantiate(); + if (ret_code != 0) { + ERROR("HW RNG Instantiate failed\n"); + return ret_code; + } + } + /* If HW RNG is still not instantiated, something must have gone wrong, + * it must be in the error state, we will not generate any random data + */ + if (is_hw_rng_instantiated(&state_handle) == 0) { + ERROR("HW RNG is in an Error state, and cannot be used\n"); + return -1; + } + /* Generate a random 256-bit value, as 32 bytes */ + ret_code = hw_rng_generate(0, 0, bytes, byte_len, state_handle); + if (ret_code != 0) { + ERROR("HW RNG Generate failed\n"); + return ret_code; + } + + return ret_code; +} diff --git a/drivers/nxp/crypto/caam/src/sec_hw_specific.c b/drivers/nxp/crypto/caam/src/sec_hw_specific.c new file mode 100644 index 0000000..92b7762 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/sec_hw_specific.c @@ -0,0 +1,635 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "sec_hw_specific.h" + + +/* Job rings used for communication with SEC HW */ +extern struct sec_job_ring_t g_job_rings[MAX_SEC_JOB_RINGS]; + +/* The current state of SEC user space driver */ +extern volatile sec_driver_state_t g_driver_state; + +/* The number of job rings used by SEC user space driver */ +extern int g_job_rings_no; + +/* LOCAL FUNCTIONS */ +static inline void hw_set_input_ring_start_addr(struct jobring_regs *regs, + phys_addr_t *start_addr) +{ +#if defined(CONFIG_PHYS_64BIT) + sec_out32(®s->irba_h, PHYS_ADDR_HI(start_addr)); +#else + sec_out32(®s->irba_h, 0); +#endif + sec_out32(®s->irba_l, PHYS_ADDR_LO(start_addr)); +} + +static inline void hw_set_output_ring_start_addr(struct jobring_regs *regs, + phys_addr_t *start_addr) +{ +#if defined(CONFIG_PHYS_64BIT) + sec_out32(®s->orba_h, PHYS_ADDR_HI(start_addr)); +#else + sec_out32(®s->orba_h, 0); +#endif + sec_out32(®s->orba_l, PHYS_ADDR_LO(start_addr)); +} + +/* ORJR - Output Ring Jobs Removed Register shows how many jobs were + * removed from the Output Ring for processing by software. This is done after + * the software has processed the entries. + */ +static inline void hw_remove_entries(sec_job_ring_t *jr, int num) +{ + struct jobring_regs *regs = + (struct jobring_regs *)jr->register_base_addr; + + sec_out32(®s->orjr, num); +} + +/* IRSA - Input Ring Slots Available register holds the number of entries in + * the Job Ring's input ring. Once a job is enqueued, the value returned is + * decremented by the hardware by the number of jobs enqueued. + */ +static inline int hw_get_available_slots(sec_job_ring_t *jr) +{ + struct jobring_regs *regs = + (struct jobring_regs *)jr->register_base_addr; + + return sec_in32(®s->irsa); +} + +/* ORSFR - Output Ring Slots Full register holds the number of jobs which were + * processed by the SEC and can be retrieved by the software. Once a job has + * been processed by software, the user will call hw_remove_one_entry in order + * to notify the SEC that the entry was processed + */ +static inline int hw_get_no_finished_jobs(sec_job_ring_t *jr) +{ + struct jobring_regs *regs = + (struct jobring_regs *)jr->register_base_addr; + + return sec_in32(®s->orsf); +} + +/* @brief Process Jump Halt Condition related errors + * @param [in] error_code The error code in the descriptor status word + */ +static inline void hw_handle_jmp_halt_cond_err(union hw_error_code error_code) +{ + ERROR("JMP %x\n", error_code.error_desc.jmp_halt_cond_src.jmp); + ERROR("Descriptor Index: %d\n", + error_code.error_desc.jmp_halt_cond_src.desc_idx); + ERROR(" Condition %x\n", error_code.error_desc.jmp_halt_cond_src.cond); +} + +/* @brief Process DECO related errors + * @param [in] error_code The error code in the descriptor status word + */ +static inline void hw_handle_deco_err(union hw_error_code error_code) +{ + ERROR("JMP %x\n", error_code.error_desc.deco_src.jmp); + ERROR("Descriptor Index: 0x%x", + error_code.error_desc.deco_src.desc_idx); + + switch (error_code.error_desc.deco_src.desc_err) { + case SEC_HW_ERR_DECO_HFN_THRESHOLD: + WARN(" Descriptor completed but exceeds the Threshold"); + break; + default: + ERROR("Error 0x%04x not implemented", + error_code.error_desc.deco_src.desc_err); + break; + } +} + +/* @brief Process Jump Halt User Status related errors + * @param [in] error_code The error code in the descriptor status word + */ +static inline void hw_handle_jmp_halt_user_err(union hw_error_code error_code) +{ + WARN(" Not implemented"); +} + +/* @brief Process CCB related errors + * @param [in] error_code The error code in the descriptor status word + */ +static inline void hw_handle_ccb_err(union hw_error_code hw_error_code) +{ + WARN(" Not implemented"); +} + +/* @brief Process Job Ring related errors + * @param [in] error_code The error code in the descriptor status word + */ +static inline void hw_handle_jr_err(union hw_error_code hw_error_code) +{ + WARN(" Not implemented"); +} + +/* GLOBAL FUNCTIONS */ + +int hw_reset_job_ring(sec_job_ring_t *job_ring) +{ + int ret = 0; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* First reset the job ring in hw */ + ret = hw_shutdown_job_ring(job_ring); + if (ret != 0) { + ERROR("Failed resetting job ring in hardware"); + return ret; + } + /* In order to have the HW JR in a workable state + *after a reset, I need to re-write the input + * queue size, input start address, output queue + * size and output start address + * Write the JR input queue size to the HW register + */ + sec_out32(®s->irs, SEC_JOB_RING_SIZE); + + /* Write the JR output queue size to the HW register */ + sec_out32(®s->ors, SEC_JOB_RING_SIZE); + + /* Write the JR input queue start address */ + hw_set_input_ring_start_addr(regs, vtop(job_ring->input_ring)); + + /* Write the JR output queue start address */ + hw_set_output_ring_start_addr(regs, vtop(job_ring->output_ring)); + + return 0; +} + +int hw_shutdown_job_ring(sec_job_ring_t *job_ring) +{ + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + unsigned int timeout = SEC_TIMEOUT; + uint32_t tmp = 0U; + + VERBOSE("Resetting Job ring\n"); + + /* + * Mask interrupts since we are going to poll + * for reset completion status + * Also, at POR, interrupts are ENABLED on a JR, thus + * this is the point where I can disable them without + * changing the code logic too much + */ + + jr_disable_irqs(job_ring); + + /* initiate flush (required prior to reset) */ + sec_out32(®s->jrcr, JR_REG_JRCR_VAL_RESET); + + /* dummy read */ + tmp = sec_in32(®s->jrcr); + + do { + tmp = sec_in32(®s->jrint); + } while (((tmp & JRINT_ERR_HALT_MASK) == + JRINT_ERR_HALT_INPROGRESS) && ((--timeout) != 0U)); + + if ((tmp & JRINT_ERR_HALT_MASK) != JRINT_ERR_HALT_COMPLETE || + timeout == 0U) { + ERROR("Failed to flush hw job ring %x\n %u", tmp, timeout); + /* unmask interrupts */ + if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) { + jr_enable_irqs(job_ring); + } + return -1; + } + /* Initiate reset */ + timeout = SEC_TIMEOUT; + sec_out32(®s->jrcr, JR_REG_JRCR_VAL_RESET); + + do { + tmp = sec_in32(®s->jrcr); + } while (((tmp & JR_REG_JRCR_VAL_RESET) != 0U) && + ((--timeout) != 0U)); + + if (timeout == 0U) { + ERROR("Failed to reset hw job ring\n"); + /* unmask interrupts */ + if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) { + jr_enable_irqs(job_ring); + } + return -1; + } + /* unmask interrupts */ + if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) { + jr_enable_irqs(job_ring); + } + return 0; + +} + +void hw_handle_job_ring_error(sec_job_ring_t *job_ring, uint32_t error_code) +{ + union hw_error_code hw_err_code; + + hw_err_code.error = error_code; + + switch (hw_err_code.error_desc.value.ssrc) { + case SEC_HW_ERR_SSRC_NO_SRC: + INFO("No Status Source "); + break; + case SEC_HW_ERR_SSRC_CCB_ERR: + INFO("CCB Status Source"); + hw_handle_ccb_err(hw_err_code); + break; + case SEC_HW_ERR_SSRC_JMP_HALT_U: + INFO("Jump Halt User Status Source"); + hw_handle_jmp_halt_user_err(hw_err_code); + break; + case SEC_HW_ERR_SSRC_DECO: + INFO("DECO Status Source"); + hw_handle_deco_err(hw_err_code); + break; + case SEC_HW_ERR_SSRC_JR: + INFO("Job Ring Status Source"); + hw_handle_jr_err(hw_err_code); + break; + case SEC_HW_ERR_SSRC_JMP_HALT_COND: + INFO("Jump Halt Condition Codes"); + hw_handle_jmp_halt_cond_err(hw_err_code); + break; + default: + INFO("Unknown SSRC"); + break; + } +} + +int hw_job_ring_error(sec_job_ring_t *job_ring) +{ + uint32_t jrint_error_code; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + if (JR_REG_JRINT_JRE_EXTRACT(sec_in32(®s->jrint)) == 0) { + return 0; + } + + jrint_error_code = + JR_REG_JRINT_ERR_TYPE_EXTRACT(sec_in32(®s->jrint)); + switch (jrint_error_code) { + case JRINT_ERR_WRITE_STATUS: + ERROR("Error writing status to Output Ring "); + break; + case JRINT_ERR_BAD_INPUT_BASE: + ERROR("Bad Input Ring Base (not on a 4-byte boundary)\n"); + break; + case JRINT_ERR_BAD_OUTPUT_BASE: + ERROR("Bad Output Ring Base (not on a 4-byte boundary)\n"); + break; + case JRINT_ERR_WRITE_2_IRBA: + ERROR("Invalid write to Input Ring Base Address Register\n"); + break; + case JRINT_ERR_WRITE_2_ORBA: + ERROR("Invalid write to Output Ring Base Address Register\n"); + break; + case JRINT_ERR_RES_B4_HALT: + ERROR("Job Ring released before Job Ring is halted\n"); + break; + case JRINT_ERR_REM_TOO_MANY: + ERROR("Removed too many jobs from job ring\n"); + break; + case JRINT_ERR_ADD_TOO_MANY: + ERROR("Added too many jobs on job ring\n"); + break; + default: + ERROR("Unknown SEC JR Error :%d\n", jrint_error_code); + break; + } + return jrint_error_code; +} + +int hw_job_ring_set_coalescing_param(sec_job_ring_t *job_ring, + uint16_t irq_coalescing_timer, + uint8_t irq_coalescing_count) +{ + uint32_t reg_val = 0U; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* Set descriptor count coalescing */ + reg_val |= (irq_coalescing_count << JR_REG_JRCFG_LO_ICDCT_SHIFT); + + /* Set coalescing timer value */ + reg_val |= (irq_coalescing_timer << JR_REG_JRCFG_LO_ICTT_SHIFT); + + /* Update parameters in HW */ + sec_out32(®s->jrcfg1, reg_val); + + VERBOSE("Set coalescing params on jr\n"); + + return 0; +} + +int hw_job_ring_enable_coalescing(sec_job_ring_t *job_ring) +{ + uint32_t reg_val = 0U; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* Get the current value of the register */ + reg_val = sec_in32(®s->jrcfg1); + + /* Enable coalescing */ + reg_val |= JR_REG_JRCFG_LO_ICEN_EN; + + /* Write in hw */ + sec_out32(®s->jrcfg1, reg_val); + + VERBOSE("Enabled coalescing on jr\n"); + + return 0; +} + +int hw_job_ring_disable_coalescing(sec_job_ring_t *job_ring) +{ + uint32_t reg_val = 0U; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* Get the current value of the register */ + reg_val = sec_in32(®s->jrcfg1); + + /* Disable coalescing */ + reg_val &= ~JR_REG_JRCFG_LO_ICEN_EN; + + /* Write in hw */ + sec_out32(®s->jrcfg1, reg_val); + + VERBOSE("Disabled coalescing on jr"); + + return 0; + +} + +void hw_flush_job_ring(struct sec_job_ring_t *job_ring, + uint32_t do_notify, + uint32_t error_code, uint32_t *notified_descs) +{ + int32_t jobs_no_to_discard = 0; + int32_t discarded_descs_no = 0; + int32_t number_of_jobs_available = 0; + + VERBOSE("JR pi[%d]i ci[%d]\n", job_ring->pidx, job_ring->cidx); + VERBOSE("error code %x\n", error_code); + VERBOSE("Notify_desc = %d\n", do_notify); + + number_of_jobs_available = hw_get_no_finished_jobs(job_ring); + + /* Discard all jobs */ + jobs_no_to_discard = number_of_jobs_available; + + VERBOSE("JR pi[%d]i ci[%d]\n", job_ring->pidx, job_ring->cidx); + VERBOSE("Discarding desc = %d\n", jobs_no_to_discard); + + while (jobs_no_to_discard > discarded_descs_no) { + discarded_descs_no++; + /* Now increment the consumer index for the current job ring, + * AFTER saving job in temporary location! + * Increment the consumer index for the current job ring + */ + + job_ring->cidx = SEC_CIRCULAR_COUNTER(job_ring->cidx, + SEC_JOB_RING_SIZE); + + hw_remove_entries(job_ring, 1); + } + + if (do_notify == true) { + if (notified_descs == NULL) { + return; + } + *notified_descs = discarded_descs_no; + } +} + +/* return >0 in case of success + * -1 in case of error from SEC block + * 0 in case job not yet processed by SEC + * or Descriptor returned is NULL after dequeue + */ +int hw_poll_job_ring(struct sec_job_ring_t *job_ring, int32_t limit) +{ + int32_t jobs_no_to_notify = 0; + int32_t number_of_jobs_available = 0; + int32_t notified_descs_no = 0; + uint32_t error_descs_no = 0U; + uint32_t sec_error_code = 0U; + uint32_t do_driver_shutdown = false; + phys_addr_t *fnptr, *arg_addr; + user_callback usercall = NULL; + uint8_t *current_desc; + void *arg; + uintptr_t current_desc_addr; + phys_addr_t current_desc_loc; + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + inv_dcache_range((uintptr_t)job_ring->register_base_addr, sizeof(struct jobring_regs)); + dmbsy(); +#endif + + /* check here if any JR error that cannot be written + * in the output status word has occurred + */ + sec_error_code = hw_job_ring_error(job_ring); + if (unlikely(sec_error_code) != 0) { + ERROR("Error here itself %x\n", sec_error_code); + return -1; + } + /* Compute the number of notifications that need to be raised to UA + * If limit < 0 -> notify all done jobs + * If limit > total number of done jobs -> notify all done jobs + * If limit = 0 -> error + * If limit > 0 && limit < total number of done jobs -> notify a number + * of done jobs equal with limit + */ + + /*compute the number of jobs available in the job ring based on the + * producer and consumer index values. + */ + + number_of_jobs_available = hw_get_no_finished_jobs(job_ring); + jobs_no_to_notify = (limit < 0 || limit > number_of_jobs_available) ? + number_of_jobs_available : limit; + VERBOSE("JR - pi %d, ci %d, ", job_ring->pidx, job_ring->cidx); + VERBOSE("Jobs submitted %d", number_of_jobs_available); + VERBOSE("Jobs to notify %d\n", jobs_no_to_notify); + + while (jobs_no_to_notify > notified_descs_no) { + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + inv_dcache_range( + (uintptr_t)(&job_ring->output_ring[job_ring->cidx]), + sizeof(struct sec_outring_entry)); + dmbsy(); +#endif + + /* Get job status here */ + sec_error_code = + sec_in32(&(job_ring->output_ring[job_ring->cidx].status)); + + /* Get completed descriptor + */ + current_desc_loc = (uintptr_t) + &job_ring->output_ring[job_ring->cidx].desc; + current_desc_addr = sec_read_addr(current_desc_loc); + + current_desc = ptov((phys_addr_t *) current_desc_addr); + if (current_desc == 0) { + ERROR("No descriptor returned from SEC"); + assert(current_desc); + return 0; + } + /* now increment the consumer index for the current job ring, + * AFTER saving job in temporary location! + */ + job_ring->cidx = SEC_CIRCULAR_COUNTER(job_ring->cidx, + SEC_JOB_RING_SIZE); + + if (sec_error_code != 0) { + ERROR("desc at cidx %d\n ", job_ring->cidx); + ERROR("generated error %x\n", sec_error_code); + + sec_handle_desc_error(job_ring, + sec_error_code, + &error_descs_no, + &do_driver_shutdown); + hw_remove_entries(job_ring, 1); + + return -1; + } + /* Signal that the job has been processed & the slot is free */ + hw_remove_entries(job_ring, 1); + notified_descs_no++; + + arg_addr = (phys_addr_t *) (current_desc + + (MAX_DESC_SIZE_WORDS * sizeof(uint32_t))); + + fnptr = (phys_addr_t *) (current_desc + + (MAX_DESC_SIZE_WORDS * sizeof(uint32_t) + + sizeof(void *))); + + arg = (void *)*(arg_addr); + if (*fnptr != 0) { + VERBOSE("Callback Function called\n"); + usercall = (user_callback) *(fnptr); + (*usercall) ((uint32_t *) current_desc, + sec_error_code, arg, job_ring); + } + } + + return notified_descs_no; +} + +void sec_handle_desc_error(sec_job_ring_t *job_ring, + uint32_t sec_error_code, + uint32_t *notified_descs, + uint32_t *do_driver_shutdown) +{ + /* Analyze the SEC error on this job ring */ + hw_handle_job_ring_error(job_ring, sec_error_code); +} + +void flush_job_rings(void) +{ + struct sec_job_ring_t *job_ring = NULL; + int i = 0; + + for (i = 0; i < g_job_rings_no; i++) { + job_ring = &g_job_rings[i]; + /* Producer index is frozen. If consumer index is not equal + * with producer index, then we have descs to flush. + */ + while (job_ring->pidx != job_ring->cidx) { + hw_flush_job_ring(job_ring, false, 0, /* no error */ + NULL); + } + } +} + +int shutdown_job_ring(struct sec_job_ring_t *job_ring) +{ + int ret = 0; + + ret = hw_shutdown_job_ring(job_ring); + if (ret != 0) { + ERROR("Failed to shutdown hardware job ring\n"); + return ret; + } + + if (job_ring->coalescing_en != 0) { + hw_job_ring_disable_coalescing(job_ring); + } + + if (job_ring->jr_mode != SEC_NOTIFICATION_TYPE_POLL) { + ret = jr_disable_irqs(job_ring); + if (ret != 0) { + ERROR("Failed to disable irqs for job ring"); + return ret; + } + } + + return 0; +} + +int jr_enable_irqs(struct sec_job_ring_t *job_ring) +{ + uint32_t reg_val = 0U; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* Get the current value of the register */ + reg_val = sec_in32(®s->jrcfg1); + + /* Enable interrupts by disabling interrupt masking*/ + reg_val &= ~JR_REG_JRCFG_LO_IMSK_EN; + + /* Update parameters in HW */ + sec_out32(®s->jrcfg1, reg_val); + + VERBOSE("Enable interrupts on JR\n"); + + return 0; +} + +int jr_disable_irqs(struct sec_job_ring_t *job_ring) +{ + uint32_t reg_val = 0U; + struct jobring_regs *regs = + (struct jobring_regs *)job_ring->register_base_addr; + + /* Get the current value of the register */ + reg_val = sec_in32(®s->jrcfg1); + + /* Disable interrupts by enabling interrupt masking*/ + reg_val |= JR_REG_JRCFG_LO_IMSK_EN; + + /* Update parameters in HW */ + sec_out32(®s->jrcfg1, reg_val); + + VERBOSE("Disable interrupts on JR\n"); + + return 0; +} diff --git a/drivers/nxp/crypto/caam/src/sec_jr_driver.c b/drivers/nxp/crypto/caam/src/sec_jr_driver.c new file mode 100644 index 0000000..1fe7007 --- /dev/null +++ b/drivers/nxp/crypto/caam/src/sec_jr_driver.c @@ -0,0 +1,241 @@ +/* + * Copyright 2021 NXP + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <arch_helpers.h> +#include "caam.h" +#include <common/debug.h> +#include "jobdesc.h" +#include "nxp_timer.h" +#include "sec_hw_specific.h" +#include "sec_jr_driver.h" + + +/* Job rings used for communication with SEC HW */ +struct sec_job_ring_t g_job_rings[MAX_SEC_JOB_RINGS]; + +/* The current state of SEC user space driver */ +volatile sec_driver_state_t g_driver_state = SEC_DRIVER_STATE_IDLE; + +int g_job_rings_no; + +uint8_t ip_ring[SEC_DMA_MEM_INPUT_RING_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); +uint8_t op_ring[SEC_DMA_MEM_OUTPUT_RING_SIZE] __aligned(CACHE_WRITEBACK_GRANULE); + +void *init_job_ring(uint8_t jr_mode, + uint16_t irq_coalescing_timer, + uint8_t irq_coalescing_count, + void *reg_base_addr, uint32_t irq_id) +{ + struct sec_job_ring_t *job_ring = &g_job_rings[g_job_rings_no++]; + int ret = 0; + + job_ring->register_base_addr = reg_base_addr; + job_ring->jr_mode = jr_mode; + job_ring->irq_fd = irq_id; + + job_ring->input_ring = vtop(ip_ring); + memset(job_ring->input_ring, 0, SEC_DMA_MEM_INPUT_RING_SIZE); + + job_ring->output_ring = (struct sec_outring_entry *)vtop(op_ring); + memset(job_ring->output_ring, 0, SEC_DMA_MEM_OUTPUT_RING_SIZE); + + dsb(); + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)(job_ring->input_ring), + SEC_DMA_MEM_INPUT_RING_SIZE), + flush_dcache_range((uintptr_t)(job_ring->output_ring), + SEC_DMA_MEM_OUTPUT_RING_SIZE), + + dmbsy(); +#endif + /* Reset job ring in SEC hw and configure job ring registers */ + ret = hw_reset_job_ring(job_ring); + if (ret != 0) { + ERROR("Failed to reset hardware job ring\n"); + return NULL; + } + + if (jr_mode == SEC_NOTIFICATION_TYPE_IRQ) { + /* Enable IRQ if driver work sin interrupt mode */ + ERROR("Enabling DONE IRQ generation on job ring\n"); + ret = jr_enable_irqs(job_ring); + if (ret != 0) { + ERROR("Failed to enable irqs for job ring\n"); + return NULL; + } + } + if ((irq_coalescing_timer != 0) || (irq_coalescing_count != 0)) { + hw_job_ring_set_coalescing_param(job_ring, + irq_coalescing_timer, + irq_coalescing_count); + + hw_job_ring_enable_coalescing(job_ring); + job_ring->coalescing_en = 1; + } + + job_ring->jr_state = SEC_JOB_RING_STATE_STARTED; + + return job_ring; +} + +int sec_release(void) +{ + int i; + + /* Validate driver state */ + if (g_driver_state == SEC_DRIVER_STATE_RELEASE) { + ERROR("Driver release is already in progress"); + return SEC_DRIVER_RELEASE_IN_PROGRESS; + } + /* Update driver state */ + g_driver_state = SEC_DRIVER_STATE_RELEASE; + + /* If any descriptors in flight , poll and wait + * until all descriptors are received and silently discarded. + */ + + flush_job_rings(); + + for (i = 0; i < g_job_rings_no; i++) { + shutdown_job_ring(&g_job_rings[i]); + } + g_job_rings_no = 0; + g_driver_state = SEC_DRIVER_STATE_IDLE; + + return SEC_SUCCESS; +} + +int sec_jr_lib_init(void) +{ + /* Validate driver state */ + if (g_driver_state != SEC_DRIVER_STATE_IDLE) { + ERROR("Driver already initialized\n"); + return 0; + } + + memset(g_job_rings, 0, sizeof(g_job_rings)); + g_job_rings_no = 0; + + /* Update driver state */ + g_driver_state = SEC_DRIVER_STATE_STARTED; + return 0; +} + +int dequeue_jr(void *job_ring_handle, int32_t limit) +{ + int ret = 0; + int notified_descs_no = 0; + struct sec_job_ring_t *job_ring = (sec_job_ring_t *) job_ring_handle; + uint64_t start_time; + + /* Validate driver state */ + if (g_driver_state != SEC_DRIVER_STATE_STARTED) { + ERROR("Driver release in progress or driver not initialized\n"); + return -1; + } + + /* Validate input arguments */ + if (job_ring == NULL) { + ERROR("job_ring_handle is NULL\n"); + return -1; + } + if (((limit == 0) || (limit > SEC_JOB_RING_SIZE))) { + ERROR("Invalid limit parameter configuration\n"); + return -1; + } + + VERBOSE("JR Polling limit[%d]\n", limit); + + /* Poll job ring + * If limit < 0 -> poll JR until no more notifications are available. + * If limit > 0 -> poll JR until limit is reached. + */ + + start_time = get_timer_val(0); + + while (notified_descs_no == 0) { + /* Run hw poll job ring */ + notified_descs_no = hw_poll_job_ring(job_ring, limit); + if (notified_descs_no < 0) { + ERROR("Error polling SEC engine job ring "); + return notified_descs_no; + } + VERBOSE("Jobs notified[%d]. ", notified_descs_no); + + if (get_timer_val(start_time) >= CAAM_TIMEOUT) { + break; + } + } + + if (job_ring->jr_mode == SEC_NOTIFICATION_TYPE_IRQ) { + + /* Always enable IRQ generation when in pure IRQ mode */ + ret = jr_enable_irqs(job_ring); + if (ret != 0) { + ERROR("Failed to enable irqs for job ring"); + return ret; + } + } + return notified_descs_no; +} + +int enq_jr_desc(void *job_ring_handle, struct job_descriptor *jobdescr) +{ + struct sec_job_ring_t *job_ring; + + job_ring = (struct sec_job_ring_t *)job_ring_handle; + + /* Validate driver state */ + if (g_driver_state != SEC_DRIVER_STATE_STARTED) { + ERROR("Driver release in progress or driver not initialized\n"); + return -1; + } + + /* Check job ring state */ + if (job_ring->jr_state != SEC_JOB_RING_STATE_STARTED) { + ERROR("Job ring is currently resetting\n"); + return -1; + } + + if (SEC_JOB_RING_IS_FULL(job_ring->pidx, job_ring->cidx, + SEC_JOB_RING_SIZE, SEC_JOB_RING_SIZE)) { + ERROR("Job ring is full\n"); + return -1; + } + + /* Set ptr in input ring to current descriptor */ + sec_write_addr(&job_ring->input_ring[job_ring->pidx], + (phys_addr_t) vtop(jobdescr->desc)); + + dsb(); + +#if defined(SEC_MEM_NON_COHERENT) && defined(IMAGE_BL2) + flush_dcache_range((uintptr_t)(&job_ring->input_ring[job_ring->pidx]), + sizeof(phys_addr_t)); + + inv_dcache_range((uintptr_t)(&job_ring->output_ring[job_ring->cidx]), + sizeof(struct sec_outring_entry)); + dmbsy(); +#endif + /* Notify HW that a new job is enqueued */ + hw_enqueue_desc_on_job_ring( + (struct jobring_regs *)job_ring->register_base_addr, 1); + + /* increment the producer index for the current job ring */ + job_ring->pidx = SEC_CIRCULAR_COUNTER(job_ring->pidx, + SEC_JOB_RING_SIZE); + + return 0; +} |