From 7dfb99118e89c41e4f2e9efb451dc7608326892c Mon Sep 17 00:00:00 2001 From: Jimmy Brisson Date: Mon, 22 Jun 2020 14:18:42 -0500 Subject: [PATCH] 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 Signed-off-by: Andre Przywara --- Makefile | 3 +- bl31/bl31.mk | 7 +- docs/getting_started/porting-guide.rst | 49 ++++++- docs/global_substitutions.txt | 1 + docs/glossary.rst | 3 + include/plat/common/plat_trng.h | 18 +++ include/plat/common/platform.h | 5 +- include/services/trng_svc.h | 57 ++++++++ make_helpers/defaults.mk | 5 +- services/std_svc/std_svc_setup.c | 10 +- services/std_svc/trng/trng_entropy_pool.c | 151 ++++++++++++++++++++++ services/std_svc/trng/trng_entropy_pool.h | 16 +++ services/std_svc/trng/trng_main.c | 143 ++++++++++++++++++++ 13 files changed, 462 insertions(+), 6 deletions(-) create mode 100644 include/plat/common/plat_trng.h create mode 100644 include/services/trng_svc.h create mode 100644 services/std_svc/trng/trng_entropy_pool.c create mode 100644 services/std_svc/trng/trng_entropy_pool.h create mode 100644 services/std_svc/trng/trng_main.c diff --git a/Makefile b/Makefile index 1501f463d..f899dacd2 100644 --- a/Makefile +++ b/Makefile @@ -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 \ diff --git a/bl31/bl31.mk b/bl31/bl31.mk index e299fe139..2088533ac 100644 --- a/bl31/bl31.mk +++ b/bl31/bl31.mk @@ -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 diff --git a/docs/getting_started/porting-guide.rst b/docs/getting_started/porting-guide.rst index d063ec7e6..be3f0bb0f 100644 --- a/docs/getting_started/porting-guide.rst +++ b/docs/getting_started/porting-guide.rst @@ -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 diff --git a/docs/global_substitutions.txt b/docs/global_substitutions.txt index d33155b9a..24ac8300e 100644 --- a/docs/global_substitutions.txt +++ b/docs/global_substitutions.txt @@ -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` diff --git a/docs/glossary.rst b/docs/glossary.rst index 08add3a70..54820e4b6 100644 --- a/docs/glossary.rst +++ b/docs/glossary.rst @@ -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 diff --git a/include/plat/common/plat_trng.h b/include/plat/common/plat_trng.h new file mode 100644 index 000000000..a9f73b679 --- /dev/null +++ b/include/plat/common/plat_trng.h @@ -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 + +/* 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 */ diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h index ebcc85577..1def86ea7 100644 --- a/include/plat/common/platform.h +++ b/include/plat/common/platform.h @@ -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 #endif +#if TRNG_SUPPORT +#include "plat_trng.h" +#endif /******************************************************************************* * Forward declarations diff --git a/include/services/trng_svc.h b/include/services/trng_svc.h new file mode 100644 index 000000000..ed4d557ca --- /dev/null +++ b/include/services/trng_svc.h @@ -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 +#include + +#include + +/* 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 */ diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk index 5217a8514..e94f3c31a 100644 --- a/make_helpers/defaults.mk +++ b/make_helpers/defaults.mk @@ -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 diff --git a/services/std_svc/std_svc_setup.c b/services/std_svc/std_svc_setup.c index cdd17bc1d..8f63c7316 100644 --- a/services/std_svc/std_svc_setup.c +++ b/services/std_svc/std_svc_setup.c @@ -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 #include #include +#include #include #include @@ -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: /* diff --git a/services/std_svc/trng/trng_entropy_pool.c b/services/std_svc/trng/trng_entropy_pool.c new file mode 100644 index 000000000..ac13b1d7a --- /dev/null +++ b/services/std_svc/trng/trng_entropy_pool.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2021, ARM Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +/* + * # 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; +} diff --git a/services/std_svc/trng/trng_entropy_pool.h b/services/std_svc/trng/trng_entropy_pool.h new file mode 100644 index 000000000..fab2367d2 --- /dev/null +++ b/services/std_svc/trng/trng_entropy_pool.h @@ -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 +#include + +bool trng_pack_entropy(uint32_t nbits, uint64_t *out); +void trng_entropy_pool_setup(void); + +#endif /* TRNG_ENTROPY_POOL_H */ diff --git a/services/std_svc/trng/trng_main.c b/services/std_svc/trng/trng_main.c new file mode 100644 index 000000000..38aa64997 --- /dev/null +++ b/services/std_svc/trng/trng_main.c @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2021, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#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 */ + } +}