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