diff --git a/drivers/crypto/CMakeLists.txt b/drivers/crypto/CMakeLists.txt index 07eb8a414ec9a3..68b63aef6ee9a6 100644 --- a/drivers/crypto/CMakeLists.txt +++ b/drivers/crypto/CMakeLists.txt @@ -5,6 +5,7 @@ zephyr_library_sources_ifdef(CONFIG_CRYPTO_TINYCRYPT_SHIM crypto_tc_shim.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_ATAES132A crypto_ataes132a.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_MBEDTLS_SHIM crypto_mtls_shim.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_STM32 crypto_stm32.c) +zephyr_library_sources_ifdef(CONFIG_CRYPTO_SMARTBOND crypto_smartbond.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_NRF_ECB crypto_nrf_ecb.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_INTEL_SHA crypto_intel_sha.c) zephyr_library_sources_ifdef(CONFIG_CRYPTO_NPCX_SHA crypto_npcx_sha.c) diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index c209ed226f7917..5342441038e66f 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -80,5 +80,6 @@ source "drivers/crypto/Kconfig.npcx" source "drivers/crypto/Kconfig.xec" source "drivers/crypto/Kconfig.it8xxx2" source "drivers/crypto/Kconfig.mcux_dcp" +source "drivers/crypto/Kconfig.smartbond" endif # CRYPTO diff --git a/drivers/crypto/Kconfig.smartbond b/drivers/crypto/Kconfig.smartbond new file mode 100644 index 00000000000000..c315fdc2e8edfd --- /dev/null +++ b/drivers/crypto/Kconfig.smartbond @@ -0,0 +1,16 @@ +# Smartbond Cryptographic Accelerator configuration options + +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +menuconfig CRYPTO_SMARTBOND + bool "Smartbond Cryptographic Accelerator driver" + depends on DT_HAS_RENESAS_SMARTBOND_CRYPTO_ENABLED + help + Enable Smartbond Cryptographic Accelerator driver. + +config CRYPTO_ASYNC + bool "Support ASYNC crypto operations." + depends on CRYPTO_SMARTBOND + help + Enable ASYNC crypto operations. diff --git a/drivers/crypto/crypto_smartbond.c b/drivers/crypto/crypto_smartbond.c new file mode 100644 index 00000000000000..06133d1dae8eed --- /dev/null +++ b/drivers/crypto/crypto_smartbond.c @@ -0,0 +1,957 @@ +/* + * Copyright (c) 2023 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_CRYPTO_LOG_LEVEL +#include + +LOG_MODULE_REGISTER(crypto_smartbond_crypto); + +#define DT_DRV_COMPAT renesas_smartbond_crypto + +#define IRQN DT_INST_IRQN(0) +#define IRQ_PRIO DT_INST_IRQ(0, priority) + +#if defined(CONFIG_CRYPTO_ASYNC) +#define CRYPTO_HW_CAPS (CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS | CAP_ASYNC_OPS | CAP_NO_IV_PREFIX) +#else +#define CRYPTO_HW_CAPS (CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS | CAP_NO_IV_PREFIX) +#endif + +#define SWAP32(_w) __builtin_bswap32(_w) + +#define CRYPTO_CTRL_REG_SET(_field, _val) \ + AES_HASH->CRYPTO_CTRL_REG = \ + (AES_HASH->CRYPTO_CTRL_REG & ~AES_HASH_CRYPTO_CTRL_REG_ ## _field ## _Msk) | \ + ((_val) << AES_HASH_CRYPTO_CTRL_REG_ ## _field ## _Pos) + +#define CRYPTO_CTRL_REG_GET(_field) \ + ((AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_ ## _field ## _Msk) >> \ + AES_HASH_CRYPTO_CTRL_REG_ ## _field ## _Pos) + + +struct crypto_smartbond_data { + /* + * Semaphore to provide mutual exlusion when a crypto session is requested. + */ + struct k_sem session_sem; + + /* + * Semaphore to provide mutual exlusion when a cryptographic task is requested. + * (a session should be requested at this point). + */ + struct k_sem device_sem; +#if defined(CONFIG_CRYPTO_ASYNC) + /* + * User-defined callbacks to be called upon completion of asynchronous + * cryptographic operations. Note that the AES and HASH modes can work + * complementary to each other. + */ + union { + cipher_completion_cb cipher_user_cb; + hash_completion_cb hash_user_cb; + }; + + /* + * Packet context should be stored during a session so that can be rertieved + * from within the crypto engine ISR context. + */ + union { + struct cipher_pkt *cipher_pkt; + struct hash_pkt *hash_pkt; + }; +#else + /* + * Semaphore used to block for as long as a synchronous cryptographic operation + * is in progress. + */ + struct k_sem sync_sem; +#endif +}; + +/* + * Status flag to indicate if the crypto engine resources have been granted. Note that the + * device integrates a single crypto engine instance. + */ +static bool in_use; + +static void crypto_smartbond_set_status(bool enable); + +static void smartbond_crypto_isr(const void *arg) +{ + struct crypto_smartbond_data *data = ((const struct device *)arg)->data; + uint32_t status = AES_HASH->CRYPTO_STATUS_REG; + + if (status & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_IRQ_ST_Msk) { + /* Clear interrupt source. Otherwise the handler will be fire constantly! */ + AES_HASH->CRYPTO_CLRIRQ_REG = 0x1; + +#if defined(CONFIG_CRYPTO_ASYNC) + /* Define the slected crypto mode (AES/HASH). */ + if (AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_CRYPTO_HASH_SEL_Msk) { + if (data->hash_user_cb) { + data->hash_user_cb(data->hash_pkt, status); + } + } else { + if (data->cipher_user_cb) { + data->cipher_user_cb(data->cipher_pkt, status); + } + } +#else + /* Designate the requested cryptographic tasks is finished. */ + k_sem_give(&data->sync_sem); +#endif + } +} + +static bool crypto_smartbond_lock_session(const struct device *dev) +{ + bool lock = false; + struct crypto_smartbond_data *data = dev->data; + + k_sem_take(&data->session_sem, K_FOREVER); + + if (!in_use) { + in_use = true; + crypto_smartbond_set_status(true); + lock = true; + } + + k_sem_give(&data->session_sem); + + return lock; +} + +static void crypto_smartbond_unlock_session(const struct device *dev) +{ + struct crypto_smartbond_data *data = dev->data; + + k_sem_take(&data->session_sem, K_FOREVER); + + if (in_use) { + in_use = false; + crypto_smartbond_set_status(false); + } + + k_sem_give(&data->session_sem); +} + +/* + * Input vector should comply with the following restrictions: + * + * mode | CRYPTO_MORE_IN = true | CRYPTO_MORE_IN = false + * ------------| -----------------------| ---------------------- + * ECB | multiple of 16 (bytes) | multiple of 16 (bytes) + * CBC | multiple of 16 | no restrictions + * CTR | multiple of 16 | no restrictions + * MD5 | multiple of 8 | no restrictions + * SHA_1 | multiple of 8 | no restrictions + * SHA_256_224 | multiple of 8 | no restrictions + * SHA_256 | multiple of 8 | no restrictions + * SHA_384 | multiple of 8 | no restrictions + * SHA_512 | multiple of 8 | no restrictions + * SHA_512_224 | multiple of 8 | no restrictions + * SHA_512_256 | multiple of 8 | no restrictions + */ +static int crypto_smartbond_check_in_restrictions(uint16_t in_len) +{ +#define CRYPTO_ALG_MD_ECB_MAGIC_0 0x00 +#define CRYPTO_ALG_MD_ECB_MAGIC_1 0x01 + + bool not_last_in_block = !!(AES_HASH->CRYPTO_CTRL_REG & + AES_HASH_CRYPTO_CTRL_REG_CRYPTO_MORE_IN_Msk); + + /* Define the slected crypto mode (AES/HASH). */ + if (AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_CRYPTO_HASH_SEL_Msk) { + if (not_last_in_block && (in_len & 0x7)) { + return -EINVAL; + } + } else { + if (in_len & 0xF) { + if (not_last_in_block) { + return -EINVAL; + } + + uint32_t crypto_mode = CRYPTO_CTRL_REG_GET(CRYPTO_ALG_MD); + + /* Check if AES mode is ECB */ + if (crypto_mode == CRYPTO_ALG_MD_ECB_MAGIC_0 || + crypto_mode == CRYPTO_ALG_MD_ECB_MAGIC_1) { + return -EINVAL; + } + } + } + + return 0; +} + +/* + * The driver model does not define the max. output length. As such, the max supported length + * per mode is applied. + */ +static int crypto_smartbond_hash_set_out_len(void) +{ + uint32_t hash_algo = (AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Msk); + + if (AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Msk) { + /* 64-bit HASH operations */ + switch (hash_algo) { + case 0x0: + /* SHA-384: 0..47 --> 1..48 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 47); + break; + case 0x1: + /* SHA-512: 0..63 --> 1..64 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 63); + break; + case 0x2: + /* SHA-512/224: 0..27 --> 1..28 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 27); + break; + case 0x3: + /* SHA-512/256: 0..31 --> 1..32 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 31); + break; + default: + break; + } + } else { + /* 32-bit HASH operations */ + switch (hash_algo) { + case 0x0: + /* MD5: 0..15 --> 1..16 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 15); + break; + case 0x1: + /* SHA-1: 0..19 --> 1..20 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 19); + break; + case 0x2: + /* SHA-256/224: 0..27 --> 1..28 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 27); + break; + case 0x3: + /* SHA-256: 0..31 --> 1..32 bytes */ + CRYPTO_CTRL_REG_SET(CRYPTO_HASH_OUT_LEN, 31); + break; + default: + break; + } + } + + /* Return the OUT size applied. */ + return CRYPTO_CTRL_REG_GET(CRYPTO_HASH_OUT_LEN) + 1; +} + +static uint32_t crypto_smartbond_swap_word(uint8_t *data) +{ + /* Check word boundaries of given address and if possible accellerate swapping */ + if ((uint32_t)data & 0x3) { + sys_mem_swap(data, sizeof(uint32_t)); + + return (*(uint32_t *)data); + } else { + return SWAP32(*(uint32_t *)data); + } +} + +static int crypto_smartbond_cipher_key_load(uint8_t *key, uint16_t key_len) +{ + if (key == NULL) { + return -EIO; + } + + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_AES_KEY_SZ_Msk); + + if (key_len == 32) { + AES_HASH->CRYPTO_CTRL_REG |= + (0x2 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_AES_KEY_SZ_Pos); + } else if (key_len == 24) { + AES_HASH->CRYPTO_CTRL_REG |= + (0x1 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_AES_KEY_SZ_Pos); + } else if (key_len == 16) { + /* Nothing to do */ + } else { + return -EINVAL; + } + + /* Key expansion is performed by the crypto engine */ + AES_HASH->CRYPTO_CTRL_REG |= AES_HASH_CRYPTO_CTRL_REG_CRYPTO_AES_KEXP_Msk; + + /* Check whether the cipher key is located in OTP (user keys segment) */ + if (IS_ADDRESS_USER_DATA_KEYS_SEGMENT((uint32_t)key)) { + + /* User keys segmnet can be accessed if not locked (stick bits are not set) */ + if (CRG_TOP->SECURE_BOOT_REG & CRG_TOP_SECURE_BOOT_REG_PROT_AES_KEY_READ_Msk) { + return -EIO; + } + + uint32_t cell_offset = da1469x_otp_address_to_cell_offset((uint32_t)key); + + da1469x_otp_read(cell_offset, + (void *)&AES_HASH->CRYPTO_KEYS_START, (uint32_t)key_len); + } else { + volatile uint32_t *kmem_ptr = &AES_HASH->CRYPTO_KEYS_START; + + do { + *(kmem_ptr++) = crypto_smartbond_swap_word(key); + key += 4; + key_len -= 4; + } while (key_len); + } + + return 0; +} + +static int crypto_smartbond_cipher_set_mode(enum cipher_mode mode) +{ + /* Select AES mode */ + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Msk | + AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Msk | + AES_HASH_CRYPTO_CTRL_REG_CRYPTO_HASH_SEL_Msk); + switch (mode) { + case CRYPTO_CIPHER_MODE_ECB: + /* Already done; CRYPTO_ALG_MD = 0x0 or 0x1 defines ECB. */ + break; + case CRYPTO_CIPHER_MODE_CTR: + AES_HASH->CRYPTO_CTRL_REG |= (0x2 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Pos); + break; + case CRYPTO_CIPHER_MODE_CBC: + AES_HASH->CRYPTO_CTRL_REG |= (0x3 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Pos); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int crypto_smartbond_hash_set_algo(enum hash_algo algo) +{ + /* Select HASH mode and reset to 32-bit mode */ + AES_HASH->CRYPTO_CTRL_REG = + (AES_HASH->CRYPTO_CTRL_REG & ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Msk | + AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Msk)) | + AES_HASH_CRYPTO_CTRL_REG_CRYPTO_HASH_SEL_Msk; + + switch (algo) { + case CRYPTO_HASH_ALGO_SHA224: + /* CRYPTO_ALG_MD = 0x0 defines 32-bit operations */ + AES_HASH->CRYPTO_CTRL_REG |= (0x2 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Pos); + break; + case CRYPTO_HASH_ALGO_SHA256: + /* CRYPTO_ALG_MD = 0x0 defines 32-bit operations */ + AES_HASH->CRYPTO_CTRL_REG |= (0x3 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Pos); + break; + case CRYPTO_HASH_ALGO_SHA384: + /* CRYPTO_ALG_MD = 0x1 defines 64-bit operations */ + AES_HASH->CRYPTO_CLRIRQ_REG |= AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Msk; + break; + case CRYPTO_HASH_ALGO_SHA512: + /* CRYPTO_ALG_MD = 0x1 defines 64-bit operations */ + AES_HASH->CRYPTO_CTRL_REG |= (AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_MD_Msk | + (0x1 << AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ALG_Pos)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int crypto_smartbond_set_in_out_buf(uint8_t *in_buf, uint8_t *out_buf, int len) +{ + if (in_buf == NULL) { + return -EIO; + } + + /* + * Input data can reside in any address space. Cryto DMA can only access physical addresses + * (not remapped). + */ + uint32_t phy_addr = black_orca_phy_addr((uint32_t)in_buf); + + if (IS_QSPIF_CACHED_ADDRESS(phy_addr)) { + /* + * To achiebe max. perfomance, peripherals should not access the Flash memory + * through the instruction cache controller (avoid cache misses). + */ + phy_addr += (MCU_QSPIF_M_BASE - MCU_QSPIF_M_CACHED_BASE); + } else if (IS_OTP_ADDRESS(phy_addr)) { + /* Peripherals should access the OTP memory through its peripheral address space. */ + phy_addr += (MCU_OTP_M_P_BASE - MCU_OTP_M_BASE); + } + + AES_HASH->CRYPTO_FETCH_ADDR_REG = phy_addr; + + /* + * OUT buffer can be NULL in case of fregmented data processing. CRYPTO_DEST_ADDR and + * CRYPTO_FETCH_ADDR are being updated as calculations prceed and OUT data are written + * into memory. + */ + if (out_buf) { + uint32_t remap_adr0 = CRG_TOP->SYS_CTRL_REG & CRG_TOP_SYS_CTRL_REG_REMAP_ADR0_Msk; + + /* + * OUT data can only be written in SYSRAM, non-cached remapped SYSRAM and + * cached non-remapped SYSRAM. + */ + if (IS_SYSRAM_ADDRESS(out_buf) || + (IS_REMAPPED_ADDRESS(out_buf) && remap_adr0 == 3)) { + AES_HASH->CRYPTO_DEST_ADDR_REG = black_orca_phy_addr((uint32_t)out_buf); + } else { + return -EIO; + } + } + + AES_HASH->CRYPTO_LEN_REG = len; + + return 0; +} + +static inline void crypto_smartbond_cipher_store_dep_data(uint32_t *words, uint32_t len_words) +{ + volatile uint32_t *mreg3 = &AES_HASH->CRYPTO_MREG3_REG; + + for (int i = 0; i < len_words; i++) { + *(mreg3--) = crypto_smartbond_swap_word((uint8_t *)(words++)); + } +} + +static int crypto_smartbond_cipher_set_mreg(uint8_t *mreg, uint32_t len_words) +{ + if (mreg == NULL || len_words == 0 || len_words > 4) { + return -EINVAL; + } + + AES_HASH->CRYPTO_MREG0_REG = 0; + AES_HASH->CRYPTO_MREG1_REG = 0; + AES_HASH->CRYPTO_MREG2_REG = 0; + AES_HASH->CRYPTO_MREG3_REG = 0; + + crypto_smartbond_cipher_store_dep_data((uint32_t *)mreg, len_words); + + return 0; +} + +static void crypto_smartbond_set_status(bool enable) +{ + unsigned int key; + + key = irq_lock(); + + if (enable) { + CRG_TOP->CLK_AMBA_REG |= (CRG_TOP_CLK_AMBA_REG_AES_CLK_ENABLE_Msk); + + AES_HASH->CRYPTO_CLRIRQ_REG = 0x1; + AES_HASH->CRYPTO_CTRL_REG |= (AES_HASH_CRYPTO_CTRL_REG_CRYPTO_IRQ_EN_Msk); + + NVIC_ClearPendingIRQ(IRQN); + NVIC_EnableIRQ(IRQN); + } else { + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_IRQ_EN_Msk); + AES_HASH->CRYPTO_CLRIRQ_REG = 0x1; + + NVIC_DisableIRQ(IRQN); + NVIC_ClearPendingIRQ(IRQN); + + CRG_TOP->CLK_AMBA_REG &= ~(CRG_TOP_CLK_AMBA_REG_AES_CLK_ENABLE_Msk); + } + + irq_unlock(key); +} + +static int crypto_smartbond_query_hw_caps(const struct device *dev) +{ + return CRYPTO_HW_CAPS; +} + +static int crypto_smartbond_cipher_ecb_handler(struct cipher_ctx *ctx, struct cipher_pkt *pkt) +{ + int ret; + struct crypto_smartbond_data *data = ctx->device->data; + + if ((AES_HASH->CRYPTO_STATUS_REG & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_INACTIVE_Msk) == 0) { + LOG_ERR("Crypto engine is already employed"); + return -EINVAL; + } + + if (pkt->out_buf_max < pkt->in_len) { + LOG_ERR("OUT buffer cannot be less that IN buffer"); + return -EINVAL; + } + + if (pkt->in_buf == NULL || pkt->out_buf == NULL) { + LOG_ERR("Missing IN or OUT buffer declaration"); + return -EIO; + } + + if (pkt->in_len > 16) { + LOG_ERR("For security reasons, do not operate on more than 16 bytes"); + return -EINVAL; + } + + k_sem_take(&data->device_sem, K_FOREVER); + + ret = crypto_smartbond_check_in_restrictions(pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN buffer size"); + k_sem_give(&data->device_sem); + return ret; + } + + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf, pkt->out_buf, pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN or OUT buffer location"); + k_sem_give(&data->device_sem); + return ret; + } + +#if defined(CONFIG_CRYPTO_ASYNC) + data->cipher_pkt = pkt; +#endif + + /* Start crypto processing */ + AES_HASH->CRYPTO_START_REG = 1; + +#if !defined(CONFIG_CRYPTO_ASYNC) + /* Wait for crypto to finish its task */ + k_sem_take(&data->sync_sem, K_FOREVER); +#endif + + /* Report that number of bytes operated upon. */ + pkt->out_len = pkt->in_len; + + k_sem_give(&data->device_sem); + + return 0; +} + +static int +crypto_smartbond_cipher_cbc_handler(struct cipher_ctx *ctx, struct cipher_pkt *pkt, uint8_t *iv) +{ + int ret; + int offset = 0; + struct crypto_smartbond_data *data = ctx->device->data; + bool is_op_encryption = + !!(AES_HASH->CRYPTO_CTRL_REG & AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ENCDEC_Msk); + + if ((AES_HASH->CRYPTO_STATUS_REG & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_INACTIVE_Msk) == 0) { + LOG_ERR("Crypto engine is already employed"); + return -EINVAL; + } + + if ((is_op_encryption && pkt->out_buf_max < (pkt->in_len + 16)) || + pkt->out_buf_max < (pkt->in_len - 16)) { + LOG_ERR("Invalid OUT buffer size"); + return -EINVAL; + } + + if (pkt->in_buf == NULL || pkt->out_buf == NULL) { + LOG_ERR("Missing IN or OUT buffer declaration"); + return -EIO; + } + + if ((ctx->flags & CAP_NO_IV_PREFIX) == 0) { + offset = 16; + if (is_op_encryption) { + /* Prefix IV to ciphertet unless CAP_NO_IV_PREFIX is set. */ + memcpy(pkt->out_buf, iv, offset); + } + } + + k_sem_take(&data->device_sem, K_FOREVER); + + ret = crypto_smartbond_check_in_restrictions(pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN buffer size"); + k_sem_give(&data->device_sem); + return ret; + } + + ret = crypto_smartbond_cipher_set_mreg(iv, 4); + if (ret < 0) { + LOG_ERR("Missing Initialization Vector (IV)"); + k_sem_give(&data->device_sem); + return ret; + } + + if (is_op_encryption) { + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf, + pkt->out_buf + offset, pkt->in_len); + } else { + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf + offset, + pkt->out_buf, pkt->in_len - offset); + } + + if (ret < 0) { + LOG_ERR("Unsupported IN or OUT buffer location"); + k_sem_give(&data->device_sem); + return ret; + } + +#if defined(CONFIG_CRYPTO_ASYNC) + data->cipher_pkt = pkt; +#endif + + /* Start crypto processing */ + AES_HASH->CRYPTO_START_REG = 1; + +#if !defined(CONFIG_CRYPTO_ASYNC) + /* Wait for crypto to finish its task */ + k_sem_take(&data->sync_sem, K_FOREVER); +#endif + + /* Report that number of bytes operated upon. */ + if (is_op_encryption) { + pkt->out_len = pkt->in_len + offset; + } else { + pkt->out_len = pkt->in_len - offset; + } + + k_sem_give(&data->device_sem); + + return 0; +} + +static int crypto_smartbond_cipher_ctr_handler(struct cipher_ctx *ctx, + struct cipher_pkt *pkt, uint8_t *ic) +{ + int ret; + /* ivlen + ctrlen = keylen, ctrl_len is expressed in bits */ + uint32_t iv_len = ctx->keylen - (ctx->mode_params.ctr_info.ctr_len >> 3); + struct crypto_smartbond_data *data = ctx->device->data; + + if ((AES_HASH->CRYPTO_STATUS_REG & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_INACTIVE_Msk) == 0) { + LOG_ERR("Crypto engine is already employed"); + return -EINVAL; + } + + if (pkt->out_buf_max < pkt->in_len) { + LOG_ERR("OUT buffer cannot be less that IN buffer"); + return -EINVAL; + } + + if (pkt->in_buf == NULL || pkt->out_buf == NULL) { + LOG_ERR("Missing IN or OUT buffer declaration"); + return -EIO; + } + + k_sem_take(&data->device_sem, K_FOREVER); + + ret = crypto_smartbond_check_in_restrictions(pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN buffer size"); + k_sem_give(&data->device_sem); + return ret; + } + + ret = crypto_smartbond_cipher_set_mreg(ic, iv_len >> 2); + if (ret < 0) { + LOG_ERR("Missing Initialization Counter (IC)"); + k_sem_give(&data->device_sem); + return ret; + } + + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf, pkt->out_buf, pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN or OUT buffer location"); + k_sem_give(&data->device_sem); + return ret; + } + +#if defined(CONFIG_CRYPTO_ASYNC) + data->cipher_pkt = pkt; +#endif + + /* Start crypto processing */ + AES_HASH->CRYPTO_START_REG = 1; + +#if !defined(CONFIG_CRYPTO_ASYNC) + /* Wait for crypto to finish its task */ + k_sem_take(&data->sync_sem, K_FOREVER); +#endif + + /* Report that number of bytes operated upon. */ + pkt->out_len = pkt->in_len; + + k_sem_give(&data->device_sem); + + return 0; +} + +static int crypto_smartbond_hash_handler(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish) +{ + int ret; + struct crypto_smartbond_data *data = ctx->device->data; + /* + * In case of framgemented data processing crypto status should be visible as busy for + * as long as the last block is to be processed. + */ + bool is_multipart_started = + (AES_HASH->CRYPTO_STATUS_REG & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_WAIT_FOR_IN_Msk) && + !(AES_HASH->CRYPTO_STATUS_REG & AES_HASH_CRYPTO_STATUS_REG_CRYPTO_INACTIVE_Msk); + + if (pkt->in_buf == NULL || (pkt->out_buf == NULL)) { + LOG_ERR("Missing IN or OUT buffer declaration"); + return -EIO; + } + + k_sem_take(&data->device_sem, K_FOREVER); + + /* Check if this is the last block to process or more blocks will follow */ + if (finish) { + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_MORE_IN_Msk); + } else { + AES_HASH->CRYPTO_CTRL_REG |= AES_HASH_CRYPTO_CTRL_REG_CRYPTO_MORE_IN_Msk; + } + + /* CRYPTO_MORE_IN should be updated prior to checking for IN restrictions! */ + ret = crypto_smartbond_check_in_restrictions(pkt->in_len); + if (ret < 0) { + LOG_ERR("Unsupported IN buffer size"); + k_sem_give(&data->device_sem); + return ret; + } + + if (!is_multipart_started) { + ret = crypto_smartbond_hash_set_out_len(); + if (ret < 0) { + LOG_ERR("Invalid OUT buffer size"); + k_sem_give(&data->device_sem); + return ret; + } + } + + if (!is_multipart_started) { + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf, pkt->out_buf, pkt->in_len); + } else { + /* Destination buffer is being updated as fragmented input is being processed. */ + ret = crypto_smartbond_set_in_out_buf(pkt->in_buf, NULL, pkt->in_len); + } + + if (ret < 0) { + LOG_ERR("Unsupported IN or OUT buffer location"); + k_sem_give(&data->device_sem); + return ret; + } + +#if defined(CONFIG_CRYPTO_ASYNC) + data->hash_pkt = pkt; +#endif + + /* Start hash processing */ + AES_HASH->CRYPTO_START_REG = 1; + +#if !defined(CONFIG_CRYPTO_ASYNC) + k_sem_take(&data->sync_sem, K_FOREVER); +#endif + + k_sem_give(&data->device_sem); + + return 0; +} + +static int +crypto_smartbond_cipher_begin_session(const struct device *dev, struct cipher_ctx *ctx, + enum cipher_algo algo, enum cipher_mode mode, enum cipher_op op_type) +{ + int ret; + + if (ctx->flags & ~(CRYPTO_HW_CAPS)) { + LOG_ERR("Unsupported flag"); + return -EINVAL; + } + + if (algo != CRYPTO_CIPHER_ALGO_AES) { + LOG_ERR("Unsupported cipher algo"); + return -EINVAL; + } + + if (!crypto_smartbond_lock_session(dev)) { + LOG_ERR("No free session for now"); + return -ENOSPC; + } + + ret = crypto_smartbond_cipher_key_load(ctx->key.bit_stream, ctx->keylen); + if (ret < 0) { + LOG_ERR("Invalid key length or key cannot be accessed"); + crypto_smartbond_unlock_session(dev); + return ret; + } + + ret = crypto_smartbond_cipher_set_mode(mode); + if (ret < 0) { + LOG_ERR("Unsupported cipher mode"); + crypto_smartbond_unlock_session(dev); + return ret; + } + + if (op_type == CRYPTO_CIPHER_OP_ENCRYPT) { + AES_HASH->CRYPTO_CTRL_REG |= AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ENCDEC_Msk; + } else { + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_ENCDEC_Msk); + } + + /* IN buffer fragmentation is not supported by the driver model */ + AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_MORE_IN_Msk); + + switch (mode) { + case CRYPTO_CIPHER_MODE_ECB: + ctx->ops.block_crypt_hndlr = crypto_smartbond_cipher_ecb_handler; + break; + case CRYPTO_CIPHER_MODE_CBC: + ctx->ops.cbc_crypt_hndlr = crypto_smartbond_cipher_cbc_handler; + break; + case CRYPTO_CIPHER_MODE_CTR: + ctx->ops.ctr_crypt_hndlr = crypto_smartbond_cipher_ctr_handler; + break; + default: + break; + } + + ctx->drv_sessn_state = NULL; + + return 0; +} + +static int crypto_smartbond_cipher_free_session(const struct device *dev, struct cipher_ctx *ctx) +{ + ARG_UNUSED(ctx); + crypto_smartbond_unlock_session(dev); + + return 0; +} + +#if defined(CONFIG_CRYPTO_ASYNC) +static int +crypto_smartbond_cipher_set_async_callback(const struct device *dev, cipher_completion_cb cb) +{ + struct crypto_smartbond_data *data = dev->data; + + data->cipher_user_cb = cb; + + return 0; +} +#endif + +static int +crypto_smartbond_hash_begin_session(const struct device *dev, + struct hash_ctx *ctx, enum hash_algo algo) +{ + int ret; + + if (ctx->flags & ~(CRYPTO_HW_CAPS)) { + LOG_ERR("Unsupported flag"); + return -EINVAL; + } + + if (!crypto_smartbond_lock_session(dev)) { + LOG_ERR("No free session for now"); + return -ENOSPC; + } + + /* + * Crypto should be disabled only if not used in other sessions. In case of failure, + * developer should next free the current session. + */ + crypto_smartbond_set_status(true); + + ret = crypto_smartbond_hash_set_algo(algo); + if (ret < 0) { + LOG_ERR("Unsupported HASH algo"); + crypto_smartbond_unlock_session(dev); + return ret; + } + + ctx->hash_hndlr = crypto_smartbond_hash_handler; + + ctx->drv_sessn_state = NULL; + + return 0; +} + +static int crypto_smartbond_hash_free_session(const struct device *dev, struct hash_ctx *ctx) +{ + ARG_UNUSED(ctx); + crypto_smartbond_unlock_session(dev); + + return 0; +} + +#if defined(CONFIG_CRYPTO_ASYNC) +static int +crypto_smartbond_hash_set_async_callback(const struct device *dev, hash_completion_cb cb) +{ + struct crypto_smartbond_data *data = dev->data; + + data->hash_user_cb = cb; + + return 0; +} +#endif + +static struct crypto_driver_api crypto_smartbond_driver_api = { + .cipher_begin_session = crypto_smartbond_cipher_begin_session, + .cipher_free_session = crypto_smartbond_cipher_free_session, +#if defined(CONFIG_CRYPTO_ASYNC) + .cipher_async_callback_set = crypto_smartbond_cipher_set_async_callback, +#endif + .hash_begin_session = crypto_smartbond_hash_begin_session, + .hash_free_session = crypto_smartbond_hash_free_session, +#if defined(CONFIG_CRYPTO_ASYNC) + .hash_async_callback_set = crypto_smartbond_hash_set_async_callback, +#endif + .query_hw_caps = crypto_smartbond_query_hw_caps +}; + +static int crypto_smartbond_init(const struct device *dev) +{ + struct crypto_smartbond_data *data = dev->data; + + /* Semaphore used during sessions (begin/free) */ + k_sem_init(&data->session_sem, 1, 1); + + /* Semaphore used to employ the crypto device */ + k_sem_init(&data->device_sem, 1, 1); + +#if !defined(CONFIG_CRYPTO_ASYNC) + /* Sempahore used when sync operations are enabled */ + k_sem_init(&data->sync_sem, 0, 1); +#endif + + IRQ_CONNECT(IRQN, IRQ_PRIO, smartbond_crypto_isr, DEVICE_DT_INST_GET(0), 0); + + crypto_smartbond_set_status(false); + + return 0; +} + +/* + * There is only one instance integrated on the SoC. Just in case that assumption becomes invalid + * in the future, we use a BUILD_ASSERT(). + */ +#define CRYPTO_INIT(inst) \ + BUILD_ASSERT((inst) == 0, \ + "multiple instances are not supported"); \ + static struct crypto_smartbond_data crypto_smartbond_data_##inst; \ + DEVICE_DT_INST_DEFINE(0, \ + crypto_smartbond_init, NULL, \ + &crypto_smartbond_data_##inst, NULL, \ + POST_KERNEL, \ + CONFIG_CRYPTO_INIT_PRIORITY, \ + &crypto_smartbond_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(CRYPTO_INIT) diff --git a/dts/bindings/crypto/renesas,smartbond-crypto.yaml b/dts/bindings/crypto/renesas,smartbond-crypto.yaml new file mode 100644 index 00000000000000..7bee8dd4f9b8a7 --- /dev/null +++ b/dts/bindings/crypto/renesas,smartbond-crypto.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2023 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +include: base.yaml + +description: Renesas SmartBond(tm) CRYPTO + +compatible: "renesas,smartbond-crypto" + +properties: + reg: + required: true + + interrupts: + required: true