xref: /aosp_15_r20/external/mesa3d/src/panfrost/tools/panfrostdump.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright (C) 2021 Collabora, Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  */
24 
25 /*
26  * Debug dump analyser for panfrost.  In case of a gpu crash/hang,
27  * the coredump should be found in:
28  *
29  *    /sys/class/devcoredump/devcd<n>/data
30  *
31  * The crashdump will hang around for 5min, it can be cleared by writing to
32  * the file, ie:
33  *
34  *    echo 1 > /sys/class/devcoredump/devcd<n>/data
35  *
36  * (the driver won't log any new crashdumps until the previous one is cleared
37  * or times out after 5min)
38  */
39 
40 #include <endian.h>
41 #include <errno.h>
42 #include <getopt.h>
43 #include <inttypes.h>
44 #include <stdbool.h>
45 #include <stdint.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 
51 #include <drm-uapi/panfrost_drm.h>
52 
53 #include "decode.h"
54 
55 /* Same as panfrost_dump_object_header, but with field
56  * entries in host byte order
57  */
58 struct panfrost_dump_object_header_ho {
59    uint32_t magic;
60    uint32_t type;
61    uint32_t file_size;
62    uint32_t file_offset;
63 
64    union {
65       struct pan_reg_hdr_ho {
66          uint64_t jc;
67          uint32_t gpu_id;
68          uint32_t major;
69          uint32_t minor;
70          uint64_t nbos;
71       } reghdr;
72 
73       struct pan_bomap_hdr_ho {
74          uint32_t valid;
75          uint64_t iova;
76          uint32_t data[2];
77       } bomap;
78 
79       uint32_t sizer[496];
80    };
81 };
82 
83 #define MAX_BODUMP_FILENAME 32
84 #define GPU_PAGE_SIZE       4096
85 
86 static bool
read_header(FILE * fp,struct panfrost_dump_object_header_ho * pdoh)87 read_header(FILE *fp, struct panfrost_dump_object_header_ho *pdoh)
88 {
89    /* Fields in the coredump file header structures
90     * are found in little-endian order
91     */
92    struct panfrost_dump_object_header doh_le;
93    size_t nr;
94 
95    nr = fread(&doh_le, 1, sizeof(struct panfrost_dump_object_header), fp);
96    if (nr < sizeof(struct panfrost_dump_object_header)) {
97       fprintf(stderr, "Wrong header read\n");
98       return false;
99    }
100 
101    /* Convert from little-endian to host byte order */
102    pdoh->magic = le32toh(doh_le.magic);
103    if (pdoh->magic != PANFROSTDUMP_MAGIC) {
104       fprintf(stderr, "Wrong header magic\n");
105       return false;
106    }
107 
108    pdoh->type = le32toh(doh_le.type);
109    pdoh->file_offset = le32toh(doh_le.file_offset);
110    pdoh->file_size = le32toh(doh_le.file_size);
111 
112    switch (pdoh->type) {
113    case PANFROSTDUMP_BUF_REG:
114       pdoh->reghdr.jc = le64toh(doh_le.reghdr.jc);
115       pdoh->reghdr.gpu_id = le32toh(doh_le.reghdr.gpu_id);
116       pdoh->reghdr.major = le32toh(doh_le.reghdr.major);
117       pdoh->reghdr.minor = le32toh(doh_le.reghdr.minor);
118       pdoh->reghdr.nbos = le64toh(doh_le.reghdr.nbos);
119       break;
120    case PANFROSTDUMP_BUF_BO:
121       pdoh->bomap.iova = le64toh(doh_le.bomap.iova);
122       pdoh->bomap.valid = le32toh(doh_le.bomap.valid);
123       pdoh->bomap.data[0] = le32toh(doh_le.bomap.data[0]);
124       pdoh->bomap.data[1] = le32toh(doh_le.bomap.data[1]);
125    }
126 
127    return true;
128 }
129 
130 static bool
read_register(uint32_t * ro,uint32_t * rv,FILE * fp)131 read_register(uint32_t *ro, uint32_t *rv, FILE *fp)
132 {
133    /* Register pair we read form memory is
134     * laid out in little-endian order
135     */
136    struct panfrost_dump_registers reg_le;
137    size_t nr;
138 
139    nr = fread(&reg_le, 1, sizeof(reg_le), fp);
140    if (nr < sizeof(reg_le)) {
141       fprintf(stderr, "Wrong register read\n");
142       return false;
143    }
144 
145    *ro = le32toh(reg_le.reg);
146    *rv = le32toh(reg_le.value);
147 
148    return true;
149 }
150 
151 static bool
read_page_addr(uint64_t * phys_page,FILE * fp)152 read_page_addr(uint64_t *phys_page, FILE *fp)
153 {
154    uint64_t phys_addr_le;
155    size_t nr;
156 
157    nr = fread(&phys_addr_le, 1, sizeof(uint64_t), fp);
158    if (nr < sizeof(uint64_t)) {
159       fprintf(stderr, "Wrong page address read\n");
160 
161       /* Skip over to the next address */
162       if (fseek(fp, sizeof(uint64_t) - nr, SEEK_CUR)) {
163          perror("fseek error");
164          return false;
165       }
166 
167       return false;
168    }
169 
170    *phys_page = le64toh(phys_addr_le);
171 
172    return true;
173 }
174 
175 /* Keeping these definitions as global/static shouldn't be
176  * an issue because this tool will always be single-threaded
177  */
178 static FILE *hdr_fp;
179 static FILE *data_fp;
180 static char **bos;
181 static uint32_t bo_num;
182 
183 static void
cleanup(void)184 cleanup(void)
185 {
186    if (hdr_fp != NULL)
187       fclose(hdr_fp);
188    if (data_fp != NULL)
189       fclose(data_fp);
190    if (bos != NULL) {
191       for (int k = 0; k < bo_num; k++)
192          free(bos[k]);
193       free(bos);
194    }
195 }
196 
197 static void
print_help(const char * progname,FILE * file)198 print_help(const char *progname, FILE *file)
199 {
200    fprintf(file,
201            "Usage: %s [OPTION] inputfile\n"
202            "Decode Panfrost coredump file.\n\n"
203            "    -h, --help             display this help and exit\n"
204            "    -a, --addr             print BO physical addresses\n"
205            "    -r, --regs             print Panfrost HW registers\n"
206            "Example:\n"
207            "    panfrostdump -a -r coredump.bin\n",
208            progname);
209 }
210 
211 int
main(int argc,char * argv[])212 main(int argc, char *argv[])
213 {
214    struct panfrost_dump_object_header_ho doh;
215    bool print_addr = false;
216    bool print_reg = false;
217    uint32_t gpu_id = 0;
218    uint64_t jc = 0;
219    size_t nbytes;
220    int i, j, k, c;
221 
222    if (argc < 2) {
223       printf("Pass a coredump file\n");
224       return EXIT_FAILURE;
225    }
226 
227    /* clang-format off */
228    const struct option longopts[] = {
229       { "addr", no_argument, (int *) &print_addr, true },
230       { "regs", no_argument, (int *) &print_reg, true },
231       { "help", no_argument, NULL, 'h' },
232       { NULL, 0, NULL, 0 }
233    };
234    /* clang-format on */
235 
236    while ((c = getopt_long(argc, argv, "arh", longopts, NULL)) != -1) {
237       switch (c) {
238       case 'h':
239          print_help(argv[0], stderr);
240          return EXIT_SUCCESS;
241       case 'a':
242          print_addr = true;
243          break;
244       case 'r':
245          print_reg = true;
246          break;
247       default:
248          fprintf(stderr, "Unknown option\n");
249          print_help(argv[0], stderr);
250          return EXIT_FAILURE;
251       }
252    }
253 
254    i = j = k = 0;
255 
256    atexit(cleanup);
257    struct pandecode_context *ctx = pandecode_create_context(false);
258 
259    hdr_fp = fopen(argv[optind], "r");
260    if (!hdr_fp) {
261       perror("failed to open file");
262       return EXIT_FAILURE;
263    }
264 
265    data_fp = fopen(argv[optind], "r");
266    if (!data_fp) {
267       perror("failed to open file");
268       return EXIT_FAILURE;
269    }
270 
271    /* Read register header */
272    if (!read_header(hdr_fp, &doh))
273       return EXIT_FAILURE;
274 
275    if (fseek(data_fp, doh.file_offset, SEEK_SET)) {
276       perror("fseek error");
277       return EXIT_FAILURE;
278    }
279 
280    if (doh.type == PANFROSTDUMP_BUF_REG) {
281       jc = doh.reghdr.jc;
282       gpu_id = doh.reghdr.gpu_id;
283       bo_num = doh.reghdr.nbos;
284 
285       bos = calloc(bo_num, sizeof(char *));
286       if (!bos) {
287          fprintf(stderr, "Failed to allocate memory for BO pointer array\n");
288          return EXIT_FAILURE;
289       }
290 
291       printf("JC: %" PRIX64 ", GPU_ID: %" PRIX32 "\n", jc, gpu_id);
292 
293       if (print_reg) {
294          puts("GPU registers:");
295          for (i = 0;
296               i < (doh.file_size / sizeof(struct panfrost_dump_registers));
297               i++) {
298             uint32_t reg_offset;
299             uint32_t reg_val;
300 
301             if (read_register(&reg_offset, &reg_val, data_fp))
302                printf("0x%04X : 0x%08X\n", reg_offset, reg_val);
303          }
304       }
305    }
306 
307    if (!read_header(hdr_fp, &doh))
308       return EXIT_FAILURE;
309 
310    if (doh.type == PANFROSTDUMP_BUF_BOMAP) {
311       uint32_t bomap_offset = doh.file_offset;
312 
313       if (!jc || !gpu_id) {
314          fprintf(stderr, "Missing initial dump header\n");
315          return EXIT_FAILURE;
316       }
317 
318       if (!read_header(hdr_fp, &doh))
319          return EXIT_FAILURE;
320 
321       while (doh.type != PANFROSTDUMP_BUF_TRAILER) {
322          if (doh.bomap.valid) {
323             if (fseek(data_fp, bomap_offset + doh.bomap.data[0], SEEK_SET)) {
324                perror("fseek error");
325                return EXIT_FAILURE;
326             }
327 
328             if (print_addr) {
329                printf("BO(%u) VA(%" PRIX64 ") SZ(%" PRIX32
330                       ") page addresses:\n",
331                       j, doh.bomap.iova, doh.file_size);
332 
333                for (k = 0; k < (doh.file_size / GPU_PAGE_SIZE); k++) {
334                   uint64_t phys_addr;
335 
336                   if (!read_page_addr(&phys_addr, data_fp))
337                      continue;
338 
339                   printf("%u: %" PRIX64 "\n", k, phys_addr);
340                }
341             }
342 
343             /* Copy the BO into external file */
344             char bodump_filename[MAX_BODUMP_FILENAME];
345             FILE *bodump;
346 
347             snprintf(bodump_filename, MAX_BODUMP_FILENAME, "bodump-%u.dump", j);
348 
349             if ((bodump = fopen(bodump_filename, "wb"))) {
350                if (fseek(data_fp, doh.file_offset, SEEK_SET)) {
351                   perror("fseek error");
352                   return EXIT_FAILURE;
353                }
354 
355                bos[j] = malloc(doh.file_size);
356                if (!bos[j]) {
357                   fprintf(stderr, "Failed to allocate memory for BO\n");
358                   return EXIT_FAILURE;
359                }
360 
361                fseek(data_fp, doh.file_offset, SEEK_SET);
362 
363                nbytes = fread(bos[j], 1, doh.file_size, data_fp);
364                if (nbytes < doh.file_size) {
365                   fprintf(stderr, "Read less than BO size: %u\n", errno);
366                   return EXIT_FAILURE;
367                }
368                nbytes = fwrite(bos[j], 1, doh.file_size, bodump);
369                if (nbytes < doh.file_size) {
370                   fprintf(stderr, "Failed to write BO contents into file: %u\n",
371                           errno);
372                   return EXIT_FAILURE;
373                }
374 
375                fclose(bodump);
376 
377                pandecode_inject_mmap(ctx, doh.bomap.iova, bos[j], doh.file_size,
378                                      NULL);
379 
380             } else {
381                perror("failed to open BO dump file");
382             }
383          } else {
384             fprintf(stderr, "BO(%u) isn't valid\n", j);
385          }
386 
387          if (!read_header(hdr_fp, &doh))
388             return EXIT_FAILURE;
389 
390          j++;
391       }
392    } else {
393       if (!read_header(hdr_fp, &doh))
394          return EXIT_FAILURE;
395    }
396 
397    if (doh.type != PANFROSTDUMP_BUF_TRAILER)
398       fprintf(stderr, "Trailing header isn't right\n");
399 
400    pandecode_jc(ctx, jc, gpu_id);
401    pandecode_destroy_context(ctx);
402 
403    fclose(data_fp);
404    fclose(hdr_fp);
405    data_fp = hdr_fp = NULL;
406 
407    return EXIT_SUCCESS;
408 }
409