1 // SPDX-License-Identifier: GPL-2.0-or-later OR BSD-3-Clause
2 /*
3 * Copyright (C) 2022, STMicroelectronics - All Rights Reserved
4 */
5 #define LOG_CATEGORY UCLASS_RNG
6
7 #include <common.h>
8
9 #include <rng.h>
10 #include <tee.h>
11 #include <dm/device.h>
12 #include <dm/device_compat.h>
13 #include <linux/sizes.h>
14 #include <tee/optee_service.h>
15
16 #define DRIVER_NAME "optee-rng"
17
18 #define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001
19
20 /*
21 * TA_CMD_GET_ENTROPY - Get Entropy from RNG
22 *
23 * param[0] (inout memref) - Entropy buffer memory reference
24 * param[1] unused
25 * param[2] unused
26 * param[3] unused
27 *
28 * Result:
29 * TEE_SUCCESS - Invoke command success
30 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
31 * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
32 * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
33 */
34 #define TA_CMD_GET_ENTROPY 0x0
35
36 #define MAX_ENTROPY_REQ_SZ SZ_4K
37
38 #define TA_HWRNG_UUID { 0xab7a617c, 0xb8e7, 0x4d8f, \
39 { 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64 } }
40
41 OPTEE_SERVICE_DRIVER(optee_rng, TA_HWRNG_UUID, DRIVER_NAME);
42
43 /** open_session_ta_hwrng() - Open session with hwrng Trusted App
44 *
45 * @dev: device
46 * @session_id: return the RNG TA session identifier
47 * Return: 0 if ok
48 */
open_session_ta_hwrng(struct udevice * dev,u32 * session_id)49 static int open_session_ta_hwrng(struct udevice *dev, u32 *session_id)
50 {
51 const struct tee_optee_ta_uuid uuid = TA_HWRNG_UUID;
52 struct tee_open_session_arg sess_arg = {0};
53 int ret;
54
55 /* Open session with hwrng Trusted App */
56 tee_optee_ta_uuid_to_octets(sess_arg.uuid, &uuid);
57 sess_arg.clnt_login = TEE_LOGIN_PUBLIC;
58
59 ret = tee_open_session(dev->parent, &sess_arg, 0, NULL);
60 if (ret || sess_arg.ret) {
61 if (!ret)
62 ret = -EIO;
63 return ret;
64 }
65
66 *session_id = sess_arg.session;
67 return 0;
68 }
69
70 /**
71 * get_optee_rng_data() - read RNG data from OP-TEE TA
72 *
73 * @dev: device
74 * @session_id: the RNG TA session identifier
75 * @entropy_shm_pool: shared memory pool used for TEE message
76 * @buf: buffer to receive data
77 * @size: size of buffer, limited by entropy_shm_pool size
78 * Return: 0 if ok
79 */
get_optee_rng_data(struct udevice * dev,u32 session_id,struct tee_shm * entropy_shm_pool,void * buf,size_t * size)80 static int get_optee_rng_data(struct udevice *dev, u32 session_id,
81 struct tee_shm *entropy_shm_pool,
82 void *buf, size_t *size)
83 {
84 int ret = 0;
85 struct tee_invoke_arg arg = {0};
86 struct tee_param param = {0};
87
88 /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */
89 arg.func = TA_CMD_GET_ENTROPY;
90 arg.session = session_id;
91
92 /* Fill invoke cmd params */
93 param.attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
94 param.u.memref.shm = entropy_shm_pool;
95 param.u.memref.size = *size;
96
97 ret = tee_invoke_func(dev->parent, &arg, 1, ¶m);
98 if (ret || arg.ret) {
99 if (!ret)
100 ret = -EPROTO;
101 dev_err(dev, "TA_CMD_GET_ENTROPY invoke err: %d 0x%x\n", ret, arg.ret);
102 *size = 0;
103
104 return ret;
105 }
106
107 memcpy(buf, param.u.memref.shm->addr, param.u.memref.size);
108 *size = param.u.memref.size;
109
110 return 0;
111 }
112
113 /**
114 * optee_rng_read() - rng read ops for OP-TEE RNG device
115 *
116 * @dev: device
117 * @buf: buffer to receive data
118 * @len: size of buffer
119 * Return: 0 if ok
120 */
optee_rng_read(struct udevice * dev,void * buf,size_t len)121 static int optee_rng_read(struct udevice *dev, void *buf, size_t len)
122 {
123 size_t read = 0, rng_size = 0;
124 struct tee_shm *entropy_shm_pool;
125 u8 *data = buf;
126 int ret;
127 u32 session_id = 0;
128
129 ret = open_session_ta_hwrng(dev, &session_id);
130 if (ret) {
131 dev_err(dev, "can't open session: %d\n", ret);
132 return ret;
133 }
134
135 ret = tee_shm_alloc(dev->parent, MAX_ENTROPY_REQ_SZ, 0, &entropy_shm_pool);
136 if (ret) {
137 dev_err(dev, "tee_shm_alloc failed: %d\n", ret);
138 goto session_close;
139 }
140
141 while (read < len) {
142 rng_size = min(len - read, (size_t)MAX_ENTROPY_REQ_SZ);
143 ret = get_optee_rng_data(dev, session_id, entropy_shm_pool, data, &rng_size);
144 if (ret)
145 goto shm_free;
146 data += rng_size;
147 read += rng_size;
148 }
149
150 shm_free:
151 tee_shm_free(entropy_shm_pool);
152
153 session_close:
154 tee_close_session(dev->parent, session_id);
155
156 return ret;
157 }
158
159 /**
160 * optee_rng_probe() - probe function for OP-TEE RNG device
161 *
162 * @dev: device
163 * Return: 0 if ok
164 */
optee_rng_probe(struct udevice * dev)165 static int optee_rng_probe(struct udevice *dev)
166 {
167 int ret;
168 u32 session_id;
169
170 ret = open_session_ta_hwrng(dev, &session_id);
171 if (ret) {
172 dev_err(dev, "can't open session: %d\n", ret);
173 return ret;
174 }
175 tee_close_session(dev->parent, session_id);
176
177 return 0;
178 }
179
180 static const struct dm_rng_ops optee_rng_ops = {
181 .read = optee_rng_read,
182 };
183
184 U_BOOT_DRIVER(optee_rng) = {
185 .name = DRIVER_NAME,
186 .id = UCLASS_RNG,
187 .ops = &optee_rng_ops,
188 .probe = optee_rng_probe,
189 };
190