Add CC13xx/CC26xx TRNG driver

This commit is contained in:
George Oikonomou 2016-06-14 20:01:48 +01:00
parent adfeaa5500
commit bb4056903c
2 changed files with 450 additions and 0 deletions

View file

@ -0,0 +1,315 @@
/*
* Copyright (c) 2016, University of Bristol - http://www.bristol.ac.uk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup cc26xx-trng
* @{
*
* \file
*
* Implementation of the CC13xx/CC26xx RNG driver
*/
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "lpm.h"
#include "sys/process.h"
#include "dev/soc-trng.h"
#include "ti-lib.h"
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/*---------------------------------------------------------------------------*/
#ifdef SOC_TRNG_CONF_CACHE_LEN
#define SOC_TRNG_CACHE_LEN SOC_TRNG_CONF_CACHE_LEN
#else
/** Size of the random number cache. Each slot holds 4 16-bit numbers */
#define SOC_TRNG_CACHE_LEN 4
#endif
/*---------------------------------------------------------------------------*/
#define MIN_REFILL_CYCLES_MAX 0x00000000
/*---------------------------------------------------------------------------*/
PROCESS(soc_trng_process, "CC13xx/CC26xx TRNG process");
/*---------------------------------------------------------------------------*/
static process_event_t rng_ready_event = PROCESS_EVENT_NONE;
/*---------------------------------------------------------------------------*/
static soc_trng_callback_t notify_cb = NULL;
/*---------------------------------------------------------------------------*/
#define soc_trng_isr TRNGIntHandler
/*---------------------------------------------------------------------------*/
static uint64_t rands_cache[SOC_TRNG_CACHE_LEN];
static bool rands_mask[SOC_TRNG_CACHE_LEN];
/*---------------------------------------------------------------------------*/
static void
disable_number_ready_interrupt(void)
{
ti_lib_trng_int_disable(TRNG_NUMBER_READY);
ti_lib_rom_int_disable(INT_TRNG);
}
/*---------------------------------------------------------------------------*/
static void
enable_number_ready_interrupt(void)
{
ti_lib_trng_int_clear(TRNG_NUMBER_READY);
ti_lib_trng_int_enable(TRNG_NUMBER_READY);
ti_lib_rom_int_enable(INT_TRNG);
}
/*---------------------------------------------------------------------------*/
static bool
accessible(void)
{
/* First, check the PD */
if(ti_lib_rom_prcm_power_domain_status(PRCM_DOMAIN_PERIPH)
!= PRCM_DOMAIN_POWER_ON) {
return false;
}
/* Then check the 'run mode' clock gate */
if(!(HWREG(PRCM_BASE + PRCM_O_SECDMACLKGR) &
PRCM_SECDMACLKGDS_TRNG_CLK_EN_M)) {
return false;
}
return true;
}
/*---------------------------------------------------------------------------*/
static void
power_up(void)
{
/* First, make sure the PERIPH PD is on */
ti_lib_rom_prcm_power_domain_on(PRCM_DOMAIN_PERIPH);
while((ti_lib_rom_prcm_power_domain_status(PRCM_DOMAIN_PERIPH)
!= PRCM_DOMAIN_POWER_ON));
/* Enable clock in active mode */
ti_lib_rom_prcm_peripheral_run_enable(PRCM_PERIPH_TRNG);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
}
/*---------------------------------------------------------------------------*/
static void
reset_synchronous(void)
{
ti_lib_trng_reset();
while(HWREG(TRNG_BASE + TRNG_O_SWRESET));
}
/*---------------------------------------------------------------------------*/
static uint8_t
request(void)
{
if(notify_cb) {
return LPM_MODE_SLEEP;
}
return LPM_MODE_MAX_SUPPORTED;
}
/*---------------------------------------------------------------------------*/
LPM_MODULE(rng_module, request, NULL, NULL, LPM_DOMAIN_NONE);
/*---------------------------------------------------------------------------*/
static uint64_t
read_number(void)
{
uint64_t ran = (uint64_t)HWREG(TRNG_BASE + TRNG_O_OUT1) << 32;
ran += ti_lib_rom_trng_number_get(TRNG_LOW_WORD);
return ran;
}
/*---------------------------------------------------------------------------*/
uint64_t
soc_trng_rand_synchronous()
{
uint64_t ran;
bool interrupts_disabled;
int i;
/* If the TRNG is gathering entropy, return a cached value */
if(notify_cb) {
for(i = 0; i < SOC_TRNG_CACHE_LEN; i++) {
if(rands_mask[i]) {
rands_mask[i] = false;
return rands_cache[i];
}
}
return 0;
}
if(!accessible()) {
power_up();
}
/*
* If we were previously enabled, we either have a number already available,
* or we need clock, which means we are calculating. If neither is true then
* we need setup from scratch.
*/
if((ti_lib_trng_status_get() & (TRNG_NEED_CLOCK | TRNG_NUMBER_READY)) == 0) {
reset_synchronous();
ti_lib_trng_configure(MIN_REFILL_CYCLES_MAX, SOC_TRNG_REFILL_CYCLES_MIN, 0);
ti_lib_trng_enable();
}
interrupts_disabled = ti_lib_int_master_disable();
while((ti_lib_trng_status_get() & TRNG_NUMBER_READY) == 0);
ran = read_number();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return ran;
}
/*---------------------------------------------------------------------------*/
uint8_t
soc_trng_rand_asynchronous(uint32_t samples, soc_trng_callback_t cb)
{
int i;
bool interrupts_disabled;
if(notify_cb != NULL) {
return SOC_TRNG_RAND_ASYNC_REQUEST_ERROR;
}
if(!accessible()) {
power_up();
}
/*
* First we need to cache some random numbers for general use in case the
* application requests them while we are calculating.
*
* If we were previously enabled, we either have a number already available,
* or we need clock, which means we are calculating. If neither is true then
* we need setup from scratch.
*/
if((ti_lib_trng_status_get() & (TRNG_NEED_CLOCK | TRNG_NUMBER_READY)) == 0) {
reset_synchronous();
}
interrupts_disabled = ti_lib_int_master_disable();
ti_lib_trng_disable();
ti_lib_trng_configure(MIN_REFILL_CYCLES_MAX, SOC_TRNG_REFILL_CYCLES_MIN, 0);
ti_lib_trng_enable();
/* Cache SOC_TRNG_CACHE_LEN min-entropy rands */
for(i = 0; i < SOC_TRNG_CACHE_LEN; i++) {
while((ti_lib_trng_status_get() & TRNG_NUMBER_READY) == 0);
rands_mask[i] = true;
rands_cache[i] = read_number();
}
/* Configure the RNG to the required entropy */
ti_lib_trng_disable();
ti_lib_trng_configure(MIN_REFILL_CYCLES_MAX, samples, 0);
/*
* Clear the TRNG_NUMBER_READY flag. This will trigger a new calculation
* as soon as the module gets enabled.
*/
ti_lib_trng_int_clear(TRNG_NUMBER_READY);
/* Enable clock in sleep mode and register with LPM */
ti_lib_rom_prcm_peripheral_sleep_enable(PRCM_PERIPH_TRNG);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
lpm_register_module(&rng_module);
notify_cb = cb;
/* Enable the number ready interrupt and fire-up the module */
enable_number_ready_interrupt();
ti_lib_trng_enable();
if(!interrupts_disabled) {
ti_lib_int_master_enable();
}
return SOC_TRNG_RAND_ASYNC_REQUEST_OK;
}
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(soc_trng_process, ev, data)
{
PROCESS_BEGIN();
while(1) {
PROCESS_YIELD_UNTIL(ev == rng_ready_event);
if(notify_cb) {
uint64_t ran = read_number();
notify_cb(ran);
notify_cb = NULL;
}
/* Disable clock in sleep mode */
ti_lib_rom_prcm_peripheral_sleep_disable(PRCM_PERIPH_TRNG);
ti_lib_prcm_load_set();
while(!ti_lib_prcm_load_get());
lpm_unregister_module(&rng_module);
}
PROCESS_END();
}
/*---------------------------------------------------------------------------*/
void
soc_trng_isr(void)
{
ENERGEST_ON(ENERGEST_TYPE_IRQ);
ti_lib_trng_disable();
disable_number_ready_interrupt();
ti_lib_trng_configure(MIN_REFILL_CYCLES_MAX, SOC_TRNG_REFILL_CYCLES_MIN, 0);
ti_lib_trng_enable();
process_post(&soc_trng_process, rng_ready_event, NULL);
ENERGEST_OFF(ENERGEST_TYPE_IRQ);
}
/*---------------------------------------------------------------------------*/
void
soc_trng_init()
{
if(rng_ready_event != PROCESS_EVENT_NONE) {
return;
}
/* Register the RNG ready event */
rng_ready_event = process_alloc_event();
process_start(&soc_trng_process, NULL);
}
/*---------------------------------------------------------------------------*/
/**
* @}
*/

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2016, University of Bristol - http://www.bristol.ac.uk
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*---------------------------------------------------------------------------*/
/**
* \addtogroup cc26xx
* @{
*
* \defgroup cc26xx-trng CC13xx/CC26xx Random Number Generator
*
* Driver for the CC13xx/CC26xx Random Number Generator
*
* @{
*
* \file
*
* Header file for the CC13xx/CC26xx TRNG driver
*/
/*---------------------------------------------------------------------------*/
#ifndef SOC_TRNG_H_
#define SOC_TRNG_H_
/*---------------------------------------------------------------------------*/
#include <stdint.h>
/*---------------------------------------------------------------------------*/
#define SOC_TRNG_RAND_ASYNC_REQUEST_ERROR 0 /**< Async request rejected */
#define SOC_TRNG_RAND_ASYNC_REQUEST_OK 1 /**< Async request accepted */
/*---------------------------------------------------------------------------*/
#define SOC_TRNG_REFILL_CYCLES_MIN 0x00000100
#define SOC_TRNG_REFILL_CYCLES_MAX 0x00000000
/*---------------------------------------------------------------------------*/
/**
* \brief Pointer to a callback to be provided as an argument to
* soc_trng_rand_asynchronous()
*/
typedef void (*soc_trng_callback_t)(uint64_t rand);
/*---------------------------------------------------------------------------*/
/**
* \name TRNG functions
* @{
*/
/**
* \brief Returns a minimum entropy random number
* \return The random number
*
* This function is synchronous. This function will make sure the PERIPH PD is
* powered and the TRNG is clocked. The function will then configure the TRNG
* to generate a random number of minimum entropy. These numbers are not
* suitable for cryptographic usage, but their generation is very fast.
*
* If a high-entropy random number is currently being generated, this function
* will return a cached random number. The cache is of configurable size and
* can hold a maximum SOC_TRNG_CONF_CACHE_LEN numbers. If the cache gets
* emptied while high-entropy generation is in progress (e.g. because a
* function requested many random numbers in a row), this function will return
* 0. Care must therefore be taken when the return value is 0, which can also
* be a valid random number.
*
* This function can be safely called from within an interrupt context.
*/
uint64_t soc_trng_rand_synchronous();
/**
* \brief Initialise the CC13xx/CC26xx TRNG driver
*/
void soc_trng_init(void);
/**
* \brief Request a 64-bit, configurable-entropy random number
* \param samples Controls the entropy generated for the random number
* \param cb A callback function to be called when the generation is complete
* \retval SOC_TRNG_RAND_ASYNC_REQUEST_ERROR There was an error adding request.
* \retval SOC_TRNG_RAND_ASYNC_REQUEST_OK Request successfully registered
*
* This function is asynchronous, it will start generation of a random number
* and will return. The caller must provide a callback that will be called when
* the generation is complete. This callback must either use the random number
* immediately or store it, since it will not be possible to retrieve it again
* later form the soc-trng module.
*
* Only one generation can be active at any given point in time. If this
* function gets called when a generation is already in progress, it will
* return SOC_TRNG_RAND_ASYNC_REQUEST_ERROR.
*
* The function will configure the TRNG to generate entropy by sampling the
* FROs for a number clock cycles controlled by the samples argument. The 8 LS
* bits of this argument will be truncated by CC13xxware/CC26xxware and the
* resulting number of clock cycles will be (samples >> 8) * 2^8. Increasing
* the value of this argument increases entropy, but it also increases latency.
* Maximum entropy can be generated by passing SOC_TRNG_REFILL_CYCLES_MAX, but
* this will take approximately 350ms. Consult the chip's technical reference
* manual for advice on what would constitute sufficient entropy for random
* numbers meant to be used for crypto.
*
* While this function is executing, calls to soc_trng_rand_synchronous() will
* return cached random numbers.
*
* This function is not re-entrant and must not be called from an interrupt
* context.
*/
uint8_t soc_trng_rand_asynchronous(uint32_t samples, soc_trng_callback_t cb);
/** @} */
/*---------------------------------------------------------------------------*/
#endif /* SOC_TRNG_H_ */
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/