Add TRNG Firmware Interface service

This adds the TRNG Firmware Interface Service to the standard
service dispatcher. This includes a method for dispatching entropy
requests to platforms and includes an entropy pool implementation to
avoid dropping any entropy requested from the platform.

Change-Id: I71cadb3cb377a507652eca9e0d68714c973026e9
Signed-off-by: Jimmy Brisson <jimmy.brisson@arm.com>
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
This commit is contained in:
Jimmy Brisson 2020-06-22 14:18:42 -05:00 committed by Manish Pandey
parent d5105d994c
commit 7dfb99118e
13 changed files with 462 additions and 6 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
# Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@ -1018,6 +1018,7 @@ $(eval $(call add_defines,\
SPM_MM \
SPMD_SPM_AT_SEL2 \
TRUSTED_BOARD_BOOT \
TRNG_SUPPORT \
USE_COHERENT_MEM \
USE_DEBUGFS \
ARM_IO_IN_DTB \

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
# Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@ -68,6 +68,11 @@ BL31_SOURCES += services/std_svc/sdei/sdei_dispatch.S \
services/std_svc/sdei/sdei_state.c
endif
ifeq (${TRNG_SUPPORT},1)
BL31_SOURCES += services/std_svc/trng/trng_main.c \
services/std_svc/trng/trng_entropy_pool.c
endif
ifeq (${ENABLE_SPE_FOR_LOWER_ELS},1)
BL31_SOURCES += lib/extensions/spe/spe.c
endif

View File

@ -2009,6 +2009,53 @@ interrupt and the interrupt ID are passed as parameters.
The default implementation only prints out a warning message.
.. _porting_guide_trng_requirements:
TRNG porting requirements
~~~~~~~~~~~~~~~~~~~~~~~~~
The |TRNG| backend requires the platform to provide the following values
and mandatory functions.
Values
......
value: uuid_t plat_trng_uuid [mandatory]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This value must be defined to the UUID of the TRNG backend that is specific to
the hardware after ``plat_trng_setup`` function is called. This value must
conform to the SMCCC calling convention; The most significant 32 bits of the
UUID must not equal ``0xffffffff`` or the signed integer ``-1`` as this value in
w0 indicates failure to get a TRNG source.
Functions
.........
Function: void plat_entropy_setup(void) [mandatory]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
Argument: none
Return: none
This function is expected to do platform-specific initialization of any TRNG
hardware. This may include generating a UUID from a hardware-specific seed.
Function: bool plat_get_entropy(uint64_t \*out) [mandatory]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
::
Argument: uint64_t *
Return: bool
Out : when the return value is true, the entropy has been written into the
storage pointed to
This function writes entropy into storage provided by the caller. If no entropy
is available, it must return false and the storage must not be written.
Power State Coordination Interface (in BL31)
--------------------------------------------
@ -2941,7 +2988,7 @@ amount of open resources per driver.
--------------
*Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved.*
*Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.*
.. _PSCI: http://infocenter.arm.com/help/topic/com.arm.doc.den0022c/DEN0022C_Power_State_Coordination_Interface.pdf
.. _Arm Generic Interrupt Controller version 2.0 (GICv2): http://infocenter.arm.com/help/topic/com.arm.doc.ihi0048b/index.html

View File

@ -56,6 +56,7 @@
.. |TF-M| replace:: :term:`TF-M`
.. |TLB| replace:: :term:`TLB`
.. |TLK| replace:: :term:`TLK`
.. |TRNG| replace:: :term:`TRNG`
.. |TSP| replace:: :term:`TSP`
.. |TZC| replace:: :term:`TZC`
.. |UBSAN| replace:: :term:`UBSAN`

View File

@ -193,6 +193,9 @@ You can find additional definitions in the `Arm Glossary`_.
TLK
Trusted Little Kernel. A Trusted OS from NVIDIA.
TRNG
True Randon Number Generator (hardware based)
TSP
Test Secure Payload

View File

