1 // SPDX-License-Identifier: GPL-2.0-only OR MIT
2 /* Copyright (c) 2023 Imagination Technologies Ltd. */
3
4 #include "pvr_device.h"
5 #include "pvr_gem.h"
6 #include "pvr_rogue_fwif.h"
7 #include "pvr_rogue_fwif_sf.h"
8 #include "pvr_fw_trace.h"
9
10 #include <drm/drm_drv.h>
11 #include <drm/drm_file.h>
12
13 #include <linux/build_bug.h>
14 #include <linux/dcache.h>
15 #include <linux/debugfs.h>
16 #include <linux/sysfs.h>
17 #include <linux/types.h>
18
19 static void
tracebuf_ctrl_init(void * cpu_ptr,void * priv)20 tracebuf_ctrl_init(void *cpu_ptr, void *priv)
21 {
22 struct rogue_fwif_tracebuf *tracebuf_ctrl = cpu_ptr;
23 struct pvr_fw_trace *fw_trace = priv;
24
25 tracebuf_ctrl->tracebuf_size_in_dwords = ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
26 tracebuf_ctrl->tracebuf_flags = 0;
27
28 if (fw_trace->group_mask)
29 tracebuf_ctrl->log_type = fw_trace->group_mask | ROGUE_FWIF_LOG_TYPE_TRACE;
30 else
31 tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
32
33 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
34 struct rogue_fwif_tracebuf_space *tracebuf_space =
35 &tracebuf_ctrl->tracebuf[thread_nr];
36 struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
37
38 pvr_fw_object_get_fw_addr(trace_buffer->buf_obj,
39 &tracebuf_space->trace_buffer_fw_addr);
40
41 tracebuf_space->trace_buffer = trace_buffer->buf;
42 tracebuf_space->trace_pointer = 0;
43 }
44 }
45
pvr_fw_trace_init(struct pvr_device * pvr_dev)46 int pvr_fw_trace_init(struct pvr_device *pvr_dev)
47 {
48 struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
49 struct drm_device *drm_dev = from_pvr_device(pvr_dev);
50 int err;
51
52 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
53 struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
54
55 trace_buffer->buf =
56 pvr_fw_object_create_and_map(pvr_dev,
57 ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS *
58 sizeof(*trace_buffer->buf),
59 PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
60 PVR_BO_FW_NO_CLEAR_ON_RESET,
61 NULL, NULL, &trace_buffer->buf_obj);
62 if (IS_ERR(trace_buffer->buf)) {
63 drm_err(drm_dev, "Unable to allocate trace buffer\n");
64 err = PTR_ERR(trace_buffer->buf);
65 trace_buffer->buf = NULL;
66 goto err_free_buf;
67 }
68 }
69
70 /* TODO: Provide control of group mask. */
71 fw_trace->group_mask = 0;
72
73 fw_trace->tracebuf_ctrl =
74 pvr_fw_object_create_and_map(pvr_dev,
75 sizeof(*fw_trace->tracebuf_ctrl),
76 PVR_BO_FW_FLAGS_DEVICE_UNCACHED |
77 PVR_BO_FW_NO_CLEAR_ON_RESET,
78 tracebuf_ctrl_init, fw_trace,
79 &fw_trace->tracebuf_ctrl_obj);
80 if (IS_ERR(fw_trace->tracebuf_ctrl)) {
81 drm_err(drm_dev, "Unable to allocate trace buffer control structure\n");
82 err = PTR_ERR(fw_trace->tracebuf_ctrl);
83 goto err_free_buf;
84 }
85
86 BUILD_BUG_ON(ARRAY_SIZE(fw_trace->tracebuf_ctrl->tracebuf) !=
87 ARRAY_SIZE(fw_trace->buffers));
88
89 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
90 struct rogue_fwif_tracebuf_space *tracebuf_space =
91 &fw_trace->tracebuf_ctrl->tracebuf[thread_nr];
92 struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
93
94 trace_buffer->tracebuf_space = tracebuf_space;
95 }
96
97 return 0;
98
99 err_free_buf:
100 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
101 struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
102
103 if (trace_buffer->buf)
104 pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
105 }
106
107 return err;
108 }
109
pvr_fw_trace_fini(struct pvr_device * pvr_dev)110 void pvr_fw_trace_fini(struct pvr_device *pvr_dev)
111 {
112 struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
113
114 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); thread_nr++) {
115 struct pvr_fw_trace_buffer *trace_buffer = &fw_trace->buffers[thread_nr];
116
117 pvr_fw_object_unmap_and_destroy(trace_buffer->buf_obj);
118 }
119 pvr_fw_object_unmap_and_destroy(fw_trace->tracebuf_ctrl_obj);
120 }
121
122 /**
123 * update_logtype() - Send KCCB command to trigger FW to update logtype
124 * @pvr_dev: Target PowerVR device
125 * @group_mask: New log group mask.
126 *
127 * Returns:
128 * * 0 on success,
129 * * Any error returned by pvr_kccb_send_cmd(), or
130 * * -%EIO if the device is lost.
131 */
132 static int
update_logtype(struct pvr_device * pvr_dev,u32 group_mask)133 update_logtype(struct pvr_device *pvr_dev, u32 group_mask)
134 {
135 struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
136 struct rogue_fwif_kccb_cmd cmd;
137 int idx;
138 int err;
139
140 if (group_mask)
141 fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_TRACE | group_mask;
142 else
143 fw_trace->tracebuf_ctrl->log_type = ROGUE_FWIF_LOG_TYPE_NONE;
144
145 fw_trace->group_mask = group_mask;
146
147 down_read(&pvr_dev->reset_sem);
148 if (!drm_dev_enter(from_pvr_device(pvr_dev), &idx)) {
149 err = -EIO;
150 goto err_up_read;
151 }
152
153 cmd.cmd_type = ROGUE_FWIF_KCCB_CMD_LOGTYPE_UPDATE;
154 cmd.kccb_flags = 0;
155
156 err = pvr_kccb_send_cmd(pvr_dev, &cmd, NULL);
157
158 drm_dev_exit(idx);
159
160 err_up_read:
161 up_read(&pvr_dev->reset_sem);
162
163 return err;
164 }
165
166 struct pvr_fw_trace_seq_data {
167 /** @buffer: Pointer to copy of trace data. */
168 u32 *buffer;
169
170 /** @start_offset: Starting offset in trace data, as reported by FW. */
171 u32 start_offset;
172
173 /** @idx: Current index into trace data. */
174 u32 idx;
175
176 /** @assert_buf: Trace assert buffer, as reported by FW. */
177 struct rogue_fwif_file_info_buf assert_buf;
178 };
179
find_sfid(u32 id)180 static u32 find_sfid(u32 id)
181 {
182 for (u32 i = 0; i < ARRAY_SIZE(stid_fmts); i++) {
183 if (stid_fmts[i].id == id)
184 return i;
185 }
186
187 return ROGUE_FW_SF_LAST;
188 }
189
read_fw_trace(struct pvr_fw_trace_seq_data * trace_seq_data,u32 offset)190 static u32 read_fw_trace(struct pvr_fw_trace_seq_data *trace_seq_data, u32 offset)
191 {
192 u32 idx;
193
194 idx = trace_seq_data->idx + offset;
195 if (idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS)
196 return 0;
197
198 idx = (idx + trace_seq_data->start_offset) % ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS;
199 return trace_seq_data->buffer[idx];
200 }
201
202 /**
203 * fw_trace_get_next() - Advance trace index to next entry
204 * @trace_seq_data: Trace sequence data.
205 *
206 * Returns:
207 * * %true if trace index is now pointing to a valid entry, or
208 * * %false if trace index is pointing to an invalid entry, or has hit the end
209 * of the trace.
210 */
fw_trace_get_next(struct pvr_fw_trace_seq_data * trace_seq_data)211 static bool fw_trace_get_next(struct pvr_fw_trace_seq_data *trace_seq_data)
212 {
213 u32 id, sf_id;
214
215 while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) {
216 id = read_fw_trace(trace_seq_data, 0);
217 trace_seq_data->idx++;
218 if (!ROGUE_FW_LOG_VALIDID(id))
219 continue;
220 if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) {
221 /* Assertion failure marks the end of the trace. */
222 return false;
223 }
224
225 sf_id = find_sfid(id);
226 if (sf_id == ROGUE_FW_SF_FIRST)
227 continue;
228 if (sf_id == ROGUE_FW_SF_LAST) {
229 /*
230 * Could not match with an ID in the SF table, trace is
231 * most likely corrupt from this point.
232 */
233 return false;
234 }
235
236 /* Skip over the timestamp, and any parameters. */
237 trace_seq_data->idx += 2 + ROGUE_FW_SF_PARAMNUM(id);
238
239 /* Ensure index is now pointing to a valid trace entry. */
240 id = read_fw_trace(trace_seq_data, 0);
241 if (!ROGUE_FW_LOG_VALIDID(id))
242 continue;
243
244 return true;
245 }
246
247 /* Hit end of trace data. */
248 return false;
249 }
250
251 /**
252 * fw_trace_get_first() - Find first valid entry in trace
253 * @trace_seq_data: Trace sequence data.
254 *
255 * Skips over invalid (usually zero) and ROGUE_FW_SF_FIRST entries.
256 *
257 * If the trace has no valid entries, this function will exit with the trace
258 * index pointing to the end of the trace. trace_seq_show() will return an error
259 * in this state.
260 */
fw_trace_get_first(struct pvr_fw_trace_seq_data * trace_seq_data)261 static void fw_trace_get_first(struct pvr_fw_trace_seq_data *trace_seq_data)
262 {
263 trace_seq_data->idx = 0;
264
265 while (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) {
266 u32 id = read_fw_trace(trace_seq_data, 0);
267
268 if (ROGUE_FW_LOG_VALIDID(id)) {
269 u32 sf_id = find_sfid(id);
270
271 if (sf_id != ROGUE_FW_SF_FIRST)
272 break;
273 }
274 trace_seq_data->idx++;
275 }
276 }
277
fw_trace_seq_start(struct seq_file * s,loff_t * pos)278 static void *fw_trace_seq_start(struct seq_file *s, loff_t *pos)
279 {
280 struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
281
282 /* Reset trace index, then advance to *pos. */
283 fw_trace_get_first(trace_seq_data);
284
285 for (u32 i = 0; i < *pos; i++) {
286 if (!fw_trace_get_next(trace_seq_data))
287 return NULL;
288 }
289
290 return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL;
291 }
292
fw_trace_seq_next(struct seq_file * s,void * v,loff_t * pos)293 static void *fw_trace_seq_next(struct seq_file *s, void *v, loff_t *pos)
294 {
295 struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
296
297 (*pos)++;
298 if (!fw_trace_get_next(trace_seq_data))
299 return NULL;
300
301 return (trace_seq_data->idx < ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS) ? pos : NULL;
302 }
303
fw_trace_seq_stop(struct seq_file * s,void * v)304 static void fw_trace_seq_stop(struct seq_file *s, void *v)
305 {
306 }
307
fw_trace_seq_show(struct seq_file * s,void * v)308 static int fw_trace_seq_show(struct seq_file *s, void *v)
309 {
310 struct pvr_fw_trace_seq_data *trace_seq_data = s->private;
311 u64 timestamp;
312 u32 id;
313 u32 sf_id;
314
315 if (trace_seq_data->idx >= ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS)
316 return -EINVAL;
317
318 id = read_fw_trace(trace_seq_data, 0);
319 /* Index is not pointing at a valid entry. */
320 if (!ROGUE_FW_LOG_VALIDID(id))
321 return -EINVAL;
322
323 sf_id = find_sfid(id);
324 /* Index is not pointing at a valid entry. */
325 if (sf_id == ROGUE_FW_SF_LAST)
326 return -EINVAL;
327
328 timestamp = ((u64)read_fw_trace(trace_seq_data, 1) << 32) |
329 read_fw_trace(trace_seq_data, 2);
330 timestamp = (timestamp & ~ROGUE_FWT_TIMESTAMP_TIME_CLRMSK) >>
331 ROGUE_FWT_TIMESTAMP_TIME_SHIFT;
332
333 seq_printf(s, "[%llu] : ", timestamp);
334 if (id == ROGUE_FW_SF_MAIN_ASSERT_FAILED) {
335 seq_printf(s, "ASSERTION %s failed at %s:%u",
336 trace_seq_data->assert_buf.info,
337 trace_seq_data->assert_buf.path,
338 trace_seq_data->assert_buf.line_num);
339 } else {
340 seq_printf(s, stid_fmts[sf_id].name,
341 read_fw_trace(trace_seq_data, 3),
342 read_fw_trace(trace_seq_data, 4),
343 read_fw_trace(trace_seq_data, 5),
344 read_fw_trace(trace_seq_data, 6),
345 read_fw_trace(trace_seq_data, 7),
346 read_fw_trace(trace_seq_data, 8),
347 read_fw_trace(trace_seq_data, 9),
348 read_fw_trace(trace_seq_data, 10),
349 read_fw_trace(trace_seq_data, 11),
350 read_fw_trace(trace_seq_data, 12),
351 read_fw_trace(trace_seq_data, 13),
352 read_fw_trace(trace_seq_data, 14),
353 read_fw_trace(trace_seq_data, 15),
354 read_fw_trace(trace_seq_data, 16),
355 read_fw_trace(trace_seq_data, 17),
356 read_fw_trace(trace_seq_data, 18),
357 read_fw_trace(trace_seq_data, 19),
358 read_fw_trace(trace_seq_data, 20),
359 read_fw_trace(trace_seq_data, 21),
360 read_fw_trace(trace_seq_data, 22));
361 }
362 seq_puts(s, "\n");
363 return 0;
364 }
365
366 static const struct seq_operations pvr_fw_trace_seq_ops = {
367 .start = fw_trace_seq_start,
368 .next = fw_trace_seq_next,
369 .stop = fw_trace_seq_stop,
370 .show = fw_trace_seq_show
371 };
372
fw_trace_open(struct inode * inode,struct file * file)373 static int fw_trace_open(struct inode *inode, struct file *file)
374 {
375 struct pvr_fw_trace_buffer *trace_buffer = inode->i_private;
376 struct rogue_fwif_tracebuf_space *tracebuf_space =
377 trace_buffer->tracebuf_space;
378 struct pvr_fw_trace_seq_data *trace_seq_data;
379 int err;
380
381 trace_seq_data = kzalloc(sizeof(*trace_seq_data), GFP_KERNEL);
382 if (!trace_seq_data)
383 return -ENOMEM;
384
385 trace_seq_data->buffer = kcalloc(ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS,
386 sizeof(*trace_seq_data->buffer), GFP_KERNEL);
387 if (!trace_seq_data->buffer) {
388 err = -ENOMEM;
389 goto err_free_data;
390 }
391
392 /*
393 * Take a local copy of the trace buffer, as firmware may still be
394 * writing to it. This will exist as long as this file is open.
395 */
396 memcpy(trace_seq_data->buffer, trace_buffer->buf,
397 ROGUE_FW_TRACE_BUF_DEFAULT_SIZE_IN_DWORDS * sizeof(u32));
398 trace_seq_data->start_offset = READ_ONCE(tracebuf_space->trace_pointer);
399 trace_seq_data->assert_buf = tracebuf_space->assert_buf;
400 fw_trace_get_first(trace_seq_data);
401
402 err = seq_open(file, &pvr_fw_trace_seq_ops);
403 if (err)
404 goto err_free_buffer;
405
406 ((struct seq_file *)file->private_data)->private = trace_seq_data;
407
408 return 0;
409
410 err_free_buffer:
411 kfree(trace_seq_data->buffer);
412
413 err_free_data:
414 kfree(trace_seq_data);
415
416 return err;
417 }
418
fw_trace_release(struct inode * inode,struct file * file)419 static int fw_trace_release(struct inode *inode, struct file *file)
420 {
421 struct pvr_fw_trace_seq_data *trace_seq_data =
422 ((struct seq_file *)file->private_data)->private;
423
424 seq_release(inode, file);
425 kfree(trace_seq_data->buffer);
426 kfree(trace_seq_data);
427
428 return 0;
429 }
430
431 static const struct file_operations pvr_fw_trace_fops = {
432 .owner = THIS_MODULE,
433 .open = fw_trace_open,
434 .read = seq_read,
435 .llseek = seq_lseek,
436 .release = fw_trace_release,
437 };
438
439 void
pvr_fw_trace_mask_update(struct pvr_device * pvr_dev,u32 old_mask,u32 new_mask)440 pvr_fw_trace_mask_update(struct pvr_device *pvr_dev, u32 old_mask, u32 new_mask)
441 {
442 if (IS_ENABLED(CONFIG_DEBUG_FS) && old_mask != new_mask)
443 update_logtype(pvr_dev, new_mask);
444 }
445
446 void
pvr_fw_trace_debugfs_init(struct pvr_device * pvr_dev,struct dentry * dir)447 pvr_fw_trace_debugfs_init(struct pvr_device *pvr_dev, struct dentry *dir)
448 {
449 struct pvr_fw_trace *fw_trace = &pvr_dev->fw_dev.fw_trace;
450
451 if (!IS_ENABLED(CONFIG_DEBUG_FS))
452 return;
453
454 static_assert(ARRAY_SIZE(fw_trace->buffers) <= 10,
455 "The filename buffer is only large enough for a single-digit thread count");
456
457 for (u32 thread_nr = 0; thread_nr < ARRAY_SIZE(fw_trace->buffers); ++thread_nr) {
458 char filename[8];
459
460 snprintf(filename, ARRAY_SIZE(filename), "trace_%u", thread_nr);
461 debugfs_create_file(filename, 0400, dir,
462 &fw_trace->buffers[thread_nr],
463 &pvr_fw_trace_fops);
464 }
465 }
466