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
dump_gpr_register(struct emu * emu,unsigned n)146 dump_gpr_register(struct emu *emu, unsigned n)
147 {
148    printf("              GPR:  ");
149    print_dst(n);
150    printf(": ");
151    if (BITSET_TEST(emu->gpr_regs.written, n)) {
152       printdelta("%08x\n", emu->gpr_regs.val[n]);
153    } else {
154       printf("%08x\n", emu->gpr_regs.val[n]);
155    }
156 }
157 
158 static void
dump_gpr_registers(struct emu * emu)159 dump_gpr_registers(struct emu *emu)
160 {
161    for (unsigned i = 0; i < ARRAY_SIZE(emu->gpr_regs.val); i++) {
162       dump_gpr_register(emu, i);
163    }
164 }
165 
166 static void
dump_gpu_register(struct emu * emu,unsigned n)167 dump_gpu_register(struct emu *emu, unsigned n)
168 {
169    printf("              GPU:  ");
170    char *name = afuc_gpu_reg_name(n);
171    if (name) {
172       printf("%s", name);
173       free(name);
174    } else {
175       printf("0x%04x", n);
176    }
177    printf(": ");
178    if (BITSET_TEST(emu->gpu_regs.written, n)) {
179       printdelta("%08x\n", emu->gpu_regs.val[n]);
180    } else {
181       printf("%08x\n", emu->gpu_regs.val[n]);
182    }
183 }
184 
185 static void
dump_pipe_register(struct emu * emu,unsigned n)186 dump_pipe_register(struct emu *emu, unsigned n)
187 {
188    printf("              PIPE: ");
189    print_pipe_reg(n);
190    printf(": ");
191    if (BITSET_TEST(emu->pipe_regs.written, n)) {
192       printdelta("%08x\n", emu->pipe_regs.val[n]);
193    } else {
194       printf("%08x\n", emu->pipe_regs.val[n]);
195    }
196 }
197 
198 static void
dump_control_register(struct emu * emu,unsigned n)199 dump_control_register(struct emu *emu, unsigned n)
200 {
201    printf("              CTRL: ");
202    print_control_reg(n);
203    printf(": ");
204    if (BITSET_TEST(emu->control_regs.written, n)) {
205       printdelta("%08x\n", emu->control_regs.val[n]);
206    } else {
207       printf("%08x\n", emu->control_regs.val[n]);
208    }
209 }
210 
211 static void
dump_gpumem(struct emu * emu,uintptr_t addr)212 dump_gpumem(struct emu *emu, uintptr_t addr)
213 {
214    uint32_t val = emu_mem_read_dword(emu, addr);
215 
216    printf("              MEM:  0x%016"PRIxPTR": ", addr);
217    if (addr == emu->gpumem_written) {
218       printdelta("0x%08x\n", val);
219    } else {
220       printf("0x%08x\n", val);
221    }
222 }
223 
224 static void
emu_write_gpr_prompt(struct emu * emu)225 emu_write_gpr_prompt(struct emu *emu)
226 {
227    clear_line();
228    printf("    GPR register (name or offset) and value: ");
229 
230    const char *name;
231    const char *value;
232 
233    if (read_two_values(&name, &value))
234       return;
235 
236    unsigned offset = afuc_gpr_reg(name);
237    uint32_t val = strtoul(value, NULL, 0);
238 
239    emu_set_gpr_reg(emu, offset, val);
240 }
241 
242 static void
emu_write_control_prompt(struct emu * emu)243 emu_write_control_prompt(struct emu *emu)
244 {
245    clear_line();
246    printf("    Control register (name or offset) and value: ");
247 
248    const char *name;
249    const char *value;
250 
251    if (read_two_values(&name, &value))
252       return;
253 
254    unsigned offset = afuc_control_reg(name);
255    uint32_t val = strtoul(value, NULL, 0);
256 
257    emu_set_control_reg(emu, offset, val);
258 }
259 
260 static void
emu_dump_control_prompt(struct emu * emu)261 emu_dump_control_prompt(struct emu *emu)
262 {
263    clear_line();
264    printf("    Control register (name or offset): ");
265 
266    const char *name;
267 
268    if (read_one_value(&name))
269       return;
270 
271    printf("\n");
272 
273    unsigned offset = afuc_control_reg(name);
274    dump_control_register(emu, offset);
275 }
276 
277 static void
emu_write_gpu_prompt(struct emu * emu)278 emu_write_gpu_prompt(struct emu *emu)
279 {
280    clear_line();
281    printf("    GPU register (name or offset) and value: ");
282 
283    const char *name;
284    const char *value;
285 
286    if (read_two_values(&name, &value))
287       return;
288 
289    unsigned offset = afuc_gpu_reg(name);
290    uint32_t val = strtoul(value, NULL, 0);
291 
292    emu_set_gpu_reg(emu, offset, val);
293 }
294 
295 static void
emu_dump_gpu_prompt(struct emu * emu)296 emu_dump_gpu_prompt(struct emu *emu)
297 {
298    clear_line();
299    printf("    GPU register (name or offset): ");
300 
301    const char *name;
302 
303    if (read_one_value(&name))
304       return;
305 
306    printf("\n");
307 
308    unsigned offset = afuc_gpu_reg(name);
309    dump_gpu_register(emu, offset);
310 }
311 
312 static void
emu_write_mem_prompt(struct emu * emu)313 emu_write_mem_prompt(struct emu *emu)
314 {
315    clear_line();
316    printf("    GPU memory offset and value: ");
317 
318    const char *offset;
319    const char *value;
320 
321    if (read_two_values(&offset, &value))
322       return;
323 
324    uintptr_t addr = strtoull(offset, NULL, 0);
325    uint32_t val = strtoul(value, NULL, 0);
326 
327    emu_mem_write_dword(emu, addr, val);
328 }
329 
330 static void
emu_dump_mem_prompt(struct emu * emu)331 emu_dump_mem_prompt(struct emu *emu)
332 {
333    clear_line();
334    printf("    GPU memory offset: ");
335 
336    const char *offset;
337 
338    if (read_one_value(&offset))
339       return;
340 
341    printf("\n");
342 
343    uintptr_t addr = strtoull(offset, NULL, 0);
344    dump_gpumem(emu, addr);
345 }
346 
347 static void
emu_dump_prompt(struct emu * emu)348 emu_dump_prompt(struct emu *emu)
349 {
350    do {
351       clear_line();
352       printf("  dump: GPR (r)egisters, (c)ontrol register, (g)pu register, (m)emory: ");
353 
354       int c = readchar();
355       printf("%c\n", c);
356 
357       if (c == 'r') {
358          /* Since there aren't too many GPR registers, just dump
359           * them all:
360           */
361          dump_gpr_registers(emu);
362          break;
363       } else if (c == 'c') {
364          emu_dump_control_prompt(emu);
365          break;
366       } else if (c == 'g') {
367          emu_dump_gpu_prompt(emu);
368          break;
369       } else if (c == 'm') {
370          emu_dump_mem_prompt(emu);
371          break;
372       } else {
373          printf("invalid option: '%c'\n", c);
374          break;
375       }
376    } while (true);
377 }
378 
379 static void
emu_write_prompt(struct emu * emu)380 emu_write_prompt(struct emu *emu)
381 {
382    do {
383       clear_line();
384       printf("  write: GPR (r)egister, (c)ontrol register, (g)pu register, (m)emory: ");
385 
386       int c = readchar();
387       printf("%c\n", c);
388 
389       if (c == 'r') {
390          emu_write_gpr_prompt(emu);
391          break;
392       } else if (c == 'c') {
393          emu_write_control_prompt(emu);
394          break;
395       } else if (c == 'g') {
396          emu_write_gpu_prompt(emu);
397          break;
398       } else if (c == 'm') {
399          emu_write_mem_prompt(emu);
400          break;
401       } else {
402          printf("invalid option: '%c'\n", c);
403          break;
404       }
405    } while (true);
406 }
407 
408 static void
emu_packet_prompt(struct emu * emu)409 emu_packet_prompt(struct emu *emu)
410 {
411    clear_line();
412    printf("  Enter packet (opc or register name), followed by payload: ");
413    fflush(stdout);
414 
415    char *p;
416    if (readline(&p) < 0)
417       return;
418 
419    printf("\n");
420 
421    const char *name = extract_string(&p);
422 
423    /* Read the payload, so we can know the size to generate correct header: */
424    uint32_t payload[0x7f];
425    unsigned cnt = 0;
426 
427    do {
428       const char *val = extract_string(&p);
429       if (!val)
430          break;
431 
432       assert(cnt < ARRAY_SIZE(payload));
433       payload[cnt++] = strtoul(val, NULL, 0);
434    } while (true);
435 
436    uint32_t hdr;
437    if (afuc_pm4_id(name) >= 0) {
438       unsigned opcode = afuc_pm4_id(name);
439       hdr = pm4_pkt7_hdr(opcode, cnt);
440    } else {
441       unsigned regindx = afuc_gpu_reg(name);
442       hdr = pm4_pkt4_hdr(regindx, cnt);
443    }
444 
445    ASSERTED bool ret = emu_queue_push(&emu->roq, hdr);
446    assert(ret);
447 
448    for (unsigned i = 0; i < cnt; i++) {
449       ASSERTED bool ret = emu_queue_push(&emu->roq, payload[i]);
450       assert(ret);
451    }
452 }
453 
454 void
emu_main_prompt(struct emu * emu)455 emu_main_prompt(struct emu *emu)
456 {
457    if (emu->run_mode)
458       return;
459 
460    do {
461       clear_line();
462       printf("(s)tep, (r)un, (d)ump, (w)rite, (p)acket, (h)elp, (q)uit: ");
463 
464       int c = readchar();
465 
466       printf("%c\n", c);
467 
468       if (c == 's') {
469          break;
470       } else if (c == 'r') {
471          emu->run_mode = true;
472          break;
473       } else if (c == 'd') {
474          emu_dump_prompt(emu);
475       } else if (c == 'w') {
476          emu_write_prompt(emu);
477       } else if (c == 'p') {
478          emu_packet_prompt(emu);
479       } else if (c == 'h') {
480          printf("  (s)tep   - single step to next instruction\n");
481          printf("  (r)un    - run until next waitin\n");
482          printf("  (d)ump   - dump memory/register menu\n");
483          printf("  (w)rite  - write memory/register menu\n");
484          printf("  (p)acket - inject a pm4 packet\n");
485          printf("  (h)elp   - show this usage message\n");
486          printf("  (q)uit   - exit emulator\n");
487       } else if (c == 'q') {
488          printf("\n");
489          exit(0);
490       } else {
491          printf("invalid option: '%c'\n", c);
492       }
493    } while (true);
494 }
495 
496 void
emu_clear_state_change(struct emu * emu)497 emu_clear_state_change(struct emu *emu)
498 {
499    memset(emu->control_regs.written, 0, sizeof(emu->control_regs.written));
500    memset(emu->pipe_regs.written,    0, sizeof(emu->pipe_regs.written));
501    memset(emu->gpu_regs.written,     0, sizeof(emu->gpu_regs.written));
502    memset(emu->gpr_regs.written,     0, sizeof(emu->gpr_regs.written));
503    emu->gpumem_written = ~0;
504 }
505 
506 void
emu_dump_state_change(struct emu * emu)507 emu_dump_state_change(struct emu *emu)
508 {
509    unsigned i;
510 
511    if (emu->quiet)
512       return;
513 
514    /* Print the GPRs that changed: */
515    BITSET_FOREACH_SET (i, emu->gpr_regs.written, EMU_NUM_GPR_REGS) {
516       dump_gpr_register(emu, i);
517    }
518 
519    BITSET_FOREACH_SET (i, emu->gpu_regs.written, EMU_NUM_GPU_REGS) {
520       dump_gpu_register(emu, i);
521    }
522 
523    BITSET_FOREACH_SET (i, emu->pipe_regs.written, EMU_NUM_PIPE_REGS) {
524       dump_pipe_register(emu, i);
525    }
526 
527    BITSET_FOREACH_SET (i, emu->control_regs.written, EMU_NUM_CONTROL_REGS) {
528       dump_control_register(emu, i);
529    }
530 
531    if (emu->gpumem_written != ~0) {
532       dump_gpumem(emu, emu->gpumem_written);
533    }
534 }
535