aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Garg <sumit.garg@linaro.org>2019-01-29 00:49:38 -0500
committerJens Wiklander <jens.wiklander@linaro.org>2019-02-01 09:12:46 -0500
commit5fe8b1cc6a03c46b3061e808256d39dcebd0d0f0 (patch)
tree02259dbae37297a4cfbae43a53db81a594dca0c4
parentc3fa24af92445c419c19df5981ab4e41a3ae3361 (diff)
hwrng: add OP-TEE based rng driver
On ARM SoC's with TrustZone enabled, peripherals like entropy sources might not be accessible to normal world (linux in this case) and rather accessible to secure world (OP-TEE in this case) only. So this driver aims to provides a generic interface to OP-TEE based random number generator service. This driver registers on TEE bus to interact with OP-TEE based rng device/service. Signed-off-by: Sumit Garg <sumit.garg@linaro.org> Reviewed-by: Daniel Thompson <daniel.thompson@linaro.org> Acked-by: Herbert Xu <herbert@gondor.apana.org.au> Signed-off-by: Jens Wiklander <jens.wiklander@linaro.org>
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/char/hw_random/Kconfig15
-rw-r--r--drivers/char/hw_random/Makefile1
-rw-r--r--drivers/char/hw_random/optee-rng.c298
4 files changed, 319 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 51029a425dbe..dcef7e938f2f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11262,6 +11262,11 @@ M: Jens Wiklander <jens.wiklander@linaro.org>
11262S: Maintained 11262S: Maintained
11263F: drivers/tee/optee/ 11263F: drivers/tee/optee/
11264 11264
11265OP-TEE RANDOM NUMBER GENERATOR (RNG) DRIVER
11266M: Sumit Garg <sumit.garg@linaro.org>
11267S: Maintained
11268F: drivers/char/hw_random/optee-rng.c
11269
11265OPA-VNIC DRIVER 11270OPA-VNIC DRIVER
11266M: Dennis Dalessandro <dennis.dalessandro@intel.com> 11271M: Dennis Dalessandro <dennis.dalessandro@intel.com>
11267M: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com> 11272M: Niranjana Vishwanathapura <niranjana.vishwanathapura@intel.com>
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
index dac895dc01b9..25a7d8ffdb5d 100644
--- a/drivers/char/hw_random/Kconfig
+++ b/drivers/char/hw_random/Kconfig
@@ -424,6 +424,21 @@ config HW_RANDOM_EXYNOS
424 will be called exynos-trng. 424 will be called exynos-trng.
425 425
426 If unsure, say Y. 426 If unsure, say Y.
427
428config HW_RANDOM_OPTEE
429 tristate "OP-TEE based Random Number Generator support"
430 depends on OPTEE
431 default HW_RANDOM
432 help
433 This driver provides support for OP-TEE based Random Number
434 Generator on ARM SoCs where hardware entropy sources are not
435 accessible to normal world (Linux).
436
437 To compile this driver as a module, choose M here: the module
438 will be called optee-rng.
439
440 If unsure, say Y.
441
427endif # HW_RANDOM 442endif # HW_RANDOM
428 443
429config UML_RANDOM 444config UML_RANDOM
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
index e35ec3ce3a20..7c9ef4a7667f 100644
--- a/drivers/char/hw_random/Makefile
+++ b/drivers/char/hw_random/Makefile
@@ -38,3 +38,4 @@ obj-$(CONFIG_HW_RANDOM_CAVIUM) += cavium-rng.o cavium-rng-vf.o
38obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o 38obj-$(CONFIG_HW_RANDOM_MTK) += mtk-rng.o
39obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o 39obj-$(CONFIG_HW_RANDOM_S390) += s390-trng.o
40obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o 40obj-$(CONFIG_HW_RANDOM_KEYSTONE) += ks-sa-rng.o
41obj-$(CONFIG_HW_RANDOM_OPTEE) += optee-rng.o
diff --git a/drivers/char/hw_random/optee-rng.c b/drivers/char/hw_random/optee-rng.c
new file mode 100644
index 000000000000..2b9fc8ac5500
--- /dev/null
+++ b/drivers/char/hw_random/optee-rng.c
@@ -0,0 +1,298 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2018-2019 Linaro Ltd.
4 */
5
6#include <linux/delay.h>
7#include <linux/of.h>
8#include <linux/hw_random.h>
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/slab.h>
12#include <linux/tee_drv.h>
13#include <linux/uuid.h>
14
15#define DRIVER_NAME "optee-rng"
16
17#define TEE_ERROR_HEALTH_TEST_FAIL 0x00000001
18
19/*
20 * TA_CMD_GET_ENTROPY - Get Entropy from RNG
21 *
22 * param[0] (inout memref) - Entropy buffer memory reference
23 * param[1] unused
24 * param[2] unused
25 * param[3] unused
26 *
27 * Result:
28 * TEE_SUCCESS - Invoke command success
29 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
30 * TEE_ERROR_NOT_SUPPORTED - Requested entropy size greater than size of pool
31 * TEE_ERROR_HEALTH_TEST_FAIL - Continuous health testing failed
32 */
33#define TA_CMD_GET_ENTROPY 0x0
34
35/*
36 * TA_CMD_GET_RNG_INFO - Get RNG information
37 *
38 * param[0] (out value) - value.a: RNG data-rate in bytes per second
39 * value.b: Quality/Entropy per 1024 bit of data
40 * param[1] unused
41 * param[2] unused
42 * param[3] unused
43 *
44 * Result:
45 * TEE_SUCCESS - Invoke command success
46 * TEE_ERROR_BAD_PARAMETERS - Incorrect input param
47 */
48#define TA_CMD_GET_RNG_INFO 0x1
49
50#define MAX_ENTROPY_REQ_SZ (4 * 1024)
51
52/**
53 * struct optee_rng_private - OP-TEE Random Number Generator private data
54 * @dev: OP-TEE based RNG device.
55 * @ctx: OP-TEE context handler.
56 * @session_id: RNG TA session identifier.
57 * @data_rate: RNG data rate.
58 * @entropy_shm_pool: Memory pool shared with RNG device.
59 * @optee_rng: OP-TEE RNG driver structure.
60 */
61struct optee_rng_private {
62 struct device *dev;
63 struct tee_context *ctx;
64 u32 session_id;
65 u32 data_rate;
66 struct tee_shm *entropy_shm_pool;
67 struct hwrng optee_rng;
68};
69
70#define to_optee_rng_private(r) \
71 container_of(r, struct optee_rng_private, optee_rng)
72
73static size_t get_optee_rng_data(struct optee_rng_private *pvt_data,
74 void *buf, size_t req_size)
75{
76 u32 ret = 0;
77 u8 *rng_data = NULL;
78 size_t rng_size = 0;
79 struct tee_ioctl_invoke_arg inv_arg = {0};
80 struct tee_param param[4] = {0};
81
82 /* Invoke TA_CMD_GET_ENTROPY function of Trusted App */
83 inv_arg.func = TA_CMD_GET_ENTROPY;
84 inv_arg.session = pvt_data->session_id;
85 inv_arg.num_params = 4;
86
87 /* Fill invoke cmd params */
88 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT;
89 param[0].u.memref.shm = pvt_data->entropy_shm_pool;
90 param[0].u.memref.size = req_size;
91 param[0].u.memref.shm_offs = 0;
92
93 ret = tee_client_invoke_func(pvt_data->ctx, &inv_arg, param);
94 if ((ret < 0) || (inv_arg.ret != 0)) {
95 dev_err(pvt_data->dev, "TA_CMD_GET_ENTROPY invoke err: %x\n",
96 inv_arg.ret);
97 return 0;
98 }
99
100 rng_data = tee_shm_get_va(pvt_data->entropy_shm_pool, 0);
101 if (IS_ERR(rng_data)) {
102 dev_err(pvt_data->dev, "tee_shm_get_va failed\n");
103 return 0;
104 }
105
106 rng_size = param[0].u.memref.size;
107 memcpy(buf, rng_data, rng_size);
108
109 return rng_size;
110}
111
112static int optee_rng_read(struct hwrng *rng, void *buf, size_t max, bool wait)
113{
114 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
115 size_t read = 0, rng_size = 0;
116 int timeout = 1;
117 u8 *data = buf;
118
119 if (max > MAX_ENTROPY_REQ_SZ)
120 max = MAX_ENTROPY_REQ_SZ;
121
122 while (read == 0) {
123 rng_size = get_optee_rng_data(pvt_data, data, (max - read));
124
125 data += rng_size;
126 read += rng_size;
127
128 if (wait) {
129 if (timeout-- == 0)
130 return read;
131 msleep((1000 * (max - read)) / pvt_data->data_rate);
132 } else {
133 return read;
134 }
135 }
136
137 return read;
138}
139
140static int optee_rng_init(struct hwrng *rng)
141{
142 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
143 struct tee_shm *entropy_shm_pool = NULL;
144
145 entropy_shm_pool = tee_shm_alloc(pvt_data->ctx, MAX_ENTROPY_REQ_SZ,
146 TEE_SHM_MAPPED | TEE_SHM_DMA_BUF);
147 if (IS_ERR(entropy_shm_pool)) {
148 dev_err(pvt_data->dev, "tee_shm_alloc failed\n");
149 return PTR_ERR(entropy_shm_pool);
150 }
151
152 pvt_data->entropy_shm_pool = entropy_shm_pool;
153
154 return 0;
155}
156
157static void optee_rng_cleanup(struct hwrng *rng)
158{
159 struct optee_rng_private *pvt_data = to_optee_rng_private(rng);
160
161 tee_shm_free(pvt_data->entropy_shm_pool);
162}
163
164static struct optee_rng_private pvt_data = {
165 .optee_rng = {
166 .name = DRIVER_NAME,
167 .init = optee_rng_init,
168 .cleanup = optee_rng_cleanup,
169 .read = optee_rng_read,
170 }
171};
172
173static int get_optee_rng_info(struct device *dev)
174{
175 u32 ret = 0;
176 struct tee_ioctl_invoke_arg inv_arg = {0};
177 struct tee_param param[4] = {0};
178
179 /* Invoke TA_CMD_GET_RNG_INFO function of Trusted App */
180 inv_arg.func = TA_CMD_GET_RNG_INFO;
181 inv_arg.session = pvt_data.session_id;
182 inv_arg.num_params = 4;
183
184 /* Fill invoke cmd params */
185 param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_OUTPUT;
186
187 ret = tee_client_invoke_func(pvt_data.ctx, &inv_arg, param);
188 if ((ret < 0) || (inv_arg.ret != 0)) {
189 dev_err(dev, "TA_CMD_GET_RNG_INFO invoke err: %x\n",
190 inv_arg.ret);
191 return -EINVAL;
192 }
193
194 pvt_data.data_rate = param[0].u.value.a;
195 pvt_data.optee_rng.quality = param[0].u.value.b;
196
197 return 0;
198}
199
200static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
201{
202 if (ver->impl_id == TEE_IMPL_ID_OPTEE)
203 return 1;
204 else
205 return 0;
206}
207
208static int optee_rng_probe(struct device *dev)
209{
210 struct tee_client_device *rng_device = to_tee_client_device(dev);
211 int ret = 0, err = -ENODEV;
212 struct tee_ioctl_open_session_arg sess_arg = {0};
213
214 /* Open context with TEE driver */
215 pvt_data.ctx = tee_client_open_context(NULL, optee_ctx_match, NULL,
216 NULL);
217 if (IS_ERR(pvt_data.ctx))
218 return -ENODEV;
219
220 /* Open session with hwrng Trusted App */
221 memcpy(sess_arg.uuid, rng_device->id.uuid.b, TEE_IOCTL_UUID_LEN);
222 sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
223 sess_arg.num_params = 0;
224
225 ret = tee_client_open_session(pvt_data.ctx, &sess_arg, NULL);
226 if ((ret < 0) || (sess_arg.ret != 0)) {
227 dev_err(dev, "tee_client_open_session failed, err: %x\n",
228 sess_arg.ret);
229 err = -EINVAL;
230 goto out_ctx;
231 }
232 pvt_data.session_id = sess_arg.session;
233
234 err = get_optee_rng_info(dev);
235 if (err)
236 goto out_sess;
237
238 err = hwrng_register(&pvt_data.optee_rng);
239 if (err) {
240 dev_err(dev, "hwrng registration failed (%d)\n", err);
241 goto out_sess;
242 }
243
244 pvt_data.dev = dev;
245
246 return 0;
247
248out_sess:
249 tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
250out_ctx:
251 tee_client_close_context(pvt_data.ctx);
252
253 return err;
254}
255
256static int optee_rng_remove(struct device *dev)
257{
258 hwrng_unregister(&pvt_data.optee_rng);
259 tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
260 tee_client_close_context(pvt_data.ctx);
261
262 return 0;
263}
264
265const struct tee_client_device_id optee_rng_id_table[] = {
266 {UUID_INIT(0xab7a617c, 0xb8e7, 0x4d8f,
267 0x83, 0x01, 0xd0, 0x9b, 0x61, 0x03, 0x6b, 0x64)},
268 {}
269};
270
271MODULE_DEVICE_TABLE(tee, optee_rng_id_table);
272
273static struct tee_client_driver optee_rng_driver = {
274 .id_table = optee_rng_id_table,
275 .driver = {
276 .name = DRIVER_NAME,
277 .bus = &tee_bus_type,
278 .probe = optee_rng_probe,
279 .remove = optee_rng_remove,
280 },
281};
282
283static int __init optee_rng_mod_init(void)
284{
285 return driver_register(&optee_rng_driver.driver);
286}
287
288static void __exit optee_rng_mod_exit(void)
289{
290 driver_unregister(&optee_rng_driver.driver);
291}
292
293module_init(optee_rng_mod_init);
294module_exit(optee_rng_mod_exit);
295
296MODULE_LICENSE("GPL v2");
297MODULE_AUTHOR("Sumit Garg <sumit.garg@linaro.org>");
298MODULE_DESCRIPTION("OP-TEE based random number generator driver");