1 /*
2 * Copyright © 2020 Google, Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 /*
7 * Decoder for devcoredump traces from drm/msm. In case of a gpu crash/hang,
8 * the coredump should be found in:
9 *
10 * /sys/class/devcoredump/devcd<n>/data
11 *
12 * The crashdump will hang around for 5min, it can be cleared by writing to
13 * the file, ie:
14 *
15 * echo 1 > /sys/class/devcoredump/devcd<n>/data
16 *
17 * (the driver won't log any new crashdumps until the previous one is cleared
18 * or times out after 5min)
19 */
20
21
22 #include "crashdec.h"
23
24 static FILE *in;
25 bool verbose;
26
27 struct rnn *rnn_gmu;
28 struct rnn *rnn_control;
29 struct rnn *rnn_pipe;
30
31 static uint64_t fault_iova;
32 static bool has_fault_iova;
33
34 struct cffdec_options options = {
35 .draw_filter = -1,
36 };
37
38 /*
39 * Helpers to read register values:
40 */
41
42 /* read registers that are 64b on 64b GPUs (ie. a5xx+) */
43 static uint64_t
regval64(const char * name)44 regval64(const char *name)
45 {
46 unsigned reg = regbase(name);
47 assert(reg);
48 uint64_t val = reg_val(reg);
49 if (is_64b())
50 val |= ((uint64_t)reg_val(reg + 1)) << 32;
51 return val;
52 }
53
54 static uint32_t
regval(const char * name)55 regval(const char *name)
56 {
57 unsigned reg = regbase(name);
58 assert(reg);
59 return reg_val(reg);
60 }
61
62 /*
63 * Line reading and string helpers:
64 */
65
66 static char *
replacestr(char * line,const char * find,const char * replace)67 replacestr(char *line, const char *find, const char *replace)
68 {
69 char *tail, *s;
70
71 if (!(s = strstr(line, find)))
72 return line;
73
74 tail = s + strlen(find);
75
76 char *newline;
77 asprintf(&newline, "%.*s%s%s", (int)(s - line), line, replace, tail);
78 free(line);
79
80 return newline;
81 }
82
83 static char *lastline;
84 static char *pushedline;
85
86 static const char *
popline(void)87 popline(void)
88 {
89 char *r = pushedline;
90
91 if (r) {
92 pushedline = NULL;
93 return r;
94 }
95
96 free(lastline);
97
98 size_t n = 0;
99 if (getline(&r, &n, in) < 0)
100 exit(0);
101
102 /* Handle section name typo's from earlier kernels: */
103 r = replacestr(r, "CP_MEMPOOOL", "CP_MEMPOOL");
104 r = replacestr(r, "CP_SEQ_STAT", "CP_SQE_STAT");
105
106 lastline = r;
107 return r;
108 }
109
110 static void
pushline(void)111 pushline(void)
112 {
113 assert(!pushedline);
114 pushedline = lastline;
115 }
116
117 static uint32_t *
popline_ascii85(uint32_t sizedwords)118 popline_ascii85(uint32_t sizedwords)
119 {
120 const char *line = popline();
121
122 /* At this point we exepct the ascii85 data to be indented *some*
123 * amount, and to terminate at the end of the line. So just eat
124 * up the leading whitespace.
125 */
126 assert(*line == ' ');
127 while (*line == ' ')
128 line++;
129
130 uint32_t *buf = calloc(1, 4 * sizedwords);
131 int idx = 0;
132
133 while (*line != '\n') {
134 if (*line == 'z') {
135 buf[idx++] = 0;
136 line++;
137 continue;
138 }
139
140 uint32_t accum = 0;
141 for (int i = 0; (i < 5) && (*line != '\n'); i++) {
142 accum *= 85;
143 accum += *line - '!';
144 line++;
145 }
146
147 buf[idx++] = accum;
148 }
149
150 return buf;
151 }
152
153 static bool
startswith(const char * line,const char * start)154 startswith(const char *line, const char *start)
155 {
156 return strstr(line, start) == line;
157 }
158
159 static bool
startswith_nowhitespace(const char * line,const char * start)160 startswith_nowhitespace(const char *line, const char *start)
161 {
162 while (*line == ' ' || *line == '\t')
163 line++;
164 return startswith(line, start);
165 }
166
167 static void
vparseline(const char * line,const char * fmt,va_list ap)168 vparseline(const char *line, const char *fmt, va_list ap)
169 {
170 int fmtlen = strlen(fmt);
171 int n = 0;
172 int l = 0;
173
174 /* scan fmt string to extract expected # of conversions: */
175 for (int i = 0; i < fmtlen; i++) {
176 if (fmt[i] == '%') {
177 if (i == (l - 1)) { /* prev char was %, ie. we have %% */
178 n--;
179 l = 0;
180 } else {
181 n++;
182 l = i;
183 }
184 }
185 }
186
187 if (vsscanf(line, fmt, ap) != n) {
188 fprintf(stderr, "parse error scanning: '%s'\n", fmt);
189 exit(1);
190 }
191 }
192
193 static void
parseline(const char * line,const char * fmt,...)194 parseline(const char *line, const char *fmt, ...)
195 {
196 va_list ap;
197 va_start(ap, fmt);
198 vparseline(line, fmt, ap);
199 va_end(ap);
200 }
201
202 static void
parseline_nowhitespace(const char * line,const char * fmt,...)203 parseline_nowhitespace(const char *line, const char *fmt, ...)
204 {
205 while (*line == ' ' || *line == '\t')
206 line++;
207
208 va_list ap;
209 va_start(ap, fmt);
210 vparseline(line, fmt, ap);
211 va_end(ap);
212 }
213
214 #define foreach_line_in_section(_line) \
215 for (const char *_line = popline(); _line; _line = popline()) \
216 /* check for start of next section */ \
217 if (_line[0] != ' ') { \
218 pushline(); \
219 break; \
220 } else
221
222 /*
223 * Decode ringbuffer section:
224 */
225
226 static struct {
227 uint64_t iova;
228 uint32_t rptr;
229 uint32_t wptr;
230 uint32_t size;
231 uint32_t *buf;
232 } ringbuffers[5];
233
234 static void
decode_ringbuffer(void)235 decode_ringbuffer(void)
236 {
237 int id = 0;
238
239 foreach_line_in_section (line) {
240 if (startswith(line, " - id:")) {
241 parseline(line, " - id: %d", &id);
242 assert(id < ARRAY_SIZE(ringbuffers));
243 } else if (startswith(line, " iova:")) {
244 parseline(line, " iova: %" PRIx64, &ringbuffers[id].iova);
245 } else if (startswith(line, " rptr:")) {
246 parseline(line, " rptr: %d", &ringbuffers[id].rptr);
247 } else if (startswith(line, " wptr:")) {
248 parseline(line, " wptr: %d", &ringbuffers[id].wptr);
249 } else if (startswith(line, " size:")) {
250 parseline(line, " size: %d", &ringbuffers[id].size);
251 } else if (startswith(line, " data: !!ascii85 |")) {
252 ringbuffers[id].buf = popline_ascii85(ringbuffers[id].size / 4);
253 add_buffer(ringbuffers[id].iova, ringbuffers[id].size,
254 ringbuffers[id].buf);
255 continue;
256 }
257
258 printf("%s", line);
259 }
260 }
261
262 /*
263 * Decode GMU log
264 */
265
266 static void
decode_gmu_log(void)267 decode_gmu_log(void)
268 {
269 uint64_t iova;
270 uint32_t size;
271
272 foreach_line_in_section (line) {
273 if (startswith(line, " iova:")) {
274 parseline(line, " iova: %" PRIx64, &iova);
275 } else if (startswith(line, " size:")) {
276 parseline(line, " size: %u", &size);
277 } else if (startswith(line, " data: !!ascii85 |")) {
278 void *buf = popline_ascii85(size / 4);
279
280 dump_hex_ascii(buf, size, 1);
281
282 free(buf);
283
284 continue;
285 }
286
287 printf("%s", line);
288 }
289 }
290
291 /*
292 * Decode HFI queues
293 */
294
295 static void
decode_gmu_hfi(void)296 decode_gmu_hfi(void)
297 {
298 struct a6xx_hfi_state hfi = {};
299
300 /* Initialize the history buffers with invalid entries (-1): */
301 memset(&hfi.history, 0xff, sizeof(hfi.history));
302
303 foreach_line_in_section (line) {
304 if (startswith(line, " iova:")) {
305 parseline(line, " iova: %" PRIx64, &hfi.iova);
306 } else if (startswith(line, " size:")) {
307 parseline(line, " size: %u", &hfi.size);
308 } else if (startswith(line, " queue-history")) {
309 unsigned qidx, dummy;
310
311 parseline(line, " queue-history[%u]:", &qidx);
312 assert(qidx < ARRAY_SIZE(hfi.history));
313
314 parseline(line, " queue-history[%u]: %d %d %d %d %d %d %d %d", &dummy,
315 &hfi.history[qidx][0], &hfi.history[qidx][1],
316 &hfi.history[qidx][2], &hfi.history[qidx][3],
317 &hfi.history[qidx][4], &hfi.history[qidx][5],
318 &hfi.history[qidx][6], &hfi.history[qidx][7]);
319 } else if (startswith(line, " data: !!ascii85 |")) {
320 hfi.buf = popline_ascii85(hfi.size / 4);
321
322 if (verbose)
323 dump_hex_ascii(hfi.buf, hfi.size, 1);
324
325 dump_gmu_hfi(&hfi);
326
327 free(hfi.buf);
328
329 continue;
330 }
331
332 printf("%s", line);
333 }
334 }
335
336 static bool
valid_header(uint32_t pkt)337 valid_header(uint32_t pkt)
338 {
339 if (options.info->chip >= 5) {
340 return pkt_is_type4(pkt) || pkt_is_type7(pkt);
341 } else {
342 /* TODO maybe we can check validish looking pkt3 opc or pkt0
343 * register offset.. the cmds sent by kernel are usually
344 * fairly limited (other than initialization) which confines
345 * the search space a bit..
346 */
347 return true;
348 }
349 }
350
351 static void
dump_cmdstream(void)352 dump_cmdstream(void)
353 {
354 uint64_t rb_base = regval64("CP_RB_BASE");
355
356 printf("got rb_base=%" PRIx64 "\n", rb_base);
357
358 options.ibs[1].base = regval64("CP_IB1_BASE");
359 if (have_rem_info())
360 options.ibs[1].rem = regval("CP_IB1_REM_SIZE");
361 options.ibs[2].base = regval64("CP_IB2_BASE");
362 if (have_rem_info())
363 options.ibs[2].rem = regval("CP_IB2_REM_SIZE");
364
365 /* Adjust remaining size to account for cmdstream slurped into ROQ
366 * but not yet consumed by SQE
367 *
368 * TODO add support for earlier GPUs once we tease out the needed
369 * registers.. see crashit.c in msmtest for hints.
370 *
371 * TODO it would be nice to be able to extract out register bitfields
372 * by name rather than hard-coding this.
373 */
374 if (have_rem_info()) {
375 uint32_t ib1_rem = regval("CP_ROQ_AVAIL_IB1") >> 16;
376 uint32_t ib2_rem = regval("CP_ROQ_AVAIL_IB2") >> 16;
377 options.ibs[1].rem += ib1_rem ? ib1_rem - 1 : 0;
378 options.ibs[2].rem += ib2_rem ? ib2_rem - 1 : 0;
379 }
380
381 printf("IB1: %" PRIx64 ", %u\n", options.ibs[1].base, options.ibs[1].rem);
382 printf("IB2: %" PRIx64 ", %u\n", options.ibs[2].base, options.ibs[2].rem);
383
384 /* now that we've got the regvals we want, reset register state
385 * so we aren't seeing values from decode_registers();
386 */
387 reset_regs();
388
389 for (int id = 0; id < ARRAY_SIZE(ringbuffers); id++) {
390 if (ringbuffers[id].iova != rb_base)
391 continue;
392 if (!ringbuffers[id].size)
393 continue;
394
395 printf("found ring!\n");
396
397 /* The kernel level ringbuffer (RB) wraps around, which
398 * cffdec doesn't really deal with.. so figure out how
399 * many dwords are unread
400 */
401 unsigned ringszdw = ringbuffers[id].size >> 2; /* in dwords */
402
403 if (verbose) {
404 handle_prefetch(ringbuffers[id].buf, ringszdw);
405 dump_commands(ringbuffers[id].buf, ringszdw, 0);
406 return;
407 }
408
409 /* helper macro to deal with modulo size math: */
410 #define mod_add(b, v) ((ringszdw + (int)(b) + (int)(v)) % ringszdw)
411
412 /* The rptr will (most likely) have moved past the IB to
413 * userspace cmdstream, so back up a bit, and then advance
414 * until we find a valid start of a packet.. this is going
415 * to be less reliable on a4xx and before (pkt0/pkt3),
416 * compared to pkt4/pkt7 with parity bits
417 */
418 const int lookback = 12;
419 unsigned rptr = mod_add(ringbuffers[id].rptr, -lookback);
420
421 for (int idx = 0; idx < lookback; idx++) {
422 if (valid_header(ringbuffers[id].buf[rptr]))
423 break;
424 rptr = mod_add(rptr, 1);
425 }
426
427 unsigned cmdszdw = mod_add(ringbuffers[id].wptr, -rptr);
428
429 printf("got cmdszdw=%d\n", cmdszdw);
430 uint32_t *buf = malloc(cmdszdw * 4);
431
432 for (int idx = 0; idx < cmdszdw; idx++) {
433 int p = mod_add(rptr, idx);
434 buf[idx] = ringbuffers[id].buf[p];
435 }
436
437 handle_prefetch(buf, cmdszdw);
438 dump_commands(buf, cmdszdw, 0);
439 free(buf);
440 }
441 }
442
443 /*
444 * Decode optional 'fault-info' section. We only get this section if
445 * the devcoredump was triggered by an iova fault:
446 */
447
448 static void
decode_fault_info(void)449 decode_fault_info(void)
450 {
451 foreach_line_in_section (line) {
452 if (startswith(line, " - far:")) {
453 parseline(line, " - far: %" PRIx64, &fault_iova);
454 has_fault_iova = true;
455 } else if (startswith(line, " - iova=")) {
456 parseline(line, " - iova=%" PRIx64, &fault_iova);
457 has_fault_iova = true;
458 }
459
460 printf("%s", line);
461 }
462 }
463
464 /*
465 * Decode 'bos' (buffers) section:
466 */
467
468 static void
decode_bos(void)469 decode_bos(void)
470 {
471 uint32_t size = 0;
472 uint64_t iova = 0;
473
474 foreach_line_in_section (line) {
475 if (startswith(line, " - iova:")) {
476 parseline(line, " - iova: %" PRIx64, &iova);
477 continue;
478 } else if (startswith(line, " size:")) {
479 parseline(line, " size: %u", &size);
480
481 /*
482 * This is a bit convoluted, vs just printing the lines as
483 * they come. But we want to have both the iova and size
484 * so we can print the end address of the buffer
485 */
486
487 uint64_t end = iova + size;
488
489 printf(" - iova: 0x%016" PRIx64 "-0x%016" PRIx64, iova, end);
490
491 if (has_fault_iova) {
492 if ((iova <= fault_iova) && (fault_iova < end)) {
493 /* Fault address was within what should be a mapped buffer!! */
494 printf("\t==");
495 } else if ((iova <= fault_iova) && (fault_iova < (end + size))) {
496 /* Fault address was near this mapped buffer */
497 printf("\t>=");
498 }
499 }
500 printf("\n");
501 printf(" size: %u (0x%x)\n", size, size);
502 continue;
503 } else if (startswith(line, " data: !!ascii85 |")) {
504 uint32_t *buf = popline_ascii85(size / 4);
505
506 if (verbose)
507 dump_hex_ascii(buf, size, 1);
508
509 add_buffer(iova, size, buf);
510
511 continue;
512 }
513
514 printf("%s", line);
515 }
516 }
517
518 /*
519 * Decode registers section:
520 */
521
522 void
dump_register(struct regacc * r)523 dump_register(struct regacc *r)
524 {
525 struct rnndecaddrinfo *info = rnn_reginfo(r->rnn, r->regbase);
526 if (info && info->typeinfo) {
527 char *decoded = rnndec_decodeval(r->rnn->vc, info->typeinfo, r->value);
528 printf("%s: %s\n", info->name, decoded);
529 } else if (info) {
530 printf("%s: %08"PRIx64"\n", info->name, r->value);
531 } else {
532 printf("<%04x>: %08"PRIx64"\n", r->regbase, r->value);
533 }
534 rnn_reginfo_free(info);
535 }
536
537 static void
decode_gmu_registers(void)538 decode_gmu_registers(void)
539 {
540 struct regacc r = regacc(rnn_gmu);
541
542 foreach_line_in_section (line) {
543 uint32_t offset, value;
544 parseline(line, " - { offset: %x, value: %x }", &offset, &value);
545
546 if (regacc_push(&r, offset / 4, value)) {
547 printf("\t%08"PRIx64"\t", r.value);
548 dump_register(&r);
549 }
550 }
551 }
552
553 static void
decode_registers(void)554 decode_registers(void)
555 {
556 struct regacc r = regacc(NULL);
557
558 foreach_line_in_section (line) {
559 uint32_t offset, value;
560 parseline(line, " - { offset: %x, value: %x }", &offset, &value);
561
562 reg_set(offset / 4, value);
563 if (regacc_push(&r, offset / 4, value)) {
564 printf("\t%08"PRIx64, r.value);
565 dump_register_val(&r, 0);
566 }
567 }
568 }
569
570 /* similar to registers section, but for banked context regs: */
571 static void
decode_clusters(void)572 decode_clusters(void)
573 {
574 struct regacc r = regacc(NULL);
575
576 foreach_line_in_section (line) {
577 if (startswith_nowhitespace(line, "- cluster-name:") ||
578 startswith_nowhitespace(line, "- context:") ||
579 startswith_nowhitespace(line, "- pipe:")) {
580 printf("%s", line);
581 continue;
582 }
583
584 uint32_t offset, value;
585 parseline_nowhitespace(line, "- { offset: %x, value: %x }", &offset, &value);
586
587 if (regacc_push(&r, offset / 4, value)) {
588 printf("\t%08"PRIx64, r.value);
589 dump_register_val(&r, 0);
590 }
591 }
592 }
593
594 /*
595 * Decode indexed-registers.. these aren't like normal registers, but a
596 * sort of FIFO where successive reads pop out associated debug state.
597 */
598
599 static void
dump_cp_sqe_stat(uint32_t * stat)600 dump_cp_sqe_stat(uint32_t *stat)
601 {
602 printf("\t PC: %04x\n", stat[0]);
603 stat++;
604
605 if (!is_a5xx() && valid_header(stat[0])) {
606 if (pkt_is_type7(stat[0])) {
607 unsigned opc = cp_type7_opcode(stat[0]);
608 const char *name = pktname(opc);
609 if (name)
610 printf("\tPKT: %s\n", name);
611 } else {
612 /* Not sure if this case can happen: */
613 }
614 }
615
616 for (int i = 0; i < 16; i++) {
617 printf("\t$%02x: %08x\t\t$%02x: %08x\n", i + 1, stat[i], i + 16 + 1,
618 stat[i + 16]);
619 }
620 }
621
622 static void
dump_control_regs(uint32_t * regs)623 dump_control_regs(uint32_t *regs)
624 {
625 if (!rnn_control)
626 return;
627
628 struct regacc r = regacc(rnn_control);
629
630 /* Control regs 0x100-0x17f are a scratch space to be used by the
631 * firmware however it wants, unlike lower regs which involve some
632 * fixed-function units. Therefore only these registers get dumped
633 * directly.
634 */
635 for (uint32_t i = 0; i < 0x80; i++) {
636 if (regacc_push(&r, i + 0x100, regs[i])) {
637 printf("\t%08"PRIx64"\t", r.value);
638 dump_register(&r);
639 }
640 }
641 }
642
643 static void
dump_cp_ucode_dbg(uint32_t * dbg)644 dump_cp_ucode_dbg(uint32_t *dbg)
645 {
646 /* Notes on the data:
647 * There seems to be a section every 4096 DWORD's. The sections aren't
648 * all the same size, so the rest of the 4096 DWORD's are filled with
649 * mirrors of the actual data.
650 */
651
652 for (int section = 0; section < 6; section++, dbg += 0x1000) {
653 switch (section) {
654 case 0:
655 /* Contains scattered data from a630_sqe.fw: */
656 printf("\tSQE instruction cache:\n");
657 dump_hex_ascii(dbg, 4 * 0x400, 1);
658 break;
659 case 1:
660 printf("\tUnknown 1:\n");
661 dump_hex_ascii(dbg, 4 * 0x80, 1);
662 break;
663 case 2:
664 printf("\tUnknown 2:\n");
665 dump_hex_ascii(dbg, 4 * 0x200, 1);
666 break;
667 case 3:
668 printf("\tUnknown 3:\n");
669 dump_hex_ascii(dbg, 4 * 0x80, 1);
670 break;
671 case 4:
672 /* Don't bother printing this normally */
673 if (verbose) {
674 printf("\tSQE packet jumptable contents:\n");
675 dump_hex_ascii(dbg, 4 * 0x80, 1);
676 }
677 break;
678 case 5:
679 printf("\tSQE scratch control regs:\n");
680 dump_control_regs(dbg);
681 break;
682 }
683 }
684 }
685
686 static void
decode_indexed_registers(void)687 decode_indexed_registers(void)
688 {
689 char *name = NULL;
690 uint32_t sizedwords = 0;
691
692 foreach_line_in_section (line) {
693 if (startswith(line, " - regs-name:")) {
694 free(name);
695 parseline(line, " - regs-name: %ms", &name);
696 } else if (startswith(line, " dwords:")) {
697 parseline(line, " dwords: %u", &sizedwords);
698 } else if (startswith(line, " data: !!ascii85 |")) {
699 uint32_t *buf = popline_ascii85(sizedwords);
700
701 /* some of the sections are pretty large, and are (at least
702 * so far) not useful, so skip them if not in verbose mode:
703 */
704 bool dump = verbose || !strcmp(name, "CP_SQE_STAT") ||
705 !strcmp(name, "CP_BV_SQE_STAT") ||
706 !strcmp(name, "CP_DRAW_STATE") ||
707 !strcmp(name, "CP_ROQ") || 0;
708
709 if (!strcmp(name, "CP_SQE_STAT") || !strcmp(name, "CP_BV_SQE_STAT"))
710 dump_cp_sqe_stat(buf);
711
712 if (!strcmp(name, "CP_UCODE_DBG_DATA"))
713 dump_cp_ucode_dbg(buf);
714
715 if (!strcmp(name, "CP_MEMPOOL"))
716 dump_cp_mem_pool(buf);
717
718 if (dump)
719 dump_hex_ascii(buf, 4 * sizedwords, 1);
720
721 free(buf);
722
723 continue;
724 }
725
726 printf("%s", line);
727 }
728 }
729
730 /*
731 * Decode shader-blocks:
732 */
733
734 static void
decode_shader_blocks(void)735 decode_shader_blocks(void)
736 {
737 char *type = NULL;
738 uint32_t sizedwords = 0;
739
740 foreach_line_in_section (line) {
741 if (startswith(line, " - type:")) {
742 free(type);
743 parseline(line, " - type: %ms", &type);
744 } else if (startswith_nowhitespace(line, "size:")) {
745 parseline_nowhitespace(line, "size: %u", &sizedwords);
746 } else if (startswith_nowhitespace(line, "data: !!ascii85 |")) {
747 uint32_t *buf = popline_ascii85(sizedwords);
748
749 /* some of the sections are pretty large, and are (at least
750 * so far) not useful, so skip them if not in verbose mode:
751 */
752 bool dump = verbose || !strcmp(type, "A6XX_SP_INST_DATA") ||
753 !strcmp(type, "A6XX_HLSQ_INST_RAM") ||
754 !strcmp(type, "A7XX_SP_INST_DATA") ||
755 !strcmp(type, "A7XX_HLSQ_INST_RAM") || 0;
756
757 if (!strcmp(type, "A6XX_SP_INST_DATA") ||
758 !strcmp(type, "A6XX_HLSQ_INST_RAM") ||
759 !strcmp(type, "A7XX_SP_INST_DATA") ||
760 !strcmp(type, "A7XX_HLSQ_INST_RAM")) {
761 /* TODO this section actually contains multiple shaders
762 * (or parts of shaders?), so perhaps we should search
763 * for ends of shaders and decode each?
764 */
765 try_disasm_a3xx(buf, sizedwords, 1, stdout, options.info->chip * 100);
766 }
767
768 if (dump)
769 dump_hex_ascii(buf, 4 * sizedwords, 1);
770
771 free(buf);
772
773 continue;
774 }
775
776 printf("%s", line);
777 }
778
779 free(type);
780 }
781
782 /*
783 * Decode debugbus section:
784 */
785
786 static void
decode_debugbus(void)787 decode_debugbus(void)
788 {
789 char *block = NULL;
790 uint32_t sizedwords = 0;
791
792 foreach_line_in_section (line) {
793 if (startswith(line, " - debugbus-block:")) {
794 free(block);
795 parseline(line, " - debugbus-block: %ms", &block);
796 } else if (startswith(line, " count:")) {
797 parseline(line, " count: %u", &sizedwords);
798 } else if (startswith(line, " data: !!ascii85 |")) {
799 uint32_t *buf = popline_ascii85(sizedwords);
800
801 /* some of the sections are pretty large, and are (at least
802 * so far) not useful, so skip them if not in verbose mode:
803 */
804 bool dump = verbose || 0;
805
806 if (dump)
807 dump_hex_ascii(buf, 4 * sizedwords, 1);
808
809 free(buf);
810
811 continue;
812 }
813
814 printf("%s", line);
815 }
816 }
817
818 /*
819 * Main crashdump decode loop:
820 */
821
822 static void
decode(void)823 decode(void)
824 {
825 const char *line;
826
827 while ((line = popline())) {
828 printf("%s", line);
829 if (startswith(line, "revision:")) {
830 unsigned core, major, minor, patchid;
831
832 parseline(line, "revision: %u (%u.%u.%u.%u)", &options.dev_id.gpu_id,
833 &core, &major, &minor, &patchid);
834
835 options.dev_id.chip_id = (core << 24) | (major << 16) | (minor << 8) | patchid;
836 options.info = fd_dev_info_raw(&options.dev_id);
837 if (!options.info) {
838 printf("Unsupported device\n");
839 break;
840 }
841
842 printf("Got chip_id=0x%"PRIx64"\n", options.dev_id.chip_id);
843
844 cffdec_init(&options);
845
846 if (is_a7xx()) {
847 rnn_gmu = rnn_new(!options.color);
848 rnn_load_file(rnn_gmu, "adreno/a6xx_gmu.xml", "A6XX");
849 rnn_control = rnn_new(!options.color);
850 rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
851 "A7XX_CONTROL_REG");
852 rnn_pipe = rnn_new(!options.color);
853 rnn_load_file(rnn_pipe, "adreno/adreno_pipe_regs.xml",
854 "A7XX_PIPE_REG");
855 } else if (is_a6xx()) {
856 rnn_gmu = rnn_new(!options.color);
857 rnn_load_file(rnn_gmu, "adreno/a6xx_gmu.xml", "A6XX");
858 rnn_control = rnn_new(!options.color);
859 rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
860 "A6XX_CONTROL_REG");
861 rnn_pipe = rnn_new(!options.color);
862 rnn_load_file(rnn_pipe, "adreno/adreno_pipe_regs.xml",
863 "A6XX_PIPE_REG");
864 } else if (is_a5xx()) {
865 rnn_control = rnn_new(!options.color);
866 rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
867 "A5XX_CONTROL_REG");
868 } else {
869 rnn_control = NULL;
870 }
871 } else if (startswith(line, "fault-info:")) {
872 decode_fault_info();
873 } else if (startswith(line, "bos:")) {
874 decode_bos();
875 } else if (startswith(line, "ringbuffer:")) {
876 decode_ringbuffer();
877 } else if (startswith(line, "gmu-log:")) {
878 decode_gmu_log();
879 } else if (startswith(line, "gmu-hfi:")) {
880 decode_gmu_hfi();
881 } else if (startswith(line, "registers:")) {
882 decode_registers();
883
884 /* after we've recorded buffer contents, and CP register values,
885 * we can take a stab at decoding the cmdstream:
886 */
887 dump_cmdstream();
888 } else if (startswith(line, "registers-gmu:")) {
889 decode_gmu_registers();
890 } else if (startswith(line, "indexed-registers:")) {
891 decode_indexed_registers();
892 } else if (startswith(line, "shader-blocks:")) {
893 decode_shader_blocks();
894 } else if (startswith(line, "clusters:")) {
895 decode_clusters();
896 } else if (startswith(line, "debugbus:")) {
897 decode_debugbus();
898 }
899 }
900 }
901
902 /*
903 * Usage and argument parsing:
904 */
905
906 static void
usage(void)907 usage(void)
908 {
909 /* clang-format off */
910 fprintf(stderr, "Usage:\n\n"
911 "\tcrashdec [-achmsv] [-f FILE]\n\n"
912 "Options:\n"
913 "\t-a, --allregs - show all registers (including ones not written since\n"
914 "\t previous draw) at each draw\n"
915 "\t-c, --color - use colors\n"
916 "\t-f, --file=FILE - read input from specified file (rather than stdin)\n"
917 "\t-h, --help - this usage message\n"
918 "\t-m, --markers - try to decode CP_NOP string markers\n"
919 "\t-s, --summary - don't show individual register writes, but just show\n"
920 "\t register values on draws\n"
921 "\t-v, --verbose - dump more verbose output, including contents of\n"
922 "\t less interesting buffers\n"
923 "\n"
924 );
925 /* clang-format on */
926 exit(2);
927 }
928
929 /* clang-format off */
930 static const struct option opts[] = {
931 { .name = "allregs", .has_arg = 0, NULL, 'a' },
932 { .name = "color", .has_arg = 0, NULL, 'c' },
933 { .name = "file", .has_arg = 1, NULL, 'f' },
934 { .name = "help", .has_arg = 0, NULL, 'h' },
935 { .name = "markers", .has_arg = 0, NULL, 'm' },
936 { .name = "summary", .has_arg = 0, NULL, 's' },
937 { .name = "verbose", .has_arg = 0, NULL, 'v' },
938 {}
939 };
940 /* clang-format on */
941
942 static bool interactive;
943
944 static void
cleanup(void)945 cleanup(void)
946 {
947 fflush(stdout);
948
949 if (interactive) {
950 pager_close();
951 }
952 }
953
954 int
main(int argc,char ** argv)955 main(int argc, char **argv)
956 {
957 int c;
958
959 interactive = isatty(STDOUT_FILENO);
960 options.color = interactive;
961
962 /* default to read from stdin: */
963 in = stdin;
964
965 while ((c = getopt_long(argc, argv, "acf:hmsv", opts, NULL)) != -1) {
966 switch (c) {
967 case 'a':
968 options.allregs = true;
969 break;
970 case 'c':
971 options.color = true;
972 break;
973 case 'f':
974 in = fopen(optarg, "r");
975 break;
976 case 'm':
977 options.decode_markers = true;
978 break;
979 case 's':
980 options.summary = true;
981 break;
982 case 'v':
983 verbose = true;
984 break;
985 case 'h':
986 default:
987 usage();
988 }
989 }
990
991 disasm_a3xx_set_debug(PRINT_RAW);
992
993 if (interactive) {
994 pager_open();
995 }
996
997 atexit(cleanup);
998
999 decode();
1000 cleanup();
1001 }
1002