17ec681f3Smrg/*
27ec681f3Smrg * Copyright © 2020 Google, Inc.
37ec681f3Smrg *
47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
57ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
67ec681f3Smrg * to deal in the Software without restriction, including without limitation
77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
97ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
107ec681f3Smrg *
117ec681f3Smrg * The above copyright notice and this permission notice (including the next
127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
137ec681f3Smrg * Software.
147ec681f3Smrg *
157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
217ec681f3Smrg * SOFTWARE.
227ec681f3Smrg */
237ec681f3Smrg
247ec681f3Smrg/*
257ec681f3Smrg * Decoder for devcoredump traces from drm/msm.  In case of a gpu crash/hang,
267ec681f3Smrg * the coredump should be found in:
277ec681f3Smrg *
287ec681f3Smrg *    /sys/class/devcoredump/devcd<n>/data
297ec681f3Smrg *
307ec681f3Smrg * The crashdump will hang around for 5min, it can be cleared by writing to
317ec681f3Smrg * the file, ie:
327ec681f3Smrg *
337ec681f3Smrg *    echo 1 > /sys/class/devcoredump/devcd<n>/data
347ec681f3Smrg *
357ec681f3Smrg * (the driver won't log any new crashdumps until the previous one is cleared
367ec681f3Smrg * or times out after 5min)
377ec681f3Smrg */
387ec681f3Smrg
397ec681f3Smrg#include <assert.h>
407ec681f3Smrg#include <getopt.h>
417ec681f3Smrg#include <inttypes.h>
427ec681f3Smrg#include <stdarg.h>
437ec681f3Smrg#include <stdbool.h>
447ec681f3Smrg#include <stdint.h>
457ec681f3Smrg#include <stdio.h>
467ec681f3Smrg#include <stdlib.h>
477ec681f3Smrg#include <string.h>
487ec681f3Smrg#include <unistd.h>
497ec681f3Smrg
507ec681f3Smrg#include "freedreno_pm4.h"
517ec681f3Smrg
527ec681f3Smrg#include "ir3/instr-a3xx.h"
537ec681f3Smrg#include "buffers.h"
547ec681f3Smrg#include "cffdec.h"
557ec681f3Smrg#include "disasm.h"
567ec681f3Smrg#include "pager.h"
577ec681f3Smrg#include "rnnutil.h"
587ec681f3Smrg#include "util.h"
597ec681f3Smrg
607ec681f3Smrgstatic FILE *in;
617ec681f3Smrgstatic bool verbose;
627ec681f3Smrg
637ec681f3Smrgstatic struct rnn *rnn_gmu;
647ec681f3Smrgstatic struct rnn *rnn_control;
657ec681f3Smrgstatic struct rnn *rnn_pipe;
667ec681f3Smrg
677ec681f3Smrgstatic struct cffdec_options options = {
687ec681f3Smrg   .draw_filter = -1,
697ec681f3Smrg};
707ec681f3Smrg
717ec681f3Smrgstatic inline bool
727ec681f3Smrgis_a6xx(void)
737ec681f3Smrg{
747ec681f3Smrg   return (600 <= options.gpu_id) && (options.gpu_id < 700);
757ec681f3Smrg}
767ec681f3Smrgstatic inline bool
777ec681f3Smrgis_a5xx(void)
787ec681f3Smrg{
797ec681f3Smrg   return (500 <= options.gpu_id) && (options.gpu_id < 600);
807ec681f3Smrg}
817ec681f3Smrgstatic inline bool
827ec681f3Smrgis_64b(void)
837ec681f3Smrg{
847ec681f3Smrg   return options.gpu_id >= 500;
857ec681f3Smrg}
867ec681f3Smrg
877ec681f3Smrg/*
887ec681f3Smrg * Helpers to read register values:
897ec681f3Smrg */
907ec681f3Smrg
917ec681f3Smrg/* read registers that are 64b on 64b GPUs (ie. a5xx+) */
927ec681f3Smrgstatic uint64_t
937ec681f3Smrgregval64(const char *name)
947ec681f3Smrg{
957ec681f3Smrg   unsigned reg = regbase(name);
967ec681f3Smrg   assert(reg);
977ec681f3Smrg   uint64_t val = reg_val(reg);
987ec681f3Smrg   if (is_64b())
997ec681f3Smrg      val |= ((uint64_t)reg_val(reg + 1)) << 32;
1007ec681f3Smrg   return val;
1017ec681f3Smrg}
1027ec681f3Smrg
1037ec681f3Smrgstatic uint32_t
1047ec681f3Smrgregval(const char *name)
1057ec681f3Smrg{
1067ec681f3Smrg   unsigned reg = regbase(name);
1077ec681f3Smrg   assert(reg);
1087ec681f3Smrg   return reg_val(reg);
1097ec681f3Smrg}
1107ec681f3Smrg
1117ec681f3Smrg/*
1127ec681f3Smrg * Line reading and string helpers:
1137ec681f3Smrg */
1147ec681f3Smrg
1157ec681f3Smrgstatic char *
1167ec681f3Smrgreplacestr(char *line, const char *find, const char *replace)
1177ec681f3Smrg{
1187ec681f3Smrg   char *tail, *s;
1197ec681f3Smrg
1207ec681f3Smrg   if (!(s = strstr(line, find)))
1217ec681f3Smrg      return line;
1227ec681f3Smrg
1237ec681f3Smrg   tail = s + strlen(find);
1247ec681f3Smrg
1257ec681f3Smrg   char *newline;
1267ec681f3Smrg   asprintf(&newline, "%.*s%s%s", (int)(s - line), line, replace, tail);
1277ec681f3Smrg   free(line);
1287ec681f3Smrg
1297ec681f3Smrg   return newline;
1307ec681f3Smrg}
1317ec681f3Smrg
1327ec681f3Smrgstatic char *lastline;
1337ec681f3Smrgstatic char *pushedline;
1347ec681f3Smrg
1357ec681f3Smrgstatic const char *
1367ec681f3Smrgpopline(void)
1377ec681f3Smrg{
1387ec681f3Smrg   char *r = pushedline;
1397ec681f3Smrg
1407ec681f3Smrg   if (r) {
1417ec681f3Smrg      pushedline = NULL;
1427ec681f3Smrg      return r;
1437ec681f3Smrg   }
1447ec681f3Smrg
1457ec681f3Smrg   free(lastline);
1467ec681f3Smrg
1477ec681f3Smrg   size_t n = 0;
1487ec681f3Smrg   if (getline(&r, &n, in) < 0)
1497ec681f3Smrg      exit(0);
1507ec681f3Smrg
1517ec681f3Smrg   /* Handle section name typo's from earlier kernels: */
1527ec681f3Smrg   r = replacestr(r, "CP_MEMPOOOL", "CP_MEMPOOL");
1537ec681f3Smrg   r = replacestr(r, "CP_SEQ_STAT", "CP_SQE_STAT");
1547ec681f3Smrg
1557ec681f3Smrg   lastline = r;
1567ec681f3Smrg   return r;
1577ec681f3Smrg}
1587ec681f3Smrg
1597ec681f3Smrgstatic void
1607ec681f3Smrgpushline(void)
1617ec681f3Smrg{
1627ec681f3Smrg   assert(!pushedline);
1637ec681f3Smrg   pushedline = lastline;
1647ec681f3Smrg}
1657ec681f3Smrg
1667ec681f3Smrgstatic uint32_t *
1677ec681f3Smrgpopline_ascii85(uint32_t sizedwords)
1687ec681f3Smrg{
1697ec681f3Smrg   const char *line = popline();
1707ec681f3Smrg
1717ec681f3Smrg   /* At this point we exepct the ascii85 data to be indented *some*
1727ec681f3Smrg    * amount, and to terminate at the end of the line.  So just eat
1737ec681f3Smrg    * up the leading whitespace.
1747ec681f3Smrg    */
1757ec681f3Smrg   assert(*line == ' ');
1767ec681f3Smrg   while (*line == ' ')
1777ec681f3Smrg      line++;
1787ec681f3Smrg
1797ec681f3Smrg   uint32_t *buf = calloc(1, 4 * sizedwords);
1807ec681f3Smrg   int idx = 0;
1817ec681f3Smrg
1827ec681f3Smrg   while (*line != '\n') {
1837ec681f3Smrg      if (*line == 'z') {
1847ec681f3Smrg         buf[idx++] = 0;
1857ec681f3Smrg         line++;
1867ec681f3Smrg         continue;
1877ec681f3Smrg      }
1887ec681f3Smrg
1897ec681f3Smrg      uint32_t accum = 0;
1907ec681f3Smrg      for (int i = 0; (i < 5) && (*line != '\n'); i++) {
1917ec681f3Smrg         accum *= 85;
1927ec681f3Smrg         accum += *line - '!';
1937ec681f3Smrg         line++;
1947ec681f3Smrg      }
1957ec681f3Smrg
1967ec681f3Smrg      buf[idx++] = accum;
1977ec681f3Smrg   }
1987ec681f3Smrg
1997ec681f3Smrg   return buf;
2007ec681f3Smrg}
2017ec681f3Smrg
2027ec681f3Smrgstatic bool
2037ec681f3Smrgstartswith(const char *line, const char *start)
2047ec681f3Smrg{
2057ec681f3Smrg   return strstr(line, start) == line;
2067ec681f3Smrg}
2077ec681f3Smrg
2087ec681f3Smrgstatic void
2097ec681f3Smrgparseline(const char *line, const char *fmt, ...)
2107ec681f3Smrg{
2117ec681f3Smrg   int fmtlen = strlen(fmt);
2127ec681f3Smrg   int n = 0;
2137ec681f3Smrg   int l = 0;
2147ec681f3Smrg
2157ec681f3Smrg   /* scan fmt string to extract expected # of conversions: */
2167ec681f3Smrg   for (int i = 0; i < fmtlen; i++) {
2177ec681f3Smrg      if (fmt[i] == '%') {
2187ec681f3Smrg         if (i == (l - 1)) { /* prev char was %, ie. we have %% */
2197ec681f3Smrg            n--;
2207ec681f3Smrg            l = 0;
2217ec681f3Smrg         } else {
2227ec681f3Smrg            n++;
2237ec681f3Smrg            l = i;
2247ec681f3Smrg         }
2257ec681f3Smrg      }
2267ec681f3Smrg   }
2277ec681f3Smrg
2287ec681f3Smrg   va_list ap;
2297ec681f3Smrg   va_start(ap, fmt);
2307ec681f3Smrg   if (vsscanf(line, fmt, ap) != n) {
2317ec681f3Smrg      fprintf(stderr, "parse error scanning: '%s'\n", fmt);
2327ec681f3Smrg      exit(1);
2337ec681f3Smrg   }
2347ec681f3Smrg   va_end(ap);
2357ec681f3Smrg}
2367ec681f3Smrg
2377ec681f3Smrg#define foreach_line_in_section(_line)                                         \
2387ec681f3Smrg   for (const char *_line = popline(); _line; _line = popline())               \
2397ec681f3Smrg      /* check for start of next section */                                    \
2407ec681f3Smrg      if (_line[0] != ' ') {                                                   \
2417ec681f3Smrg         pushline();                                                           \
2427ec681f3Smrg         break;                                                                \
2437ec681f3Smrg      } else
2447ec681f3Smrg
2457ec681f3Smrg/*
2467ec681f3Smrg * Decode ringbuffer section:
2477ec681f3Smrg */
2487ec681f3Smrg
2497ec681f3Smrgstatic struct {
2507ec681f3Smrg   uint64_t iova;
2517ec681f3Smrg   uint32_t rptr;
2527ec681f3Smrg   uint32_t wptr;
2537ec681f3Smrg   uint32_t size;
2547ec681f3Smrg   uint32_t *buf;
2557ec681f3Smrg} ringbuffers[5];
2567ec681f3Smrg
2577ec681f3Smrgstatic void
2587ec681f3Smrgdecode_ringbuffer(void)
2597ec681f3Smrg{
2607ec681f3Smrg   int id = 0;
2617ec681f3Smrg
2627ec681f3Smrg   foreach_line_in_section (line) {
2637ec681f3Smrg      if (startswith(line, "  - id:")) {
2647ec681f3Smrg         parseline(line, "  - id: %d", &id);
2657ec681f3Smrg         assert(id < ARRAY_SIZE(ringbuffers));
2667ec681f3Smrg      } else if (startswith(line, "    iova:")) {
2677ec681f3Smrg         parseline(line, "    iova: %" PRIx64, &ringbuffers[id].iova);
2687ec681f3Smrg      } else if (startswith(line, "    rptr:")) {
2697ec681f3Smrg         parseline(line, "    rptr: %d", &ringbuffers[id].rptr);
2707ec681f3Smrg      } else if (startswith(line, "    wptr:")) {
2717ec681f3Smrg         parseline(line, "    wptr: %d", &ringbuffers[id].wptr);
2727ec681f3Smrg      } else if (startswith(line, "    size:")) {
2737ec681f3Smrg         parseline(line, "    size: %d", &ringbuffers[id].size);
2747ec681f3Smrg      } else if (startswith(line, "    data: !!ascii85 |")) {
2757ec681f3Smrg         ringbuffers[id].buf = popline_ascii85(ringbuffers[id].size / 4);
2767ec681f3Smrg         add_buffer(ringbuffers[id].iova, ringbuffers[id].size,
2777ec681f3Smrg                    ringbuffers[id].buf);
2787ec681f3Smrg         continue;
2797ec681f3Smrg      }
2807ec681f3Smrg
2817ec681f3Smrg      printf("%s", line);
2827ec681f3Smrg   }
2837ec681f3Smrg}
2847ec681f3Smrg
2857ec681f3Smrgstatic bool
2867ec681f3Smrgvalid_header(uint32_t pkt)
2877ec681f3Smrg{
2887ec681f3Smrg   if (options.gpu_id >= 500) {
2897ec681f3Smrg      return pkt_is_type4(pkt) || pkt_is_type7(pkt);
2907ec681f3Smrg   } else {
2917ec681f3Smrg      /* TODO maybe we can check validish looking pkt3 opc or pkt0
2927ec681f3Smrg       * register offset.. the cmds sent by kernel are usually
2937ec681f3Smrg       * fairly limited (other than initialization) which confines
2947ec681f3Smrg       * the search space a bit..
2957ec681f3Smrg       */
2967ec681f3Smrg      return true;
2977ec681f3Smrg   }
2987ec681f3Smrg}
2997ec681f3Smrg
3007ec681f3Smrgstatic void
3017ec681f3Smrgdump_cmdstream(void)
3027ec681f3Smrg{
3037ec681f3Smrg   uint64_t rb_base = regval64("CP_RB_BASE");
3047ec681f3Smrg
3057ec681f3Smrg   printf("got rb_base=%" PRIx64 "\n", rb_base);
3067ec681f3Smrg
3077ec681f3Smrg   options.ibs[1].base = regval64("CP_IB1_BASE");
3087ec681f3Smrg   options.ibs[1].rem = regval("CP_IB1_REM_SIZE");
3097ec681f3Smrg   options.ibs[2].base = regval64("CP_IB2_BASE");
3107ec681f3Smrg   options.ibs[2].rem = regval("CP_IB2_REM_SIZE");
3117ec681f3Smrg
3127ec681f3Smrg   /* Adjust remaining size to account for cmdstream slurped into ROQ
3137ec681f3Smrg    * but not yet consumed by SQE
3147ec681f3Smrg    *
3157ec681f3Smrg    * TODO add support for earlier GPUs once we tease out the needed
3167ec681f3Smrg    * registers.. see crashit.c in msmtest for hints.
3177ec681f3Smrg    *
3187ec681f3Smrg    * TODO it would be nice to be able to extract out register bitfields
3197ec681f3Smrg    * by name rather than hard-coding this.
3207ec681f3Smrg    */
3217ec681f3Smrg   if (is_a6xx()) {
3227ec681f3Smrg      options.ibs[1].rem += regval("CP_CSQ_IB1_STAT") >> 16;
3237ec681f3Smrg      options.ibs[2].rem += regval("CP_CSQ_IB2_STAT") >> 16;
3247ec681f3Smrg   }
3257ec681f3Smrg
3267ec681f3Smrg   printf("IB1: %" PRIx64 ", %u\n", options.ibs[1].base, options.ibs[1].rem);
3277ec681f3Smrg   printf("IB2: %" PRIx64 ", %u\n", options.ibs[2].base, options.ibs[2].rem);
3287ec681f3Smrg
3297ec681f3Smrg   /* now that we've got the regvals we want, reset register state
3307ec681f3Smrg    * so we aren't seeing values from decode_registers();
3317ec681f3Smrg    */
3327ec681f3Smrg   reset_regs();
3337ec681f3Smrg
3347ec681f3Smrg   for (int id = 0; id < ARRAY_SIZE(ringbuffers); id++) {
3357ec681f3Smrg      if (ringbuffers[id].iova != rb_base)
3367ec681f3Smrg         continue;
3377ec681f3Smrg      if (!ringbuffers[id].size)
3387ec681f3Smrg         continue;
3397ec681f3Smrg
3407ec681f3Smrg      printf("found ring!\n");
3417ec681f3Smrg
3427ec681f3Smrg      /* The kernel level ringbuffer (RB) wraps around, which
3437ec681f3Smrg       * cffdec doesn't really deal with.. so figure out how
3447ec681f3Smrg       * many dwords are unread
3457ec681f3Smrg       */
3467ec681f3Smrg      unsigned ringszdw = ringbuffers[id].size >> 2; /* in dwords */
3477ec681f3Smrg
3487ec681f3Smrg      if (verbose) {
3497ec681f3Smrg         dump_commands(ringbuffers[id].buf, ringszdw, 0);
3507ec681f3Smrg         return;
3517ec681f3Smrg      }
3527ec681f3Smrg
3537ec681f3Smrg/* helper macro to deal with modulo size math: */
3547ec681f3Smrg#define mod_add(b, v) ((ringszdw + (int)(b) + (int)(v)) % ringszdw)
3557ec681f3Smrg
3567ec681f3Smrg      /* The rptr will (most likely) have moved past the IB to
3577ec681f3Smrg       * userspace cmdstream, so back up a bit, and then advance
3587ec681f3Smrg       * until we find a valid start of a packet.. this is going
3597ec681f3Smrg       * to be less reliable on a4xx and before (pkt0/pkt3),
3607ec681f3Smrg       * compared to pkt4/pkt7 with parity bits
3617ec681f3Smrg       */
3627ec681f3Smrg      const int lookback = 12;
3637ec681f3Smrg      unsigned rptr = mod_add(ringbuffers[id].rptr, -lookback);
3647ec681f3Smrg
3657ec681f3Smrg      for (int idx = 0; idx < lookback; idx++) {
3667ec681f3Smrg         if (valid_header(ringbuffers[id].buf[rptr]))
3677ec681f3Smrg            break;
3687ec681f3Smrg         rptr = mod_add(rptr, 1);
3697ec681f3Smrg      }
3707ec681f3Smrg
3717ec681f3Smrg      unsigned cmdszdw = mod_add(ringbuffers[id].wptr, -rptr);
3727ec681f3Smrg
3737ec681f3Smrg      printf("got cmdszdw=%d\n", cmdszdw);
3747ec681f3Smrg      uint32_t *buf = malloc(cmdszdw * 4);
3757ec681f3Smrg
3767ec681f3Smrg      for (int idx = 0; idx < cmdszdw; idx++) {
3777ec681f3Smrg         int p = mod_add(rptr, idx);
3787ec681f3Smrg         buf[idx] = ringbuffers[id].buf[p];
3797ec681f3Smrg      }
3807ec681f3Smrg
3817ec681f3Smrg      dump_commands(buf, cmdszdw, 0);
3827ec681f3Smrg      free(buf);
3837ec681f3Smrg   }
3847ec681f3Smrg}
3857ec681f3Smrg
3867ec681f3Smrg/*
3877ec681f3Smrg * Decode 'bos' (buffers) section:
3887ec681f3Smrg */
3897ec681f3Smrg
3907ec681f3Smrgstatic void
3917ec681f3Smrgdecode_bos(void)
3927ec681f3Smrg{
3937ec681f3Smrg   uint32_t size = 0;
3947ec681f3Smrg   uint64_t iova = 0;
3957ec681f3Smrg
3967ec681f3Smrg   foreach_line_in_section (line) {
3977ec681f3Smrg      if (startswith(line, "  - iova:")) {
3987ec681f3Smrg         parseline(line, "  - iova: %" PRIx64, &iova);
3997ec681f3Smrg      } else if (startswith(line, "    size:")) {
4007ec681f3Smrg         parseline(line, "    size: %u", &size);
4017ec681f3Smrg      } else if (startswith(line, "    data: !!ascii85 |")) {
4027ec681f3Smrg         uint32_t *buf = popline_ascii85(size / 4);
4037ec681f3Smrg
4047ec681f3Smrg         if (verbose)
4057ec681f3Smrg            dump_hex_ascii(buf, size, 1);
4067ec681f3Smrg
4077ec681f3Smrg         add_buffer(iova, size, buf);
4087ec681f3Smrg
4097ec681f3Smrg         continue;
4107ec681f3Smrg      }
4117ec681f3Smrg
4127ec681f3Smrg      printf("%s", line);
4137ec681f3Smrg   }
4147ec681f3Smrg}
4157ec681f3Smrg
4167ec681f3Smrg/*
4177ec681f3Smrg * Decode registers section:
4187ec681f3Smrg */
4197ec681f3Smrg
4207ec681f3Smrgstatic void
4217ec681f3Smrgdump_register(struct rnn *rnn, uint32_t offset, uint32_t value)
4227ec681f3Smrg{
4237ec681f3Smrg   struct rnndecaddrinfo *info = rnn_reginfo(rnn, offset);
4247ec681f3Smrg   if (info && info->typeinfo) {
4257ec681f3Smrg      char *decoded = rnndec_decodeval(rnn->vc, info->typeinfo, value);
4267ec681f3Smrg      printf("%s: %s\n", info->name, decoded);
4277ec681f3Smrg   } else if (info) {
4287ec681f3Smrg      printf("%s: %08x\n", info->name, value);
4297ec681f3Smrg   } else {
4307ec681f3Smrg      printf("<%04x>: %08x\n", offset, value);
4317ec681f3Smrg   }
4327ec681f3Smrg}
4337ec681f3Smrg
4347ec681f3Smrgstatic void
4357ec681f3Smrgdecode_gmu_registers(void)
4367ec681f3Smrg{
4377ec681f3Smrg   foreach_line_in_section (line) {
4387ec681f3Smrg      uint32_t offset, value;
4397ec681f3Smrg      parseline(line, "  - { offset: %x, value: %x }", &offset, &value);
4407ec681f3Smrg
4417ec681f3Smrg      printf("\t%08x\t", value);
4427ec681f3Smrg      dump_register(rnn_gmu, offset / 4, value);
4437ec681f3Smrg   }
4447ec681f3Smrg}
4457ec681f3Smrg
4467ec681f3Smrgstatic void
4477ec681f3Smrgdecode_registers(void)
4487ec681f3Smrg{
4497ec681f3Smrg   foreach_line_in_section (line) {
4507ec681f3Smrg      uint32_t offset, value;
4517ec681f3Smrg      parseline(line, "  - { offset: %x, value: %x }", &offset, &value);
4527ec681f3Smrg
4537ec681f3Smrg      reg_set(offset / 4, value);
4547ec681f3Smrg      printf("\t%08x", value);
4557ec681f3Smrg      dump_register_val(offset / 4, value, 0);
4567ec681f3Smrg   }
4577ec681f3Smrg}
4587ec681f3Smrg
4597ec681f3Smrg/* similar to registers section, but for banked context regs: */
4607ec681f3Smrgstatic void
4617ec681f3Smrgdecode_clusters(void)
4627ec681f3Smrg{
4637ec681f3Smrg   foreach_line_in_section (line) {
4647ec681f3Smrg      if (startswith(line, "  - cluster-name:") ||
4657ec681f3Smrg          startswith(line, "    - context:")) {
4667ec681f3Smrg         printf("%s", line);
4677ec681f3Smrg         continue;
4687ec681f3Smrg      }
4697ec681f3Smrg
4707ec681f3Smrg      uint32_t offset, value;
4717ec681f3Smrg      parseline(line, "      - { offset: %x, value: %x }", &offset, &value);
4727ec681f3Smrg
4737ec681f3Smrg      printf("\t%08x", value);
4747ec681f3Smrg      dump_register_val(offset / 4, value, 0);
4757ec681f3Smrg   }
4767ec681f3Smrg}
4777ec681f3Smrg
4787ec681f3Smrg/*
4797ec681f3Smrg * Decode indexed-registers.. these aren't like normal registers, but a
4807ec681f3Smrg * sort of FIFO where successive reads pop out associated debug state.
4817ec681f3Smrg */
4827ec681f3Smrg
4837ec681f3Smrgstatic void
4847ec681f3Smrgdump_cp_sqe_stat(uint32_t *stat)
4857ec681f3Smrg{
4867ec681f3Smrg   printf("\t PC: %04x\n", stat[0]);
4877ec681f3Smrg   stat++;
4887ec681f3Smrg
4897ec681f3Smrg   if (is_a6xx() && valid_header(stat[0])) {
4907ec681f3Smrg      if (pkt_is_type7(stat[0])) {
4917ec681f3Smrg         unsigned opc = cp_type7_opcode(stat[0]);
4927ec681f3Smrg         const char *name = pktname(opc);
4937ec681f3Smrg         if (name)
4947ec681f3Smrg            printf("\tPKT: %s\n", name);
4957ec681f3Smrg      } else {
4967ec681f3Smrg         /* Not sure if this case can happen: */
4977ec681f3Smrg      }
4987ec681f3Smrg   }
4997ec681f3Smrg
5007ec681f3Smrg   for (int i = 0; i < 16; i++) {
5017ec681f3Smrg      printf("\t$%02x: %08x\t\t$%02x: %08x\n", i + 1, stat[i], i + 16 + 1,
5027ec681f3Smrg             stat[i + 16]);
5037ec681f3Smrg   }
5047ec681f3Smrg}
5057ec681f3Smrg
5067ec681f3Smrgstatic void
5077ec681f3Smrgdump_control_regs(uint32_t *regs)
5087ec681f3Smrg{
5097ec681f3Smrg   if (!rnn_control)
5107ec681f3Smrg      return;
5117ec681f3Smrg
5127ec681f3Smrg   /* Control regs 0x100-0x17f are a scratch space to be used by the
5137ec681f3Smrg    * firmware however it wants, unlike lower regs which involve some
5147ec681f3Smrg    * fixed-function units. Therefore only these registers get dumped
5157ec681f3Smrg    * directly.
5167ec681f3Smrg    */
5177ec681f3Smrg   for (uint32_t i = 0; i < 0x80; i++) {
5187ec681f3Smrg      printf("\t%08x\t", regs[i]);
5197ec681f3Smrg      dump_register(rnn_control, i + 0x100, regs[i]);
5207ec681f3Smrg   }
5217ec681f3Smrg}
5227ec681f3Smrg
5237ec681f3Smrgstatic void
5247ec681f3Smrgdump_cp_ucode_dbg(uint32_t *dbg)
5257ec681f3Smrg{
5267ec681f3Smrg   /* Notes on the data:
5277ec681f3Smrg    * There seems to be a section every 4096 DWORD's. The sections aren't
5287ec681f3Smrg    * all the same size, so the rest of the 4096 DWORD's are filled with
5297ec681f3Smrg    * mirrors of the actual data.
5307ec681f3Smrg    */
5317ec681f3Smrg
5327ec681f3Smrg   for (int section = 0; section < 6; section++, dbg += 0x1000) {
5337ec681f3Smrg      switch (section) {
5347ec681f3Smrg      case 0:
5357ec681f3Smrg         /* Contains scattered data from a630_sqe.fw: */
5367ec681f3Smrg         printf("\tSQE instruction cache:\n");
5377ec681f3Smrg         dump_hex_ascii(dbg, 4 * 0x400, 1);
5387ec681f3Smrg         break;
5397ec681f3Smrg      case 1:
5407ec681f3Smrg         printf("\tUnknown 1:\n");
5417ec681f3Smrg         dump_hex_ascii(dbg, 4 * 0x80, 1);
5427ec681f3Smrg         break;
5437ec681f3Smrg      case 2:
5447ec681f3Smrg         printf("\tUnknown 2:\n");
5457ec681f3Smrg         dump_hex_ascii(dbg, 4 * 0x200, 1);
5467ec681f3Smrg         break;
5477ec681f3Smrg      case 3:
5487ec681f3Smrg         printf("\tUnknown 3:\n");
5497ec681f3Smrg         dump_hex_ascii(dbg, 4 * 0x80, 1);
5507ec681f3Smrg         break;
5517ec681f3Smrg      case 4:
5527ec681f3Smrg         /* Don't bother printing this normally */
5537ec681f3Smrg         if (verbose) {
5547ec681f3Smrg            printf("\tSQE packet jumptable contents:\n");
5557ec681f3Smrg            dump_hex_ascii(dbg, 4 * 0x80, 1);
5567ec681f3Smrg         }
5577ec681f3Smrg         break;
5587ec681f3Smrg      case 5:
5597ec681f3Smrg         printf("\tSQE scratch control regs:\n");
5607ec681f3Smrg         dump_control_regs(dbg);
5617ec681f3Smrg         break;
5627ec681f3Smrg      }
5637ec681f3Smrg   }
5647ec681f3Smrg}
5657ec681f3Smrg
5667ec681f3Smrgstatic void
5677ec681f3Smrgdump_mem_pool_reg_write(unsigned reg, uint32_t data, unsigned context,
5687ec681f3Smrg                        bool pipe)
5697ec681f3Smrg{
5707ec681f3Smrg   if (pipe) {
5717ec681f3Smrg      struct rnndecaddrinfo *info = rnn_reginfo(rnn_pipe, reg);
5727ec681f3Smrg      printf("\t\twrite %s (%02x) pipe\n", info->name, reg);
5737ec681f3Smrg
5747ec681f3Smrg      if (!strcmp(info->typeinfo->name, "void")) {
5757ec681f3Smrg         /* registers that ignore their payload */
5767ec681f3Smrg      } else {
5777ec681f3Smrg         printf("\t\t\t");
5787ec681f3Smrg         dump_register(rnn_pipe, reg, data);
5797ec681f3Smrg      }
5807ec681f3Smrg   } else {
5817ec681f3Smrg      printf("\t\twrite %s (%05x) context %d\n", regname(reg, 1), reg, context);
5827ec681f3Smrg      dump_register_val(reg, data, 2);
5837ec681f3Smrg   }
5847ec681f3Smrg}
5857ec681f3Smrg
5867ec681f3Smrgstatic void
5877ec681f3Smrgdump_mem_pool_chunk(const uint32_t *chunk)
5887ec681f3Smrg{
5897ec681f3Smrg   struct __attribute__((packed)) {
5907ec681f3Smrg      bool reg0_enabled : 1;
5917ec681f3Smrg      bool reg1_enabled : 1;
5927ec681f3Smrg      uint32_t data0 : 32;
5937ec681f3Smrg      uint32_t data1 : 32;
5947ec681f3Smrg      uint32_t reg0 : 18;
5957ec681f3Smrg      uint32_t reg1 : 18;
5967ec681f3Smrg      bool reg0_pipe : 1;
5977ec681f3Smrg      bool reg1_pipe : 1;
5987ec681f3Smrg      uint32_t reg0_context : 1;
5997ec681f3Smrg      uint32_t reg1_context : 1;
6007ec681f3Smrg      uint32_t padding : 22;
6017ec681f3Smrg   } fields;
6027ec681f3Smrg
6037ec681f3Smrg   memcpy(&fields, chunk, 4 * sizeof(uint32_t));
6047ec681f3Smrg
6057ec681f3Smrg   if (fields.reg0_enabled) {
6067ec681f3Smrg      dump_mem_pool_reg_write(fields.reg0, fields.data0, fields.reg0_context,
6077ec681f3Smrg                              fields.reg0_pipe);
6087ec681f3Smrg   }
6097ec681f3Smrg
6107ec681f3Smrg   if (fields.reg1_enabled) {
6117ec681f3Smrg      dump_mem_pool_reg_write(fields.reg1, fields.data1, fields.reg1_context,
6127ec681f3Smrg                              fields.reg1_pipe);
6137ec681f3Smrg   }
6147ec681f3Smrg}
6157ec681f3Smrg
6167ec681f3Smrgstatic void
6177ec681f3Smrgdump_cp_mem_pool(uint32_t *mempool)
6187ec681f3Smrg{
6197ec681f3Smrg   /* The mem pool is a shared pool of memory used for storing in-flight
6207ec681f3Smrg    * register writes. There are 6 different queues, one for each
6217ec681f3Smrg    * cluster. Writing to $data (or for some special registers, $addr)
6227ec681f3Smrg    * pushes data onto the appropriate queue, and each queue is pulled
6237ec681f3Smrg    * from by the appropriate cluster. The queues are thus written to
6247ec681f3Smrg    * in-order, but may be read out-of-order.
6257ec681f3Smrg    *
6267ec681f3Smrg    * The queues are conceptually divided into 128-bit "chunks", and the
6277ec681f3Smrg    * read and write pointers are in units of chunks.  These chunks are
6287ec681f3Smrg    * organized internally into 8-chunk "blocks", and memory is allocated
6297ec681f3Smrg    * dynamically in terms of blocks. Each queue is represented as a
6307ec681f3Smrg    * singly-linked list of blocks, as well as 3-bit start/end chunk
6317ec681f3Smrg    * pointers that point within the first/last block.  The next pointers
6327ec681f3Smrg    * are located in a separate array, rather than inline.
6337ec681f3Smrg    */
6347ec681f3Smrg
6357ec681f3Smrg   /* TODO: The firmware CP_MEM_POOL save/restore routines do something
6367ec681f3Smrg    * like:
6377ec681f3Smrg    *
6387ec681f3Smrg    * cread $02, [ $00 + 0 ]
6397ec681f3Smrg    * and $02, $02, 0x118
6407ec681f3Smrg    * ...
6417ec681f3Smrg    * brne $02, 0, #label
6427ec681f3Smrg    * mov $03, 0x2000
6437ec681f3Smrg    * mov $03, 0x1000
6447ec681f3Smrg    * label:
6457ec681f3Smrg    * ...
6467ec681f3Smrg    *
6477ec681f3Smrg    * I think that control register 0 is the GPU version, and some
6487ec681f3Smrg    * versions have a smaller mem pool. It seems some models have a mem
6497ec681f3Smrg    * pool that's half the size, and a bunch of offsets are shifted
6507ec681f3Smrg    * accordingly. Unfortunately the kernel driver's dumping code doesn't
6517ec681f3Smrg    * seem to take this into account, even the downstream android driver,
6527ec681f3Smrg    * and we don't know which versions 0x8, 0x10, or 0x100 correspond
6537ec681f3Smrg    * to. Or maybe we can use CP_DBG_MEM_POOL_SIZE to figure this out?
6547ec681f3Smrg    */
6557ec681f3Smrg   bool small_mem_pool = false;
6567ec681f3Smrg
6577ec681f3Smrg   /* The array of next pointers for each block. */
6587ec681f3Smrg   const uint32_t *next_pointers =
6597ec681f3Smrg      small_mem_pool ? &mempool[0x800] : &mempool[0x1000];
6607ec681f3Smrg
6617ec681f3Smrg   /* Maximum number of blocks in the pool, also the size of the pointers
6627ec681f3Smrg    * array.
6637ec681f3Smrg    */
6647ec681f3Smrg   const int num_blocks = small_mem_pool ? 0x30 : 0x80;
6657ec681f3Smrg
6667ec681f3Smrg   /* Number of queues */
6677ec681f3Smrg   const unsigned num_queues = 6;
6687ec681f3Smrg
6697ec681f3Smrg   /* Unfortunately the per-queue state is a little more complicated than
6707ec681f3Smrg    * a simple pair of begin/end pointers. Instead of a single beginning
6717ec681f3Smrg    * block, there are *two*, with the property that either the two are
6727ec681f3Smrg    * equal or the second is the "next" of the first. Similarly there are
6737ec681f3Smrg    * two end blocks. Thus the queue either looks like this:
6747ec681f3Smrg    *
6757ec681f3Smrg    * A -> B -> ... -> C -> D
6767ec681f3Smrg    *
6777ec681f3Smrg    * Or like this, or some combination:
6787ec681f3Smrg    *
6797ec681f3Smrg    * A/B -> ... -> C/D
6807ec681f3Smrg    *
6817ec681f3Smrg    * However, there's only one beginning/end chunk offset. Now the
6827ec681f3Smrg    * question is, which of A or B is the actual start? I.e. is the chunk
6837ec681f3Smrg    * offset an offset inside A or B? It depends. I'll show a typical read
6847ec681f3Smrg    * cycle, starting here (read pointer marked with a *) with a chunk
6857ec681f3Smrg    * offset of 0:
6867ec681f3Smrg    *
6877ec681f3Smrg    *	  A                    B
6887ec681f3Smrg    *  _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _
6897ec681f3Smrg    * |_|_|_|_|_|_|_|_| -> |*|_|_|_|_|_|_|_| -> |_|_|_|_|_|_|_|_|
6907ec681f3Smrg    *
6917ec681f3Smrg    * Once the pointer advances far enough, the hardware decides to free
6927ec681f3Smrg    * A, after which the read-side state looks like:
6937ec681f3Smrg    *
6947ec681f3Smrg    *	(free)                A/B
6957ec681f3Smrg    *  _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _
6967ec681f3Smrg    * |_|_|_|_|_|_|_|_|    |_|_|_|*|_|_|_|_| -> |_|_|_|_|_|_|_|_|
6977ec681f3Smrg    *
6987ec681f3Smrg    * Then after advancing the pointer a bit more, the hardware fetches
6997ec681f3Smrg    * the "next" pointer for A and stores it in B:
7007ec681f3Smrg    *
7017ec681f3Smrg    *	(free)                 A                     B
7027ec681f3Smrg    *  _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _
7037ec681f3Smrg    * |_|_|_|_|_|_|_|_|    |_|_|_|_|_|_|_|*| -> |_|_|_|_|_|_|_|_|
7047ec681f3Smrg    *
7057ec681f3Smrg    * Then the read pointer advances into B, at which point we've come
7067ec681f3Smrg    * back to the first state having advanced a whole block:
7077ec681f3Smrg    *
7087ec681f3Smrg    *	(free)                 A                     B
7097ec681f3Smrg    *  _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _      _ _ _ _ _ _ _ _
7107ec681f3Smrg    * |_|_|_|_|_|_|_|_|    |_|_|_|_|_|_|_|_| -> |*|_|_|_|_|_|_|_|
7117ec681f3Smrg    *
7127ec681f3Smrg    *
7137ec681f3Smrg    * There is a similar cycle for the write pointer. Now, the question
7147ec681f3Smrg    * is, how do we know which state we're in? We need to know this to
7157ec681f3Smrg    * know whether the pointer (*) is in A or B if they're different. It
7167ec681f3Smrg    * seems like there should be some bit somewhere describing this, but
7177ec681f3Smrg    * after lots of experimentation I've come up empty-handed. For now we
7187ec681f3Smrg    * assume that if the pointer is in the first half, then we're in
7197ec681f3Smrg    * either the first or second state and use B, and otherwise we're in
7207ec681f3Smrg    * the second or third state and use A. So far I haven't seen anything
7217ec681f3Smrg    * that violates this assumption.
7227ec681f3Smrg    */
7237ec681f3Smrg
7247ec681f3Smrg   struct {
7257ec681f3Smrg      uint32_t unk0;
7267ec681f3Smrg      uint32_t padding0[7]; /* Mirrors of unk0 */
7277ec681f3Smrg
7287ec681f3Smrg      struct {
7297ec681f3Smrg         uint32_t chunk : 3;
7307ec681f3Smrg         uint32_t first_block : 32 - 3;
7317ec681f3Smrg      } writer[6];
7327ec681f3Smrg      uint32_t padding1[2]; /* Mirrors of writer[4], writer[5] */
7337ec681f3Smrg
7347ec681f3Smrg      uint32_t unk1;
7357ec681f3Smrg      uint32_t padding2[7]; /* Mirrors of unk1 */
7367ec681f3Smrg
7377ec681f3Smrg      uint32_t writer_second_block[6];
7387ec681f3Smrg      uint32_t padding3[2];
7397ec681f3Smrg
7407ec681f3Smrg      uint32_t unk2[6];
7417ec681f3Smrg      uint32_t padding4[2];
7427ec681f3Smrg
7437ec681f3Smrg      struct {
7447ec681f3Smrg         uint32_t chunk : 3;
7457ec681f3Smrg         uint32_t first_block : 32 - 3;
7467ec681f3Smrg      } reader[6];
7477ec681f3Smrg      uint32_t padding5[2]; /* Mirrors of reader[4], reader[5] */
7487ec681f3Smrg
7497ec681f3Smrg      uint32_t unk3;
7507ec681f3Smrg      uint32_t padding6[7]; /* Mirrors of unk3 */
7517ec681f3Smrg
7527ec681f3Smrg      uint32_t reader_second_block[6];
7537ec681f3Smrg      uint32_t padding7[2];
7547ec681f3Smrg
7557ec681f3Smrg      uint32_t block_count[6];
7567ec681f3Smrg      uint32_t padding[2];
7577ec681f3Smrg
7587ec681f3Smrg      uint32_t unk4;
7597ec681f3Smrg      uint32_t padding9[7]; /* Mirrors of unk4 */
7607ec681f3Smrg   } data1;
7617ec681f3Smrg
7627ec681f3Smrg   const uint32_t *data1_ptr =
7637ec681f3Smrg      small_mem_pool ? &mempool[0xc00] : &mempool[0x1800];
7647ec681f3Smrg   memcpy(&data1, data1_ptr, sizeof(data1));
7657ec681f3Smrg
7667ec681f3Smrg   /* Based on the kernel, the first dword is the mem pool size (in
7677ec681f3Smrg    * blocks?) and mirrors CP_MEM_POOL_DBG_SIZE.
7687ec681f3Smrg    */
7697ec681f3Smrg   const uint32_t *data2_ptr =
7707ec681f3Smrg      small_mem_pool ? &mempool[0x1000] : &mempool[0x2000];
7717ec681f3Smrg   const int data2_size = 0x60;
7727ec681f3Smrg
7737ec681f3Smrg   /* This seems to be the size of each queue in chunks. */
7747ec681f3Smrg   const uint32_t *queue_sizes = &data2_ptr[0x18];
7757ec681f3Smrg
7767ec681f3Smrg   printf("\tdata2:\n");
7777ec681f3Smrg   dump_hex_ascii(data2_ptr, 4 * data2_size, 1);
7787ec681f3Smrg
7797ec681f3Smrg   /* These seem to be some kind of counter of allocated/deallocated blocks */
7807ec681f3Smrg   if (verbose) {
7817ec681f3Smrg      printf("\tunk0: %x\n", data1.unk0);
7827ec681f3Smrg      printf("\tunk1: %x\n", data1.unk1);
7837ec681f3Smrg      printf("\tunk3: %x\n", data1.unk3);
7847ec681f3Smrg      printf("\tunk4: %x\n\n", data1.unk4);
7857ec681f3Smrg   }
7867ec681f3Smrg
7877ec681f3Smrg   for (int queue = 0; queue < num_queues; queue++) {
7887ec681f3Smrg      const char *cluster_names[6] = {"FE",   "SP_VS", "PC_VS",
7897ec681f3Smrg                                      "GRAS", "SP_PS", "PS"};
7907ec681f3Smrg      printf("\tCLUSTER_%s:\n\n", cluster_names[queue]);
7917ec681f3Smrg
7927ec681f3Smrg      if (verbose) {
7937ec681f3Smrg         printf("\t\twriter_first_block: 0x%x\n",
7947ec681f3Smrg                data1.writer[queue].first_block);
7957ec681f3Smrg         printf("\t\twriter_second_block: 0x%x\n",
7967ec681f3Smrg                data1.writer_second_block[queue]);
7977ec681f3Smrg         printf("\t\twriter_chunk: %d\n", data1.writer[queue].chunk);
7987ec681f3Smrg         printf("\t\treader_first_block: 0x%x\n",
7997ec681f3Smrg                data1.reader[queue].first_block);
8007ec681f3Smrg         printf("\t\treader_second_block: 0x%x\n",
8017ec681f3Smrg                data1.reader_second_block[queue]);
8027ec681f3Smrg         printf("\t\treader_chunk: %d\n", data1.reader[queue].chunk);
8037ec681f3Smrg         printf("\t\tblock_count: %d\n", data1.block_count[queue]);
8047ec681f3Smrg         printf("\t\tunk2: 0x%x\n", data1.unk2[queue]);
8057ec681f3Smrg         printf("\t\tqueue_size: %d\n\n", queue_sizes[queue]);
8067ec681f3Smrg      }
8077ec681f3Smrg
8087ec681f3Smrg      uint32_t cur_chunk = data1.reader[queue].chunk;
8097ec681f3Smrg      uint32_t cur_block = cur_chunk > 3 ? data1.reader[queue].first_block
8107ec681f3Smrg                                         : data1.reader_second_block[queue];
8117ec681f3Smrg      uint32_t last_chunk = data1.writer[queue].chunk;
8127ec681f3Smrg      uint32_t last_block = last_chunk > 3 ? data1.writer[queue].first_block
8137ec681f3Smrg                                           : data1.writer_second_block[queue];
8147ec681f3Smrg
8157ec681f3Smrg      if (verbose)
8167ec681f3Smrg         printf("\tblock %x\n", cur_block);
8177ec681f3Smrg      if (cur_block >= num_blocks) {
8187ec681f3Smrg         fprintf(stderr, "block %x too large\n", cur_block);
8197ec681f3Smrg         exit(1);
8207ec681f3Smrg      }
8217ec681f3Smrg      unsigned calculated_queue_size = 0;
8227ec681f3Smrg      while (cur_block != last_block || cur_chunk != last_chunk) {
8237ec681f3Smrg         calculated_queue_size++;
8247ec681f3Smrg         uint32_t *chunk_ptr = &mempool[cur_block * 0x20 + cur_chunk * 4];
8257ec681f3Smrg
8267ec681f3Smrg         dump_mem_pool_chunk(chunk_ptr);
8277ec681f3Smrg
8287ec681f3Smrg         printf("\t%05x: %08x %08x %08x %08x\n",
8297ec681f3Smrg                4 * (cur_block * 0x20 + cur_chunk + 4), chunk_ptr[0],
8307ec681f3Smrg                chunk_ptr[1], chunk_ptr[2], chunk_ptr[3]);
8317ec681f3Smrg
8327ec681f3Smrg         cur_chunk++;
8337ec681f3Smrg         if (cur_chunk == 8) {
8347ec681f3Smrg            cur_block = next_pointers[cur_block];
8357ec681f3Smrg            if (verbose)
8367ec681f3Smrg               printf("\tblock %x\n", cur_block);
8377ec681f3Smrg            if (cur_block >= num_blocks) {
8387ec681f3Smrg               fprintf(stderr, "block %x too large\n", cur_block);
8397ec681f3Smrg               exit(1);
8407ec681f3Smrg            }
8417ec681f3Smrg            cur_chunk = 0;
8427ec681f3Smrg         }
8437ec681f3Smrg      }
8447ec681f3Smrg      if (calculated_queue_size != queue_sizes[queue]) {
8457ec681f3Smrg         printf("\t\tCALCULATED SIZE %d DOES NOT MATCH!\n",
8467ec681f3Smrg                calculated_queue_size);
8477ec681f3Smrg      }
8487ec681f3Smrg      printf("\n");
8497ec681f3Smrg   }
8507ec681f3Smrg}
8517ec681f3Smrg
8527ec681f3Smrgstatic void
8537ec681f3Smrgdecode_indexed_registers(void)
8547ec681f3Smrg{
8557ec681f3Smrg   char *name = NULL;
8567ec681f3Smrg   uint32_t sizedwords = 0;
8577ec681f3Smrg
8587ec681f3Smrg   foreach_line_in_section (line) {
8597ec681f3Smrg      if (startswith(line, "  - regs-name:")) {
8607ec681f3Smrg         free(name);
8617ec681f3Smrg         parseline(line, "  - regs-name: %ms", &name);
8627ec681f3Smrg      } else if (startswith(line, "    dwords:")) {
8637ec681f3Smrg         parseline(line, "    dwords: %u", &sizedwords);
8647ec681f3Smrg      } else if (startswith(line, "    data: !!ascii85 |")) {
8657ec681f3Smrg         uint32_t *buf = popline_ascii85(sizedwords);
8667ec681f3Smrg
8677ec681f3Smrg         /* some of the sections are pretty large, and are (at least
8687ec681f3Smrg          * so far) not useful, so skip them if not in verbose mode:
8697ec681f3Smrg          */
8707ec681f3Smrg         bool dump = verbose || !strcmp(name, "CP_SQE_STAT") ||
8717ec681f3Smrg                     !strcmp(name, "CP_DRAW_STATE") ||
8727ec681f3Smrg                     !strcmp(name, "CP_ROQ") || 0;
8737ec681f3Smrg
8747ec681f3Smrg         if (!strcmp(name, "CP_SQE_STAT"))
8757ec681f3Smrg            dump_cp_sqe_stat(buf);
8767ec681f3Smrg
8777ec681f3Smrg         if (!strcmp(name, "CP_UCODE_DBG_DATA"))
8787ec681f3Smrg            dump_cp_ucode_dbg(buf);
8797ec681f3Smrg
8807ec681f3Smrg         if (!strcmp(name, "CP_MEMPOOL"))
8817ec681f3Smrg            dump_cp_mem_pool(buf);
8827ec681f3Smrg
8837ec681f3Smrg         if (dump)
8847ec681f3Smrg            dump_hex_ascii(buf, 4 * sizedwords, 1);
8857ec681f3Smrg
8867ec681f3Smrg         free(buf);
8877ec681f3Smrg
8887ec681f3Smrg         continue;
8897ec681f3Smrg      }
8907ec681f3Smrg
8917ec681f3Smrg      printf("%s", line);
8927ec681f3Smrg   }
8937ec681f3Smrg}
8947ec681f3Smrg
8957ec681f3Smrg/*
8967ec681f3Smrg * Decode shader-blocks:
8977ec681f3Smrg */
8987ec681f3Smrg
8997ec681f3Smrgstatic void
9007ec681f3Smrgdecode_shader_blocks(void)
9017ec681f3Smrg{
9027ec681f3Smrg   char *type = NULL;
9037ec681f3Smrg   uint32_t sizedwords = 0;
9047ec681f3Smrg
9057ec681f3Smrg   foreach_line_in_section (line) {
9067ec681f3Smrg      if (startswith(line, "  - type:")) {
9077ec681f3Smrg         free(type);
9087ec681f3Smrg         parseline(line, "  - type: %ms", &type);
9097ec681f3Smrg      } else if (startswith(line, "      size:")) {
9107ec681f3Smrg         parseline(line, "      size: %u", &sizedwords);
9117ec681f3Smrg      } else if (startswith(line, "    data: !!ascii85 |")) {
9127ec681f3Smrg         uint32_t *buf = popline_ascii85(sizedwords);
9137ec681f3Smrg
9147ec681f3Smrg         /* some of the sections are pretty large, and are (at least
9157ec681f3Smrg          * so far) not useful, so skip them if not in verbose mode:
9167ec681f3Smrg          */
9177ec681f3Smrg         bool dump = verbose || !strcmp(type, "A6XX_SP_INST_DATA") ||
9187ec681f3Smrg                     !strcmp(type, "A6XX_HLSQ_INST_RAM") || 0;
9197ec681f3Smrg
9207ec681f3Smrg         if (!strcmp(type, "A6XX_SP_INST_DATA") ||
9217ec681f3Smrg             !strcmp(type, "A6XX_HLSQ_INST_RAM")) {
9227ec681f3Smrg            /* TODO this section actually contains multiple shaders
9237ec681f3Smrg             * (or parts of shaders?), so perhaps we should search
9247ec681f3Smrg             * for ends of shaders and decode each?
9257ec681f3Smrg             */
9267ec681f3Smrg            try_disasm_a3xx(buf, sizedwords, 1, stdout, options.gpu_id);
9277ec681f3Smrg         }
9287ec681f3Smrg
9297ec681f3Smrg         if (dump)
9307ec681f3Smrg            dump_hex_ascii(buf, 4 * sizedwords, 1);
9317ec681f3Smrg
9327ec681f3Smrg         free(buf);
9337ec681f3Smrg
9347ec681f3Smrg         continue;
9357ec681f3Smrg      }
9367ec681f3Smrg
9377ec681f3Smrg      printf("%s", line);
9387ec681f3Smrg   }
9397ec681f3Smrg
9407ec681f3Smrg   free(type);
9417ec681f3Smrg}
9427ec681f3Smrg
9437ec681f3Smrg/*
9447ec681f3Smrg * Decode debugbus section:
9457ec681f3Smrg */
9467ec681f3Smrg
9477ec681f3Smrgstatic void
9487ec681f3Smrgdecode_debugbus(void)
9497ec681f3Smrg{
9507ec681f3Smrg   char *block = NULL;
9517ec681f3Smrg   uint32_t sizedwords = 0;
9527ec681f3Smrg
9537ec681f3Smrg   foreach_line_in_section (line) {
9547ec681f3Smrg      if (startswith(line, "  - debugbus-block:")) {
9557ec681f3Smrg         free(block);
9567ec681f3Smrg         parseline(line, "  - debugbus-block: %ms", &block);
9577ec681f3Smrg      } else if (startswith(line, "    count:")) {
9587ec681f3Smrg         parseline(line, "    count: %u", &sizedwords);
9597ec681f3Smrg      } else if (startswith(line, "    data: !!ascii85 |")) {
9607ec681f3Smrg         uint32_t *buf = popline_ascii85(sizedwords);
9617ec681f3Smrg
9627ec681f3Smrg         /* some of the sections are pretty large, and are (at least
9637ec681f3Smrg          * so far) not useful, so skip them if not in verbose mode:
9647ec681f3Smrg          */
9657ec681f3Smrg         bool dump = verbose || 0;
9667ec681f3Smrg
9677ec681f3Smrg         if (dump)
9687ec681f3Smrg            dump_hex_ascii(buf, 4 * sizedwords, 1);
9697ec681f3Smrg
9707ec681f3Smrg         free(buf);
9717ec681f3Smrg
9727ec681f3Smrg         continue;
9737ec681f3Smrg      }
9747ec681f3Smrg
9757ec681f3Smrg      printf("%s", line);
9767ec681f3Smrg   }
9777ec681f3Smrg}
9787ec681f3Smrg
9797ec681f3Smrg/*
9807ec681f3Smrg * Main crashdump decode loop:
9817ec681f3Smrg */
9827ec681f3Smrg
9837ec681f3Smrgstatic void
9847ec681f3Smrgdecode(void)
9857ec681f3Smrg{
9867ec681f3Smrg   const char *line;
9877ec681f3Smrg
9887ec681f3Smrg   while ((line = popline())) {
9897ec681f3Smrg      printf("%s", line);
9907ec681f3Smrg      if (startswith(line, "revision:")) {
9917ec681f3Smrg         parseline(line, "revision: %u", &options.gpu_id);
9927ec681f3Smrg         printf("Got gpu_id=%u\n", options.gpu_id);
9937ec681f3Smrg
9947ec681f3Smrg         cffdec_init(&options);
9957ec681f3Smrg
9967ec681f3Smrg         if (is_a6xx()) {
9977ec681f3Smrg            rnn_gmu = rnn_new(!options.color);
9987ec681f3Smrg            rnn_load_file(rnn_gmu, "adreno/a6xx_gmu.xml", "A6XX");
9997ec681f3Smrg            rnn_control = rnn_new(!options.color);
10007ec681f3Smrg            rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
10017ec681f3Smrg                          "A6XX_CONTROL_REG");
10027ec681f3Smrg            rnn_pipe = rnn_new(!options.color);
10037ec681f3Smrg            rnn_load_file(rnn_pipe, "adreno/adreno_pipe_regs.xml",
10047ec681f3Smrg                          "A6XX_PIPE_REG");
10057ec681f3Smrg         } else if (is_a5xx()) {
10067ec681f3Smrg            rnn_control = rnn_new(!options.color);
10077ec681f3Smrg            rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",
10087ec681f3Smrg                          "A5XX_CONTROL_REG");
10097ec681f3Smrg         } else {
10107ec681f3Smrg            rnn_control = NULL;
10117ec681f3Smrg         }
10127ec681f3Smrg      } else if (startswith(line, "bos:")) {
10137ec681f3Smrg         decode_bos();
10147ec681f3Smrg      } else if (startswith(line, "ringbuffer:")) {
10157ec681f3Smrg         decode_ringbuffer();
10167ec681f3Smrg      } else if (startswith(line, "registers:")) {
10177ec681f3Smrg         decode_registers();
10187ec681f3Smrg
10197ec681f3Smrg         /* after we've recorded buffer contents, and CP register values,
10207ec681f3Smrg          * we can take a stab at decoding the cmdstream:
10217ec681f3Smrg          */
10227ec681f3Smrg         dump_cmdstream();
10237ec681f3Smrg      } else if (startswith(line, "registers-gmu:")) {
10247ec681f3Smrg         decode_gmu_registers();
10257ec681f3Smrg      } else if (startswith(line, "indexed-registers:")) {
10267ec681f3Smrg         decode_indexed_registers();
10277ec681f3Smrg      } else if (startswith(line, "shader-blocks:")) {
10287ec681f3Smrg         decode_shader_blocks();
10297ec681f3Smrg      } else if (startswith(line, "clusters:")) {
10307ec681f3Smrg         decode_clusters();
10317ec681f3Smrg      } else if (startswith(line, "debugbus:")) {
10327ec681f3Smrg         decode_debugbus();
10337ec681f3Smrg      }
10347ec681f3Smrg   }
10357ec681f3Smrg}
10367ec681f3Smrg
10377ec681f3Smrg/*
10387ec681f3Smrg * Usage and argument parsing:
10397ec681f3Smrg */
10407ec681f3Smrg
10417ec681f3Smrgstatic void
10427ec681f3Smrgusage(void)
10437ec681f3Smrg{
10447ec681f3Smrg   /* clang-format off */
10457ec681f3Smrg   fprintf(stderr, "Usage:\n\n"
10467ec681f3Smrg           "\tcrashdec [-achmsv] [-f FILE]\n\n"
10477ec681f3Smrg           "Options:\n"
10487ec681f3Smrg           "\t-a, --allregs   - show all registers (including ones not written since\n"
10497ec681f3Smrg           "\t                  previous draw) at each draw\n"
10507ec681f3Smrg           "\t-c, --color     - use colors\n"
10517ec681f3Smrg           "\t-f, --file=FILE - read input from specified file (rather than stdin)\n"
10527ec681f3Smrg           "\t-h, --help      - this usage message\n"
10537ec681f3Smrg           "\t-m, --markers   - try to decode CP_NOP string markers\n"
10547ec681f3Smrg           "\t-s, --summary   - don't show individual register writes, but just show\n"
10557ec681f3Smrg           "\t                  register values on draws\n"
10567ec681f3Smrg           "\t-v, --verbose   - dump more verbose output, including contents of\n"
10577ec681f3Smrg           "\t                  less interesting buffers\n"
10587ec681f3Smrg           "\n"
10597ec681f3Smrg   );
10607ec681f3Smrg   /* clang-format on */
10617ec681f3Smrg   exit(2);
10627ec681f3Smrg}
10637ec681f3Smrg
10647ec681f3Smrg/* clang-format off */
10657ec681f3Smrgstatic const struct option opts[] = {
10667ec681f3Smrg      { .name = "allregs", .has_arg = 0, NULL, 'a' },
10677ec681f3Smrg      { .name = "color",   .has_arg = 0, NULL, 'c' },
10687ec681f3Smrg      { .name = "file",    .has_arg = 1, NULL, 'f' },
10697ec681f3Smrg      { .name = "help",    .has_arg = 0, NULL, 'h' },
10707ec681f3Smrg      { .name = "markers", .has_arg = 0, NULL, 'm' },
10717ec681f3Smrg      { .name = "summary", .has_arg = 0, NULL, 's' },
10727ec681f3Smrg      { .name = "verbose", .has_arg = 0, NULL, 'v' },
10737ec681f3Smrg      {}
10747ec681f3Smrg};
10757ec681f3Smrg/* clang-format on */
10767ec681f3Smrg
10777ec681f3Smrgstatic bool interactive;
10787ec681f3Smrg
10797ec681f3Smrgstatic void
10807ec681f3Smrgcleanup(void)
10817ec681f3Smrg{
10827ec681f3Smrg   fflush(stdout);
10837ec681f3Smrg
10847ec681f3Smrg   if (interactive) {
10857ec681f3Smrg      pager_close();
10867ec681f3Smrg   }
10877ec681f3Smrg}
10887ec681f3Smrg
10897ec681f3Smrgint
10907ec681f3Smrgmain(int argc, char **argv)
10917ec681f3Smrg{
10927ec681f3Smrg   int c;
10937ec681f3Smrg
10947ec681f3Smrg   interactive = isatty(STDOUT_FILENO);
10957ec681f3Smrg   options.color = interactive;
10967ec681f3Smrg
10977ec681f3Smrg   /* default to read from stdin: */
10987ec681f3Smrg   in = stdin;
10997ec681f3Smrg
11007ec681f3Smrg   while ((c = getopt_long(argc, argv, "acf:hmsv", opts, NULL)) != -1) {
11017ec681f3Smrg      switch (c) {
11027ec681f3Smrg      case 'a':
11037ec681f3Smrg         options.allregs = true;
11047ec681f3Smrg         break;
11057ec681f3Smrg      case 'c':
11067ec681f3Smrg         options.color = true;
11077ec681f3Smrg         break;
11087ec681f3Smrg      case 'f':
11097ec681f3Smrg         in = fopen(optarg, "r");
11107ec681f3Smrg         break;
11117ec681f3Smrg      case 'm':
11127ec681f3Smrg         options.decode_markers = true;
11137ec681f3Smrg         break;
11147ec681f3Smrg      case 's':
11157ec681f3Smrg         options.summary = true;
11167ec681f3Smrg         break;
11177ec681f3Smrg      case 'v':
11187ec681f3Smrg         verbose = true;
11197ec681f3Smrg         break;
11207ec681f3Smrg      case 'h':
11217ec681f3Smrg      default:
11227ec681f3Smrg         usage();
11237ec681f3Smrg      }
11247ec681f3Smrg   }
11257ec681f3Smrg
11267ec681f3Smrg   disasm_a3xx_set_debug(PRINT_RAW);
11277ec681f3Smrg
11287ec681f3Smrg   if (interactive) {
11297ec681f3Smrg      pager_open();
11307ec681f3Smrg   }
11317ec681f3Smrg
11327ec681f3Smrg   atexit(cleanup);
11337ec681f3Smrg
11347ec681f3Smrg   decode();
11357ec681f3Smrg   cleanup();
11367ec681f3Smrg}
1137