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