Import Mbed OS hard-float snapshot
This commit is contained in:
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
* 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 */
|
||||
Reference in New Issue
Block a user