17ec681f3Smrg/*
27ec681f3Smrg * Copyright (C) 2017-2019 Alyssa Rosenzweig
37ec681f3Smrg * Copyright (C) 2017-2019 Connor Abbott
47ec681f3Smrg * Copyright (C) 2019 Collabora, Ltd.
57ec681f3Smrg *
67ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
77ec681f3Smrg * copy of this software and associated documentation files (the "Software"),
87ec681f3Smrg * to deal in the Software without restriction, including without limitation
97ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
107ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the
117ec681f3Smrg * Software is furnished to do so, subject to the following conditions:
127ec681f3Smrg *
137ec681f3Smrg * The above copyright notice and this permission notice (including the next
147ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the
157ec681f3Smrg * Software.
167ec681f3Smrg *
177ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
187ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
197ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
207ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
217ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
227ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
237ec681f3Smrg * SOFTWARE.
247ec681f3Smrg */
257ec681f3Smrg
267ec681f3Smrg#include <agx_pack.h>
277ec681f3Smrg#include <stdio.h>
287ec681f3Smrg#include <stdlib.h>
297ec681f3Smrg#include <memory.h>
307ec681f3Smrg#include <stdbool.h>
317ec681f3Smrg#include <stdarg.h>
327ec681f3Smrg#include <ctype.h>
337ec681f3Smrg#include <sys/mman.h>
347ec681f3Smrg
357ec681f3Smrg#include "decode.h"
367ec681f3Smrg#include "io.h"
377ec681f3Smrg#include "hexdump.h"
387ec681f3Smrg
397ec681f3Smrgstatic const char *agx_alloc_types[AGX_NUM_ALLOC] = { "mem", "map", "cmd" };
407ec681f3Smrg
417ec681f3Smrgstatic void
427ec681f3Smrgagx_disassemble(void *_code, size_t maxlen, FILE *fp)
437ec681f3Smrg{
447ec681f3Smrg   /* stub */
457ec681f3Smrg}
467ec681f3Smrg
477ec681f3SmrgFILE *agxdecode_dump_stream;
487ec681f3Smrg
497ec681f3Smrg#define MAX_MAPPINGS 4096
507ec681f3Smrg
517ec681f3Smrgstruct agx_bo mmap_array[MAX_MAPPINGS];
527ec681f3Smrgunsigned mmap_count = 0;
537ec681f3Smrg
547ec681f3Smrgstruct agx_bo *ro_mappings[MAX_MAPPINGS];
557ec681f3Smrgunsigned ro_mapping_count = 0;
567ec681f3Smrg
577ec681f3Smrgstatic struct agx_bo *
587ec681f3Smrgagxdecode_find_mapped_gpu_mem_containing_rw(uint64_t addr)
597ec681f3Smrg{
607ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i) {
617ec681f3Smrg      if (mmap_array[i].type == AGX_ALLOC_REGULAR && addr >= mmap_array[i].ptr.gpu && (addr - mmap_array[i].ptr.gpu) < mmap_array[i].size)
627ec681f3Smrg         return mmap_array + i;
637ec681f3Smrg   }
647ec681f3Smrg
657ec681f3Smrg   return NULL;
667ec681f3Smrg}
677ec681f3Smrg
687ec681f3Smrgstatic struct agx_bo *
697ec681f3Smrgagxdecode_find_mapped_gpu_mem_containing(uint64_t addr)
707ec681f3Smrg{
717ec681f3Smrg   struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing_rw(addr);
727ec681f3Smrg
737ec681f3Smrg   if (mem && mem->ptr.cpu && !mem->ro) {
747ec681f3Smrg      mprotect(mem->ptr.cpu, mem->size, PROT_READ);
757ec681f3Smrg      mem->ro = true;
767ec681f3Smrg      ro_mappings[ro_mapping_count++] = mem;
777ec681f3Smrg      assert(ro_mapping_count < MAX_MAPPINGS);
787ec681f3Smrg   }
797ec681f3Smrg
807ec681f3Smrg   if (mem && !mem->mapped) {
817ec681f3Smrg      fprintf(stderr, "[ERROR] access to memory not mapped (GPU %" PRIx64 ", handle %u)\n", mem->ptr.gpu, mem->handle);
827ec681f3Smrg   }
837ec681f3Smrg
847ec681f3Smrg   return mem;
857ec681f3Smrg}
867ec681f3Smrg
877ec681f3Smrgstatic struct agx_bo *
887ec681f3Smrgagxdecode_find_handle(unsigned handle, unsigned type)
897ec681f3Smrg{
907ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i) {
917ec681f3Smrg      if (mmap_array[i].type != type)
927ec681f3Smrg         continue;
937ec681f3Smrg
947ec681f3Smrg      if (mmap_array[i].handle != handle)
957ec681f3Smrg         continue;
967ec681f3Smrg
977ec681f3Smrg      return &mmap_array[i];
987ec681f3Smrg   }
997ec681f3Smrg
1007ec681f3Smrg   return NULL;
1017ec681f3Smrg}
1027ec681f3Smrg
1037ec681f3Smrgstatic void
1047ec681f3Smrgagxdecode_mark_mapped(unsigned handle)
1057ec681f3Smrg{
1067ec681f3Smrg   struct agx_bo *bo = agxdecode_find_handle(handle, AGX_ALLOC_REGULAR);
1077ec681f3Smrg
1087ec681f3Smrg   if (!bo) {
1097ec681f3Smrg      fprintf(stderr, "ERROR - unknown BO mapped with handle %u\n", handle);
1107ec681f3Smrg      return;
1117ec681f3Smrg   }
1127ec681f3Smrg
1137ec681f3Smrg   /* Mark mapped for future consumption */
1147ec681f3Smrg   bo->mapped = true;
1157ec681f3Smrg}
1167ec681f3Smrg
1177ec681f3Smrgstatic void
1187ec681f3Smrgagxdecode_validate_map(void *map)
1197ec681f3Smrg{
1207ec681f3Smrg   unsigned nr_handles = 0;
1217ec681f3Smrg
1227ec681f3Smrg   /* First, mark everything unmapped */
1237ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i)
1247ec681f3Smrg      mmap_array[i].mapped = false;
1257ec681f3Smrg
1267ec681f3Smrg   /* Check the header */
1277ec681f3Smrg   struct agx_map_header *hdr = map;
1287ec681f3Smrg   if (hdr->nr_entries == 0) {
1297ec681f3Smrg      fprintf(stderr, "ERROR - empty map\n");
1307ec681f3Smrg      return;
1317ec681f3Smrg   }
1327ec681f3Smrg
1337ec681f3Smrg   for (unsigned i = 0; i < 6; ++i) {
1347ec681f3Smrg      unsigned handle = hdr->indices[i];
1357ec681f3Smrg      if (handle) {
1367ec681f3Smrg         agxdecode_mark_mapped(handle);
1377ec681f3Smrg         nr_handles++;
1387ec681f3Smrg      }
1397ec681f3Smrg   }
1407ec681f3Smrg
1417ec681f3Smrg   /* Check the entries */
1427ec681f3Smrg   struct agx_map_entry *entries = (struct agx_map_entry *) (&hdr[1]);
1437ec681f3Smrg   for (unsigned i = 0; i < hdr->nr_entries - 1; ++i) {
1447ec681f3Smrg      struct agx_map_entry entry = entries[i];
1457ec681f3Smrg
1467ec681f3Smrg      for (unsigned j = 0; j < 6; ++j) {
1477ec681f3Smrg         unsigned handle = entry.indices[j];
1487ec681f3Smrg         if (handle) {
1497ec681f3Smrg            agxdecode_mark_mapped(handle);
1507ec681f3Smrg            nr_handles++;
1517ec681f3Smrg         }
1527ec681f3Smrg      }
1537ec681f3Smrg   }
1547ec681f3Smrg
1557ec681f3Smrg   /* Check the sentinel */
1567ec681f3Smrg   if (entries[hdr->nr_entries - 1].indices[0]) {
1577ec681f3Smrg      fprintf(stderr, "ERROR - last entry nonzero %u\n", entries[hdr->nr_entries - 1].indices[0]);
1587ec681f3Smrg      return;
1597ec681f3Smrg   }
1607ec681f3Smrg
1617ec681f3Smrg   /* Check the handle count */
1627ec681f3Smrg   if (nr_handles != hdr->nr_handles) {
1637ec681f3Smrg      fprintf(stderr, "ERROR - wrong handle count, got %u, expected %u\n",
1647ec681f3Smrg            nr_handles, hdr->nr_handles);
1657ec681f3Smrg   }
1667ec681f3Smrg}
1677ec681f3Smrg
1687ec681f3Smrgstatic inline void *
1697ec681f3Smrg__agxdecode_fetch_gpu_mem(const struct agx_bo *mem,
1707ec681f3Smrg                          uint64_t gpu_va, size_t size,
1717ec681f3Smrg                          int line, const char *filename)
1727ec681f3Smrg{
1737ec681f3Smrg   if (!mem)
1747ec681f3Smrg      mem = agxdecode_find_mapped_gpu_mem_containing(gpu_va);
1757ec681f3Smrg
1767ec681f3Smrg   if (!mem) {
1777ec681f3Smrg      fprintf(stderr, "Access to unknown memory %" PRIx64 " in %s:%d\n",
1787ec681f3Smrg              gpu_va, filename, line);
1797ec681f3Smrg      fflush(agxdecode_dump_stream);
1807ec681f3Smrg      assert(0);
1817ec681f3Smrg   }
1827ec681f3Smrg
1837ec681f3Smrg   assert(mem);
1847ec681f3Smrg   assert(size + (gpu_va - mem->ptr.gpu) <= mem->size);
1857ec681f3Smrg
1867ec681f3Smrg   return mem->ptr.cpu + gpu_va - mem->ptr.gpu;
1877ec681f3Smrg}
1887ec681f3Smrg
1897ec681f3Smrg#define agxdecode_fetch_gpu_mem(gpu_va, size) \
1907ec681f3Smrg	__agxdecode_fetch_gpu_mem(NULL, gpu_va, size, __LINE__, __FILE__)
1917ec681f3Smrg
1927ec681f3Smrgstatic void
1937ec681f3Smrgagxdecode_map_read_write(void)
1947ec681f3Smrg{
1957ec681f3Smrg   for (unsigned i = 0; i < ro_mapping_count; ++i) {
1967ec681f3Smrg      ro_mappings[i]->ro = false;
1977ec681f3Smrg      mprotect(ro_mappings[i]->ptr.cpu, ro_mappings[i]->size,
1987ec681f3Smrg               PROT_READ | PROT_WRITE);
1997ec681f3Smrg   }
2007ec681f3Smrg
2017ec681f3Smrg   ro_mapping_count = 0;
2027ec681f3Smrg}
2037ec681f3Smrg
2047ec681f3Smrg/* Helpers for parsing the cmdstream */
2057ec681f3Smrg
2067ec681f3Smrg#define DUMP_UNPACKED(T, var, str) { \
2077ec681f3Smrg        agxdecode_log(str); \
2087ec681f3Smrg        agx_print(agxdecode_dump_stream, T, var, (agxdecode_indent + 1) * 2); \
2097ec681f3Smrg}
2107ec681f3Smrg
2117ec681f3Smrg#define DUMP_CL(T, cl, str) {\
2127ec681f3Smrg        agx_unpack(agxdecode_dump_stream, cl, T, temp); \
2137ec681f3Smrg        DUMP_UNPACKED(T, temp, str "\n"); \
2147ec681f3Smrg}
2157ec681f3Smrg
2167ec681f3Smrg#define agxdecode_log(str) fputs(str, agxdecode_dump_stream)
2177ec681f3Smrg#define agxdecode_msg(str) fprintf(agxdecode_dump_stream, "// %s", str)
2187ec681f3Smrg
2197ec681f3Smrgunsigned agxdecode_indent = 0;
2207ec681f3Smrguint64_t pipeline_base = 0;
2217ec681f3Smrg
2227ec681f3Smrgstatic void
2237ec681f3Smrgagxdecode_dump_bo(struct agx_bo *bo, const char *name)
2247ec681f3Smrg{
2257ec681f3Smrg   fprintf(agxdecode_dump_stream, "%s %s (%u)\n", name, bo->name ?: "", bo->handle);
2267ec681f3Smrg   hexdump(agxdecode_dump_stream, bo->ptr.cpu, bo->size, false);
2277ec681f3Smrg}
2287ec681f3Smrg
2297ec681f3Smrg/* Abstraction for command stream parsing */
2307ec681f3Smrgtypedef unsigned (*decode_cmd)(const uint8_t *map, bool verbose);
2317ec681f3Smrg
2327ec681f3Smrg#define STATE_DONE (0xFFFFFFFFu)
2337ec681f3Smrg
2347ec681f3Smrgstatic void
2357ec681f3Smrgagxdecode_stateful(uint64_t va, const char *label, decode_cmd decoder, bool verbose)
2367ec681f3Smrg{
2377ec681f3Smrg   struct agx_bo *alloc = agxdecode_find_mapped_gpu_mem_containing(va);
2387ec681f3Smrg   assert(alloc != NULL && "nonexistant object");
2397ec681f3Smrg   fprintf(agxdecode_dump_stream, "%s (%" PRIx64 ", handle %u)\n", label, va, alloc->handle);
2407ec681f3Smrg   fflush(agxdecode_dump_stream);
2417ec681f3Smrg
2427ec681f3Smrg   uint8_t *map = agxdecode_fetch_gpu_mem(va, 64);
2437ec681f3Smrg   uint8_t *end = (uint8_t *) alloc->ptr.cpu + alloc->size;
2447ec681f3Smrg
2457ec681f3Smrg   if (verbose)
2467ec681f3Smrg      agxdecode_dump_bo(alloc, label);
2477ec681f3Smrg   fflush(agxdecode_dump_stream);
2487ec681f3Smrg
2497ec681f3Smrg   while (map < end) {
2507ec681f3Smrg      unsigned count = decoder(map, verbose);
2517ec681f3Smrg
2527ec681f3Smrg      /* If we fail to decode, default to a hexdump (don't hang) */
2537ec681f3Smrg      if (count == 0) {
2547ec681f3Smrg         hexdump(agxdecode_dump_stream, map, 8, false);
2557ec681f3Smrg         count = 8;
2567ec681f3Smrg      }
2577ec681f3Smrg
2587ec681f3Smrg      map += count;
2597ec681f3Smrg      fflush(agxdecode_dump_stream);
2607ec681f3Smrg
2617ec681f3Smrg      if (count == STATE_DONE)
2627ec681f3Smrg         break;
2637ec681f3Smrg   }
2647ec681f3Smrg}
2657ec681f3Smrg
2667ec681f3Smrgunsigned COUNTER = 0;
2677ec681f3Smrgstatic unsigned
2687ec681f3Smrgagxdecode_pipeline(const uint8_t *map, UNUSED bool verbose)
2697ec681f3Smrg{
2707ec681f3Smrg   uint8_t zeroes[16] = { 0 };
2717ec681f3Smrg
2727ec681f3Smrg   if (map[0] == 0x4D && map[1] == 0xbd) {
2737ec681f3Smrg      /* TODO: Disambiguation for extended is a guess */
2747ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, SET_SHADER_EXTENDED, cmd);
2757ec681f3Smrg      DUMP_UNPACKED(SET_SHADER_EXTENDED, cmd, "Set shader\n");
2767ec681f3Smrg
2777ec681f3Smrg      if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) {
2787ec681f3Smrg         agxdecode_log("Preshader\n");
2797ec681f3Smrg         agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048),
2807ec681f3Smrg                         2048, agxdecode_dump_stream);
2817ec681f3Smrg         agxdecode_log("\n---\n");
2827ec681f3Smrg      }
2837ec681f3Smrg
2847ec681f3Smrg      agxdecode_log("\n");
2857ec681f3Smrg      agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048),
2867ec681f3Smrg                      2048, agxdecode_dump_stream);
2877ec681f3Smrg      agxdecode_log("\n");
2887ec681f3Smrg
2897ec681f3Smrg      char *name;
2907ec681f3Smrg      asprintf(&name, "file%u.bin", COUNTER++);
2917ec681f3Smrg      FILE *fp = fopen(name, "wb");
2927ec681f3Smrg      fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp);
2937ec681f3Smrg      fclose(fp);
2947ec681f3Smrg      free(name);
2957ec681f3Smrg      agxdecode_log("\n");
2967ec681f3Smrg
2977ec681f3Smrg      return AGX_SET_SHADER_EXTENDED_LENGTH;
2987ec681f3Smrg   } else if (map[0] == 0x4D) {
2997ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, SET_SHADER, cmd);
3007ec681f3Smrg      DUMP_UNPACKED(SET_SHADER, cmd, "Set shader\n");
3017ec681f3Smrg      fflush(agxdecode_dump_stream);
3027ec681f3Smrg
3037ec681f3Smrg      if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) {
3047ec681f3Smrg         agxdecode_log("Preshader\n");
3057ec681f3Smrg         agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048),
3067ec681f3Smrg                         2048, agxdecode_dump_stream);
3077ec681f3Smrg         agxdecode_log("\n---\n");
3087ec681f3Smrg      }
3097ec681f3Smrg
3107ec681f3Smrg      agxdecode_log("\n");
3117ec681f3Smrg      agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048),
3127ec681f3Smrg                      2048, agxdecode_dump_stream);
3137ec681f3Smrg      char *name;
3147ec681f3Smrg      asprintf(&name, "file%u.bin", COUNTER++);
3157ec681f3Smrg      FILE *fp = fopen(name, "wb");
3167ec681f3Smrg      fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp);
3177ec681f3Smrg      fclose(fp);
3187ec681f3Smrg      free(name);
3197ec681f3Smrg      agxdecode_log("\n");
3207ec681f3Smrg
3217ec681f3Smrg      return AGX_SET_SHADER_LENGTH;
3227ec681f3Smrg   } else if (map[0] == 0xDD) {
3237ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, BIND_TEXTURE, temp);
3247ec681f3Smrg      DUMP_UNPACKED(BIND_TEXTURE, temp, "Bind texture\n");
3257ec681f3Smrg
3267ec681f3Smrg      uint8_t *tex = agxdecode_fetch_gpu_mem(temp.buffer, 64);
3277ec681f3Smrg      DUMP_CL(TEXTURE, tex, "Texture");
3287ec681f3Smrg      hexdump(agxdecode_dump_stream, tex + AGX_TEXTURE_LENGTH, 64 - AGX_TEXTURE_LENGTH, false);
3297ec681f3Smrg
3307ec681f3Smrg      return AGX_BIND_TEXTURE_LENGTH;
3317ec681f3Smrg   } else if (map[0] == 0x9D) {
3327ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, BIND_SAMPLER, temp);
3337ec681f3Smrg      DUMP_UNPACKED(BIND_SAMPLER, temp, "Bind sampler\n");
3347ec681f3Smrg
3357ec681f3Smrg      uint8_t *samp = agxdecode_fetch_gpu_mem(temp.buffer, 64);
3367ec681f3Smrg      DUMP_CL(SAMPLER, samp, "Sampler");
3377ec681f3Smrg      hexdump(agxdecode_dump_stream, samp + AGX_SAMPLER_LENGTH, 64 - AGX_SAMPLER_LENGTH, false);
3387ec681f3Smrg
3397ec681f3Smrg      return AGX_BIND_SAMPLER_LENGTH;
3407ec681f3Smrg   } else if (map[0] == 0x1D) {
3417ec681f3Smrg      DUMP_CL(BIND_UNIFORM, map, "Bind uniform");
3427ec681f3Smrg      return AGX_BIND_UNIFORM_LENGTH;
3437ec681f3Smrg   } else if (memcmp(map, zeroes, 16) == 0) {
3447ec681f3Smrg      /* TODO: Termination */
3457ec681f3Smrg      return STATE_DONE;
3467ec681f3Smrg   } else {
3477ec681f3Smrg      return 0;
3487ec681f3Smrg   }
3497ec681f3Smrg}
3507ec681f3Smrg
3517ec681f3Smrgstatic void
3527ec681f3Smrgagxdecode_record(uint64_t va, size_t size, bool verbose)
3537ec681f3Smrg{
3547ec681f3Smrg   uint8_t *map = agxdecode_fetch_gpu_mem(va, size);
3557ec681f3Smrg   uint32_t tag = 0;
3567ec681f3Smrg   memcpy(&tag, map, 4);
3577ec681f3Smrg
3587ec681f3Smrg   if (tag == 0x00000C00) {
3597ec681f3Smrg      assert(size == AGX_VIEWPORT_LENGTH);
3607ec681f3Smrg      DUMP_CL(VIEWPORT, map, "Viewport");
3617ec681f3Smrg   } else if (tag == 0x0C020000) {
3627ec681f3Smrg      assert(size == AGX_LINKAGE_LENGTH);
3637ec681f3Smrg      DUMP_CL(LINKAGE, map, "Linkage");
3647ec681f3Smrg   } else if (tag == 0x10000b5) {
3657ec681f3Smrg      assert(size == AGX_RASTERIZER_LENGTH);
3667ec681f3Smrg      DUMP_CL(RASTERIZER, map, "Rasterizer");
3677ec681f3Smrg   } else if (tag == 0x200000) {
3687ec681f3Smrg      assert(size == AGX_CULL_LENGTH);
3697ec681f3Smrg      DUMP_CL(CULL, map, "Cull");
3707ec681f3Smrg   } else if (tag == 0x800000) {
3717ec681f3Smrg      assert(size == (AGX_BIND_PIPELINE_LENGTH + 4));
3727ec681f3Smrg
3737ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd);
3747ec681f3Smrg      agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
3757ec681f3Smrg
3767ec681f3Smrg      /* TODO: parse */
3777ec681f3Smrg      if (cmd.fs_varyings) {
3787ec681f3Smrg         uint8_t *map = agxdecode_fetch_gpu_mem(cmd.fs_varyings, 128);
3797ec681f3Smrg         hexdump(agxdecode_dump_stream, map, 128, false);
3807ec681f3Smrg
3817ec681f3Smrg         DUMP_CL(VARYING_HEADER, map, "Varying header:");
3827ec681f3Smrg         map += AGX_VARYING_HEADER_LENGTH;
3837ec681f3Smrg
3847ec681f3Smrg         for (unsigned i = 0; i < cmd.input_count; ++i) {
3857ec681f3Smrg            DUMP_CL(VARYING, map, "Varying:");
3867ec681f3Smrg            map += AGX_VARYING_LENGTH;
3877ec681f3Smrg         }
3887ec681f3Smrg      }
3897ec681f3Smrg
3907ec681f3Smrg      DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind fragment pipeline\n");
3917ec681f3Smrg   } else if (size == 0) {
3927ec681f3Smrg      pipeline_base = va;
3937ec681f3Smrg   } else {
3947ec681f3Smrg      fprintf(agxdecode_dump_stream, "Record %" PRIx64 "\n", va);
3957ec681f3Smrg      hexdump(agxdecode_dump_stream, map, size, false);
3967ec681f3Smrg   }
3977ec681f3Smrg}
3987ec681f3Smrg
3997ec681f3Smrgstatic unsigned
4007ec681f3Smrgagxdecode_cmd(const uint8_t *map, bool verbose)
4017ec681f3Smrg{
4027ec681f3Smrg   if (map[0] == 0x02 && map[1] == 0x10 && map[2] == 0x00 && map[3] == 0x00) {
4037ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, LAUNCH, cmd);
4047ec681f3Smrg      agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
4057ec681f3Smrg      DUMP_UNPACKED(LAUNCH, cmd, "Launch\n");
4067ec681f3Smrg      return AGX_LAUNCH_LENGTH;
4077ec681f3Smrg   } else if (map[0] == 0x2E && map[1] == 0x00 && map[2] == 0x00 && map[3] == 0x40) {
4087ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd);
4097ec681f3Smrg      agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
4107ec681f3Smrg      DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind vertex pipeline\n");
4117ec681f3Smrg
4127ec681f3Smrg      /* Random unaligned null byte, it's pretty awful.. */
4137ec681f3Smrg      if (map[AGX_BIND_PIPELINE_LENGTH]) {
4147ec681f3Smrg         fprintf(agxdecode_dump_stream, "Unk unaligned %X\n",
4157ec681f3Smrg               map[AGX_BIND_PIPELINE_LENGTH]);
4167ec681f3Smrg      }
4177ec681f3Smrg
4187ec681f3Smrg      return AGX_BIND_PIPELINE_LENGTH + 1;
4197ec681f3Smrg   } else if (map[1] == 0xc0 && map[2] == 0x61) {
4207ec681f3Smrg      DUMP_CL(DRAW, map - 1, "Draw");
4217ec681f3Smrg      return AGX_DRAW_LENGTH;
4227ec681f3Smrg   } else if (map[1] == 0x00 && map[2] == 0x00) {
4237ec681f3Smrg      /* No need to explicitly dump the record */
4247ec681f3Smrg      agx_unpack(agxdecode_dump_stream, map, RECORD, cmd);
4257ec681f3Smrg
4267ec681f3Smrg      /* XXX: Why? */
4277ec681f3Smrg      if (pipeline_base && ((cmd.data >> 32) == 0)) {
4287ec681f3Smrg         cmd.data |= pipeline_base & 0xFF00000000ull;
4297ec681f3Smrg      }
4307ec681f3Smrg
4317ec681f3Smrg      struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing(cmd.data);
4327ec681f3Smrg
4337ec681f3Smrg      if (mem)
4347ec681f3Smrg         agxdecode_record(cmd.data, cmd.size_words * 4, verbose);
4357ec681f3Smrg      else
4367ec681f3Smrg         DUMP_UNPACKED(RECORD, cmd, "Non-existant record (XXX)\n");
4377ec681f3Smrg
4387ec681f3Smrg      return AGX_RECORD_LENGTH;
4397ec681f3Smrg   } else if (map[0] == 0 && map[1] == 0 && map[2] == 0xC0 && map[3] == 0x00) {
4407ec681f3Smrg      ASSERTED unsigned zero[4] = { 0 };
4417ec681f3Smrg      assert(memcmp(map + 4, zero, sizeof(zero)) == 0);
4427ec681f3Smrg      return STATE_DONE;
4437ec681f3Smrg   } else {
4447ec681f3Smrg      return 0;
4457ec681f3Smrg   }
4467ec681f3Smrg}
4477ec681f3Smrg
4487ec681f3Smrgvoid
4497ec681f3Smrgagxdecode_cmdstream(unsigned cmdbuf_handle, unsigned map_handle, bool verbose)
4507ec681f3Smrg{
4517ec681f3Smrg   agxdecode_dump_file_open();
4527ec681f3Smrg
4537ec681f3Smrg   struct agx_bo *cmdbuf = agxdecode_find_handle(cmdbuf_handle, AGX_ALLOC_CMDBUF);
4547ec681f3Smrg   struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP);
4557ec681f3Smrg   assert(cmdbuf != NULL && "nonexistant command buffer");
4567ec681f3Smrg   assert(map != NULL && "nonexistant mapping");
4577ec681f3Smrg
4587ec681f3Smrg   if (verbose) {
4597ec681f3Smrg      agxdecode_dump_bo(cmdbuf, "Command buffer");
4607ec681f3Smrg      agxdecode_dump_bo(map, "Mapping");
4617ec681f3Smrg   }
4627ec681f3Smrg
4637ec681f3Smrg   /* Before decoding anything, validate the map. Set bo->mapped fields */
4647ec681f3Smrg   agxdecode_validate_map(map->ptr.cpu);
4657ec681f3Smrg
4667ec681f3Smrg   /* Print the IOGPU stuff */
4677ec681f3Smrg   agx_unpack(agxdecode_dump_stream, cmdbuf->ptr.cpu, IOGPU_HEADER, cmd);
4687ec681f3Smrg   DUMP_UNPACKED(IOGPU_HEADER, cmd, "IOGPU Header\n");
4697ec681f3Smrg   assert(cmd.attachment_offset_1 == cmd.attachment_offset_2);
4707ec681f3Smrg
4717ec681f3Smrg   uint32_t *attachments = (uint32_t *) ((uint8_t *) cmdbuf->ptr.cpu + cmd.attachment_offset_1);
4727ec681f3Smrg   unsigned attachment_count = attachments[3];
4737ec681f3Smrg   for (unsigned i = 0; i < attachment_count; ++i) {
4747ec681f3Smrg      uint32_t *ptr = attachments + 4 + (i * AGX_IOGPU_ATTACHMENT_LENGTH / 4);
4757ec681f3Smrg      DUMP_CL(IOGPU_ATTACHMENT, ptr, "Attachment");
4767ec681f3Smrg   }
4777ec681f3Smrg
4787ec681f3Smrg   /* TODO: What else is in here? */
4797ec681f3Smrg   uint64_t *encoder = ((uint64_t *) cmdbuf->ptr.cpu) + 7;
4807ec681f3Smrg   agxdecode_stateful(*encoder, "Encoder", agxdecode_cmd, verbose);
4817ec681f3Smrg
4827ec681f3Smrg   uint64_t *clear_pipeline = ((uint64_t *) cmdbuf->ptr.cpu) + 79;
4837ec681f3Smrg   if (*clear_pipeline) {
4847ec681f3Smrg      assert(((*clear_pipeline) & 0xF) == 0x4);
4857ec681f3Smrg      agxdecode_stateful((*clear_pipeline) & ~0xF, "Clear pipeline",
4867ec681f3Smrg            agxdecode_pipeline, verbose);
4877ec681f3Smrg   }
4887ec681f3Smrg
4897ec681f3Smrg   uint64_t *store_pipeline = ((uint64_t *) cmdbuf->ptr.cpu) + 82;
4907ec681f3Smrg   if (*store_pipeline) {
4917ec681f3Smrg      assert(((*store_pipeline) & 0xF) == 0x4);
4927ec681f3Smrg      agxdecode_stateful((*store_pipeline) & ~0xF, "Store pipeline",
4937ec681f3Smrg            agxdecode_pipeline, verbose);
4947ec681f3Smrg   }
4957ec681f3Smrg
4967ec681f3Smrg   agxdecode_map_read_write();
4977ec681f3Smrg}
4987ec681f3Smrg
4997ec681f3Smrgvoid
5007ec681f3Smrgagxdecode_dump_mappings(unsigned map_handle)
5017ec681f3Smrg{
5027ec681f3Smrg   agxdecode_dump_file_open();
5037ec681f3Smrg
5047ec681f3Smrg   struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP);
5057ec681f3Smrg   assert(map != NULL && "nonexistant mapping");
5067ec681f3Smrg   agxdecode_validate_map(map->ptr.cpu);
5077ec681f3Smrg
5087ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i) {
5097ec681f3Smrg      if (!mmap_array[i].ptr.cpu || !mmap_array[i].size || !mmap_array[i].mapped)
5107ec681f3Smrg         continue;
5117ec681f3Smrg
5127ec681f3Smrg      assert(mmap_array[i].type < AGX_NUM_ALLOC);
5137ec681f3Smrg
5147ec681f3Smrg      fprintf(agxdecode_dump_stream, "Buffer: type %s, gpu %" PRIx64 ", handle %u.bin:\n\n",
5157ec681f3Smrg              agx_alloc_types[mmap_array[i].type],
5167ec681f3Smrg              mmap_array[i].ptr.gpu, mmap_array[i].handle);
5177ec681f3Smrg
5187ec681f3Smrg      hexdump(agxdecode_dump_stream, mmap_array[i].ptr.cpu, mmap_array[i].size, false);
5197ec681f3Smrg      fprintf(agxdecode_dump_stream, "\n");
5207ec681f3Smrg   }
5217ec681f3Smrg}
5227ec681f3Smrg
5237ec681f3Smrgvoid
5247ec681f3Smrgagxdecode_track_alloc(struct agx_bo *alloc)
5257ec681f3Smrg{
5267ec681f3Smrg   assert((mmap_count + 1) < MAX_MAPPINGS);
5277ec681f3Smrg
5287ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i) {
5297ec681f3Smrg      struct agx_bo *bo = &mmap_array[i];
5307ec681f3Smrg      bool match = (bo->handle == alloc->handle && bo->type == alloc->type);
5317ec681f3Smrg      assert(!match && "tried to alloc already allocated BO");
5327ec681f3Smrg   }
5337ec681f3Smrg
5347ec681f3Smrg   mmap_array[mmap_count++] = *alloc;
5357ec681f3Smrg}
5367ec681f3Smrg
5377ec681f3Smrgvoid
5387ec681f3Smrgagxdecode_track_free(struct agx_bo *bo)
5397ec681f3Smrg{
5407ec681f3Smrg   bool found = false;
5417ec681f3Smrg
5427ec681f3Smrg   for (unsigned i = 0; i < mmap_count; ++i) {
5437ec681f3Smrg      if (mmap_array[i].handle == bo->handle && mmap_array[i].type == bo->type) {
5447ec681f3Smrg         assert(!found && "mapped multiple times!");
5457ec681f3Smrg         found = true;
5467ec681f3Smrg
5477ec681f3Smrg         memset(&mmap_array[i], 0, sizeof(mmap_array[i]));
5487ec681f3Smrg      }
5497ec681f3Smrg   }
5507ec681f3Smrg
5517ec681f3Smrg   assert(found && "freed unmapped memory");
5527ec681f3Smrg}
5537ec681f3Smrg
5547ec681f3Smrgstatic int agxdecode_dump_frame_count = 0;
5557ec681f3Smrg
5567ec681f3Smrgvoid
5577ec681f3Smrgagxdecode_dump_file_open(void)
5587ec681f3Smrg{
5597ec681f3Smrg   if (agxdecode_dump_stream)
5607ec681f3Smrg      return;
5617ec681f3Smrg
5627ec681f3Smrg   /* This does a getenv every frame, so it is possible to use
5637ec681f3Smrg    * setenv to change the base at runtime.
5647ec681f3Smrg    */
5657ec681f3Smrg   const char *dump_file_base = getenv("PANDECODE_DUMP_FILE") ?: "agxdecode.dump";
5667ec681f3Smrg   if (!strcmp(dump_file_base, "stderr"))
5677ec681f3Smrg      agxdecode_dump_stream = stderr;
5687ec681f3Smrg   else {
5697ec681f3Smrg      char buffer[1024];
5707ec681f3Smrg      snprintf(buffer, sizeof(buffer), "%s.%04d", dump_file_base, agxdecode_dump_frame_count);
5717ec681f3Smrg      printf("agxdecode: dump command stream to file %s\n", buffer);
5727ec681f3Smrg      agxdecode_dump_stream = fopen(buffer, "w");
5737ec681f3Smrg      if (!agxdecode_dump_stream)
5747ec681f3Smrg         fprintf(stderr,
5757ec681f3Smrg                 "agxdecode: failed to open command stream log file %s\n",
5767ec681f3Smrg                 buffer);
5777ec681f3Smrg   }
5787ec681f3Smrg}
5797ec681f3Smrg
5807ec681f3Smrgstatic void
5817ec681f3Smrgagxdecode_dump_file_close(void)
5827ec681f3Smrg{
5837ec681f3Smrg   if (agxdecode_dump_stream && agxdecode_dump_stream != stderr) {
5847ec681f3Smrg      fclose(agxdecode_dump_stream);
5857ec681f3Smrg      agxdecode_dump_stream = NULL;
5867ec681f3Smrg   }
5877ec681f3Smrg}
5887ec681f3Smrg
5897ec681f3Smrgvoid
5907ec681f3Smrgagxdecode_next_frame(void)
5917ec681f3Smrg{
5927ec681f3Smrg   agxdecode_dump_file_close();
5937ec681f3Smrg   agxdecode_dump_frame_count++;
5947ec681f3Smrg}
5957ec681f3Smrg
5967ec681f3Smrgvoid
5977ec681f3Smrgagxdecode_close(void)
5987ec681f3Smrg{
5997ec681f3Smrg   agxdecode_dump_file_close();
6007ec681f3Smrg}
601