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