1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright 2019 Broadcom. |
4 | */ |
5 | |
6 | #include <linux/kernel.h> |
7 | #include <linux/module.h> |
8 | #include <linux/sizes.h> |
9 | #include <linux/slab.h> |
10 | #include <linux/tee_drv.h> |
11 | #include <linux/uuid.h> |
12 | |
13 | #include <linux/firmware/broadcom/tee_bnxt_fw.h> |
14 | |
15 | #define MAX_SHM_MEM_SZ SZ_4M |
16 | |
17 | #define MAX_TEE_PARAM_ARRY_MEMB 4 |
18 | |
19 | enum ta_cmd { |
20 | /* |
21 | * TA_CMD_BNXT_FASTBOOT - boot bnxt device by copying f/w into sram |
22 | * |
23 | * param[0] unused |
24 | * param[1] unused |
25 | * param[2] unused |
26 | * param[3] unused |
27 | * |
28 | * Result: |
29 | * TEE_SUCCESS - Invoke command success |
30 | * TEE_ERROR_ITEM_NOT_FOUND - Corrupt f/w image found on memory |
31 | */ |
32 | TA_CMD_BNXT_FASTBOOT = 0, |
33 | |
34 | /* |
35 | * TA_CMD_BNXT_COPY_COREDUMP - copy the core dump into shm |
36 | * |
37 | * param[0] (inout memref) - Coredump buffer memory reference |
38 | * param[1] (in value) - value.a: offset, data to be copied from |
39 | * value.b: size of data to be copied |
40 | * param[2] unused |
41 | * param[3] unused |
42 | * |
43 | * Result: |
44 | * TEE_SUCCESS - Invoke command success |
45 | * TEE_ERROR_BAD_PARAMETERS - Incorrect input param |
46 | * TEE_ERROR_ITEM_NOT_FOUND - Corrupt core dump |
47 | */ |
48 | TA_CMD_BNXT_COPY_COREDUMP = 3, |
49 | }; |
50 | |
51 | /** |
52 | * struct tee_bnxt_fw_private - OP-TEE bnxt private data |
53 | * @dev: OP-TEE based bnxt device. |
54 | * @ctx: OP-TEE context handler. |
55 | * @session_id: TA session identifier. |
56 | */ |
57 | struct tee_bnxt_fw_private { |
58 | struct device *dev; |
59 | struct tee_context *ctx; |
60 | u32 session_id; |
61 | struct tee_shm *fw_shm_pool; |
62 | }; |
63 | |
64 | static struct tee_bnxt_fw_private pvt_data; |
65 | |
66 | static void prepare_args(int cmd, |
67 | struct tee_ioctl_invoke_arg *arg, |
68 | struct tee_param *param) |
69 | { |
70 | memset(arg, 0, sizeof(*arg)); |
71 | memset(param, 0, MAX_TEE_PARAM_ARRY_MEMB * sizeof(*param)); |
72 | |
73 | arg->func = cmd; |
74 | arg->session = pvt_data.session_id; |
75 | arg->num_params = MAX_TEE_PARAM_ARRY_MEMB; |
76 | |
77 | /* Fill invoke cmd params */ |
78 | switch (cmd) { |
79 | case TA_CMD_BNXT_COPY_COREDUMP: |
80 | param[0].attr = TEE_IOCTL_PARAM_ATTR_TYPE_MEMREF_INOUT; |
81 | param[0].u.memref.shm = pvt_data.fw_shm_pool; |
82 | param[0].u.memref.size = MAX_SHM_MEM_SZ; |
83 | param[0].u.memref.shm_offs = 0; |
84 | param[1].attr = TEE_IOCTL_PARAM_ATTR_TYPE_VALUE_INPUT; |
85 | break; |
86 | case TA_CMD_BNXT_FASTBOOT: |
87 | default: |
88 | /* Nothing to do */ |
89 | break; |
90 | } |
91 | } |
92 | |
93 | /** |
94 | * tee_bnxt_fw_load() - Load the bnxt firmware |
95 | * Uses an OP-TEE call to start a secure |
96 | * boot process. |
97 | * Returns 0 on success, negative errno otherwise. |
98 | */ |
99 | int tee_bnxt_fw_load(void) |
100 | { |
101 | int ret = 0; |
102 | struct tee_ioctl_invoke_arg arg; |
103 | struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; |
104 | |
105 | if (!pvt_data.ctx) |
106 | return -ENODEV; |
107 | |
108 | prepare_args(cmd: TA_CMD_BNXT_FASTBOOT, arg: &arg, param); |
109 | |
110 | ret = tee_client_invoke_func(ctx: pvt_data.ctx, arg: &arg, param); |
111 | if (ret < 0 || arg.ret != 0) { |
112 | dev_err(pvt_data.dev, |
113 | "TA_CMD_BNXT_FASTBOOT invoke failed TEE err: %x, ret:%x\n" , |
114 | arg.ret, ret); |
115 | return -EINVAL; |
116 | } |
117 | |
118 | return 0; |
119 | } |
120 | EXPORT_SYMBOL(tee_bnxt_fw_load); |
121 | |
122 | /** |
123 | * tee_bnxt_copy_coredump() - Copy coredump from the allocated memory |
124 | * Uses an OP-TEE call to copy coredump |
125 | * @buf: destination buffer where core dump is copied into |
126 | * @offset: offset from the base address of core dump area |
127 | * @size: size of the dump |
128 | * |
129 | * Returns 0 on success, negative errno otherwise. |
130 | */ |
131 | int tee_bnxt_copy_coredump(void *buf, u32 offset, u32 size) |
132 | { |
133 | struct tee_ioctl_invoke_arg arg; |
134 | struct tee_param param[MAX_TEE_PARAM_ARRY_MEMB]; |
135 | void *core_data; |
136 | u32 rbytes = size; |
137 | u32 nbytes = 0; |
138 | int ret = 0; |
139 | |
140 | if (!pvt_data.ctx) |
141 | return -ENODEV; |
142 | |
143 | prepare_args(cmd: TA_CMD_BNXT_COPY_COREDUMP, arg: &arg, param); |
144 | |
145 | while (rbytes) { |
146 | nbytes = rbytes; |
147 | |
148 | nbytes = min_t(u32, rbytes, param[0].u.memref.size); |
149 | |
150 | /* Fill additional invoke cmd params */ |
151 | param[1].u.value.a = offset; |
152 | param[1].u.value.b = nbytes; |
153 | |
154 | ret = tee_client_invoke_func(ctx: pvt_data.ctx, arg: &arg, param); |
155 | if (ret < 0 || arg.ret != 0) { |
156 | dev_err(pvt_data.dev, |
157 | "TA_CMD_BNXT_COPY_COREDUMP invoke failed TEE err: %x, ret:%x\n" , |
158 | arg.ret, ret); |
159 | return -EINVAL; |
160 | } |
161 | |
162 | core_data = tee_shm_get_va(shm: pvt_data.fw_shm_pool, offs: 0); |
163 | if (IS_ERR(ptr: core_data)) { |
164 | dev_err(pvt_data.dev, "tee_shm_get_va failed\n" ); |
165 | return PTR_ERR(ptr: core_data); |
166 | } |
167 | |
168 | memcpy(buf, core_data, nbytes); |
169 | |
170 | rbytes -= nbytes; |
171 | buf += nbytes; |
172 | offset += nbytes; |
173 | } |
174 | |
175 | return 0; |
176 | } |
177 | EXPORT_SYMBOL(tee_bnxt_copy_coredump); |
178 | |
179 | static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data) |
180 | { |
181 | return (ver->impl_id == TEE_IMPL_ID_OPTEE); |
182 | } |
183 | |
184 | static int tee_bnxt_fw_probe(struct device *dev) |
185 | { |
186 | struct tee_client_device *bnxt_device = to_tee_client_device(dev); |
187 | int ret, err = -ENODEV; |
188 | struct tee_ioctl_open_session_arg sess_arg; |
189 | struct tee_shm *fw_shm_pool; |
190 | |
191 | memset(&sess_arg, 0, sizeof(sess_arg)); |
192 | |
193 | /* Open context with TEE driver */ |
194 | pvt_data.ctx = tee_client_open_context(NULL, match: optee_ctx_match, NULL, |
195 | NULL); |
196 | if (IS_ERR(ptr: pvt_data.ctx)) |
197 | return -ENODEV; |
198 | |
199 | /* Open session with Bnxt load Trusted App */ |
200 | export_uuid(dst: sess_arg.uuid, src: &bnxt_device->id.uuid); |
201 | sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC; |
202 | sess_arg.num_params = 0; |
203 | |
204 | ret = tee_client_open_session(ctx: pvt_data.ctx, arg: &sess_arg, NULL); |
205 | if (ret < 0 || sess_arg.ret != 0) { |
206 | dev_err(dev, "tee_client_open_session failed, err: %x\n" , |
207 | sess_arg.ret); |
208 | err = -EINVAL; |
209 | goto out_ctx; |
210 | } |
211 | pvt_data.session_id = sess_arg.session; |
212 | |
213 | pvt_data.dev = dev; |
214 | |
215 | fw_shm_pool = tee_shm_alloc_kernel_buf(ctx: pvt_data.ctx, MAX_SHM_MEM_SZ); |
216 | if (IS_ERR(ptr: fw_shm_pool)) { |
217 | dev_err(pvt_data.dev, "tee_shm_alloc_kernel_buf failed\n" ); |
218 | err = PTR_ERR(ptr: fw_shm_pool); |
219 | goto out_sess; |
220 | } |
221 | |
222 | pvt_data.fw_shm_pool = fw_shm_pool; |
223 | |
224 | return 0; |
225 | |
226 | out_sess: |
227 | tee_client_close_session(ctx: pvt_data.ctx, session: pvt_data.session_id); |
228 | out_ctx: |
229 | tee_client_close_context(ctx: pvt_data.ctx); |
230 | |
231 | return err; |
232 | } |
233 | |
234 | static int tee_bnxt_fw_remove(struct device *dev) |
235 | { |
236 | tee_shm_free(shm: pvt_data.fw_shm_pool); |
237 | tee_client_close_session(ctx: pvt_data.ctx, session: pvt_data.session_id); |
238 | tee_client_close_context(ctx: pvt_data.ctx); |
239 | pvt_data.ctx = NULL; |
240 | |
241 | return 0; |
242 | } |
243 | |
244 | static void tee_bnxt_fw_shutdown(struct device *dev) |
245 | { |
246 | tee_shm_free(shm: pvt_data.fw_shm_pool); |
247 | tee_client_close_session(ctx: pvt_data.ctx, session: pvt_data.session_id); |
248 | tee_client_close_context(ctx: pvt_data.ctx); |
249 | pvt_data.ctx = NULL; |
250 | } |
251 | |
252 | static const struct tee_client_device_id tee_bnxt_fw_id_table[] = { |
253 | {UUID_INIT(0x6272636D, 0x2019, 0x0716, |
254 | 0x42, 0x43, 0x4D, 0x5F, 0x53, 0x43, 0x48, 0x49)}, |
255 | {} |
256 | }; |
257 | |
258 | MODULE_DEVICE_TABLE(tee, tee_bnxt_fw_id_table); |
259 | |
260 | static struct tee_client_driver tee_bnxt_fw_driver = { |
261 | .id_table = tee_bnxt_fw_id_table, |
262 | .driver = { |
263 | .name = KBUILD_MODNAME, |
264 | .bus = &tee_bus_type, |
265 | .probe = tee_bnxt_fw_probe, |
266 | .remove = tee_bnxt_fw_remove, |
267 | .shutdown = tee_bnxt_fw_shutdown, |
268 | }, |
269 | }; |
270 | |
271 | static int __init tee_bnxt_fw_mod_init(void) |
272 | { |
273 | return driver_register(drv: &tee_bnxt_fw_driver.driver); |
274 | } |
275 | |
276 | static void __exit tee_bnxt_fw_mod_exit(void) |
277 | { |
278 | driver_unregister(drv: &tee_bnxt_fw_driver.driver); |
279 | } |
280 | |
281 | module_init(tee_bnxt_fw_mod_init); |
282 | module_exit(tee_bnxt_fw_mod_exit); |
283 | |
284 | MODULE_AUTHOR("Vikas Gupta <vikas.gupta@broadcom.com>" ); |
285 | MODULE_DESCRIPTION("Broadcom bnxt firmware manager" ); |
286 | MODULE_LICENSE("GPL v2" ); |
287 | |