Path: blob/21.2-virgl/src/freedreno/decode/crashdec.c
4565 views
/*1* Copyright © 2020 Google, Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*/2223/*24* Decoder for devcoredump traces from drm/msm. In case of a gpu crash/hang,25* the coredump should be found in:26*27* /sys/class/devcoredump/devcd<n>/data28*29* The crashdump will hang around for 5min, it can be cleared by writing to30* the file, ie:31*32* echo 1 > /sys/class/devcoredump/devcd<n>/data33*34* (the driver won't log any new crashdumps until the previous one is cleared35* or times out after 5min)36*/3738#include <assert.h>39#include <getopt.h>40#include <inttypes.h>41#include <stdarg.h>42#include <stdbool.h>43#include <stdint.h>44#include <stdio.h>45#include <stdlib.h>46#include <string.h>47#include <unistd.h>4849#include "freedreno_pm4.h"5051#include "ir3/instr-a3xx.h"52#include "buffers.h"53#include "cffdec.h"54#include "disasm.h"55#include "pager.h"56#include "rnnutil.h"57#include "util.h"5859static FILE *in;60static bool verbose;6162static struct rnn *rnn_gmu;63static struct rnn *rnn_control;64static struct rnn *rnn_pipe;6566static struct cffdec_options options = {67.draw_filter = -1,68};6970static inline bool71is_a6xx(void)72{73return (600 <= options.gpu_id) && (options.gpu_id < 700);74}75static inline bool76is_a5xx(void)77{78return (500 <= options.gpu_id) && (options.gpu_id < 600);79}80static inline bool81is_64b(void)82{83return options.gpu_id >= 500;84}8586/*87* Helpers to read register values:88*/8990/* read registers that are 64b on 64b GPUs (ie. a5xx+) */91static uint64_t92regval64(const char *name)93{94unsigned reg = regbase(name);95assert(reg);96uint64_t val = reg_val(reg);97if (is_64b())98val |= ((uint64_t)reg_val(reg + 1)) << 32;99return val;100}101102static uint32_t103regval(const char *name)104{105unsigned reg = regbase(name);106assert(reg);107return reg_val(reg);108}109110/*111* Line reading and string helpers:112*/113114static char *115replacestr(char *line, const char *find, const char *replace)116{117char *tail, *s;118119if (!(s = strstr(line, find)))120return line;121122tail = s + strlen(find);123124char *newline;125asprintf(&newline, "%.*s%s%s", (int)(s - line), line, replace, tail);126free(line);127128return newline;129}130131static char *lastline;132static char *pushedline;133134static const char *135popline(void)136{137char *r = pushedline;138139if (r) {140pushedline = NULL;141return r;142}143144free(lastline);145146size_t n = 0;147if (getline(&r, &n, in) < 0)148exit(0);149150/* Handle section name typo's from earlier kernels: */151r = replacestr(r, "CP_MEMPOOOL", "CP_MEMPOOL");152r = replacestr(r, "CP_SEQ_STAT", "CP_SQE_STAT");153154lastline = r;155return r;156}157158static void159pushline(void)160{161assert(!pushedline);162pushedline = lastline;163}164165static uint32_t *166popline_ascii85(uint32_t sizedwords)167{168const char *line = popline();169170/* At this point we exepct the ascii85 data to be indented *some*171* amount, and to terminate at the end of the line. So just eat172* up the leading whitespace.173*/174assert(*line == ' ');175while (*line == ' ')176line++;177178uint32_t *buf = calloc(1, 4 * sizedwords);179int idx = 0;180181while (*line != '\n') {182if (*line == 'z') {183buf[idx++] = 0;184line++;185continue;186}187188uint32_t accum = 0;189for (int i = 0; (i < 5) && (*line != '\n'); i++) {190accum *= 85;191accum += *line - '!';192line++;193}194195buf[idx++] = accum;196}197198return buf;199}200201static bool202startswith(const char *line, const char *start)203{204return strstr(line, start) == line;205}206207static void208parseline(const char *line, const char *fmt, ...)209{210int fmtlen = strlen(fmt);211int n = 0;212int l = 0;213214/* scan fmt string to extract expected # of conversions: */215for (int i = 0; i < fmtlen; i++) {216if (fmt[i] == '%') {217if (i == (l - 1)) { /* prev char was %, ie. we have %% */218n--;219l = 0;220} else {221n++;222l = i;223}224}225}226227va_list ap;228va_start(ap, fmt);229if (vsscanf(line, fmt, ap) != n) {230fprintf(stderr, "parse error scanning: '%s'\n", fmt);231exit(1);232}233va_end(ap);234}235236#define foreach_line_in_section(_line) \237for (const char *_line = popline(); _line; _line = popline()) \238/* check for start of next section */ \239if (_line[0] != ' ') { \240pushline(); \241break; \242} else243244/*245* Decode ringbuffer section:246*/247248static struct {249uint64_t iova;250uint32_t rptr;251uint32_t wptr;252uint32_t size;253uint32_t *buf;254} ringbuffers[5];255256static void257decode_ringbuffer(void)258{259int id = 0;260261foreach_line_in_section (line) {262if (startswith(line, " - id:")) {263parseline(line, " - id: %d", &id);264assert(id < ARRAY_SIZE(ringbuffers));265} else if (startswith(line, " iova:")) {266parseline(line, " iova: %" PRIx64, &ringbuffers[id].iova);267} else if (startswith(line, " rptr:")) {268parseline(line, " rptr: %d", &ringbuffers[id].rptr);269} else if (startswith(line, " wptr:")) {270parseline(line, " wptr: %d", &ringbuffers[id].wptr);271} else if (startswith(line, " size:")) {272parseline(line, " size: %d", &ringbuffers[id].size);273} else if (startswith(line, " data: !!ascii85 |")) {274ringbuffers[id].buf = popline_ascii85(ringbuffers[id].size / 4);275add_buffer(ringbuffers[id].iova, ringbuffers[id].size,276ringbuffers[id].buf);277continue;278}279280printf("%s", line);281}282}283284static bool285valid_header(uint32_t pkt)286{287if (options.gpu_id >= 500) {288return pkt_is_type4(pkt) || pkt_is_type7(pkt);289} else {290/* TODO maybe we can check validish looking pkt3 opc or pkt0291* register offset.. the cmds sent by kernel are usually292* fairly limited (other than initialization) which confines293* the search space a bit..294*/295return true;296}297}298299static void300dump_cmdstream(void)301{302uint64_t rb_base = regval64("CP_RB_BASE");303304printf("got rb_base=%" PRIx64 "\n", rb_base);305306options.ibs[1].base = regval64("CP_IB1_BASE");307options.ibs[1].rem = regval("CP_IB1_REM_SIZE");308options.ibs[2].base = regval64("CP_IB2_BASE");309options.ibs[2].rem = regval("CP_IB2_REM_SIZE");310311/* Adjust remaining size to account for cmdstream slurped into ROQ312* but not yet consumed by SQE313*314* TODO add support for earlier GPUs once we tease out the needed315* registers.. see crashit.c in msmtest for hints.316*317* TODO it would be nice to be able to extract out register bitfields318* by name rather than hard-coding this.319*/320if (is_a6xx()) {321options.ibs[1].rem += regval("CP_CSQ_IB1_STAT") >> 16;322options.ibs[2].rem += regval("CP_CSQ_IB2_STAT") >> 16;323}324325printf("IB1: %" PRIx64 ", %u\n", options.ibs[1].base, options.ibs[1].rem);326printf("IB2: %" PRIx64 ", %u\n", options.ibs[2].base, options.ibs[2].rem);327328/* now that we've got the regvals we want, reset register state329* so we aren't seeing values from decode_registers();330*/331reset_regs();332333for (int id = 0; id < ARRAY_SIZE(ringbuffers); id++) {334if (ringbuffers[id].iova != rb_base)335continue;336if (!ringbuffers[id].size)337continue;338339printf("found ring!\n");340341/* The kernel level ringbuffer (RB) wraps around, which342* cffdec doesn't really deal with.. so figure out how343* many dwords are unread344*/345unsigned ringszdw = ringbuffers[id].size >> 2; /* in dwords */346347/* helper macro to deal with modulo size math: */348#define mod_add(b, v) ((ringszdw + (int)(b) + (int)(v)) % ringszdw)349350/* The rptr will (most likely) have moved past the IB to351* userspace cmdstream, so back up a bit, and then advance352* until we find a valid start of a packet.. this is going353* to be less reliable on a4xx and before (pkt0/pkt3),354* compared to pkt4/pkt7 with parity bits355*/356const int lookback = 12;357unsigned rptr = mod_add(ringbuffers[id].rptr, -lookback);358359for (int idx = 0; idx < lookback; idx++) {360if (valid_header(ringbuffers[id].buf[rptr]))361break;362rptr = mod_add(rptr, 1);363}364365unsigned cmdszdw = mod_add(ringbuffers[id].wptr, -rptr);366367printf("got cmdszdw=%d\n", cmdszdw);368uint32_t *buf = malloc(cmdszdw * 4);369370for (int idx = 0; idx < cmdszdw; idx++) {371int p = mod_add(rptr, idx);372buf[idx] = ringbuffers[id].buf[p];373}374375dump_commands(buf, cmdszdw, 0);376free(buf);377}378}379380/*381* Decode 'bos' (buffers) section:382*/383384static void385decode_bos(void)386{387uint32_t size = 0;388uint64_t iova = 0;389390foreach_line_in_section (line) {391if (startswith(line, " - iova:")) {392parseline(line, " - iova: %" PRIx64, &iova);393} else if (startswith(line, " size:")) {394parseline(line, " size: %u", &size);395} else if (startswith(line, " data: !!ascii85 |")) {396uint32_t *buf = popline_ascii85(size / 4);397398if (verbose)399dump_hex_ascii(buf, size, 1);400401add_buffer(iova, size, buf);402403continue;404}405406printf("%s", line);407}408}409410/*411* Decode registers section:412*/413414static void415dump_register(struct rnn *rnn, uint32_t offset, uint32_t value)416{417struct rnndecaddrinfo *info = rnn_reginfo(rnn, offset);418if (info && info->typeinfo) {419char *decoded = rnndec_decodeval(rnn->vc, info->typeinfo, value);420printf("%s: %s\n", info->name, decoded);421} else if (info) {422printf("%s: %08x\n", info->name, value);423} else {424printf("<%04x>: %08x\n", offset, value);425}426}427428static void429decode_gmu_registers(void)430{431foreach_line_in_section (line) {432uint32_t offset, value;433parseline(line, " - { offset: %x, value: %x }", &offset, &value);434435printf("\t%08x\t", value);436dump_register(rnn_gmu, offset / 4, value);437}438}439440static void441decode_registers(void)442{443foreach_line_in_section (line) {444uint32_t offset, value;445parseline(line, " - { offset: %x, value: %x }", &offset, &value);446447reg_set(offset / 4, value);448printf("\t%08x", value);449dump_register_val(offset / 4, value, 0);450}451}452453/* similar to registers section, but for banked context regs: */454static void455decode_clusters(void)456{457foreach_line_in_section (line) {458if (startswith(line, " - cluster-name:") ||459startswith(line, " - context:")) {460printf("%s", line);461continue;462}463464uint32_t offset, value;465parseline(line, " - { offset: %x, value: %x }", &offset, &value);466467printf("\t%08x", value);468dump_register_val(offset / 4, value, 0);469}470}471472/*473* Decode indexed-registers.. these aren't like normal registers, but a474* sort of FIFO where successive reads pop out associated debug state.475*/476477static void478dump_cp_sqe_stat(uint32_t *stat)479{480printf("\t PC: %04x\n", stat[0]);481stat++;482483if (is_a6xx() && valid_header(stat[0])) {484if (pkt_is_type7(stat[0])) {485unsigned opc = cp_type7_opcode(stat[0]);486const char *name = pktname(opc);487if (name)488printf("\tPKT: %s\n", name);489} else {490/* Not sure if this case can happen: */491}492}493494for (int i = 0; i < 16; i++) {495printf("\t$%02x: %08x\t\t$%02x: %08x\n", i + 1, stat[i], i + 16 + 1,496stat[i + 16]);497}498}499500static void501dump_control_regs(uint32_t *regs)502{503if (!rnn_control)504return;505506/* Control regs 0x100-0x17f are a scratch space to be used by the507* firmware however it wants, unlike lower regs which involve some508* fixed-function units. Therefore only these registers get dumped509* directly.510*/511for (uint32_t i = 0; i < 0x80; i++) {512printf("\t%08x\t", regs[i]);513dump_register(rnn_control, i + 0x100, regs[i]);514}515}516517static void518dump_cp_ucode_dbg(uint32_t *dbg)519{520/* Notes on the data:521* There seems to be a section every 4096 DWORD's. The sections aren't522* all the same size, so the rest of the 4096 DWORD's are filled with523* mirrors of the actual data.524*/525526for (int section = 0; section < 6; section++, dbg += 0x1000) {527switch (section) {528case 0:529/* Contains scattered data from a630_sqe.fw: */530printf("\tSQE instruction cache:\n");531dump_hex_ascii(dbg, 4 * 0x400, 1);532break;533case 1:534printf("\tUnknown 1:\n");535dump_hex_ascii(dbg, 4 * 0x80, 1);536break;537case 2:538printf("\tUnknown 2:\n");539dump_hex_ascii(dbg, 4 * 0x200, 1);540break;541case 3:542printf("\tUnknown 3:\n");543dump_hex_ascii(dbg, 4 * 0x80, 1);544break;545case 4:546/* Don't bother printing this normally */547if (verbose) {548printf("\tSQE packet jumptable contents:\n");549dump_hex_ascii(dbg, 4 * 0x80, 1);550}551break;552case 5:553printf("\tSQE scratch control regs:\n");554dump_control_regs(dbg);555break;556}557}558}559560static void561dump_mem_pool_reg_write(unsigned reg, uint32_t data, unsigned context,562bool pipe)563{564if (pipe) {565struct rnndecaddrinfo *info = rnn_reginfo(rnn_pipe, reg);566printf("\t\twrite %s (%02x) pipe\n", info->name, reg);567568if (!strcmp(info->typeinfo->name, "void")) {569/* registers that ignore their payload */570} else {571printf("\t\t\t");572dump_register(rnn_pipe, reg, data);573}574} else {575printf("\t\twrite %s (%05x) context %d\n", regname(reg, 1), reg, context);576dump_register_val(reg, data, 2);577}578}579580static void581dump_mem_pool_chunk(const uint32_t *chunk)582{583struct __attribute__((packed)) {584bool reg0_enabled : 1;585bool reg1_enabled : 1;586uint32_t data0 : 32;587uint32_t data1 : 32;588uint32_t reg0 : 18;589uint32_t reg1 : 18;590bool reg0_pipe : 1;591bool reg1_pipe : 1;592uint32_t reg0_context : 1;593uint32_t reg1_context : 1;594uint32_t padding : 22;595} fields;596597memcpy(&fields, chunk, 4 * sizeof(uint32_t));598599if (fields.reg0_enabled) {600dump_mem_pool_reg_write(fields.reg0, fields.data0, fields.reg0_context,601fields.reg0_pipe);602}603604if (fields.reg1_enabled) {605dump_mem_pool_reg_write(fields.reg1, fields.data1, fields.reg1_context,606fields.reg1_pipe);607}608}609610static void611dump_cp_mem_pool(uint32_t *mempool)612{613/* The mem pool is a shared pool of memory used for storing in-flight614* register writes. There are 6 different queues, one for each615* cluster. Writing to $data (or for some special registers, $addr)616* pushes data onto the appropriate queue, and each queue is pulled617* from by the appropriate cluster. The queues are thus written to618* in-order, but may be read out-of-order.619*620* The queues are conceptually divided into 128-bit "chunks", and the621* read and write pointers are in units of chunks. These chunks are622* organized internally into 8-chunk "blocks", and memory is allocated623* dynamically in terms of blocks. Each queue is represented as a624* singly-linked list of blocks, as well as 3-bit start/end chunk625* pointers that point within the first/last block. The next pointers626* are located in a separate array, rather than inline.627*/628629/* TODO: The firmware CP_MEM_POOL save/restore routines do something630* like:631*632* cread $02, [ $00 + 0 ]633* and $02, $02, 0x118634* ...635* brne $02, 0, #label636* mov $03, 0x2000637* mov $03, 0x1000638* label:639* ...640*641* I think that control register 0 is the GPU version, and some642* versions have a smaller mem pool. It seems some models have a mem643* pool that's half the size, and a bunch of offsets are shifted644* accordingly. Unfortunately the kernel driver's dumping code doesn't645* seem to take this into account, even the downstream android driver,646* and we don't know which versions 0x8, 0x10, or 0x100 correspond647* to. Or maybe we can use CP_DBG_MEM_POOL_SIZE to figure this out?648*/649bool small_mem_pool = false;650651/* The array of next pointers for each block. */652const uint32_t *next_pointers =653small_mem_pool ? &mempool[0x800] : &mempool[0x1000];654655/* Maximum number of blocks in the pool, also the size of the pointers656* array.657*/658const int num_blocks = small_mem_pool ? 0x30 : 0x80;659660/* Number of queues */661const unsigned num_queues = 6;662663/* Unfortunately the per-queue state is a little more complicated than664* a simple pair of begin/end pointers. Instead of a single beginning665* block, there are *two*, with the property that either the two are666* equal or the second is the "next" of the first. Similarly there are667* two end blocks. Thus the queue either looks like this:668*669* A -> B -> ... -> C -> D670*671* Or like this, or some combination:672*673* A/B -> ... -> C/D674*675* However, there's only one beginning/end chunk offset. Now the676* question is, which of A or B is the actual start? I.e. is the chunk677* offset an offset inside A or B? It depends. I'll show a typical read678* cycle, starting here (read pointer marked with a *) with a chunk679* offset of 0:680*681* A B682* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _683* |_|_|_|_|_|_|_|_| -> |*|_|_|_|_|_|_|_| -> |_|_|_|_|_|_|_|_|684*685* Once the pointer advances far enough, the hardware decides to free686* A, after which the read-side state looks like:687*688* (free) A/B689* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _690* |_|_|_|_|_|_|_|_| |_|_|_|*|_|_|_|_| -> |_|_|_|_|_|_|_|_|691*692* Then after advancing the pointer a bit more, the hardware fetches693* the "next" pointer for A and stores it in B:694*695* (free) A B696* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _697* |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|*| -> |_|_|_|_|_|_|_|_|698*699* Then the read pointer advances into B, at which point we've come700* back to the first state having advanced a whole block:701*702* (free) A B703* _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _704* |_|_|_|_|_|_|_|_| |_|_|_|_|_|_|_|_| -> |*|_|_|_|_|_|_|_|705*706*707* There is a similar cycle for the write pointer. Now, the question708* is, how do we know which state we're in? We need to know this to709* know whether the pointer (*) is in A or B if they're different. It710* seems like there should be some bit somewhere describing this, but711* after lots of experimentation I've come up empty-handed. For now we712* assume that if the pointer is in the first half, then we're in713* either the first or second state and use B, and otherwise we're in714* the second or third state and use A. So far I haven't seen anything715* that violates this assumption.716*/717718struct {719uint32_t unk0;720uint32_t padding0[7]; /* Mirrors of unk0 */721722struct {723uint32_t chunk : 3;724uint32_t first_block : 32 - 3;725} writer[6];726uint32_t padding1[2]; /* Mirrors of writer[4], writer[5] */727728uint32_t unk1;729uint32_t padding2[7]; /* Mirrors of unk1 */730731uint32_t writer_second_block[6];732uint32_t padding3[2];733734uint32_t unk2[6];735uint32_t padding4[2];736737struct {738uint32_t chunk : 3;739uint32_t first_block : 32 - 3;740} reader[6];741uint32_t padding5[2]; /* Mirrors of reader[4], reader[5] */742743uint32_t unk3;744uint32_t padding6[7]; /* Mirrors of unk3 */745746uint32_t reader_second_block[6];747uint32_t padding7[2];748749uint32_t block_count[6];750uint32_t padding[2];751752uint32_t unk4;753uint32_t padding9[7]; /* Mirrors of unk4 */754} data1;755756const uint32_t *data1_ptr =757small_mem_pool ? &mempool[0xc00] : &mempool[0x1800];758memcpy(&data1, data1_ptr, sizeof(data1));759760/* Based on the kernel, the first dword is the mem pool size (in761* blocks?) and mirrors CP_MEM_POOL_DBG_SIZE.762*/763const uint32_t *data2_ptr =764small_mem_pool ? &mempool[0x1000] : &mempool[0x2000];765const int data2_size = 0x60;766767/* This seems to be the size of each queue in chunks. */768const uint32_t *queue_sizes = &data2_ptr[0x18];769770printf("\tdata2:\n");771dump_hex_ascii(data2_ptr, 4 * data2_size, 1);772773/* These seem to be some kind of counter of allocated/deallocated blocks */774if (verbose) {775printf("\tunk0: %x\n", data1.unk0);776printf("\tunk1: %x\n", data1.unk1);777printf("\tunk3: %x\n", data1.unk3);778printf("\tunk4: %x\n\n", data1.unk4);779}780781for (int queue = 0; queue < num_queues; queue++) {782const char *cluster_names[6] = {"FE", "SP_VS", "PC_VS",783"GRAS", "SP_PS", "PS"};784printf("\tCLUSTER_%s:\n\n", cluster_names[queue]);785786if (verbose) {787printf("\t\twriter_first_block: 0x%x\n",788data1.writer[queue].first_block);789printf("\t\twriter_second_block: 0x%x\n",790data1.writer_second_block[queue]);791printf("\t\twriter_chunk: %d\n", data1.writer[queue].chunk);792printf("\t\treader_first_block: 0x%x\n",793data1.reader[queue].first_block);794printf("\t\treader_second_block: 0x%x\n",795data1.reader_second_block[queue]);796printf("\t\treader_chunk: %d\n", data1.reader[queue].chunk);797printf("\t\tblock_count: %d\n", data1.block_count[queue]);798printf("\t\tunk2: 0x%x\n", data1.unk2[queue]);799printf("\t\tqueue_size: %d\n\n", queue_sizes[queue]);800}801802uint32_t cur_chunk = data1.reader[queue].chunk;803uint32_t cur_block = cur_chunk > 3 ? data1.reader[queue].first_block804: data1.reader_second_block[queue];805uint32_t last_chunk = data1.writer[queue].chunk;806uint32_t last_block = last_chunk > 3 ? data1.writer[queue].first_block807: data1.writer_second_block[queue];808809if (verbose)810printf("\tblock %x\n", cur_block);811if (cur_block >= num_blocks) {812fprintf(stderr, "block %x too large\n", cur_block);813exit(1);814}815unsigned calculated_queue_size = 0;816while (cur_block != last_block || cur_chunk != last_chunk) {817calculated_queue_size++;818uint32_t *chunk_ptr = &mempool[cur_block * 0x20 + cur_chunk * 4];819820dump_mem_pool_chunk(chunk_ptr);821822printf("\t%05x: %08x %08x %08x %08x\n",8234 * (cur_block * 0x20 + cur_chunk + 4), chunk_ptr[0],824chunk_ptr[1], chunk_ptr[2], chunk_ptr[3]);825826cur_chunk++;827if (cur_chunk == 8) {828cur_block = next_pointers[cur_block];829if (verbose)830printf("\tblock %x\n", cur_block);831if (cur_block >= num_blocks) {832fprintf(stderr, "block %x too large\n", cur_block);833exit(1);834}835cur_chunk = 0;836}837}838if (calculated_queue_size != queue_sizes[queue]) {839printf("\t\tCALCULATED SIZE %d DOES NOT MATCH!\n",840calculated_queue_size);841}842printf("\n");843}844}845846static void847decode_indexed_registers(void)848{849char *name = NULL;850uint32_t sizedwords = 0;851852foreach_line_in_section (line) {853if (startswith(line, " - regs-name:")) {854free(name);855parseline(line, " - regs-name: %ms", &name);856} else if (startswith(line, " dwords:")) {857parseline(line, " dwords: %u", &sizedwords);858} else if (startswith(line, " data: !!ascii85 |")) {859uint32_t *buf = popline_ascii85(sizedwords);860861/* some of the sections are pretty large, and are (at least862* so far) not useful, so skip them if not in verbose mode:863*/864bool dump = verbose || !strcmp(name, "CP_SQE_STAT") ||865!strcmp(name, "CP_DRAW_STATE") ||866!strcmp(name, "CP_ROQ") || 0;867868if (!strcmp(name, "CP_SQE_STAT"))869dump_cp_sqe_stat(buf);870871if (!strcmp(name, "CP_UCODE_DBG_DATA"))872dump_cp_ucode_dbg(buf);873874if (!strcmp(name, "CP_MEMPOOL"))875dump_cp_mem_pool(buf);876877if (dump)878dump_hex_ascii(buf, 4 * sizedwords, 1);879880free(buf);881882continue;883}884885printf("%s", line);886}887}888889/*890* Decode shader-blocks:891*/892893static void894decode_shader_blocks(void)895{896char *type = NULL;897uint32_t sizedwords = 0;898899foreach_line_in_section (line) {900if (startswith(line, " - type:")) {901free(type);902parseline(line, " - type: %ms", &type);903} else if (startswith(line, " size:")) {904parseline(line, " size: %u", &sizedwords);905} else if (startswith(line, " data: !!ascii85 |")) {906uint32_t *buf = popline_ascii85(sizedwords);907908/* some of the sections are pretty large, and are (at least909* so far) not useful, so skip them if not in verbose mode:910*/911bool dump = verbose || !strcmp(type, "A6XX_SP_INST_DATA") ||912!strcmp(type, "A6XX_HLSQ_INST_RAM") || 0;913914if (!strcmp(type, "A6XX_SP_INST_DATA") ||915!strcmp(type, "A6XX_HLSQ_INST_RAM")) {916/* TODO this section actually contains multiple shaders917* (or parts of shaders?), so perhaps we should search918* for ends of shaders and decode each?919*/920try_disasm_a3xx(buf, sizedwords, 1, stdout, options.gpu_id);921}922923if (dump)924dump_hex_ascii(buf, 4 * sizedwords, 1);925926free(buf);927928continue;929}930931printf("%s", line);932}933934free(type);935}936937/*938* Decode debugbus section:939*/940941static void942decode_debugbus(void)943{944char *block = NULL;945uint32_t sizedwords = 0;946947foreach_line_in_section (line) {948if (startswith(line, " - debugbus-block:")) {949free(block);950parseline(line, " - debugbus-block: %ms", &block);951} else if (startswith(line, " count:")) {952parseline(line, " count: %u", &sizedwords);953} else if (startswith(line, " data: !!ascii85 |")) {954uint32_t *buf = popline_ascii85(sizedwords);955956/* some of the sections are pretty large, and are (at least957* so far) not useful, so skip them if not in verbose mode:958*/959bool dump = verbose || 0;960961if (dump)962dump_hex_ascii(buf, 4 * sizedwords, 1);963964free(buf);965966continue;967}968969printf("%s", line);970}971}972973/*974* Main crashdump decode loop:975*/976977static void978decode(void)979{980const char *line;981982while ((line = popline())) {983printf("%s", line);984if (startswith(line, "revision:")) {985parseline(line, "revision: %u", &options.gpu_id);986printf("Got gpu_id=%u\n", options.gpu_id);987988cffdec_init(&options);989990if (is_a6xx()) {991rnn_gmu = rnn_new(!options.color);992rnn_load_file(rnn_gmu, "adreno/a6xx_gmu.xml", "A6XX");993rnn_control = rnn_new(!options.color);994rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",995"A6XX_CONTROL_REG");996rnn_pipe = rnn_new(!options.color);997rnn_load_file(rnn_pipe, "adreno/adreno_pipe_regs.xml",998"A6XX_PIPE_REG");999} else if (is_a5xx()) {1000rnn_control = rnn_new(!options.color);1001rnn_load_file(rnn_control, "adreno/adreno_control_regs.xml",1002"A5XX_CONTROL_REG");1003} else {1004rnn_control = NULL;1005}1006} else if (startswith(line, "bos:")) {1007decode_bos();1008} else if (startswith(line, "ringbuffer:")) {1009decode_ringbuffer();1010} else if (startswith(line, "registers:")) {1011decode_registers();10121013/* after we've recorded buffer contents, and CP register values,1014* we can take a stab at decoding the cmdstream:1015*/1016dump_cmdstream();1017} else if (startswith(line, "registers-gmu:")) {1018decode_gmu_registers();1019} else if (startswith(line, "indexed-registers:")) {1020decode_indexed_registers();1021} else if (startswith(line, "shader-blocks:")) {1022decode_shader_blocks();1023} else if (startswith(line, "clusters:")) {1024decode_clusters();1025} else if (startswith(line, "debugbus:")) {1026decode_debugbus();1027}1028}1029}10301031/*1032* Usage and argument parsing:1033*/10341035static void1036usage(void)1037{1038/* clang-format off */1039fprintf(stderr, "Usage:\n\n"1040"\tcrashdec [-achmsv] [-f FILE]\n\n"1041"Options:\n"1042"\t-a, --allregs - show all registers (including ones not written since\n"1043"\t previous draw) at each draw\n"1044"\t-c, --color - use colors\n"1045"\t-f, --file=FILE - read input from specified file (rather than stdin)\n"1046"\t-h, --help - this usage message\n"1047"\t-m, --markers - try to decode CP_NOP string markers\n"1048"\t-s, --summary - don't show individual register writes, but just show\n"1049"\t register values on draws\n"1050"\t-v, --verbose - dump more verbose output, including contents of\n"1051"\t less interesting buffers\n"1052"\n"1053);1054/* clang-format on */1055exit(2);1056}10571058/* clang-format off */1059static const struct option opts[] = {1060{ .name = "allregs", .has_arg = 0, NULL, 'a' },1061{ .name = "color", .has_arg = 0, NULL, 'c' },1062{ .name = "file", .has_arg = 1, NULL, 'f' },1063{ .name = "help", .has_arg = 0, NULL, 'h' },1064{ .name = "markers", .has_arg = 0, NULL, 'm' },1065{ .name = "summary", .has_arg = 0, NULL, 's' },1066{ .name = "verbose", .has_arg = 0, NULL, 'v' },1067{}1068};1069/* clang-format on */10701071static bool interactive;10721073static void1074cleanup(void)1075{1076fflush(stdout);10771078if (interactive) {1079pager_close();1080}1081}10821083int1084main(int argc, char **argv)1085{1086int c;10871088interactive = isatty(STDOUT_FILENO);1089options.color = interactive;10901091/* default to read from stdin: */1092in = stdin;10931094while ((c = getopt_long(argc, argv, "acf:hmsv", opts, NULL)) != -1) {1095switch (c) {1096case 'a':1097options.allregs = true;1098break;1099case 'c':1100options.color = true;1101break;1102case 'f':1103in = fopen(optarg, "r");1104break;1105case 'm':1106options.decode_markers = true;1107break;1108case 's':1109options.summary = true;1110break;1111case 'v':1112verbose = true;1113break;1114case 'h':1115default:1116usage();1117}1118}11191120disasm_a3xx_set_debug(PRINT_RAW);11211122if (interactive) {1123pager_open();1124}11251126atexit(cleanup);11271128decode();1129cleanup();1130}113111321133