mbed-os/features/mbedtls/targets/TARGET_Silicon_Labs/crypto_management.c

404 lines
15 KiB
C

/*
* Silicon Labs CRYPTO device management interface.
*
* Copyright (C) 2016, Silicon Labs, http://www.silabs.com
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "crypto_management.h"
#include "em_core.h"
#include "em_bus.h"
#if defined( CRYPTO_PRESENT )
/* Conversion macro for compatibility with the 5.3.x release of the Gecko SDK */
#if defined( MBEDTLS_CRYPTO_DEVICE_PREEMPTION )
#warning "MBEDTLS_CRYPTO_DEVICE_PREEMPTION is deprecated, please define " \
"CRYPTO_DEVICE_PREEMPTION instead."
#endif
#if defined( MBEDTLS_THREADING_C )
#include "mbedtls/threading.h"
static mbedtls_threading_mutex_t crypto_locks[CRYPTO_COUNT];
static volatile bool crypto_locks_initialized = false;
static unsigned int acquire_count = 0U;
#endif /* MBEDTLS_THREADING_C */
#if defined( CRYPTO_DEVICE_PREEMPTION )
/** Preemptable context of CRYPTO hardware module. */
typedef struct
{
uint32_t CTRL; /*!< Control Register */
uint32_t WAC; /*!< Wide Arithmetic Configuration */
uint32_t SEQCTRL; /*!< Sequence Control */
uint32_t SEQCTRLB; /*!< Sequence Control B */
uint32_t IEN; /*!< Interrupt Enable Register */
uint32_t SEQ[5]; /*!< Instruction Sequence registers */
CRYPTO_Data260_TypeDef DDATA[5]; /*!< DDATA registers. Covers all data
registers
of CRYPTO, including DATA(128 bit),
DDATA (256bit/260bit),
QDATA (512bit) registers. */
uint32_t regmask; /*!< Bitmask for which registers to save */
uint32_t operands; /*!< Saving the currently selected operands */
bool carry; /*!< Saving the status of the carry flag */
} crypto_context_t;
static crypto_context_t preemption_context;
static bool is_preempted = false;
static CORE_DECLARE_IRQ_STATE;
#endif /* CRYPTO_DEVICE_PREEMPTION */
typedef enum
{
#if defined( CRYPTO0 )
CRYPTO0_ID = 0,
#elif defined( CRYPTO )
CRYPTO_ID = 0,
#endif
#if defined( CRYPTO1 )
CRYPTO1_ID = 1,
#endif
} crypto_instance_number_t;
typedef struct {
CRYPTO_TypeDef *device;
uint32_t clockMask;
} crypto_device_t;
static const crypto_device_t crypto_devices[CRYPTO_COUNT] =
{
#if defined( CRYPTO0 )
{
CRYPTO0,
_CMU_HFBUSCLKEN0_CRYPTO0_SHIFT
},
#elif defined( CRYPTO )
{
CRYPTO,
_CMU_HFBUSCLKEN0_CRYPTO_SHIFT
},
#endif
#if defined( CRYPTO1 )
{
CRYPTO1,
_CMU_HFBUSCLKEN0_CRYPTO1_SHIFT
},
#endif
};
static inline int crypto_management_index_by_device( CRYPTO_TypeDef *device )
{
#if defined( CRYPTO0 )
if ( device == CRYPTO0 ) return 0;
#elif defined( CRYPTO )
if ( device == CRYPTO ) return 0;
#endif
#if defined( CRYPTO1 )
if ( device == CRYPTO1 ) return 1;
#endif
return -1;
}
/* Use bitband for clock enable/disable operations, such that they are atomic */
#define CRYPTO_CLOCK_ENABLE(clk) BUS_RegBitWrite(&(CMU->HFBUSCLKEN0), (clk), 1)
#define CRYPTO_CLOCK_DISABLE(clk) BUS_RegBitWrite(&(CMU->HFBUSCLKEN0), (clk), 0)
#define CRYPTO_CLOCK_ENABLED(clk) BUS_RegBitRead(&(CMU->HFBUSCLKEN0), (clk))
/* Get ownership of an available crypto device */
CRYPTO_TypeDef *crypto_management_acquire( void )
{
CRYPTO_TypeDef *device = NULL;
#if defined( MBEDTLS_THREADING_C )
/* Initialize mutexes if that hasn't happened yet */
CORE_DECLARE_IRQ_STATE;
if ( !crypto_locks_initialized ) {
CORE_ENTER_CRITICAL();
if ( !crypto_locks_initialized ) {
for ( int i = 0; i < CRYPTO_COUNT; i++ ) {
mbedtls_mutex_init(&crypto_locks[i]);
}
crypto_locks_initialized = true;
}
CORE_EXIT_CRITICAL();
}
/* Wrapping this in SL_THREADING_ALT pending non-blocking mutex in official
* threading API. */
#if defined( SL_THREADING_ALT )
/* Try to take an available crypto instance */
unsigned int devno = 0;
for ( ; devno < CRYPTO_COUNT; devno++ ) {
if ( 0 == THREADING_TakeMutexNonBlocking(&crypto_locks[devno]) ) {
device = crypto_devices[devno].device;
break;
}
}
#endif // SL_THREADING_ALT
/* If no device immediately available, do naieve round-robin */
if ( device == NULL ) {
devno = acquire_count % CRYPTO_COUNT;
mbedtls_mutex_lock( &crypto_locks[devno] );
device = crypto_devices[devno].device;
}
/* Doing this outside of critical section is safe, since we own the lock
* and are using bitband to poke the clock enable bit */
CRYPTO_CLOCK_ENABLE( crypto_devices[devno].clockMask );
acquire_count++;
#else // !MBEDTLS_THREADING_C
device = crypto_devices[0].device;
CRYPTO_CLOCK_ENABLE( crypto_devices[0].clockMask );
#endif // MBEDTLS_THREADING_C
return device;
}
/* Get ownership of the default crypto device (CRYPTO0/CRYPTO) */
CRYPTO_TypeDef *crypto_management_acquire_default( void )
{
CRYPTO_TypeDef *device = NULL;
#if defined( MBEDTLS_THREADING_C )
/* Initialize mutexes if that hasn't happened yet */
CORE_DECLARE_IRQ_STATE;
if ( !crypto_locks_initialized ) {
CORE_ENTER_CRITICAL();
if ( !crypto_locks_initialized ) {
for ( int i = 0; i < CRYPTO_COUNT; i++ ) {
mbedtls_mutex_init(&crypto_locks[i]);
}
crypto_locks_initialized = true;
}
CORE_EXIT_CRITICAL();
}
mbedtls_mutex_lock( &crypto_locks[0] );
device = crypto_devices[0].device;
/* Doing this outside of critical section is safe, since we own the lock
* and are using bitband to poke the clock enable bit */
CRYPTO_CLOCK_ENABLE( crypto_devices[0].clockMask );
#else // !MBEDTLS_THREADING_C
device = crypto_devices[0].device;
CRYPTO_CLOCK_ENABLE( crypto_devices[0].clockMask );
#endif // MBEDTLS_THREADING_C
return device;
}
/* Release ownership of an available crypto device */
void crypto_management_release( CRYPTO_TypeDef *device )
{
int devno = crypto_management_index_by_device( device );
if ( devno < 0 ) {
return;
}
/* Doing this outside of critical section is safe, since we still own the lock
* and are using bitband to poke the clock enable bit */
CRYPTO_CLOCK_DISABLE( crypto_devices[devno].clockMask );
#if defined ( MBEDTLS_THREADING_C )
mbedtls_mutex_unlock( &crypto_locks[devno] );
#endif
}
/* Acquire a device with preemption. NOT thread-safe! */
CRYPTO_TypeDef *crypto_management_acquire_preemption( uint32_t regmask )
{
#if defined( CRYPTO_DEVICE_PREEMPTION )
CRYPTO_TypeDef *device = NULL;
/* Turn off interrupts */
CORE_ENTER_CRITICAL();
/* Check if there is an unused CRYPTO instance */
for ( int i = 0; i < CRYPTO_COUNT; i++ ) {
if ( !CRYPTO_CLOCK_ENABLED( crypto_devices[i].clockMask ) ) {
/* Found an unused device */
CRYPTO_CLOCK_ENABLE( crypto_devices[i].clockMask );
device = crypto_devices[i].device;
break;
}
}
/* If there is no unused instance, preempt the last one */
if ( device == NULL ) {
is_preempted = true;
device = crypto_devices[CRYPTO_COUNT - 1].device;
/* In case this instance is still working on anything */
CRYPTO_InstructionSequenceWait(device);
/* Store operational context */
preemption_context.regmask = regmask;
preemption_context.WAC = device->WAC;
preemption_context.CTRL = device->CTRL;
preemption_context.SEQCTRL = device->SEQCTRL;
preemption_context.SEQCTRLB = device->SEQCTRLB;
preemption_context.IEN = device->IEN;
preemption_context.operands = device->CSTATUS;
preemption_context.carry = (device->DSTATUS & CRYPTO_DSTATUS_CARRY) != 0;
if ( (preemption_context.WAC & _CRYPTO_WAC_RESULTWIDTH_MASK) == CRYPTO_WAC_RESULTWIDTH_260BIT)
{
CRYPTO_DData0Read260(device, preemption_context.DDATA[0]); /* Always save DDATA0 because it'll get clobbered in 260-bit mode*/
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA1) != 0 ) {
device->CMD = CRYPTO_CMD_INSTR_DDATA1TODDATA0; /* Move DDATA1 to DDATA0
in order to read. */
CRYPTO_DData0Read260(device, preemption_context.DDATA[1]);
}
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA2) != 0 ) {
device->CMD = CRYPTO_CMD_INSTR_DDATA2TODDATA0; /* Move DDATA2 to DDATA0
in order to read. */
CRYPTO_DData0Read260(device, preemption_context.DDATA[2]);
}
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA3) != 0 ) {
device->CMD = CRYPTO_CMD_INSTR_DDATA3TODDATA0; /* Move DDATA3 to DDATA0
in order to read. */
CRYPTO_DData0Read260(device, preemption_context.DDATA[3]);
}
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA4) != 0 ) {
device->CMD = CRYPTO_CMD_INSTR_DDATA4TODDATA0; /* Move DDATA4 to DDATA0
in order to read. */
CRYPTO_DData0Read260(device, preemption_context.DDATA[4]);
}
}
else
{
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA0) != 0 )
CRYPTO_DDataRead(&device->DDATA0, preemption_context.DDATA[0]);
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA1) != 0 )
CRYPTO_DDataRead(&device->DDATA1, preemption_context.DDATA[1]);
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA2) != 0 )
CRYPTO_DDataRead(&device->DDATA2, preemption_context.DDATA[2]);
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA3) != 0 )
CRYPTO_DDataRead(&device->DDATA3, preemption_context.DDATA[3]);
if ( (regmask & CRYPTO_MANAGEMENT_SAVE_DDATA4) != 0 )
CRYPTO_DDataRead(&device->DDATA4, preemption_context.DDATA[4]);
}
/* Search for possible EXEC commands and replace with END. */
for ( size_t j = 0; j < (regmask & 0x7U)*sizeof(uint32_t); j++ ) {
if ( (j & 0x03) == 0 ) {
preemption_context.SEQ[j / sizeof(uint32_t)] = *((&device->SEQ0) + (j / sizeof(uint32_t)));
}
if ( ((uint8_t*)preemption_context.SEQ)[j] == CRYPTO_CMD_INSTR_EXEC ) {
((uint8_t*)preemption_context.SEQ)[j] = CRYPTO_CMD_INSTR_END;
}
}
}
return device;
#else
(void) regmask;
return crypto_management_acquire();
#endif
}
/* Release a device from preemption */
void crypto_management_release_preemption( CRYPTO_TypeDef *device )
{
if ( crypto_management_index_by_device( device ) < 0 ) {
return;
}
#if defined( CRYPTO_DEVICE_PREEMPTION )
if ( is_preempted ) {
/* If we preempted something, put their context back */
device->WAC = preemption_context.WAC;
device->CTRL = preemption_context.CTRL;
device->SEQCTRL = preemption_context.SEQCTRL;
device->SEQCTRLB = preemption_context.SEQCTRLB;
device->IEN = preemption_context.IEN;
if ( (preemption_context.WAC & _CRYPTO_WAC_RESULTWIDTH_MASK) == CRYPTO_WAC_RESULTWIDTH_260BIT)
{
/* Start by writing the DDATA1 value to DDATA0 and move to DDATA1. */
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA1) != 0 ) {
CRYPTO_DData0Write260(device, preemption_context.DDATA[1]);
device->CMD = CRYPTO_CMD_INSTR_DDATA0TODDATA1;
}
/* Write the DDATA2 value to DDATA0 and move to DDATA2. */
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA2) != 0 ) {
CRYPTO_DData0Write260(device, preemption_context.DDATA[2]);
device->CMD = CRYPTO_CMD_INSTR_DDATA0TODDATA2;
}
/* Write the DDATA3 value to DDATA0 and move to DDATA3. */
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA3) != 0 ) {
CRYPTO_DData0Write260(device, preemption_context.DDATA[3]);
device->CMD = CRYPTO_CMD_INSTR_DDATA0TODDATA3;
}
/* Write the DDATA4 value to DDATA0 and move to DDATA4. */
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA4) != 0 ) {
CRYPTO_DData0Write260(device, preemption_context.DDATA[4]);
device->CMD = CRYPTO_CMD_INSTR_DDATA0TODDATA4;
}
/* Finally write DDATA0 */
CRYPTO_DData0Write260(device, preemption_context.DDATA[0]);
}
else
{
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA0) != 0 )
CRYPTO_DDataWrite(&device->DDATA0, preemption_context.DDATA[0]);
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA1) != 0 )
CRYPTO_DDataWrite(&device->DDATA1, preemption_context.DDATA[1]);
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA2) != 0 )
CRYPTO_DDataWrite(&device->DDATA2, preemption_context.DDATA[2]);
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA3) != 0 )
CRYPTO_DDataWrite(&device->DDATA3, preemption_context.DDATA[3]);
if ( (preemption_context.regmask & CRYPTO_MANAGEMENT_SAVE_DDATA4) != 0 )
CRYPTO_DDataWrite(&device->DDATA4, preemption_context.DDATA[4]);
}
if (preemption_context.carry) {
device->CMD = CRYPTO_CMD_INSTR_CSET;
} else {
device->CMD = CRYPTO_CMD_INSTR_CCLR;
}
device->CMD = (preemption_context.operands & 0x7U) |
(((preemption_context.operands >> 8) & 0x7U) << 3) |
0xC0;
for (size_t i = 0; i < (preemption_context.regmask & 0x7U); i++ ) {
*((&device->SEQ0) + i) = preemption_context.SEQ[i];
}
is_preempted = false;
} else {
/* If we didn't preempt anything, turn crypto clock back off */
CRYPTO_CLOCK_DISABLE( crypto_devices[crypto_management_index_by_device( device )].clockMask );
}
/* Turn interrupts back on */
CORE_EXIT_CRITICAL();
#else
crypto_management_release(device);
#endif
}
#endif /* CRYPTO_PRESENT */