1 /*
2  * Copyright (C) 2017-2019 Alyssa Rosenzweig
3  * Copyright (C) 2017-2019 Connor Abbott
4  * Copyright (C) 2019 Collabora, Ltd.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the "Software"),
8  * to deal in the Software without restriction, including without limitation
9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10  * and/or sell copies of the Software, and to permit persons to whom the
11  * Software is furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the next
14  * paragraph) shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25 
26 #include <agx_pack.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <memory.h>
30 #include <stdbool.h>
31 #include <stdarg.h>
32 #include <ctype.h>
33 #include <sys/mman.h>
34 
35 #include "decode.h"
36 #include "io.h"
37 #include "hexdump.h"
38 
39 static const char *agx_alloc_types[AGX_NUM_ALLOC] = { "mem", "map", "cmd" };
40 
41 static void
agx_disassemble(void *_code, size_t maxlen, FILE *fp)42 agx_disassemble(void *_code, size_t maxlen, FILE *fp)
43 {
44    /* stub */
45 }
46 
47 FILE *agxdecode_dump_stream;
48 
49 #define MAX_MAPPINGS 4096
50 
51 struct agx_bo mmap_array[MAX_MAPPINGS];
52 unsigned mmap_count = 0;
53 
54 struct agx_bo *ro_mappings[MAX_MAPPINGS];
55 unsigned ro_mapping_count = 0;
56 
57 static struct agx_bo *
agxdecode_find_mapped_gpu_mem_containing_rw(uint64_t addr)58 agxdecode_find_mapped_gpu_mem_containing_rw(uint64_t addr)
59 {
60    for (unsigned i = 0; i < mmap_count; ++i) {
61       if (mmap_array[i].type == AGX_ALLOC_REGULAR && addr >= mmap_array[i].ptr.gpu && (addr - mmap_array[i].ptr.gpu) < mmap_array[i].size)
62          return mmap_array + i;
63    }
64 
65    return NULL;
66 }
67 
68 static struct agx_bo *
agxdecode_find_mapped_gpu_mem_containing(uint64_t addr)69 agxdecode_find_mapped_gpu_mem_containing(uint64_t addr)
70 {
71    struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing_rw(addr);
72 
73    if (mem && mem->ptr.cpu && !mem->ro) {
74       mprotect(mem->ptr.cpu, mem->size, PROT_READ);
75       mem->ro = true;
76       ro_mappings[ro_mapping_count++] = mem;
77       assert(ro_mapping_count < MAX_MAPPINGS);
78    }
79 
80    if (mem && !mem->mapped) {
81       fprintf(stderr, "[ERROR] access to memory not mapped (GPU %" PRIx64 ", handle %u)\n", mem->ptr.gpu, mem->handle);
82    }
83 
84    return mem;
85 }
86 
87 static struct agx_bo *
agxdecode_find_handle(unsigned handle, unsigned type)88 agxdecode_find_handle(unsigned handle, unsigned type)
89 {
90    for (unsigned i = 0; i < mmap_count; ++i) {
91       if (mmap_array[i].type != type)
92          continue;
93 
94       if (mmap_array[i].handle != handle)
95          continue;
96 
97       return &mmap_array[i];
98    }
99 
100    return NULL;
101 }
102 
103 static void
agxdecode_mark_mapped(unsigned handle)104 agxdecode_mark_mapped(unsigned handle)
105 {
106    struct agx_bo *bo = agxdecode_find_handle(handle, AGX_ALLOC_REGULAR);
107 
108    if (!bo) {
109       fprintf(stderr, "ERROR - unknown BO mapped with handle %u\n", handle);
110       return;
111    }
112 
113    /* Mark mapped for future consumption */
114    bo->mapped = true;
115 }
116 
117 static void
agxdecode_decode_segment_list(void *segment_list)118 agxdecode_decode_segment_list(void *segment_list)
119 {
120    unsigned nr_handles = 0;
121 
122    /* First, mark everything unmapped */
123    for (unsigned i = 0; i < mmap_count; ++i)
124       mmap_array[i].mapped = false;
125 
126    /* Check the header */
127    struct agx_map_header *hdr = segment_list;
128    if (hdr->resource_group_count == 0) {
129       fprintf(agxdecode_dump_stream, "ERROR - empty map\n");
130       return;
131    }
132 
133    if (hdr->segment_count != 1) {
134       fprintf(agxdecode_dump_stream, "ERROR - can't handle segment count %u\n",
135             hdr->segment_count);
136    }
137 
138    fprintf(agxdecode_dump_stream, "Segment list:\n");
139    fprintf(agxdecode_dump_stream, "  Command buffer shmem ID: %" PRIx64 "\n", hdr->cmdbuf_id);
140    fprintf(agxdecode_dump_stream, "  Encoder ID: %" PRIx64 "\n", hdr->encoder_id);
141    fprintf(agxdecode_dump_stream, "  Kernel commands start offset: %u\n",
142          hdr->kernel_commands_start_offset);
143    fprintf(agxdecode_dump_stream, "  Kernel commands end offset: %u\n",
144          hdr->kernel_commands_end_offset);
145    fprintf(agxdecode_dump_stream, "  Unknown: 0x%X\n", hdr->unk);
146 
147    /* Expected structure: header followed by resource groups */
148    size_t length = sizeof(struct agx_map_header);
149    length += sizeof(struct agx_map_entry) * hdr->resource_group_count;
150 
151    if (length != hdr->length) {
152       fprintf(agxdecode_dump_stream, "ERROR: expected length %zu, got %u\n",
153               length, hdr->length);
154    }
155 
156    if (hdr->padding[0] || hdr->padding[1])
157       fprintf(agxdecode_dump_stream, "ERROR - padding tripped\n");
158 
159    /* Check the entries */
160    struct agx_map_entry *groups = ((void *) hdr) + sizeof(*hdr);
161    for (unsigned i = 0; i < hdr->resource_group_count; ++i) {
162       struct agx_map_entry group = groups[i];
163       unsigned count = group.resource_count;
164 
165       STATIC_ASSERT(ARRAY_SIZE(group.resource_id) == 6);
166       STATIC_ASSERT(ARRAY_SIZE(group.resource_unk) == 6);
167       STATIC_ASSERT(ARRAY_SIZE(group.resource_flags) == 6);
168 
169       if ((count < 1) || (count > 6)) {
170          fprintf(agxdecode_dump_stream, "ERROR - invalid count %u\n", count);
171          continue;
172       }
173 
174       for (unsigned j = 0; j < count; ++j) {
175          unsigned handle = group.resource_id[j];
176          unsigned unk = group.resource_unk[j];
177          unsigned flags = group.resource_flags[j];
178 
179          if (!handle) {
180             fprintf(agxdecode_dump_stream, "ERROR - invalid handle %u\n", handle);
181             continue;
182          }
183 
184          agxdecode_mark_mapped(handle);
185          nr_handles++;
186 
187          fprintf(agxdecode_dump_stream, "%u (0x%X, 0x%X)\n", handle, unk, flags);
188       }
189 
190       if (group.unka)
191          fprintf(agxdecode_dump_stream, "ERROR - unknown 0x%X\n", group.unka);
192 
193       /* Visual separator for resource groups */
194       fprintf(agxdecode_dump_stream, "\n");
195    }
196 
197    /* Check the handle count */
198    if (nr_handles != hdr->total_resources) {
199       fprintf(agxdecode_dump_stream, "ERROR - wrong handle count, got %u, expected %u (%u entries)\n",
200             nr_handles, hdr->total_resources, hdr->resource_group_count);
201    }
202 }
203 
204 static inline void *
__agxdecode_fetch_gpu_mem(const struct agx_bo *mem, uint64_t gpu_va, size_t size, int line, const char *filename)205 __agxdecode_fetch_gpu_mem(const struct agx_bo *mem,
206                           uint64_t gpu_va, size_t size,
207                           int line, const char *filename)
208 {
209    if (!mem)
210       mem = agxdecode_find_mapped_gpu_mem_containing(gpu_va);
211 
212    if (!mem) {
213       fprintf(stderr, "Access to unknown memory %" PRIx64 " in %s:%d\n",
214               gpu_va, filename, line);
215       fflush(agxdecode_dump_stream);
216       assert(0);
217    }
218 
219    assert(mem);
220    assert(size + (gpu_va - mem->ptr.gpu) <= mem->size);
221 
222    return mem->ptr.cpu + gpu_va - mem->ptr.gpu;
223 }
224 
225 #define agxdecode_fetch_gpu_mem(gpu_va, size) \
226 	__agxdecode_fetch_gpu_mem(NULL, gpu_va, size, __LINE__, __FILE__)
227 
228 static void
agxdecode_map_read_write(void)229 agxdecode_map_read_write(void)
230 {
231    for (unsigned i = 0; i < ro_mapping_count; ++i) {
232       ro_mappings[i]->ro = false;
233       mprotect(ro_mappings[i]->ptr.cpu, ro_mappings[i]->size,
234                PROT_READ | PROT_WRITE);
235    }
236 
237    ro_mapping_count = 0;
238 }
239 
240 /* Helpers for parsing the cmdstream */
241 
242 #define DUMP_UNPACKED(T, var, str) { \
243         agxdecode_log(str); \
244         agx_print(agxdecode_dump_stream, T, var, (agxdecode_indent + 1) * 2); \
245 }
246 
247 #define DUMP_CL(T, cl, str) {\
248         agx_unpack(agxdecode_dump_stream, cl, T, temp); \
249         DUMP_UNPACKED(T, temp, str "\n"); \
250 }
251 
252 #define agxdecode_log(str) fputs(str, agxdecode_dump_stream)
253 #define agxdecode_msg(str) fprintf(agxdecode_dump_stream, "// %s", str)
254 
255 unsigned agxdecode_indent = 0;
256 uint64_t pipeline_base = 0;
257 
258 static void
agxdecode_dump_bo(struct agx_bo *bo, const char *name)259 agxdecode_dump_bo(struct agx_bo *bo, const char *name)
260 {
261    fprintf(agxdecode_dump_stream, "%s %s (%u)\n", name, bo->name ?: "", bo->handle);
262    hexdump(agxdecode_dump_stream, bo->ptr.cpu, bo->size, false);
263 }
264 
265 /* Abstraction for command stream parsing */
266 typedef unsigned (*decode_cmd)(const uint8_t *map, bool verbose);
267 
268 #define STATE_DONE (0xFFFFFFFFu)
269 
270 static void
agxdecode_stateful(uint64_t va, const char *label, decode_cmd decoder, bool verbose)271 agxdecode_stateful(uint64_t va, const char *label, decode_cmd decoder, bool verbose)
272 {
273    struct agx_bo *alloc = agxdecode_find_mapped_gpu_mem_containing(va);
274    assert(alloc != NULL && "nonexistant object");
275    fprintf(agxdecode_dump_stream, "%s (%" PRIx64 ", handle %u)\n", label, va, alloc->handle);
276    fflush(agxdecode_dump_stream);
277 
278    uint8_t *map = agxdecode_fetch_gpu_mem(va, 64);
279    uint8_t *end = (uint8_t *) alloc->ptr.cpu + alloc->size;
280 
281    if (verbose)
282       agxdecode_dump_bo(alloc, label);
283    fflush(agxdecode_dump_stream);
284 
285    while (map < end) {
286       unsigned count = decoder(map, verbose);
287 
288       /* If we fail to decode, default to a hexdump (don't hang) */
289       if (count == 0) {
290          hexdump(agxdecode_dump_stream, map, 8, false);
291          count = 8;
292       }
293 
294       map += count;
295       fflush(agxdecode_dump_stream);
296 
297       if (count == STATE_DONE)
298          break;
299    }
300 }
301 
302 unsigned COUNTER = 0;
303 static unsigned
agxdecode_pipeline(const uint8_t *map, UNUSED bool verbose)304 agxdecode_pipeline(const uint8_t *map, UNUSED bool verbose)
305 {
306    uint8_t zeroes[16] = { 0 };
307 
308    if (map[0] == 0x4D && map[1] == 0xbd) {
309       /* TODO: Disambiguation for extended is a guess */
310       agx_unpack(agxdecode_dump_stream, map, SET_SHADER_EXTENDED, cmd);
311       DUMP_UNPACKED(SET_SHADER_EXTENDED, cmd, "Set shader\n");
312 
313       if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) {
314          agxdecode_log("Preshader\n");
315          agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048),
316                          2048, agxdecode_dump_stream);
317          agxdecode_log("\n---\n");
318       }
319 
320       agxdecode_log("\n");
321       agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048),
322                       2048, agxdecode_dump_stream);
323       agxdecode_log("\n");
324 
325       char *name;
326       asprintf(&name, "file%u.bin", COUNTER++);
327       FILE *fp = fopen(name, "wb");
328       fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp);
329       fclose(fp);
330       free(name);
331       agxdecode_log("\n");
332 
333       return AGX_SET_SHADER_EXTENDED_LENGTH;
334    } else if (map[0] == 0x4D) {
335       agx_unpack(agxdecode_dump_stream, map, SET_SHADER, cmd);
336       DUMP_UNPACKED(SET_SHADER, cmd, "Set shader\n");
337       fflush(agxdecode_dump_stream);
338 
339       if (cmd.preshader_mode == AGX_PRESHADER_MODE_PRESHADER) {
340          agxdecode_log("Preshader\n");
341          agx_disassemble(agxdecode_fetch_gpu_mem(cmd.preshader_code, 2048),
342                          2048, agxdecode_dump_stream);
343          agxdecode_log("\n---\n");
344       }
345 
346       agxdecode_log("\n");
347       agx_disassemble(agxdecode_fetch_gpu_mem(cmd.code, 2048),
348                       2048, agxdecode_dump_stream);
349       char *name;
350       asprintf(&name, "file%u.bin", COUNTER++);
351       FILE *fp = fopen(name, "wb");
352       fwrite(agxdecode_fetch_gpu_mem(cmd.code, 2048), 1, 2048, fp);
353       fclose(fp);
354       free(name);
355       agxdecode_log("\n");
356 
357       return AGX_SET_SHADER_LENGTH;
358    } else if (map[0] == 0xDD) {
359       agx_unpack(agxdecode_dump_stream, map, BIND_TEXTURE, temp);
360       DUMP_UNPACKED(BIND_TEXTURE, temp, "Bind texture\n");
361 
362       uint8_t *tex = agxdecode_fetch_gpu_mem(temp.buffer, 64);
363       /* Texture length seen to be <= 0x18 bytes, samplers only need 8 byte
364        * alignment */
365       agx_unpack(agxdecode_dump_stream, tex, TEXTURE, t);
366       DUMP_CL(TEXTURE, tex, "Texture");
367       DUMP_CL(RENDER_TARGET, tex, "Render target");
368 
369       return AGX_BIND_TEXTURE_LENGTH;
370    } else if (map[0] == 0x9D) {
371       agx_unpack(agxdecode_dump_stream, map, BIND_SAMPLER, temp);
372       DUMP_UNPACKED(BIND_SAMPLER, temp, "Bind sampler\n");
373 
374       uint8_t *samp = agxdecode_fetch_gpu_mem(temp.buffer, 64);
375       DUMP_CL(SAMPLER, samp, "Sampler");
376       hexdump(agxdecode_dump_stream, samp + AGX_SAMPLER_LENGTH, 64 - AGX_SAMPLER_LENGTH, false);
377 
378       return AGX_BIND_SAMPLER_LENGTH;
379    } else if (map[0] == 0x1D) {
380       DUMP_CL(BIND_UNIFORM, map, "Bind uniform");
381       return AGX_BIND_UNIFORM_LENGTH;
382    } else if (memcmp(map, zeroes, 16) == 0) {
383       /* TODO: Termination */
384       return STATE_DONE;
385    } else {
386       return 0;
387    }
388 }
389 
390 static void
agxdecode_record(uint64_t va, size_t size, bool verbose)391 agxdecode_record(uint64_t va, size_t size, bool verbose)
392 {
393    uint8_t *map = agxdecode_fetch_gpu_mem(va, size);
394    uint32_t tag = 0;
395    memcpy(&tag, map, 4);
396 
397    if (tag == 0x00000C00) {
398       assert(size == AGX_VIEWPORT_LENGTH);
399       DUMP_CL(VIEWPORT, map, "Viewport");
400    } else if (tag == 0x0C020000) {
401       assert(size == AGX_LINKAGE_LENGTH);
402       DUMP_CL(LINKAGE, map, "Linkage");
403    } else if (tag == 0x200004a) {
404       assert(size == AGX_UNKNOWN_4A_LENGTH);
405       DUMP_CL(UNKNOWN_4A, map, "Unknown 4a");
406    } else if (tag == 0x10000b5) {
407       assert(size == AGX_RASTERIZER_LENGTH);
408       DUMP_CL(RASTERIZER, map, "Rasterizer");
409    } else if (tag == 0x200000) {
410       assert(size == AGX_CULL_LENGTH);
411       DUMP_CL(CULL, map, "Cull");
412    } else if (tag == 0x000100) {
413       assert(size == AGX_SET_INDEX_LENGTH);
414       DUMP_CL(SET_INDEX, map, "Set index");
415    } else if (tag == 0x800000) {
416       assert(size == (AGX_BIND_PIPELINE_LENGTH - 4));
417 
418       agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd);
419       agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
420 
421       /* TODO: parse */
422       if (cmd.fs_varyings) {
423          uint8_t *map = agxdecode_fetch_gpu_mem(cmd.fs_varyings, 128);
424          hexdump(agxdecode_dump_stream, map, 128, false);
425 
426          DUMP_CL(VARYING_HEADER, map, "Varying header:");
427          map += AGX_VARYING_HEADER_LENGTH;
428 
429          for (unsigned i = 0; i < cmd.input_count; ++i) {
430             DUMP_CL(VARYING, map, "Varying:");
431             map += AGX_VARYING_LENGTH;
432          }
433       }
434 
435       DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind fragment pipeline\n");
436    } else if (size == 0) {
437       pipeline_base = va;
438    } else {
439       fprintf(agxdecode_dump_stream, "Record %" PRIx64 "\n", va);
440       hexdump(agxdecode_dump_stream, map, size, false);
441    }
442 }
443 
444 static unsigned
agxdecode_cmd(const uint8_t *map, bool verbose)445 agxdecode_cmd(const uint8_t *map, bool verbose)
446 {
447    if (map[0] == 0x02 && map[1] == 0x10 && map[2] == 0x00 && map[3] == 0x00) {
448       agx_unpack(agxdecode_dump_stream, map, LAUNCH, cmd);
449       agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
450       DUMP_UNPACKED(LAUNCH, cmd, "Launch\n");
451       return AGX_LAUNCH_LENGTH;
452    } else if (map[0] == 0x2E && map[1] == 0x00 && map[2] == 0x00 && map[3] == 0x40) {
453       agx_unpack(agxdecode_dump_stream, map, BIND_PIPELINE, cmd);
454       agxdecode_stateful(cmd.pipeline, "Pipeline", agxdecode_pipeline, verbose);
455       DUMP_UNPACKED(BIND_PIPELINE, cmd, "Bind vertex pipeline\n");
456       return AGX_BIND_PIPELINE_LENGTH;
457    } else if (map[3] == 0x61) {
458       DUMP_CL(DRAW, map, "Draw");
459       return AGX_DRAW_LENGTH;
460    } else if (map[2] == 0x00 && map[3] == 0x00) {
461       /* No need to explicitly dump the record */
462       agx_unpack(agxdecode_dump_stream, map, RECORD, cmd);
463 
464       uint64_t address = (((uint64_t) cmd.pointer_hi) << 32) | cmd.pointer_lo;
465       struct agx_bo *mem = agxdecode_find_mapped_gpu_mem_containing(address);
466 
467       if (mem)
468          agxdecode_record(address, cmd.size_words * 4, verbose);
469       else
470          DUMP_UNPACKED(RECORD, cmd, "Non-existant record (XXX)\n");
471 
472       return AGX_RECORD_LENGTH;
473    } else if (map[1] == 0 && map[2] == 0 && map[3] == 0xC0 && map[4] == 0x00) {
474       ASSERTED unsigned zero[4] = { 0 };
475       assert(memcmp(map + 4, zero, sizeof(zero)) == 0);
476       return STATE_DONE;
477    } else {
478       return 0;
479    }
480 }
481 
482 void
agxdecode_cmdstream(unsigned cmdbuf_handle, unsigned map_handle, bool verbose)483 agxdecode_cmdstream(unsigned cmdbuf_handle, unsigned map_handle, bool verbose)
484 {
485    agxdecode_dump_file_open();
486 
487    struct agx_bo *cmdbuf = agxdecode_find_handle(cmdbuf_handle, AGX_ALLOC_CMDBUF);
488    struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP);
489    assert(cmdbuf != NULL && "nonexistant command buffer");
490    assert(map != NULL && "nonexistant mapping");
491 
492    /* Before decoding anything, validate the map. Set bo->mapped fields */
493    agxdecode_decode_segment_list(map->ptr.cpu);
494 
495    /* Print the IOGPU stuff */
496    agx_unpack(agxdecode_dump_stream, cmdbuf->ptr.cpu, IOGPU_HEADER, cmd);
497    DUMP_UNPACKED(IOGPU_HEADER, cmd, "IOGPU Header\n");
498    agx_unpack(agxdecode_dump_stream, ((uint32_t *) cmdbuf->ptr.cpu) + 160,
499               IOGPU_INTERNAL_PIPELINES, pip);
500 
501    DUMP_CL(IOGPU_INTERNAL_PIPELINES, ((uint32_t *) cmdbuf->ptr.cpu) + 160, "Internal pipelines");
502    DUMP_CL(IOGPU_AUX_FRAMEBUFFER, ((uint32_t *) cmdbuf->ptr.cpu) + 228, "Aux Framebuffer");
503 
504    agx_unpack(agxdecode_dump_stream, ((uint32_t *) cmdbuf->ptr.cpu) + 292,
505          IOGPU_CLEAR_Z_S, clearzs);
506    DUMP_UNPACKED(IOGPU_CLEAR_Z_S, clearzs, "Clear Z/S");
507 
508    /* Guard against changes */
509    uint32_t zeroes[356 - 344] = { 0 };
510    assert(memcmp(((uint32_t *) cmdbuf->ptr.cpu) + 344, zeroes, 4 * (356 - 344)) == 0);
511 
512    DUMP_CL(IOGPU_MISC, ((uint32_t *) cmdbuf->ptr.cpu) + 356, "Misc");
513 
514    /* Should be unused, we think */
515    for (unsigned i = (0x6B0 / 4); i < (cmd.attachment_offset / 4); ++i) {
516       assert(((uint32_t *) cmdbuf->ptr.cpu)[i] == 0);
517    }
518 
519    DUMP_CL(IOGPU_ATTACHMENT_COUNT, ((uint8_t *) cmdbuf->ptr.cpu +
520             cmd.attachment_offset), "Attachment count");
521 
522    uint32_t *attachments = (uint32_t *) ((uint8_t *) cmdbuf->ptr.cpu + cmd.attachment_offset);
523    unsigned attachment_count = attachments[3];
524    for (unsigned i = 0; i < attachment_count; ++i) {
525       uint32_t *ptr = attachments + 4 + (i * AGX_IOGPU_ATTACHMENT_LENGTH / 4);
526       DUMP_CL(IOGPU_ATTACHMENT, ptr, "Attachment");
527    }
528 
529    uint64_t *encoder = ((uint64_t *) cmdbuf->ptr.cpu) + 7;
530    agxdecode_stateful(*encoder, "Encoder", agxdecode_cmd, verbose);
531 
532    if (pip.clear_pipeline_unk) {
533       assert(pip.clear_pipeline_unk == 0x4);
534       agxdecode_stateful(pip.clear_pipeline, "Clear pipeline",
535             agxdecode_pipeline, verbose);
536    }
537 
538    if (pip.store_pipeline_unk) {
539       assert(pip.store_pipeline_unk == 0x4);
540       agxdecode_stateful(pip.store_pipeline, "Store pipeline",
541             agxdecode_pipeline, verbose);
542    }
543 
544    assert((clearzs.partial_reload_pipeline_unk & 0xF) == 0x4);
545    if (clearzs.partial_reload_pipeline) {
546       agxdecode_stateful(clearzs.partial_reload_pipeline,
547             "Partial reload pipeline", agxdecode_pipeline, verbose);
548    }
549 
550    if (clearzs.partial_store_pipeline) {
551       agxdecode_stateful(clearzs.partial_store_pipeline,
552             "Partial store pipeline", agxdecode_pipeline, verbose);
553    }
554 
555    agxdecode_map_read_write();
556 }
557 
558 void
agxdecode_dump_mappings(unsigned map_handle)559 agxdecode_dump_mappings(unsigned map_handle)
560 {
561    agxdecode_dump_file_open();
562 
563    struct agx_bo *map = agxdecode_find_handle(map_handle, AGX_ALLOC_MEMMAP);
564    assert(map != NULL && "nonexistant mapping");
565    agxdecode_decode_segment_list(map->ptr.cpu);
566 
567    for (unsigned i = 0; i < mmap_count; ++i) {
568       if (!mmap_array[i].ptr.cpu || !mmap_array[i].size || !mmap_array[i].mapped)
569          continue;
570 
571       assert(mmap_array[i].type < AGX_NUM_ALLOC);
572 
573       fprintf(agxdecode_dump_stream, "Buffer: type %s, gpu %" PRIx64 ", handle %u.bin:\n\n",
574               agx_alloc_types[mmap_array[i].type],
575               mmap_array[i].ptr.gpu, mmap_array[i].handle);
576 
577       hexdump(agxdecode_dump_stream, mmap_array[i].ptr.cpu, mmap_array[i].size, false);
578       fprintf(agxdecode_dump_stream, "\n");
579    }
580 }
581 
582 void
agxdecode_track_alloc(struct agx_bo *alloc)583 agxdecode_track_alloc(struct agx_bo *alloc)
584 {
585    assert((mmap_count + 1) < MAX_MAPPINGS);
586 
587    for (unsigned i = 0; i < mmap_count; ++i) {
588       struct agx_bo *bo = &mmap_array[i];
589       bool match = (bo->handle == alloc->handle && bo->type == alloc->type);
590       assert(!match && "tried to alloc already allocated BO");
591    }
592 
593    mmap_array[mmap_count++] = *alloc;
594 }
595 
596 void
agxdecode_track_free(struct agx_bo *bo)597 agxdecode_track_free(struct agx_bo *bo)
598 {
599    bool found = false;
600 
601    for (unsigned i = 0; i < mmap_count; ++i) {
602       if (mmap_array[i].handle == bo->handle && mmap_array[i].type == bo->type) {
603          assert(!found && "mapped multiple times!");
604          found = true;
605 
606          memset(&mmap_array[i], 0, sizeof(mmap_array[i]));
607       }
608    }
609 
610    assert(found && "freed unmapped memory");
611 }
612 
613 static int agxdecode_dump_frame_count = 0;
614 
615 void
agxdecode_dump_file_open(void)616 agxdecode_dump_file_open(void)
617 {
618    if (agxdecode_dump_stream)
619       return;
620 
621    /* This does a getenv every frame, so it is possible to use
622     * setenv to change the base at runtime.
623     */
624    const char *dump_file_base = getenv("AGXDECODE_DUMP_FILE") ?: "agxdecode.dump";
625    if (!strcmp(dump_file_base, "stderr"))
626       agxdecode_dump_stream = stderr;
627    else {
628       char buffer[1024];
629       snprintf(buffer, sizeof(buffer), "%s.%04d", dump_file_base, agxdecode_dump_frame_count);
630       printf("agxdecode: dump command stream to file %s\n", buffer);
631       agxdecode_dump_stream = fopen(buffer, "w");
632       if (!agxdecode_dump_stream)
633          fprintf(stderr,
634                  "agxdecode: failed to open command stream log file %s\n",
635                  buffer);
636    }
637 }
638 
639 static void
agxdecode_dump_file_close(void)640 agxdecode_dump_file_close(void)
641 {
642    if (agxdecode_dump_stream && agxdecode_dump_stream != stderr) {
643       fclose(agxdecode_dump_stream);
644       agxdecode_dump_stream = NULL;
645    }
646 }
647 
648 void
agxdecode_next_frame(void)649 agxdecode_next_frame(void)
650 {
651    agxdecode_dump_file_close();
652    agxdecode_dump_frame_count++;
653 }
654 
655 void
agxdecode_close(void)656 agxdecode_close(void)
657 {
658    agxdecode_dump_file_close();
659 }
660