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