mirror of https://github.com/ARMmbed/mbed-os.git
404 lines
15 KiB
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 */
|