aboutsummaryrefslogtreecommitdiff
path: root/services/std_svc/trng/trng_main.c
blob: 38aa6499733ad054d16d116b5dd453a54af37a68 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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 */
	}
}