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