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