@ -0,0 +1,18 @@
/*
* Copyright (c) 2021, ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PLAT_TRNG_H
#define PLAT_TRNG_H
#include <tools_share/uuid.h>
/* TRNG platform functions */
extern uuid_t plat_trng_uuid;
void plat_entropy_setup(void);
bool plat_get_entropy(uint64_t *out);
#endif /* PLAT_TRNG_H */

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@ -13,6 +13,9 @@
#if defined(SPD_spmd)
#include <services/spm_core_manifest.h>
#endif
#if TRNG_SUPPORT
#include "plat_trng.h"
#endif
/*******************************************************************************
* Forward declarations

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) 2021, ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TRNG_SVC_H
#define TRNG_SVC_H
#include <stdbool.h>
#include <stdint.h>
#include <lib/smccc.h>
/* SMC function IDs for TRNG queries */
#define ARM_TRNG_VERSION U(0x84000050)
#define ARM_TRNG_FEATURES U(0x84000051)
#define ARM_TRNG_GET_UUID U(0x84000052)
#define ARM_TRNG_RND32 U(0x84000053)
#define ARM_TRNG_RND64 U(0xc4000053)
/* TRNG version numbers */
#define TRNG_VERSION_MAJOR (0x1)
#define TRNG_VERSION_MINOR (0x0)
/* TRNG Error Numbers */
#define TRNG_E_SUCCESS (0)
#define TRNG_E_NOT_SUPPORTED (-1)
#define TRNG_E_INVALID_PARAMS (-2)
#define TRNG_E_NO_ENTROPY (-3)
#define TRNG_E_NOT_IMPLEMENTED (-4)
#if TRNG_SUPPORT
void trng_setup(void);
bool is_trng_fid(uint32_t smc_fid);
#else
static inline void trng_setup(void)
{
}
static inline bool is_trng_fid(uint32_t smc_fid)
{
return false;
}
#endif
uintptr_t trng_smc_handler(
uint32_t smc_fid,
u_register_t x1,
u_register_t x2,
u_register_t x3,
u_register_t x4,
void *cookie,
void *handle,
u_register_t flags
);
#endif /* TRNG_SVC_H */

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2016-2020, ARM Limited. All rights reserved.
# Copyright (c) 2016-2021, ARM Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
@ -209,6 +209,9 @@ SAVE_KEYS := 0
# Software Delegated Exception support
SDEI_SUPPORT := 0
# True Random Number firmware Interface
TRNG_SUPPORT := 0
# Whether code and read-only data should be put on separate memory pages. The
# platform Makefile is free to override this value.
SEPARATE_CODE_AND_RODATA := 0

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved.
* Copyright (c) 2014-2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
@ -17,6 +17,7 @@
#include <services/spm_mm_svc.h>
#include <services/spmd_svc.h>
#include <services/std_svc.h>
#include <services/trng_svc.h>
#include <smccc_helpers.h>
#include <tools_share/uuid.h>
@ -63,6 +64,8 @@ static int32_t std_svc_setup(void)
sdei_init();
#endif
trng_setup();
return ret;
}
@ -139,6 +142,11 @@ static uintptr_t std_svc_smc_handler(uint32_t smc_fid,
}
#endif
if (is_trng_fid(smc_fid)) {
return trng_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle,
flags);
}
switch (smc_fid) {
case ARM_STD_SVC_CALL_COUNT:
/*

View File

@ -0,0 +1,151 @@
/*
* Copyright (c) 2021, ARM Limited. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <lib/spinlock.h>
#include <plat/common/plat_trng.h>
/*
* # Entropy pool
* Note that the TRNG Firmware interface can request up to 192 bits of entropy
* in a single call or three 64bit words per call. We have 4 words in the pool
* so that when we have 1-63 bits in the pool, and we have a request for
* 192 bits of entropy, we don't have to throw out the leftover 1-63 bits of
* entropy.
*/
#define WORDS_IN_POOL (4)
static uint64_t entropy[WORDS_IN_POOL];
/* index in bits of the first bit of usable entropy */
static uint32_t entropy_bit_index;
/* then number of valid bits in the entropy pool */
static uint32_t entropy_bit_size;
static spinlock_t trng_pool_lock;
#define BITS_PER_WORD (sizeof(entropy[0]) * 8)
#define BITS_IN_POOL (WORDS_IN_POOL * BITS_PER_WORD)
#define ENTROPY_MIN_WORD (entropy_bit_index / BITS_PER_WORD)
#define ENTROPY_FREE_BIT (entropy_bit_size + entropy_bit_index)
#define _ENTROPY_FREE_WORD (ENTROPY_FREE_BIT / BITS_PER_WORD)
#define ENTROPY_FREE_INDEX (_ENTROPY_FREE_WORD % WORDS_IN_POOL)
/* ENTROPY_WORD_INDEX(0) includes leftover bits in the lower bits */
#define ENTROPY_WORD_INDEX(i) ((ENTROPY_MIN_WORD + i) % WORDS_IN_POOL)
/*
* Fill the entropy pool until we have at least as many bits as requested.
* Returns true after filling the pool, and false if the entropy source is out
* of entropy and the pool could not be filled.
* Assumes locks are taken.
*/
static bool trng_fill_entropy(uint32_t nbits)
{
while (nbits > entropy_bit_size) {
bool valid = plat_get_entropy(&entropy[ENTROPY_FREE_INDEX]);
if (valid) {
entropy_bit_size += BITS_PER_WORD;
assert(entropy_bit_size <= BITS_IN_POOL);
} else {
return false;
}
}
return true;
}
/*
* Pack entropy into the out buffer, filling and taking locks as needed.
* Returns true on success, false on failure.
*
* Note: out must have enough space for nbits of entropy
*/
bool trng_pack_entropy(uint32_t nbits, uint64_t *out)
{
bool success = true;
spin_lock(&trng_pool_lock);
if (!trng_fill_entropy(nbits)) {
success = false;
goto out;
}
const unsigned int rshift = entropy_bit_index % BITS_PER_WORD;
const unsigned int lshift = BITS_PER_WORD - rshift;
const int to_fill = ((nbits + BITS_PER_WORD - 1) / BITS_PER_WORD);
int word_i;
for (word_i = 0; word_i < to_fill; word_i++) {
/*
* Repack the entropy from the pool into the passed in out
* buffer. This takes the lower bits from the valid upper bits
* of word_i and the upper bits from the lower bits of
* (word_i + 1).
*
* I found the following diagram useful. note: `e` represents
* valid entropy, ` ` represents invalid bits (not entropy) and
* `x` represents valid entropy that must not end up in the
* packed word.
*
* |---------entropy pool----------|
* C var |--(word_i + 1)-|----word_i-----|
* bit idx |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
* [x,x,e,e,e,e,e,e|e,e, , , , , , ]
* | [e,e,e,e,e,e,e,e] |
* | |--out[word_i]--| |
* lshift|---| |--rshift---|
*
* ==== Which is implemented as ====
*
* |---------entropy pool----------|
* C var |--(word_i + 1)-|----word_i-----|
* bit idx |7 6 5 4 3 2 1 0|7 6 5 4 3 2 1 0|
* [x,x,e,e,e,e,e,e|e,e, , , , , , ]
* C expr << lshift >> rshift
* bit idx 5 4 3 2 1 0 7 6
* [e,e,e,e,e,e,0,0|0,0,0,0,0,0,e,e]
* ==== bit-wise or ====
* 5 4 3 2 1 0 7 6
* [e,e,e,e,e,e,e,e]
*/
out[word_i] = 0;
out[word_i] |= entropy[ENTROPY_WORD_INDEX(word_i)] >> rshift;
/*
* Note that a shift of 64 bits is treated as a shift of 0 bits.
* When the shift amount is the same as the BITS_PER_WORD, we
* don't want to include the next word of entropy, so we skip
* the `|=` operation.
*/
if (lshift != BITS_PER_WORD) {
out[word_i] |= entropy[ENTROPY_WORD_INDEX(word_i + 1)]
<< lshift;
}
}
const uint64_t mask = ~0ULL >> (BITS_PER_WORD - (nbits % BITS_PER_WORD));
out[to_fill - 1] &= mask;
entropy_bit_index = (entropy_bit_index + nbits) % BITS_IN_POOL;
entropy_bit_size -= nbits;
out:
spin_unlock(&trng_pool_lock);
return success;
}
void trng_entropy_pool_setup(void)
{
int i;
for (i = 0; i < WORDS_IN_POOL; i++) {
entropy[i] = 0;
}
entropy_bit_index = 0;
entropy_bit_size = 0;
}

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef TRNG_ENTROPY_POOL_H
#define TRNG_ENTROPY_POOL_H
#include <stdbool.h>
#include <stdint.h>
bool trng_pack_entropy(uint32_t nbits, uint64_t *out);
void trng_entropy_pool_setup(void);
#endif /* TRNG_ENTROPY_POOL_H */

View File

@ -0,0 +1,143 @@
/*
* Copyright (c) 2021, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <arch_features.h>
#include <lib/smccc.h>
#include <services/trng_svc.h>
#include <smccc_helpers.h>
#include <plat/common/plat_trng.h>
#include "trng_entropy_pool.h"
static const uuid_t uuid_null;
/* handle the RND call in SMC 32 bit mode */
static uintptr_t trng_rnd32(uint32_t nbits, void *handle)
{
uint32_t mask = ~0U;
uint64_t ent[2];
if (nbits == 0U || nbits > 96U) {
SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
}
if (!trng_pack_entropy(nbits, &ent[0])) {
SMC_RET1(handle, TRNG_E_NO_ENTROPY);
}
if ((nbits % 32U) != 0U) {
mask >>= 32U - (nbits % 32U);
}
switch ((nbits - 1U) / 32U) {
case 0:
SMC_RET4(handle, TRNG_E_SUCCESS, 0, 0, ent[0] & mask);
break; /* unreachable */
case 1:
SMC_RET4(handle, TRNG_E_SUCCESS, 0, (ent[0] >> 32) & mask,
ent[0] & 0xFFFFFFFF);
break; /* unreachable */
case 2:
SMC_RET4(handle, TRNG_E_SUCCESS, ent[1] & mask,
(ent[0] >> 32) & 0xFFFFFFFF, ent[0] & 0xFFFFFFFF);
break; /* unreachable */
default:
SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
break; /* unreachable */
}
}
/* handle the RND call in SMC 64 bit mode */
static uintptr_t trng_rnd64(uint32_t nbits, void *handle)
{
uint64_t mask = ~0ULL;
uint64_t ent[3];
if (nbits == 0U || nbits > 192U) {
SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
}
if (!trng_pack_entropy(nbits, &ent[0])) {
SMC_RET1(handle, TRNG_E_NO_ENTROPY);
}
/* Mask off higher bits if only part of register requested */
if ((nbits % 64U) != 0U) {
mask >>= 64U - (nbits % 64U);
}
switch ((nbits - 1U) / 64U) {
case 0:
SMC_RET4(handle, TRNG_E_SUCCESS, 0, 0, ent[0] & mask);
break; /* unreachable */
case 1:
SMC_RET4(handle, TRNG_E_SUCCESS, 0, ent[1] & mask, ent[0]);
break; /* unreachable */
case 2:
SMC_RET4(handle, TRNG_E_SUCCESS, ent[2] & mask, ent[1], ent[0]);
break; /* unreachable */
default:
SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
break; /* unreachable */
}
}
void trng_setup(void)
{
trng_entropy_pool_setup();
plat_entropy_setup();
}
/* Predicate indicating that a function id is part of TRNG */
bool is_trng_fid(uint32_t smc_fid)
{
return ((smc_fid == ARM_TRNG_VERSION) ||
(smc_fid == ARM_TRNG_FEATURES) ||
(smc_fid == ARM_TRNG_GET_UUID) ||
(smc_fid == ARM_TRNG_RND32) ||
(smc_fid == ARM_TRNG_RND64));
}
uintptr_t trng_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2,
u_register_t x3, u_register_t x4, void *cookie,
void *handle, u_register_t flags)
{
if (!memcmp(&plat_trng_uuid, &uuid_null, sizeof(uuid_t))) {
SMC_RET1(handle, TRNG_E_NOT_IMPLEMENTED);
}
switch (smc_fid) {
case ARM_TRNG_VERSION:
SMC_RET1(handle, MAKE_SMCCC_VERSION(
TRNG_VERSION_MAJOR, TRNG_VERSION_MINOR
));
break; /* unreachable */
case ARM_TRNG_FEATURES:
if (is_trng_fid((uint32_t)x1)) {
SMC_RET1(handle, TRNG_E_SUCCESS);
} else {
SMC_RET1(handle, TRNG_E_NOT_SUPPORTED);
}
break; /* unreachable */
case ARM_TRNG_GET_UUID:
SMC_UUID_RET(handle, plat_trng_uuid);
break; /* unreachable */
case ARM_TRNG_RND32:
return trng_rnd32((uint32_t)x1, handle);
case ARM_TRNG_RND64:
return trng_rnd64((uint32_t)x1, handle);
default:
WARN("Unimplemented TRNG Service Call: 0x%x\n",
smc_fid);
SMC_RET1(handle, TRNG_E_NOT_IMPLEMENTED);
break; /* unreachable */
}
}