/* * Copyright (c) 2023 Renesas Electronics Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include LOG_MODULE_REGISTER(crypto_smartbond_crypto, CONFIG_CRYPTO_LOG_LEVEL); #define DT_DRV_COMPAT renesas_smartbond_crypto #define SMARTBOND_IRQN DT_INST_IRQN(0) #define SMARTBOND_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) __REV(_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 inline void crypto_smartbond_pm_policy_state_lock_get(const struct device *dev) { /* * Prevent the SoC from entering the normal sleep state as PDC does not support * waking up the application core following AES/HASH events. */ pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); } static inline void crypto_smartbond_pm_policy_state_lock_put(const struct device *dev) { /* Allow the SoC to enter the normal sleep state once AES/HASH operations are done. */ pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); } 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; /* Prevent sleep as long as a cryptographic session is in place */ da1469x_pd_acquire(MCU_PD_DOMAIN_SYS); crypto_smartbond_pm_policy_state_lock_get(dev); 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); crypto_smartbond_pm_policy_state_lock_put(dev); da1469x_pd_release_nowait(MCU_PD_DOMAIN_SYS); } 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) { return SWAP32(sys_get_le32(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); irq_enable(SMARTBOND_IRQN); } else { AES_HASH->CRYPTO_CTRL_REG &= ~(AES_HASH_CRYPTO_CTRL_REG_CRYPTO_IRQ_EN_Msk); AES_HASH->CRYPTO_CLRIRQ_REG = 0x1; irq_disable(SMARTBOND_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; } /* First check if the requested cryptographic algo is supported */ ret = crypto_smartbond_cipher_set_mode(mode); if (ret < 0) { LOG_ERR("Unsupported cipher mode"); crypto_smartbond_unlock_session(dev); return ret; } ret = crypto_smartbond_cipher_key_load((uint8_t *)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; } 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 DEVICE_API(crypto, 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 }; #if defined(CONFIG_PM_DEVICE) static int crypto_smartbond_pm_action(const struct device *dev, enum pm_device_action action) { int ret = 0; switch (action) { case PM_DEVICE_ACTION_SUSPEND: /* * No need to perform any actions here as the AES/HASH controller * should already be turned off. */ break; case PM_DEVICE_ACTION_RESUME: /* * No need to perform any actions here as the AES/HASH controller * will be initialized upon acquiring a cryptographic session. */ break; default: return -ENOTSUP; } return ret; } #endif 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(SMARTBOND_IRQN, SMARTBOND_IRQ_PRIO, smartbond_crypto_isr, DEVICE_DT_INST_GET(0), 0); /* Controller should be initialized once a crypyographic session is requested */ 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 SMARTBOND_CRYPTO_INIT(inst) \ BUILD_ASSERT((inst) == 0, \ "multiple instances are not supported"); \ \ PM_DEVICE_DT_INST_DEFINE(inst, crypto_smartbond_pm_action); \ \ static struct crypto_smartbond_data crypto_smartbond_data_##inst; \ \ DEVICE_DT_INST_DEFINE(0, \ crypto_smartbond_init, \ PM_DEVICE_DT_INST_GET(inst), \ &crypto_smartbond_data_##inst, NULL, \ POST_KERNEL, \ CONFIG_CRYPTO_INIT_PRIORITY, \ &crypto_smartbond_driver_api); DT_INST_FOREACH_STATUS_OKAY(SMARTBOND_CRYPTO_INIT)