17dfb9911SJimmy Brisson /*
2*0b22e591SJayanth Dodderi Chidanand * Copyright (c) 2021-2022, ARM Limited and Contributors. All rights reserved.
37dfb9911SJimmy Brisson *
47dfb9911SJimmy Brisson * SPDX-License-Identifier: BSD-3-Clause
57dfb9911SJimmy Brisson */
67dfb9911SJimmy Brisson
77dfb9911SJimmy Brisson #include <assert.h>
87dfb9911SJimmy Brisson #include <stdbool.h>
97dfb9911SJimmy Brisson #include <stdint.h>
107dfb9911SJimmy Brisson
117dfb9911SJimmy Brisson #include <arch_features.h>
127dfb9911SJimmy Brisson #include <lib/smccc.h>
137dfb9911SJimmy Brisson #include <services/trng_svc.h>
147dfb9911SJimmy Brisson #include <smccc_helpers.h>
157dfb9911SJimmy Brisson
167dfb9911SJimmy Brisson #include <plat/common/plat_trng.h>
177dfb9911SJimmy Brisson
187dfb9911SJimmy Brisson #include "trng_entropy_pool.h"
197dfb9911SJimmy Brisson
207dfb9911SJimmy Brisson static const uuid_t uuid_null;
217dfb9911SJimmy Brisson
227dfb9911SJimmy Brisson /* handle the RND call in SMC 32 bit mode */
trng_rnd32(uint32_t nbits,void * handle)237dfb9911SJimmy Brisson static uintptr_t trng_rnd32(uint32_t nbits, void *handle)
247dfb9911SJimmy Brisson {
257dfb9911SJimmy Brisson uint32_t mask = ~0U;
26*0b22e591SJayanth Dodderi Chidanand uint64_t ent[2] = {0};
277dfb9911SJimmy Brisson
28*0b22e591SJayanth Dodderi Chidanand if (nbits == 0U || nbits > TRNG_RND32_ENTROPY_MAXBITS) {
297dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
307dfb9911SJimmy Brisson }
317dfb9911SJimmy Brisson
327dfb9911SJimmy Brisson if (!trng_pack_entropy(nbits, &ent[0])) {
337dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_NO_ENTROPY);
347dfb9911SJimmy Brisson }
357dfb9911SJimmy Brisson
367dfb9911SJimmy Brisson if ((nbits % 32U) != 0U) {
377dfb9911SJimmy Brisson mask >>= 32U - (nbits % 32U);
387dfb9911SJimmy Brisson }
397dfb9911SJimmy Brisson
407dfb9911SJimmy Brisson switch ((nbits - 1U) / 32U) {
417dfb9911SJimmy Brisson case 0:
427dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, 0, 0, ent[0] & mask);
437dfb9911SJimmy Brisson break; /* unreachable */
447dfb9911SJimmy Brisson case 1:
457dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, 0, (ent[0] >> 32) & mask,
467dfb9911SJimmy Brisson ent[0] & 0xFFFFFFFF);
477dfb9911SJimmy Brisson break; /* unreachable */
487dfb9911SJimmy Brisson case 2:
497dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, ent[1] & mask,
507dfb9911SJimmy Brisson (ent[0] >> 32) & 0xFFFFFFFF, ent[0] & 0xFFFFFFFF);
517dfb9911SJimmy Brisson break; /* unreachable */
527dfb9911SJimmy Brisson default:
537dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
547dfb9911SJimmy Brisson break; /* unreachable */
557dfb9911SJimmy Brisson }
567dfb9911SJimmy Brisson }
577dfb9911SJimmy Brisson
587dfb9911SJimmy Brisson /* handle the RND call in SMC 64 bit mode */
trng_rnd64(uint32_t nbits,void * handle)597dfb9911SJimmy Brisson static uintptr_t trng_rnd64(uint32_t nbits, void *handle)
607dfb9911SJimmy Brisson {
617dfb9911SJimmy Brisson uint64_t mask = ~0ULL;
62*0b22e591SJayanth Dodderi Chidanand uint64_t ent[3] = {0};
637dfb9911SJimmy Brisson
64*0b22e591SJayanth Dodderi Chidanand if (nbits == 0U || nbits > TRNG_RND64_ENTROPY_MAXBITS) {
657dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
667dfb9911SJimmy Brisson }
677dfb9911SJimmy Brisson
687dfb9911SJimmy Brisson if (!trng_pack_entropy(nbits, &ent[0])) {
697dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_NO_ENTROPY);
707dfb9911SJimmy Brisson }
717dfb9911SJimmy Brisson
727dfb9911SJimmy Brisson /* Mask off higher bits if only part of register requested */
737dfb9911SJimmy Brisson if ((nbits % 64U) != 0U) {
747dfb9911SJimmy Brisson mask >>= 64U - (nbits % 64U);
757dfb9911SJimmy Brisson }
767dfb9911SJimmy Brisson
777dfb9911SJimmy Brisson switch ((nbits - 1U) / 64U) {
787dfb9911SJimmy Brisson case 0:
797dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, 0, 0, ent[0] & mask);
807dfb9911SJimmy Brisson break; /* unreachable */
817dfb9911SJimmy Brisson case 1:
827dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, 0, ent[1] & mask, ent[0]);
837dfb9911SJimmy Brisson break; /* unreachable */
847dfb9911SJimmy Brisson case 2:
857dfb9911SJimmy Brisson SMC_RET4(handle, TRNG_E_SUCCESS, ent[2] & mask, ent[1], ent[0]);
867dfb9911SJimmy Brisson break; /* unreachable */
877dfb9911SJimmy Brisson default:
887dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_INVALID_PARAMS);
897dfb9911SJimmy Brisson break; /* unreachable */
907dfb9911SJimmy Brisson }
917dfb9911SJimmy Brisson }
927dfb9911SJimmy Brisson
trng_setup(void)937dfb9911SJimmy Brisson void trng_setup(void)
947dfb9911SJimmy Brisson {
957dfb9911SJimmy Brisson trng_entropy_pool_setup();
967dfb9911SJimmy Brisson plat_entropy_setup();
977dfb9911SJimmy Brisson }
987dfb9911SJimmy Brisson
997dfb9911SJimmy Brisson /* Predicate indicating that a function id is part of TRNG */
is_trng_fid(uint32_t smc_fid)1007dfb9911SJimmy Brisson bool is_trng_fid(uint32_t smc_fid)
1017dfb9911SJimmy Brisson {
1027dfb9911SJimmy Brisson return ((smc_fid == ARM_TRNG_VERSION) ||
1037dfb9911SJimmy Brisson (smc_fid == ARM_TRNG_FEATURES) ||
1047dfb9911SJimmy Brisson (smc_fid == ARM_TRNG_GET_UUID) ||
1057dfb9911SJimmy Brisson (smc_fid == ARM_TRNG_RND32) ||
1067dfb9911SJimmy Brisson (smc_fid == ARM_TRNG_RND64));
1077dfb9911SJimmy Brisson }
1087dfb9911SJimmy Brisson
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)1097dfb9911SJimmy Brisson uintptr_t trng_smc_handler(uint32_t smc_fid, u_register_t x1, u_register_t x2,
1107dfb9911SJimmy Brisson u_register_t x3, u_register_t x4, void *cookie,
1117dfb9911SJimmy Brisson void *handle, u_register_t flags)
1127dfb9911SJimmy Brisson {
1137dfb9911SJimmy Brisson if (!memcmp(&plat_trng_uuid, &uuid_null, sizeof(uuid_t))) {
1147dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_NOT_IMPLEMENTED);
1157dfb9911SJimmy Brisson }
1167dfb9911SJimmy Brisson
1177dfb9911SJimmy Brisson switch (smc_fid) {
1187dfb9911SJimmy Brisson case ARM_TRNG_VERSION:
1197dfb9911SJimmy Brisson SMC_RET1(handle, MAKE_SMCCC_VERSION(
120*0b22e591SJayanth Dodderi Chidanand TRNG_VERSION_MAJOR, TRNG_VERSION_MINOR));
1217dfb9911SJimmy Brisson break; /* unreachable */
122*0b22e591SJayanth Dodderi Chidanand
1237dfb9911SJimmy Brisson case ARM_TRNG_FEATURES:
1247dfb9911SJimmy Brisson if (is_trng_fid((uint32_t)x1)) {
1257dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_SUCCESS);
1267dfb9911SJimmy Brisson } else {
1277dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_NOT_SUPPORTED);
1287dfb9911SJimmy Brisson }
1297dfb9911SJimmy Brisson break; /* unreachable */
130*0b22e591SJayanth Dodderi Chidanand
1317dfb9911SJimmy Brisson case ARM_TRNG_GET_UUID:
1327dfb9911SJimmy Brisson SMC_UUID_RET(handle, plat_trng_uuid);
1337dfb9911SJimmy Brisson break; /* unreachable */
134*0b22e591SJayanth Dodderi Chidanand
1357dfb9911SJimmy Brisson case ARM_TRNG_RND32:
1367dfb9911SJimmy Brisson return trng_rnd32((uint32_t)x1, handle);
137*0b22e591SJayanth Dodderi Chidanand
1387dfb9911SJimmy Brisson case ARM_TRNG_RND64:
1397dfb9911SJimmy Brisson return trng_rnd64((uint32_t)x1, handle);
140*0b22e591SJayanth Dodderi Chidanand
1417dfb9911SJimmy Brisson default:
142*0b22e591SJayanth Dodderi Chidanand WARN("Unimplemented TRNG Service Call: 0x%x\n", smc_fid);
1437dfb9911SJimmy Brisson SMC_RET1(handle, TRNG_E_NOT_IMPLEMENTED);
1447dfb9911SJimmy Brisson break; /* unreachable */
1457dfb9911SJimmy Brisson }
1467dfb9911SJimmy Brisson }
147