xref: /aosp_15_r20/external/mesa3d/src/freedreno/afuc/emu-ui.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2021 Google, Inc.
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include <assert.h>
7 #include <ctype.h>
8 #include <inttypes.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <termios.h>
12 #include <unistd.h>
13 
14 #include "freedreno_pm4.h"
15 
16 #include "emu.h"
17 #include "util.h"
18 
19 /*
20  * Emulator User Interface:
21  *
22  * Handles the user prompts and input parsing.
23  */
24 
25 static void
clear_line(void)26 clear_line(void)
27 {
28    if (!isatty(STDOUT_FILENO))
29       return;
30    printf("\r                                                           \r");
31 }
32 
33 static int
readchar(void)34 readchar(void)
35 {
36    static struct termios saved_termios, unbuffered_termios;
37    int c;
38 
39    fflush(stdout);
40 
41    tcgetattr(STDIN_FILENO, &saved_termios);
42    unbuffered_termios = saved_termios;
43    cfmakeraw(&unbuffered_termios);
44 
45    tcsetattr(STDIN_FILENO, TCSANOW, &unbuffered_termios);
46    do {
47       c = getchar();
48    } while (isspace(c));
49    tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios);
50 
51    /* TODO, read from script until EOF and then read from stdin: */
52    if (c == -1)
53       exit(0);
54 
55    return c;
56 }
57 
58 static const char *
extract_string(char ** buf)59 extract_string(char **buf)
60 {
61    char *p = *buf;
62 
63    /* eat any leading whitespace: */
64    while (*p && isspace(*p))
65       p++;
66 
67    if (!*p)
68       return NULL;
69 
70    char *ret = p;
71 
72    /* skip to next whitespace: */
73    while (*p && !isspace(*p))
74       p++;
75 
76    if (*p)
77       *p = '\0';
78 
79    *buf = ++p;
80 
81    return ret;
82 }
83 
84 static size_t
readline(char ** p)85 readline(char **p)
86 {
87    static char *buf;
88    static size_t n;
89 
90    ssize_t ret = getline(&buf, &n, stdin);
91    if (ret < 0)
92       return ret;
93 
94    *p = buf;
95    return 0;
96 }
97 
98 static ssize_t
read_two_values(const char ** val1,const char ** val2)99 read_two_values(const char **val1, const char **val2)
100 {
101    char *p;
102 
103    ssize_t ret = readline(&p);
104    if (ret < 0)
105       return ret;
106 
107    *val1 = extract_string(&p);
108    *val2 = extract_string(&p);
109 
110    return 0;
111 }
112 
113 static ssize_t
read_one_value(const char ** val)114 read_one_value(const char **val)
115 {
116    char *p;
117 
118    ssize_t ret = readline(&p);
119    if (ret < 0)
120       return ret;
121 
122    *val = extract_string(&p);
123 
124    return 0;
125 }
126 
127 static void
print_dst(unsigned reg)128 print_dst(unsigned reg)
129 {
130    if (reg == REG_REM)
131       printf("$rem"); /* remainding dwords in packet */
132    else if (reg == REG_ADDR)
133       printf("$addr");
134    else if (reg == REG_USRADDR)
135       printf("$usraddr");
136    else if (reg == REG_DATA)
137       printf("$data");
138    else
139       printf("$%02x", reg);
140 }
141 
142 static void
dump_gpr_register(struct emu * emu,unsigned n)143 dump_gpr_register(struct emu *emu, unsigned n)
144 {
145    printf("              GPR:  ");
146    print_dst(n);
147    printf(": ");
148    if (BITSET_TEST(emu->gpr_regs.written, n)) {
149       printdelta("%08x\n", emu->gpr_regs.val[n]);
150    } else {
151       printf("%08x\n", emu->gpr_regs.val[n]);
152    }
153 }
154 
155 static void
dump_gpr_registers(struct emu * emu)156 dump_gpr_registers(struct emu *emu)
157 {
158    for (unsigned i = 0; i < ARRAY_SIZE(emu->gpr_regs.val); i++) {
159       dump_gpr_register(emu, i);
160    }
161 }
162 
163 static void
dump_gpu_register(struct emu * emu,unsigned n)164 dump_gpu_register(struct emu *emu, unsigned n)
165 {
166    printf("              GPU:  ");
167    char *name = afuc_gpu_reg_name(n);
168    if (name) {
169       printf("%s", name);
170       free(name);
171    } else {
172       printf("0x%04x", n);
173    }
174    printf(": ");
175    if (BITSET_TEST(emu->gpu_regs.written, n)) {
176       printdelta("%08x\n", emu->gpu_regs.val[n]);
177    } else {
178       printf("%08x\n", emu->gpu_regs.val[n]);
179    }
180 }
181 
182 static void
dump_pipe_register(struct emu * emu,unsigned n)183 dump_pipe_register(struct emu *emu, unsigned n)
184 {
185    printf("              PIPE: ");
186    print_pipe_reg(n);
187    printf(": ");
188    if (BITSET_TEST(emu->pipe_regs.written, n)) {
189       printdelta("%08x\n", emu->pipe_regs.val[n]);
190    } else {
191       printf("%08x\n", emu->pipe_regs.val[n]);
192    }
193 }
194 
195 static void
dump_control_register(struct emu * emu,unsigned n)196 dump_control_register(struct emu *emu, unsigned n)
197 {
198    printf("              CTRL: ");
199    print_control_reg(n);
200    printf(": ");
201    if (BITSET_TEST(emu->control_regs.written, n)) {
202       printdelta("%08x\n", emu->control_regs.val[n]);
203    } else {
204       printf("%08x\n", emu->control_regs.val[n]);
205    }
206 }
207 
208 static void
dump_sqe_register(struct emu * emu,unsigned n)209 dump_sqe_register(struct emu *emu, unsigned n)
210 {
211    printf("              SQE: ");
212    print_sqe_reg(n);
213    printf(": ");
214    if (BITSET_TEST(emu->sqe_regs.written, n)) {
215       printdelta("%08x\n", emu->sqe_regs.val[n]);
216    } else {
217       printf("%08x\n", emu->sqe_regs.val[n]);
218    }
219 }
220 
221 static void
dump_sqe_registers(struct emu * emu)222 dump_sqe_registers(struct emu *emu)
223 {
224    for (unsigned i = 0; i < ARRAY_SIZE(emu->sqe_regs.val); i++) {
225       dump_sqe_register(emu, i);
226    }
227 }
228 
229 
230 static void
dump_gpumem(struct emu * emu,uintptr_t addr)231 dump_gpumem(struct emu *emu, uintptr_t addr)
232 {
233    uint32_t val = emu_mem_read_dword(emu, addr);
234 
235    printf("              MEM:  0x%016"PRIxPTR": ", addr);
236    if (addr == emu->gpumem_written) {
237       printdelta("0x%08x\n", val);
238    } else {
239       printf("0x%08x\n", val);
240    }
241 }
242 
243 static void
emu_write_gpr_prompt(struct emu * emu)244 emu_write_gpr_prompt(struct emu *emu)
245 {
246    clear_line();
247    printf("    GPR register (name or offset) and value: ");
248 
249    const char *name;
250    const char *value;
251 
252    if (read_two_values(&name, &value))
253       return;
254 
255    unsigned offset = afuc_gpr_reg(name);
256    uint32_t val = strtoul(value, NULL, 0);
257 
258    emu_set_gpr_reg(emu, offset, val);
259 }
260 
261 static void
emu_write_control_prompt(struct emu * emu)262 emu_write_control_prompt(struct emu *emu)
263 {
264    clear_line();
265    printf("    Control register (name or offset) and value: ");
266 
267    const char *name;
268    const char *value;
269 
270    if (read_two_values(&name, &value))
271       return;
272 
273    unsigned offset = afuc_control_reg(name);
274    uint32_t val = strtoul(value, NULL, 0);
275 
276    emu_set_control_reg(emu, offset, val);
277 }
278 
279 static void
emu_dump_control_prompt(struct emu * emu)280 emu_dump_control_prompt(struct emu *emu)
281 {
282    clear_line();
283    printf("    Control register (name or offset): ");
284 
285    const char *name;
286 
287    if (read_one_value(&name))
288       return;
289 
290    printf("\n");
291 
292    unsigned offset = afuc_control_reg(name);
293    dump_control_register(emu, offset);
294 }
295 
296 static void
emu_write_sqe_prompt(struct emu * emu)297 emu_write_sqe_prompt(struct emu *emu)
298 {
299    clear_line();
300    printf("    SQE register (name or offset) and value: ");
301 
302    const char *name;
303    const char *value;
304 
305    if (read_two_values(&name, &value))
306       return;
307 
308    unsigned offset = afuc_sqe_reg(name);
309    uint32_t val = strtoul(value, NULL, 0);
310 
311    emu_set_sqe_reg(emu, offset, val);
312 }
313 
314 static void
emu_write_gpu_prompt(struct emu * emu)315 emu_write_gpu_prompt(struct emu *emu)
316 {
317    clear_line();
318    printf("    GPU register (name or offset) and value: ");
319 
320    const char *name;
321    const char *value;
322 
323    if (read_two_values(&name, &value))
324       return;
325 
326    unsigned offset = afuc_gpu_reg(name);
327    uint32_t val = strtoul(value, NULL, 0);
328 
329    emu_set_gpu_reg(emu, offset, val);
330 }
331 
332 static void
emu_dump_gpu_prompt(struct emu * emu)333 emu_dump_gpu_prompt(struct emu *emu)
334 {
335    clear_line();
336    printf("    GPU register (name or offset): ");
337 
338    const char *name;
339 
340    if (read_one_value(&name))
341       return;
342 
343    printf("\n");
344 
345    unsigned offset = afuc_gpu_reg(name);
346    dump_gpu_register(emu, offset);
347 }
348 
349 static void
emu_write_mem_prompt(struct emu * emu)350 emu_write_mem_prompt(struct emu *emu)
351 {
352    clear_line();
353    printf("    GPU memory offset and value: ");
354 
355    const char *offset;
356    const char *value;
357 
358    if (read_two_values(&offset, &value))
359       return;
360 
361    uintptr_t addr = strtoull(offset, NULL, 0);
362    uint32_t val = strtoul(value, NULL, 0);
363 
364    emu_mem_write_dword(emu, addr, val);
365 }
366 
367 static void
emu_dump_mem_prompt(struct emu * emu)368 emu_dump_mem_prompt(struct emu *emu)
369 {
370    clear_line();
371    printf("    GPU memory offset: ");
372 
373    const char *offset;
374 
375    if (read_one_value(&offset))
376       return;
377 
378    printf("\n");
379 
380    uintptr_t addr = strtoull(offset, NULL, 0);
381    dump_gpumem(emu, addr);
382 }
383 
384 static void
emu_dump_prompt(struct emu * emu)385 emu_dump_prompt(struct emu *emu)
386 {
387    do {
388       clear_line();
389       printf("  dump: GPR (r)egisters, (c)ontrol register, (s)qe registers, (g)pu register, (m)emory: ");
390 
391       int c = readchar();
392       printf("%c\n", c);
393 
394       if (c == 'r') {
395          /* Since there aren't too many GPR registers, just dump
396           * them all:
397           */
398          dump_gpr_registers(emu);
399          break;
400       } else if (c == 's') {
401          /* Similarly, just dump all the SQE registers */
402          dump_sqe_registers(emu);
403          break;
404       } else if (c == 'c') {
405          emu_dump_control_prompt(emu);
406          break;
407       } else if (c == 'g') {
408          emu_dump_gpu_prompt(emu);
409          break;
410       } else if (c == 'm') {
411          emu_dump_mem_prompt(emu);
412          break;
413       } else {
414          printf("invalid option: '%c'\n", c);
415          break;
416       }
417    } while (true);
418 }
419 
420 static void
emu_write_prompt(struct emu * emu)421 emu_write_prompt(struct emu *emu)
422 {
423    do {
424       clear_line();
425       printf("  write: GPR (r)egister, (c)ontrol register, (s)sqe register, (g)pu register, (m)emory: ");
426 
427       int c = readchar();
428       printf("%c\n", c);
429 
430       if (c == 'r') {
431          emu_write_gpr_prompt(emu);
432          break;
433       } else if (c == 's') {
434          emu_write_sqe_prompt(emu);
435          break;
436       } else if (c == 'c') {
437          emu_write_control_prompt(emu);
438          break;
439       } else if (c == 'g') {
440          emu_write_gpu_prompt(emu);
441          break;
442       } else if (c == 'm') {
443          emu_write_mem_prompt(emu);
444          break;
445       } else {
446          printf("invalid option: '%c'\n", c);
447          break;
448       }
449    } while (true);
450 }
451 
452 static void
emu_packet_prompt(struct emu * emu)453 emu_packet_prompt(struct emu *emu)
454 {
455    clear_line();
456    printf("  Enter packet (opc or register name), followed by payload: ");
457    fflush(stdout);
458 
459    char *p;
460    if (readline(&p) < 0)
461       return;
462 
463    printf("\n");
464 
465    const char *name = extract_string(&p);
466 
467    /* Read the payload, so we can know the size to generate correct header: */
468    uint32_t payload[0x7f];
469    unsigned cnt = 0;
470 
471    do {
472       const char *val = extract_string(&p);
473       if (!val)
474          break;
475 
476       assert(cnt < ARRAY_SIZE(payload));
477       payload[cnt++] = strtoul(val, NULL, 0);
478    } while (true);
479 
480    uint32_t hdr;
481    if (afuc_pm4_id(name) >= 0) {
482       unsigned opcode = afuc_pm4_id(name);
483       hdr = pm4_pkt7_hdr(opcode, cnt);
484    } else {
485       unsigned regindx = afuc_gpu_reg(name);
486       hdr = pm4_pkt4_hdr(regindx, cnt);
487    }
488 
489    ASSERTED bool ret = emu_queue_push(&emu->roq, hdr);
490    assert(ret);
491 
492    for (unsigned i = 0; i < cnt; i++) {
493       ASSERTED bool ret = emu_queue_push(&emu->roq, payload[i]);
494       assert(ret);
495    }
496 }
497 
498 void
emu_main_prompt(struct emu * emu)499 emu_main_prompt(struct emu *emu)
500 {
501    if (emu->run_mode)
502       return;
503 
504    do {
505       clear_line();
506       printf("(s)tep, (r)un, (d)ump, (w)rite, (p)acket, (h)elp, (q)uit: ");
507 
508       int c = readchar();
509 
510       printf("%c\n", c);
511 
512       if (c == 's') {
513          break;
514       } else if (c == 'r') {
515          emu->run_mode = true;
516          break;
517       } else if (c == 'd') {
518          emu_dump_prompt(emu);
519       } else if (c == 'w') {
520          emu_write_prompt(emu);
521       } else if (c == 'p') {
522          emu_packet_prompt(emu);
523       } else if (c == 'h') {
524          printf("  (s)tep   - single step to next instruction\n");
525          printf("  (r)un    - run until next waitin\n");
526          printf("  (d)ump   - dump memory/register menu\n");
527          printf("  (w)rite  - write memory/register menu\n");
528          printf("  (p)acket - inject a pm4 packet\n");
529          printf("  (h)elp   - show this usage message\n");
530          printf("  (q)uit   - exit emulator\n");
531       } else if (c == 'q') {
532          printf("\n");
533          exit(0);
534       } else {
535          printf("invalid option: '%c'\n", c);
536       }
537    } while (true);
538 }
539 
540 void
emu_clear_state_change(struct emu * emu)541 emu_clear_state_change(struct emu *emu)
542 {
543    memset(emu->control_regs.written, 0, sizeof(emu->control_regs.written));
544    memset(emu->sqe_regs.written,     0, sizeof(emu->sqe_regs.written));
545    memset(emu->pipe_regs.written,    0, sizeof(emu->pipe_regs.written));
546    memset(emu->gpu_regs.written,     0, sizeof(emu->gpu_regs.written));
547    memset(emu->gpr_regs.written,     0, sizeof(emu->gpr_regs.written));
548    emu->gpumem_written = ~0;
549 }
550 
551 void
emu_dump_state_change(struct emu * emu)552 emu_dump_state_change(struct emu *emu)
553 {
554    unsigned i;
555 
556    if (emu->quiet)
557       return;
558 
559    /* Print the GPRs that changed: */
560    BITSET_FOREACH_SET (i, emu->gpr_regs.written, EMU_NUM_GPR_REGS) {
561       dump_gpr_register(emu, i);
562    }
563 
564    BITSET_FOREACH_SET (i, emu->gpu_regs.written, EMU_NUM_GPU_REGS) {
565       dump_gpu_register(emu, i);
566    }
567 
568    BITSET_FOREACH_SET (i, emu->pipe_regs.written, EMU_NUM_PIPE_REGS) {
569       dump_pipe_register(emu, i);
570    }
571 
572    BITSET_FOREACH_SET (i, emu->control_regs.written, EMU_NUM_CONTROL_REGS) {
573       dump_control_register(emu, i);
574    }
575 
576    BITSET_FOREACH_SET (i, emu->sqe_regs.written, EMU_NUM_SQE_REGS) {
577       dump_sqe_register(emu, i);
578    }
579 
580    if (emu->gpumem_written != ~0) {
581       dump_gpumem(emu, emu->gpumem_written);
582    }
583 }
584