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