1 | /* |
2 | * Copyright 2022 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | */ |
23 | |
24 | #include "amdgpu.h" |
25 | #include "amdgpu_psp_ta.h" |
26 | |
27 | #if defined(CONFIG_DEBUG_FS) |
28 | |
29 | static ssize_t ta_if_load_debugfs_write(struct file *fp, const char *buf, |
30 | size_t len, loff_t *off); |
31 | static ssize_t ta_if_unload_debugfs_write(struct file *fp, const char *buf, |
32 | size_t len, loff_t *off); |
33 | static ssize_t ta_if_invoke_debugfs_write(struct file *fp, const char *buf, |
34 | size_t len, loff_t *off); |
35 | |
36 | static uint32_t get_bin_version(const uint8_t *bin) |
37 | { |
38 | const struct common_firmware_header *hdr = |
39 | (const struct common_firmware_header *)bin; |
40 | |
41 | return hdr->ucode_version; |
42 | } |
43 | |
44 | static int prep_ta_mem_context(struct ta_mem_context *mem_context, |
45 | uint8_t *shared_buf, |
46 | uint32_t shared_buf_len) |
47 | { |
48 | if (mem_context->shared_mem_size < shared_buf_len) |
49 | return -EINVAL; |
50 | memset(mem_context->shared_buf, 0, mem_context->shared_mem_size); |
51 | memcpy((void *)mem_context->shared_buf, shared_buf, shared_buf_len); |
52 | |
53 | return 0; |
54 | } |
55 | |
56 | static bool is_ta_type_valid(enum ta_type_id ta_type) |
57 | { |
58 | switch (ta_type) { |
59 | case TA_TYPE_RAS: |
60 | return true; |
61 | default: |
62 | return false; |
63 | } |
64 | } |
65 | |
66 | static const struct ta_funcs ras_ta_funcs = { |
67 | .fn_ta_initialize = psp_ras_initialize, |
68 | .fn_ta_invoke = psp_ras_invoke, |
69 | .fn_ta_terminate = psp_ras_terminate |
70 | }; |
71 | |
72 | static void set_ta_context_funcs(struct psp_context *psp, |
73 | enum ta_type_id ta_type, |
74 | struct ta_context **pcontext) |
75 | { |
76 | switch (ta_type) { |
77 | case TA_TYPE_RAS: |
78 | *pcontext = &psp->ras_context.context; |
79 | psp->ta_funcs = &ras_ta_funcs; |
80 | break; |
81 | default: |
82 | break; |
83 | } |
84 | } |
85 | |
86 | static const struct file_operations ta_load_debugfs_fops = { |
87 | .write = ta_if_load_debugfs_write, |
88 | .llseek = default_llseek, |
89 | .owner = THIS_MODULE |
90 | }; |
91 | |
92 | static const struct file_operations ta_unload_debugfs_fops = { |
93 | .write = ta_if_unload_debugfs_write, |
94 | .llseek = default_llseek, |
95 | .owner = THIS_MODULE |
96 | }; |
97 | |
98 | static const struct file_operations ta_invoke_debugfs_fops = { |
99 | .write = ta_if_invoke_debugfs_write, |
100 | .llseek = default_llseek, |
101 | .owner = THIS_MODULE |
102 | }; |
103 | |
104 | /* |
105 | * DOC: AMDGPU TA debugfs interfaces |
106 | * |
107 | * Three debugfs interfaces can be opened by a program to |
108 | * load/invoke/unload TA, |
109 | * |
110 | * - /sys/kernel/debug/dri/<N>/ta_if/ta_load |
111 | * - /sys/kernel/debug/dri/<N>/ta_if/ta_invoke |
112 | * - /sys/kernel/debug/dri/<N>/ta_if/ta_unload |
113 | * |
114 | * How to use the interfaces in a program? |
115 | * |
116 | * A program needs to provide transmit buffer to the interfaces |
117 | * and will receive buffer from the interfaces below, |
118 | * |
119 | * - For TA load debugfs interface: |
120 | * Transmit buffer: |
121 | * - TA type (4bytes) |
122 | * - TA bin length (4bytes) |
123 | * - TA bin |
124 | * Receive buffer: |
125 | * - TA ID (4bytes) |
126 | * |
127 | * - For TA invoke debugfs interface: |
128 | * Transmit buffer: |
129 | * - TA type (4bytes) |
130 | * - TA ID (4bytes) |
131 | * - TA CMD ID (4bytes) |
132 | * - TA shard buf length |
133 | * (4bytes, value not beyond TA shared memory size) |
134 | * - TA shared buf |
135 | * Receive buffer: |
136 | * - TA shared buf |
137 | * |
138 | * - For TA unload debugfs interface: |
139 | * Transmit buffer: |
140 | * - TA type (4bytes) |
141 | * - TA ID (4bytes) |
142 | */ |
143 | |
144 | static ssize_t ta_if_load_debugfs_write(struct file *fp, const char *buf, size_t len, loff_t *off) |
145 | { |
146 | uint32_t ta_type = 0; |
147 | uint32_t ta_bin_len = 0; |
148 | uint8_t *ta_bin = NULL; |
149 | uint32_t copy_pos = 0; |
150 | int ret = 0; |
151 | |
152 | struct amdgpu_device *adev = (struct amdgpu_device *)file_inode(f: fp)->i_private; |
153 | struct psp_context *psp = &adev->psp; |
154 | struct ta_context *context = NULL; |
155 | |
156 | if (!buf) |
157 | return -EINVAL; |
158 | |
159 | ret = copy_from_user(to: (void *)&ta_type, from: &buf[copy_pos], n: sizeof(uint32_t)); |
160 | if (ret || (!is_ta_type_valid(ta_type))) |
161 | return -EFAULT; |
162 | |
163 | copy_pos += sizeof(uint32_t); |
164 | |
165 | ret = copy_from_user(to: (void *)&ta_bin_len, from: &buf[copy_pos], n: sizeof(uint32_t)); |
166 | if (ret) |
167 | return -EFAULT; |
168 | |
169 | copy_pos += sizeof(uint32_t); |
170 | |
171 | ta_bin = kzalloc(size: ta_bin_len, GFP_KERNEL); |
172 | if (!ta_bin) |
173 | return -ENOMEM; |
174 | if (copy_from_user(to: (void *)ta_bin, from: &buf[copy_pos], n: ta_bin_len)) { |
175 | ret = -EFAULT; |
176 | goto err_free_bin; |
177 | } |
178 | |
179 | /* Set TA context and functions */ |
180 | set_ta_context_funcs(psp, ta_type, pcontext: &context); |
181 | |
182 | if (!psp->ta_funcs || !psp->ta_funcs->fn_ta_terminate) { |
183 | dev_err(adev->dev, "Unsupported function to terminate TA\n" ); |
184 | ret = -EOPNOTSUPP; |
185 | goto err_free_bin; |
186 | } |
187 | |
188 | /* |
189 | * Allocate TA shared buf in case shared buf was freed |
190 | * due to loading TA failed before. |
191 | */ |
192 | if (!context->mem_context.shared_buf) { |
193 | ret = psp_ta_init_shared_buf(psp, mem_ctx: &context->mem_context); |
194 | if (ret) { |
195 | ret = -ENOMEM; |
196 | goto err_free_bin; |
197 | } |
198 | } |
199 | |
200 | ret = psp_fn_ta_terminate(psp); |
201 | if (ret || context->resp_status) { |
202 | dev_err(adev->dev, |
203 | "Failed to unload embedded TA (%d) and status (0x%X)\n" , |
204 | ret, context->resp_status); |
205 | if (!ret) |
206 | ret = -EINVAL; |
207 | goto err_free_ta_shared_buf; |
208 | } |
209 | |
210 | /* Prepare TA context for TA initialization */ |
211 | context->ta_type = ta_type; |
212 | context->bin_desc.fw_version = get_bin_version(bin: ta_bin); |
213 | context->bin_desc.size_bytes = ta_bin_len; |
214 | context->bin_desc.start_addr = ta_bin; |
215 | |
216 | if (!psp->ta_funcs->fn_ta_initialize) { |
217 | dev_err(adev->dev, "Unsupported function to initialize TA\n" ); |
218 | ret = -EOPNOTSUPP; |
219 | goto err_free_ta_shared_buf; |
220 | } |
221 | |
222 | ret = psp_fn_ta_initialize(psp); |
223 | if (ret || context->resp_status) { |
224 | dev_err(adev->dev, "Failed to load TA via debugfs (%d) and status (0x%X)\n" , |
225 | ret, context->resp_status); |
226 | if (!ret) |
227 | ret = -EINVAL; |
228 | goto err_free_ta_shared_buf; |
229 | } |
230 | |
231 | if (copy_to_user(to: (char *)buf, from: (void *)&context->session_id, n: sizeof(uint32_t))) |
232 | ret = -EFAULT; |
233 | |
234 | err_free_ta_shared_buf: |
235 | /* Only free TA shared buf when returns error code */ |
236 | if (ret && context->mem_context.shared_buf) |
237 | psp_ta_free_shared_buf(mem_ctx: &context->mem_context); |
238 | err_free_bin: |
239 | kfree(objp: ta_bin); |
240 | |
241 | return ret; |
242 | } |
243 | |
244 | static ssize_t ta_if_unload_debugfs_write(struct file *fp, const char *buf, size_t len, loff_t *off) |
245 | { |
246 | uint32_t ta_type = 0; |
247 | uint32_t ta_id = 0; |
248 | uint32_t copy_pos = 0; |
249 | int ret = 0; |
250 | |
251 | struct amdgpu_device *adev = (struct amdgpu_device *)file_inode(f: fp)->i_private; |
252 | struct psp_context *psp = &adev->psp; |
253 | struct ta_context *context = NULL; |
254 | |
255 | if (!buf) |
256 | return -EINVAL; |
257 | |
258 | ret = copy_from_user(to: (void *)&ta_type, from: &buf[copy_pos], n: sizeof(uint32_t)); |
259 | if (ret || (!is_ta_type_valid(ta_type))) |
260 | return -EFAULT; |
261 | |
262 | copy_pos += sizeof(uint32_t); |
263 | |
264 | ret = copy_from_user(to: (void *)&ta_id, from: &buf[copy_pos], n: sizeof(uint32_t)); |
265 | if (ret) |
266 | return -EFAULT; |
267 | |
268 | set_ta_context_funcs(psp, ta_type, pcontext: &context); |
269 | context->session_id = ta_id; |
270 | |
271 | if (!psp->ta_funcs || !psp->ta_funcs->fn_ta_terminate) { |
272 | dev_err(adev->dev, "Unsupported function to terminate TA\n" ); |
273 | return -EOPNOTSUPP; |
274 | } |
275 | |
276 | ret = psp_fn_ta_terminate(psp); |
277 | if (ret || context->resp_status) { |
278 | dev_err(adev->dev, "Failed to unload TA via debugfs (%d) and status (0x%X)\n" , |
279 | ret, context->resp_status); |
280 | if (!ret) |
281 | ret = -EINVAL; |
282 | } |
283 | |
284 | if (context->mem_context.shared_buf) |
285 | psp_ta_free_shared_buf(mem_ctx: &context->mem_context); |
286 | |
287 | return ret; |
288 | } |
289 | |
290 | static ssize_t ta_if_invoke_debugfs_write(struct file *fp, const char *buf, size_t len, loff_t *off) |
291 | { |
292 | uint32_t ta_type = 0; |
293 | uint32_t ta_id = 0; |
294 | uint32_t cmd_id = 0; |
295 | uint32_t shared_buf_len = 0; |
296 | uint8_t *shared_buf = NULL; |
297 | uint32_t copy_pos = 0; |
298 | int ret = 0; |
299 | |
300 | struct amdgpu_device *adev = (struct amdgpu_device *)file_inode(f: fp)->i_private; |
301 | struct psp_context *psp = &adev->psp; |
302 | struct ta_context *context = NULL; |
303 | |
304 | if (!buf) |
305 | return -EINVAL; |
306 | |
307 | ret = copy_from_user(to: (void *)&ta_type, from: &buf[copy_pos], n: sizeof(uint32_t)); |
308 | if (ret) |
309 | return -EFAULT; |
310 | copy_pos += sizeof(uint32_t); |
311 | |
312 | ret = copy_from_user(to: (void *)&ta_id, from: &buf[copy_pos], n: sizeof(uint32_t)); |
313 | if (ret) |
314 | return -EFAULT; |
315 | copy_pos += sizeof(uint32_t); |
316 | |
317 | ret = copy_from_user(to: (void *)&cmd_id, from: &buf[copy_pos], n: sizeof(uint32_t)); |
318 | if (ret) |
319 | return -EFAULT; |
320 | copy_pos += sizeof(uint32_t); |
321 | |
322 | ret = copy_from_user(to: (void *)&shared_buf_len, from: &buf[copy_pos], n: sizeof(uint32_t)); |
323 | if (ret) |
324 | return -EFAULT; |
325 | copy_pos += sizeof(uint32_t); |
326 | |
327 | shared_buf = kzalloc(size: shared_buf_len, GFP_KERNEL); |
328 | if (!shared_buf) |
329 | return -ENOMEM; |
330 | if (copy_from_user(to: (void *)shared_buf, from: &buf[copy_pos], n: shared_buf_len)) { |
331 | ret = -EFAULT; |
332 | goto err_free_shared_buf; |
333 | } |
334 | |
335 | set_ta_context_funcs(psp, ta_type, pcontext: &context); |
336 | |
337 | if (!context->initialized) { |
338 | dev_err(adev->dev, "TA is not initialized\n" ); |
339 | ret = -EINVAL; |
340 | goto err_free_shared_buf; |
341 | } |
342 | |
343 | if (!psp->ta_funcs || !psp->ta_funcs->fn_ta_invoke) { |
344 | dev_err(adev->dev, "Unsupported function to invoke TA\n" ); |
345 | ret = -EOPNOTSUPP; |
346 | goto err_free_shared_buf; |
347 | } |
348 | |
349 | context->session_id = ta_id; |
350 | |
351 | ret = prep_ta_mem_context(mem_context: &context->mem_context, shared_buf, shared_buf_len); |
352 | if (ret) |
353 | goto err_free_shared_buf; |
354 | |
355 | ret = psp_fn_ta_invoke(psp, cmd_id); |
356 | if (ret || context->resp_status) { |
357 | dev_err(adev->dev, "Failed to invoke TA via debugfs (%d) and status (0x%X)\n" , |
358 | ret, context->resp_status); |
359 | if (!ret) { |
360 | ret = -EINVAL; |
361 | goto err_free_shared_buf; |
362 | } |
363 | } |
364 | |
365 | if (copy_to_user(to: (char *)&buf[copy_pos], from: context->mem_context.shared_buf, n: shared_buf_len)) |
366 | ret = -EFAULT; |
367 | |
368 | err_free_shared_buf: |
369 | kfree(objp: shared_buf); |
370 | |
371 | return ret; |
372 | } |
373 | |
374 | void amdgpu_ta_if_debugfs_init(struct amdgpu_device *adev) |
375 | { |
376 | struct drm_minor *minor = adev_to_drm(adev)->primary; |
377 | |
378 | struct dentry *dir = debugfs_create_dir(name: "ta_if" , parent: minor->debugfs_root); |
379 | |
380 | debugfs_create_file(name: "ta_load" , mode: 0200, parent: dir, data: adev, |
381 | fops: &ta_load_debugfs_fops); |
382 | |
383 | debugfs_create_file(name: "ta_unload" , mode: 0200, parent: dir, |
384 | data: adev, fops: &ta_unload_debugfs_fops); |
385 | |
386 | debugfs_create_file(name: "ta_invoke" , mode: 0200, parent: dir, |
387 | data: adev, fops: &ta_invoke_debugfs_fops); |
388 | } |
389 | |
390 | #else |
391 | void amdgpu_ta_if_debugfs_init(struct amdgpu_device *adev) |
392 | { |
393 | |
394 | } |
395 | #endif |
396 | |