/* * Copyright (c) 2022, STMicroelectronics - All Rights Reserved * * SPDX-License-Identifier: BSD-3-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #define UINT8_BIT 8U #define AES_BLOCK_SIZE_BIT 128U #define AES_BLOCK_SIZE (AES_BLOCK_SIZE_BIT / UINT8_BIT) #define AES_KEYSIZE_128 16U #define AES_KEYSIZE_256 32U #define AES_IVSIZE 16U /* SAES control register */ #define _SAES_CR 0x0U /* SAES status register */ #define _SAES_SR 0x04U /* SAES data input register */ #define _SAES_DINR 0x08U /* SAES data output register */ #define _SAES_DOUTR 0x0CU /* SAES key registers [0-3] */ #define _SAES_KEYR0 0x10U #define _SAES_KEYR1 0x14U #define _SAES_KEYR2 0x18U #define _SAES_KEYR3 0x1CU /* SAES initialization vector registers [0-3] */ #define _SAES_IVR0 0x20U #define _SAES_IVR1 0x24U #define _SAES_IVR2 0x28U #define _SAES_IVR3 0x2CU /* SAES key registers [4-7] */ #define _SAES_KEYR4 0x30U #define _SAES_KEYR5 0x34U #define _SAES_KEYR6 0x38U #define _SAES_KEYR7 0x3CU /* SAES suspend registers [0-7] */ #define _SAES_SUSPR0 0x40U #define _SAES_SUSPR1 0x44U #define _SAES_SUSPR2 0x48U #define _SAES_SUSPR3 0x4CU #define _SAES_SUSPR4 0x50U #define _SAES_SUSPR5 0x54U #define _SAES_SUSPR6 0x58U #define _SAES_SUSPR7 0x5CU /* SAES Interrupt Enable Register */ #define _SAES_IER 0x300U /* SAES Interrupt Status Register */ #define _SAES_ISR 0x304U /* SAES Interrupt Clear Register */ #define _SAES_ICR 0x308U /* SAES control register fields */ #define _SAES_CR_RESET_VALUE 0x0U #define _SAES_CR_IPRST BIT(31) #define _SAES_CR_KEYSEL_MASK GENMASK(30, 28) #define _SAES_CR_KEYSEL_SHIFT 28U #define _SAES_CR_KEYSEL_SOFT 0x0U #define _SAES_CR_KEYSEL_DHUK 0x1U #define _SAES_CR_KEYSEL_BHK 0x2U #define _SAES_CR_KEYSEL_BHU_XOR_BH_K 0x4U #define _SAES_CR_KEYSEL_TEST 0x7U #define _SAES_CR_KSHAREID_MASK GENMASK(27, 26) #define _SAES_CR_KSHAREID_SHIFT 26U #define _SAES_CR_KSHAREID_CRYP 0x0U #define _SAES_CR_KEYMOD_MASK GENMASK(25, 24) #define _SAES_CR_KEYMOD_SHIFT 24U #define _SAES_CR_KEYMOD_NORMAL 0x0U #define _SAES_CR_KEYMOD_WRAPPED 0x1U #define _SAES_CR_KEYMOD_SHARED 0x2U #define _SAES_CR_NPBLB_MASK GENMASK(23, 20) #define _SAES_CR_NPBLB_SHIFT 20U #define _SAES_CR_KEYPROT BIT(19) #define _SAES_CR_KEYSIZE BIT(18) #define _SAES_CR_GCMPH_MASK GENMASK(14, 13) #define _SAES_CR_GCMPH_SHIFT 13U #define _SAES_CR_GCMPH_INIT 0U #define _SAES_CR_GCMPH_HEADER 1U #define _SAES_CR_GCMPH_PAYLOAD 2U #define _SAES_CR_GCMPH_FINAL 3U #define _SAES_CR_DMAOUTEN BIT(12) #define _SAES_CR_DMAINEN BIT(11) #define _SAES_CR_CHMOD_MASK (BIT(16) | GENMASK(6, 5)) #define _SAES_CR_CHMOD_SHIFT 5U #define _SAES_CR_CHMOD_ECB 0x0U #define _SAES_CR_CHMOD_CBC 0x1U #define _SAES_CR_CHMOD_CTR 0x2U #define _SAES_CR_CHMOD_GCM 0x3U #define _SAES_CR_CHMOD_GMAC 0x3U #define _SAES_CR_CHMOD_CCM 0x800U #define _SAES_CR_MODE_MASK GENMASK(4, 3) #define _SAES_CR_MODE_SHIFT 3U #define _SAES_CR_MODE_ENC 0U #define _SAES_CR_MODE_KEYPREP 1U #define _SAES_CR_MODE_DEC 2U #define _SAES_CR_DATATYPE_MASK GENMASK(2, 1) #define _SAES_CR_DATATYPE_SHIFT 1U #define _SAES_CR_DATATYPE_NONE 0U #define _SAES_CR_DATATYPE_HALF_WORD 1U #define _SAES_CR_DATATYPE_BYTE 2U #define _SAES_CR_DATATYPE_BIT 3U #define _SAES_CR_EN BIT(0) /* SAES status register fields */ #define _SAES_SR_KEYVALID BIT(7) #define _SAES_SR_BUSY BIT(3) #define _SAES_SR_WRERR BIT(2) #define _SAES_SR_RDERR BIT(1) #define _SAES_SR_CCF BIT(0) /* SAES interrupt registers fields */ #define _SAES_I_RNG_ERR BIT(3) #define _SAES_I_KEY_ERR BIT(2) #define _SAES_I_RW_ERR BIT(1) #define _SAES_I_CC BIT(0) #define SAES_TIMEOUT_US 100000U #define TIMEOUT_US_1MS 1000U #define SAES_RESET_DELAY 20U #define IS_CHAINING_MODE(mod, cr) \ (((cr) & _SAES_CR_CHMOD_MASK) == (_SAES_CR_CHMOD_##mod << _SAES_CR_CHMOD_SHIFT)) #define SET_CHAINING_MODE(mod, cr) \ mmio_clrsetbits_32((cr), _SAES_CR_CHMOD_MASK, _SAES_CR_CHMOD_##mod << _SAES_CR_CHMOD_SHIFT) #define pragma weak stm32_saes_get_platdata static struct stm32_saes_platdata saes_pdata; int stm32_saes_get_platdata(struct stm32_saes_platdata *pdata) { return -ENODEV; } static int stm32_saes_parse_fdt(struct stm32_saes_platdata *pdata) { int node; struct dt_node_info info; void *fdt; if (fdt_get_address(&fdt) == 0) { return -FDT_ERR_NOTFOUND; } node = dt_get_node(&info, -1, DT_SAES_COMPAT); if (node < 0) { ERROR("No SAES entry in DT\n"); return -FDT_ERR_NOTFOUND; } if (info.status == DT_DISABLED) { return -FDT_ERR_NOTFOUND; } if ((info.base == 0U) || (info.clock < 0) || (info.reset < 0)) { return -FDT_ERR_BADVALUE; } pdata->base = (uintptr_t)info.base; pdata->clock_id = (unsigned long)info.clock; pdata->reset_id = (unsigned int)info.reset; return 0; } static bool does_chaining_mode_need_iv(uint32_t cr) { return !(IS_CHAINING_MODE(ECB, cr)); } static bool is_encrypt(uint32_t cr) { return (cr & _SAES_CR_MODE_MASK) == (_SAES_CR_MODE_ENC << _SAES_CR_MODE_SHIFT); } static bool is_decrypt(uint32_t cr) { return (cr & _SAES_CR_MODE_MASK) == (_SAES_CR_MODE_DEC << _SAES_CR_MODE_SHIFT); } static int wait_computation_completed(uintptr_t base) { uint64_t timeout = timeout_init_us(SAES_TIMEOUT_US); while ((mmio_read_32(base + _SAES_SR) & _SAES_SR_CCF) != _SAES_SR_CCF) { if (timeout_elapsed(timeout)) { WARN("%s: timeout\n", __func__); return -ETIMEDOUT; } } return 0; } static void clear_computation_completed(uintptr_t base) { mmio_setbits_32(base + _SAES_ICR, _SAES_I_CC); } static int saes_start(struct stm32_saes_context *ctx) { uint64_t timeout; /* Reset IP */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_IPRST); udelay(SAES_RESET_DELAY); mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_IPRST); timeout = timeout_init_us(SAES_TIMEOUT_US); while ((mmio_read_32(ctx->base + _SAES_SR) & _SAES_SR_BUSY) == _SAES_SR_BUSY) { if (timeout_elapsed(timeout)) { WARN("%s: timeout\n", __func__); return -ETIMEDOUT; } } return 0; } static void saes_end(struct stm32_saes_context *ctx, int prev_error) { if (prev_error != 0) { /* Reset IP */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_IPRST); udelay(SAES_RESET_DELAY); mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_IPRST); } /* Disable the SAES peripheral */ mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); } static void saes_write_iv(struct stm32_saes_context *ctx) { /* If chaining mode need to restore IV */ if (does_chaining_mode_need_iv(ctx->cr)) { uint8_t i; /* Restore the _SAES_IVRx */ for (i = 0U; i < AES_IVSIZE / sizeof(uint32_t); i++) { mmio_write_32(ctx->base + _SAES_IVR0 + i * sizeof(uint32_t), ctx->iv[i]); } } } static void saes_write_key(struct stm32_saes_context *ctx) { /* Restore the _SAES_KEYRx if SOFTWARE key */ if ((ctx->cr & _SAES_CR_KEYSEL_MASK) == (_SAES_CR_KEYSEL_SOFT << _SAES_CR_KEYSEL_SHIFT)) { uint8_t i; for (i = 0U; i < AES_KEYSIZE_128 / sizeof(uint32_t); i++) { mmio_write_32(ctx->base + _SAES_KEYR0 + i * sizeof(uint32_t), ctx->key[i]); } if ((ctx->cr & _SAES_CR_KEYSIZE) == _SAES_CR_KEYSIZE) { for (i = 0U; i < (AES_KEYSIZE_256 / 2U) / sizeof(uint32_t); i++) { mmio_write_32(ctx->base + _SAES_KEYR4 + i * sizeof(uint32_t), ctx->key[i + 4U]); } } } } static int saes_prepare_key(struct stm32_saes_context *ctx) { /* Disable the SAES peripheral */ mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); /* Set key size */ if ((ctx->cr & _SAES_CR_KEYSIZE) != 0U) { mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_KEYSIZE); } else { mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_KEYSIZE); } saes_write_key(ctx); /* For ECB/CBC decryption, key preparation mode must be selected to populate the key */ if ((IS_CHAINING_MODE(ECB, ctx->cr) || IS_CHAINING_MODE(CBC, ctx->cr)) && is_decrypt(ctx->cr)) { int ret; /* Select Mode 2 */ mmio_clrsetbits_32(ctx->base + _SAES_CR, _SAES_CR_MODE_MASK, _SAES_CR_MODE_KEYPREP << _SAES_CR_MODE_SHIFT); /* Enable SAES */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); /* Wait Computation completed */ ret = wait_computation_completed(ctx->base); if (ret != 0) { return ret; } clear_computation_completed(ctx->base); /* Set Mode 3 */ mmio_clrsetbits_32(ctx->base + _SAES_CR, _SAES_CR_MODE_MASK, _SAES_CR_MODE_DEC << _SAES_CR_MODE_SHIFT); } return 0; } static int save_context(struct stm32_saes_context *ctx) { if ((mmio_read_32(ctx->base + _SAES_SR) & _SAES_SR_CCF) != 0U) { /* Device should not be in a processing phase */ return -EINVAL; } /* Save CR */ ctx->cr = mmio_read_32(ctx->base + _SAES_CR); /* If chaining mode need to save current IV */ if (does_chaining_mode_need_iv(ctx->cr)) { uint8_t i; /* Save IV */ for (i = 0U; i < AES_IVSIZE / sizeof(uint32_t); i++) { ctx->iv[i] = mmio_read_32(ctx->base + _SAES_IVR0 + i * sizeof(uint32_t)); } } /* Disable the SAES peripheral */ mmio_clrbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); return 0; } /* To resume the processing of a message */ static int restore_context(struct stm32_saes_context *ctx) { int ret; /* IP should be disabled */ if ((mmio_read_32(ctx->base + _SAES_CR) & _SAES_CR_EN) != 0U) { VERBOSE("%s: Device is still enabled\n", __func__); return -EINVAL; } /* Reset internal state */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_IPRST); /* Restore the _SAES_CR */ mmio_write_32(ctx->base + _SAES_CR, ctx->cr); /* Preparation decrypt key */ ret = saes_prepare_key(ctx); if (ret != 0) { return ret; } saes_write_iv(ctx); /* Enable the SAES peripheral */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); return 0; } /** * @brief Initialize SAES driver. * @param None. * @retval 0 if OK; negative value else. */ int stm32_saes_driver_init(void) { int err; err = stm32_saes_parse_fdt(&saes_pdata); if (err != 0) { err = stm32_saes_get_platdata(&saes_pdata); if (err != 0) { return err; } } clk_enable(saes_pdata.clock_id); if (stm32mp_reset_assert(saes_pdata.reset_id, TIMEOUT_US_1MS) != 0) { panic(); } udelay(SAES_RESET_DELAY); if (stm32mp_reset_deassert(saes_pdata.reset_id, TIMEOUT_US_1MS) != 0) { panic(); } return 0; } /** * @brief Start a AES computation. * @param ctx: SAES process context * @param is_dec: true if decryption, false if encryption * @param ch_mode: define the chaining mode * @param key_select: define where the key comes from. * @param key: pointer to key (if key_select is KEY_SOFT, else unused) * @param key_size: key size * @param iv: pointer to initialization vectore (unsed if ch_mode is ECB) * @param iv_size: iv size * @note this function doesn't access to hardware but store in ctx the values * * @retval 0 if OK; negative value else. */ int stm32_saes_init(struct stm32_saes_context *ctx, bool is_dec, enum stm32_saes_chaining_mode ch_mode, enum stm32_saes_key_selection key_select, const void *key, size_t key_size, const void *iv, size_t iv_size) { unsigned int i; const uint32_t *iv_u32; const uint32_t *key_u32; ctx->assoc_len = 0U; ctx->load_len = 0U; ctx->base = saes_pdata.base; ctx->cr = _SAES_CR_RESET_VALUE; /* We want buffer to be u32 aligned */ assert((uintptr_t)key % __alignof__(uint32_t) == 0); assert((uintptr_t)iv % __alignof__(uint32_t) == 0); iv_u32 = iv; key_u32 = key; if (is_dec) { /* Save Mode 3 = decrypt */ mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_MODE_MASK, _SAES_CR_MODE_DEC << _SAES_CR_MODE_SHIFT); } else { /* Save Mode 1 = crypt */ mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_MODE_MASK, _SAES_CR_MODE_ENC << _SAES_CR_MODE_SHIFT); } /* Save chaining mode */ switch (ch_mode) { case STM32_SAES_MODE_ECB: SET_CHAINING_MODE(ECB, (uintptr_t)&(ctx->cr)); break; case STM32_SAES_MODE_CBC: SET_CHAINING_MODE(CBC, (uintptr_t)&(ctx->cr)); break; case STM32_SAES_MODE_CTR: SET_CHAINING_MODE(CTR, (uintptr_t)&(ctx->cr)); break; case STM32_SAES_MODE_GCM: SET_CHAINING_MODE(GCM, (uintptr_t)&(ctx->cr)); break; case STM32_SAES_MODE_CCM: SET_CHAINING_MODE(CCM, (uintptr_t)&(ctx->cr)); break; default: return -EINVAL; } /* We will use HW Byte swap (_SAES_CR_DATATYPE_BYTE) for data. * so we won't need to * htobe32(data) before write to DINR * nor * be32toh after reading from DOUTR * * But note that wrap key only accept _SAES_CR_DATATYPE_NONE */ mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_DATATYPE_MASK, _SAES_CR_DATATYPE_BYTE << _SAES_CR_DATATYPE_SHIFT); /* Configure keysize */ switch (key_size) { case AES_KEYSIZE_128: mmio_clrbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSIZE); break; case AES_KEYSIZE_256: mmio_setbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSIZE); break; default: return -EINVAL; } /* Configure key */ switch (key_select) { case STM32_SAES_KEY_SOFT: mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSEL_MASK, _SAES_CR_KEYSEL_SOFT << _SAES_CR_KEYSEL_SHIFT); /* Save key */ switch (key_size) { case AES_KEYSIZE_128: /* First 16 bytes == 4 u32 */ for (i = 0U; i < AES_KEYSIZE_128 / sizeof(uint32_t); i++) { mmio_write_32((uintptr_t)(ctx->key + i), htobe32(key_u32[3 - i])); /* /!\ we save the key in HW byte order * and word order : key[i] is for _SAES_KEYRi */ } break; case AES_KEYSIZE_256: for (i = 0U; i < AES_KEYSIZE_256 / sizeof(uint32_t); i++) { mmio_write_32((uintptr_t)(ctx->key + i), htobe32(key_u32[7 - i])); /* /!\ we save the key in HW byte order * and word order : key[i] is for _SAES_KEYRi */ } break; default: return -EINVAL; } break; case STM32_SAES_KEY_DHU: mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSEL_MASK, _SAES_CR_KEYSEL_DHUK << _SAES_CR_KEYSEL_SHIFT); break; case STM32_SAES_KEY_BH: mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSEL_MASK, _SAES_CR_KEYSEL_BHK << _SAES_CR_KEYSEL_SHIFT); break; case STM32_SAES_KEY_BHU_XOR_BH: mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSEL_MASK, _SAES_CR_KEYSEL_BHU_XOR_BH_K << _SAES_CR_KEYSEL_SHIFT); break; case STM32_SAES_KEY_WRAPPED: mmio_clrsetbits_32((uintptr_t)&(ctx->cr), _SAES_CR_KEYSEL_MASK, _SAES_CR_KEYSEL_SOFT << _SAES_CR_KEYSEL_SHIFT); break; default: return -EINVAL; } /* Save IV */ if (ch_mode != STM32_SAES_MODE_ECB) { if ((iv == NULL) || (iv_size != AES_IVSIZE)) { return -EINVAL; } for (i = 0U; i < AES_IVSIZE / sizeof(uint32_t); i++) { mmio_write_32((uintptr_t)(ctx->iv + i), htobe32(iv_u32[3 - i])); /* /!\ We save the iv in HW byte order */ } } return saes_start(ctx); } /** * @brief Update (or start) a AES authentificate process of associated data (CCM or GCM). * @param ctx: SAES process context * @param last_block: true if last assoc data block * @param data: pointer to associated data * @param data_size: data size * * @retval 0 if OK; negative value else. */ int stm32_saes_update_assodata(struct stm32_saes_context *ctx, bool last_block, uint8_t *data, size_t data_size) { int ret; uint32_t *data_u32; unsigned int i = 0U; /* We want buffers to be u32 aligned */ assert((uintptr_t)data % __alignof__(uint32_t) == 0); data_u32 = (uint32_t *)data; /* Init phase */ ret = restore_context(ctx); if (ret != 0) { goto out; } ret = wait_computation_completed(ctx->base); if (ret != 0) { return ret; } clear_computation_completed(ctx->base); if ((data == NULL) || (data_size == 0U)) { /* No associated data */ /* ret already = 0 */ goto out; } /* There is an header/associated data phase */ mmio_clrsetbits_32(ctx->base + _SAES_CR, _SAES_CR_GCMPH_MASK, _SAES_CR_GCMPH_HEADER << _SAES_CR_GCMPH_SHIFT); /* Enable the SAES peripheral */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); while (i < round_down(data_size, AES_BLOCK_SIZE)) { unsigned int w; /* Word index */ w = i / sizeof(uint32_t); /* No need to htobe() as we configure the HW to swap bytes */ mmio_write_32(ctx->base + _SAES_DINR, data_u32[w + 0U]); mmio_write_32(ctx->base + _SAES_DINR, data_u32[w + 1U]); mmio_write_32(ctx->base + _SAES_DINR, data_u32[w + 2U]); mmio_write_32(ctx->base + _SAES_DINR, data_u32[w + 3U]); ret = wait_computation_completed(ctx->base); if (ret != 0) { goto out; } clear_computation_completed(ctx->base); /* Process next block */ i += AES_BLOCK_SIZE; ctx->assoc_len += AES_BLOCK_SIZE_BIT; } /* Manage last block if not a block size multiple */ if ((last_block) && (i < data_size)) { /* We don't manage unaligned last block yet */ ret = -ENODEV; goto out; } out: if (ret != 0) { saes_end(ctx, ret); } return ret; } /** * @brief Update (or start) a AES authenticate and de/encrypt with payload data (CCM or GCM). * @param ctx: SAES process context * @param last_block: true if last payload data block * @param data_in: pointer to payload * @param data_out: pointer where to save de/encrypted payload * @param data_size: payload size * * @retval 0 if OK; negative value else. */ int stm32_saes_update_load(struct stm32_saes_context *ctx, bool last_block, uint8_t *data_in, uint8_t *data_out, size_t data_size) { int ret = 0; uint32_t *data_in_u32; uint32_t *data_out_u32; unsigned int i = 0U; uint32_t prev_cr; /* We want buffers to be u32 aligned */ assert((uintptr_t)data_in % __alignof__(uint32_t) == 0); assert((uintptr_t)data_out % __alignof__(uint32_t) == 0); data_in_u32 = (uint32_t *)data_in; data_out_u32 = (uint32_t *)data_out; prev_cr = mmio_read_32(ctx->base + _SAES_CR); if ((data_in == NULL) || (data_size == 0U)) { /* there is no data */ goto out; } /* There is a load phase */ mmio_clrsetbits_32(ctx->base + _SAES_CR, _SAES_CR_GCMPH_MASK, _SAES_CR_GCMPH_PAYLOAD << _SAES_CR_GCMPH_SHIFT); if ((prev_cr & _SAES_CR_GCMPH_MASK) == (_SAES_CR_GCMPH_INIT << _SAES_CR_GCMPH_SHIFT)) { /* Still in initialization phase, no header * We need to enable the SAES peripheral */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); } while (i < round_down(data_size, AES_BLOCK_SIZE)) { unsigned int w; /* Word index */ w = i / sizeof(uint32_t); /* No need to htobe() as we configure the HW to swap bytes */ mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 0U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 1U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 2U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 3U]); ret = wait_computation_completed(ctx->base); if (ret != 0) { goto out; } /* No need to htobe() as we configure the HW to swap bytes */ data_out_u32[w + 0U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 1U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 2U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 3U] = mmio_read_32(ctx->base + _SAES_DOUTR); clear_computation_completed(ctx->base); /* Process next block */ i += AES_BLOCK_SIZE; ctx->load_len += AES_BLOCK_SIZE_BIT; } /* Manage last block if not a block size multiple */ if ((last_block) && (i < data_size)) { uint32_t block_in[AES_BLOCK_SIZE / sizeof(uint32_t)] = {0}; uint32_t block_out[AES_BLOCK_SIZE / sizeof(uint32_t)] = {0}; memcpy(block_in, data_in + i, data_size - i); /* No need to htobe() as we configure the HW to swap bytes */ mmio_write_32(ctx->base + _SAES_DINR, block_in[0U]); mmio_write_32(ctx->base + _SAES_DINR, block_in[1U]); mmio_write_32(ctx->base + _SAES_DINR, block_in[2U]); mmio_write_32(ctx->base + _SAES_DINR, block_in[3U]); ret = wait_computation_completed(ctx->base); if (ret != 0) { VERBOSE("%s %d\n", __func__, __LINE__); goto out; } /* No need to htobe() as we configure the HW to swap bytes */ block_out[0U] = mmio_read_32(ctx->base + _SAES_DOUTR); block_out[1U] = mmio_read_32(ctx->base + _SAES_DOUTR); block_out[2U] = mmio_read_32(ctx->base + _SAES_DOUTR); block_out[3U] = mmio_read_32(ctx->base + _SAES_DOUTR); clear_computation_completed(ctx->base); memcpy(data_out + i, block_out, data_size - i); ctx->load_len += (data_size - i) * UINT8_BIT; } out: if (ret != 0) { saes_end(ctx, ret); } return ret; } /** * @brief Get authentication tag for AES authenticated algorithms (CCM or GCM). * @param ctx: SAES process context * @param tag: pointer where to save the tag * @param data_size: tag size * * @retval 0 if OK; negative value else. */ int stm32_saes_final(struct stm32_saes_context *ctx, uint8_t *tag, size_t tag_size) { int ret; uint32_t tag_u32[4]; uint32_t prev_cr; prev_cr = mmio_read_32(ctx->base + _SAES_CR); mmio_clrsetbits_32(ctx->base + _SAES_CR, _SAES_CR_GCMPH_MASK, _SAES_CR_GCMPH_FINAL << _SAES_CR_GCMPH_SHIFT); if ((prev_cr & _SAES_CR_GCMPH_MASK) == (_SAES_CR_GCMPH_INIT << _SAES_CR_GCMPH_SHIFT)) { /* Still in initialization phase, no header * We need to enable the SAES peripheral */ mmio_setbits_32(ctx->base + _SAES_CR, _SAES_CR_EN); } /* No need to htobe() as we configure the HW to swap bytes */ mmio_write_32(ctx->base + _SAES_DINR, 0); mmio_write_32(ctx->base + _SAES_DINR, ctx->assoc_len); mmio_write_32(ctx->base + _SAES_DINR, 0); mmio_write_32(ctx->base + _SAES_DINR, ctx->load_len); ret = wait_computation_completed(ctx->base); if (ret != 0) { goto out; } /* No need to htobe() as we configure the HW to swap bytes */ tag_u32[0] = mmio_read_32(ctx->base + _SAES_DOUTR); tag_u32[1] = mmio_read_32(ctx->base + _SAES_DOUTR); tag_u32[2] = mmio_read_32(ctx->base + _SAES_DOUTR); tag_u32[3] = mmio_read_32(ctx->base + _SAES_DOUTR); clear_computation_completed(ctx->base); memcpy(tag, tag_u32, MIN(sizeof(tag_u32), tag_size)); out: saes_end(ctx, ret); return ret; } /** * @brief Update (or start) a AES de/encrypt process (ECB, CBC or CTR). * @param ctx: SAES process context * @param last_block: true if last payload data block * @param data_in: pointer to payload * @param data_out: pointer where to save de/encrypted payload * @param data_size: payload size * * @retval 0 if OK; negative value else. */ int stm32_saes_update(struct stm32_saes_context *ctx, bool last_block, uint8_t *data_in, uint8_t *data_out, size_t data_size) { int ret; uint32_t *data_in_u32; uint32_t *data_out_u32; unsigned int i = 0U; /* We want buffers to be u32 aligned */ assert((uintptr_t)data_in % __alignof__(uint32_t) == 0); assert((uintptr_t)data_out % __alignof__(uint32_t) == 0); data_in_u32 = (uint32_t *)data_in; data_out_u32 = (uint32_t *)data_out; if ((!last_block) && (round_down(data_size, AES_BLOCK_SIZE) != data_size)) { ERROR("%s: non last block must be multiple of 128 bits\n", __func__); ret = -EINVAL; goto out; } /* In CBC encryption we need to manage specifically last 2 128bits * blocks if total size in not a block size aligned * work TODO. Currently return ENODEV. * Morevoer as we need to know last 2 block, if unaligned and * call with less than two block, return -EINVAL. */ if (last_block && IS_CHAINING_MODE(CBC, ctx->cr) && is_encrypt(ctx->cr) && (round_down(data_size, AES_BLOCK_SIZE) != data_size)) { if (data_size < AES_BLOCK_SIZE * 2U) { ERROR("if CBC, last part size should be at least 2 * AES_BLOCK_SIZE\n"); ret = -EINVAL; goto out; } /* Moreover the CBC specific padding for encrypt is not yet implemented */ ret = -ENODEV; goto out; } ret = restore_context(ctx); if (ret != 0) { goto out; } while (i < round_down(data_size, AES_BLOCK_SIZE)) { unsigned int w; /* Word index */ w = i / sizeof(uint32_t); /* No need to htobe() as we configure the HW to swap bytes */ mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 0U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 1U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 2U]); mmio_write_32(ctx->base + _SAES_DINR, data_in_u32[w + 3U]); ret = wait_computation_completed(ctx->base); if (ret != 0) { goto out; } /* No need to htobe() as we configure the HW to swap bytes */ data_out_u32[w + 0U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 1U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 2U] = mmio_read_32(ctx->base + _SAES_DOUTR); data_out_u32[w + 3U] = mmio_read_32(ctx->base + _SAES_DOUTR); clear_computation_completed(ctx->base); /* Process next block */ i += AES_BLOCK_SIZE; } /* Manage last block if not a block size multiple */ if ((last_block) && (i < data_size)) { /* In and out buffer have same size so should be AES_BLOCK_SIZE multiple */ ret = -ENODEV; goto out; } if (!last_block) { ret = save_context(ctx); } out: /* If last block or error, end of SAES process */ if (last_block || (ret != 0)) { saes_end(ctx, ret); } return ret; }