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