Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/freedreno/afuc/emu-ui.c
4565 views
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
44
clear_line(void)
45
{
46
if (!isatty(STDOUT_FILENO))
47
return;
48
printf("\r \r");
49
}
50
51
static int
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
536