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