| /* |
| * Copyright (c) 2022, STMicroelectronics - All Rights Reserved |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| #include <assert.h> |
| #include <endian.h> |
| #include <errno.h> |
| #include <stdint.h> |
| |
| #include <drivers/clk.h> |
| #include <drivers/delay_timer.h> |
| #include <drivers/st/stm32_saes.h> |
| #include <drivers/st/stm32mp_reset.h> |
| #include <lib/mmio.h> |
| #include <lib/utils_def.h> |
| #include <libfdt.h> |
| |
| #include <platform_def.h> |
| |
| #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; |
| } |