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