diff options
Diffstat (limited to 'src/freedreno/afuc/emu-ui.c')
-rw-r--r-- | src/freedreno/afuc/emu-ui.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/src/freedreno/afuc/emu-ui.c b/src/freedreno/afuc/emu-ui.c new file mode 100644 index 00000000000..0b24ea5f3aa --- /dev/null +++ b/src/freedreno/afuc/emu-ui.c @@ -0,0 +1,531 @@ +/* + * Copyright © 2021 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <assert.h> +#include <ctype.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <termios.h> +#include <unistd.h> + +#include "freedreno_pm4.h" + +#include "emu.h" +#include "util.h" + +/* + * Emulator User Interface: + * + * Handles the user prompts and input parsing. + */ + +static void +clear_line(void) +{ + if (!isatty(STDOUT_FILENO)) + return; + printf("\r \r"); +} + +static int +readchar(void) +{ + static struct termios saved_termios, unbuffered_termios; + int c; + + fflush(stdout); + + tcgetattr(STDIN_FILENO, &saved_termios); + unbuffered_termios = saved_termios; + cfmakeraw(&unbuffered_termios); + + tcsetattr(STDIN_FILENO, TCSANOW, &unbuffered_termios); + do { + c = getchar(); + } while (isspace(c)); + tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios); + + /* TODO, read from script until EOF and then read from stdin: */ + if (c == -1) + exit(0); + + return c; +} + +static const char * +extract_string(char **buf) +{ + char *p = *buf; + + /* eat any leading whitespace: */ + while (*p && isspace(*p)) + p++; + + if (!*p) + return NULL; + + char *ret = p; + + /* skip to next whitespace: */ + while (*p && !isspace(*p)) + p++; + + if (*p) + *p = '\0'; + + *buf = ++p; + + return ret; +} + +static size_t +readline(char **p) +{ + static char *buf; + static size_t n; + + ssize_t ret = getline(&buf, &n, stdin); + if (ret < 0) + return ret; + + *p = buf; + return 0; +} + +static ssize_t +read_two_values(const char **val1, const char **val2) +{ + char *p; + + ssize_t ret = readline(&p); + if (ret < 0) + return ret; + + *val1 = extract_string(&p); + *val2 = extract_string(&p); + + return 0; +} + +static ssize_t +read_one_value(const char **val) +{ + char *p; + + ssize_t ret = readline(&p); + if (ret < 0) + return ret; + + *val = extract_string(&p); + + return 0; +} + +static void +dump_gpr_register(struct emu *emu, unsigned n) +{ + printf(" GPR: "); + print_dst(n); + printf(": "); + if (BITSET_TEST(emu->gpr_regs.written, n)) { + printdelta("%08x\n", emu->gpr_regs.val[n]); + } else { + printf("%08x\n", emu->gpr_regs.val[n]); + } +} + +static void +dump_gpr_registers(struct emu *emu) +{ + for (unsigned i = 0; i < ARRAY_SIZE(emu->gpr_regs.val); i++) { + dump_gpr_register(emu, i); + } +} + +static void +dump_gpu_register(struct emu *emu, unsigned n) +{ + printf(" GPU: "); + char *name = afuc_gpu_reg_name(n); + if (name) { + printf("%s", name); + free(name); + } else { + printf("0x%04x", n); + } + printf(": "); + if (BITSET_TEST(emu->gpu_regs.written, n)) { + printdelta("%08x\n", emu->gpu_regs.val[n]); + } else { + printf("%08x\n", emu->gpu_regs.val[n]); + } +} + +static void +dump_pipe_register(struct emu *emu, unsigned n) +{ + printf(" PIPE: "); + print_pipe_reg(n); + printf(": "); + if (BITSET_TEST(emu->pipe_regs.written, n)) { + printdelta("%08x\n", emu->pipe_regs.val[n]); + } else { + printf("%08x\n", emu->pipe_regs.val[n]); + } +} + +static void +dump_control_register(struct emu *emu, unsigned n) +{ + printf(" CTRL: "); + print_control_reg(n); + printf(": "); + if (BITSET_TEST(emu->control_regs.written, n)) { + printdelta("%08x\n", emu->control_regs.val[n]); + } else { + printf("%08x\n", emu->control_regs.val[n]); + } +} + +static void +dump_gpumem(struct emu *emu, uintptr_t addr) +{ + uint32_t val = emu_mem_read_dword(emu, addr); + + printf(" MEM: 0x%016"PRIx64": ", addr); + if (addr == emu->gpumem_written) { + printdelta("0x%08x\n", val); + } else { + printf("0x%08x\n", val); + } +} + +static void +emu_write_gpr_prompt(struct emu *emu) +{ + clear_line(); + printf(" GPR register (name or offset) and value: "); + + const char *name; + const char *value; + + if (read_two_values(&name, &value)) + return; + + unsigned offset = afuc_gpr_reg(name); + uint32_t val = strtoul(value, NULL, 0); + + emu_set_gpr_reg(emu, offset, val); +} + +static void +emu_write_control_prompt(struct emu *emu) +{ + clear_line(); + printf(" Control register (name or offset) and value: "); + + const char *name; + const char *value; + + if (read_two_values(&name, &value)) + return; + + unsigned offset = afuc_control_reg(name); + uint32_t val = strtoul(value, NULL, 0); + + emu_set_control_reg(emu, offset, val); +} + +static void +emu_dump_control_prompt(struct emu *emu) +{ + clear_line(); + printf(" Control register (name or offset): "); + + const char *name; + + if (read_one_value(&name)) + return; + + printf("\n"); + + unsigned offset = afuc_control_reg(name); + dump_control_register(emu, offset); +} + +static void +emu_write_gpu_prompt(struct emu *emu) +{ + clear_line(); + printf(" GPU register (name or offset) and value: "); + + const char *name; + const char *value; + + if (read_two_values(&name, &value)) + return; + + unsigned offset = afuc_gpu_reg(name); + uint32_t val = strtoul(value, NULL, 0); + + emu_set_gpu_reg(emu, offset, val); +} + +static void +emu_dump_gpu_prompt(struct emu *emu) +{ + clear_line(); + printf(" GPU register (name or offset): "); + + const char *name; + + if (read_one_value(&name)) + return; + + printf("\n"); + + unsigned offset = afuc_gpu_reg(name); + dump_gpu_register(emu, offset); +} + +static void +emu_write_mem_prompt(struct emu *emu) +{ + clear_line(); + printf(" GPU memory offset and value: "); + + const char *offset; + const char *value; + + if (read_two_values(&offset, &value)) + return; + + uintptr_t addr = strtoull(offset, NULL, 0); + uint32_t val = strtoul(value, NULL, 0); + + emu_mem_write_dword(emu, addr, val); +} + +static void +emu_dump_mem_prompt(struct emu *emu) +{ + clear_line(); + printf(" GPU memory offset: "); + + const char *offset; + + if (read_one_value(&offset)) + return; + + printf("\n"); + + uintptr_t addr = strtoull(offset, NULL, 0); + dump_gpumem(emu, addr); +} + +static void +emu_dump_prompt(struct emu *emu) +{ + do { + clear_line(); + printf(" dump: GPR (r)egisters, (c)ontrol register, (g)pu register, (m)emory: "); + + int c = readchar(); + printf("%c\n", c); + + if (c == 'r') { + /* Since there aren't too many GPR registers, just dump + * them all: + */ + dump_gpr_registers(emu); + break; + } else if (c == 'c') { + emu_dump_control_prompt(emu); + break; + } else if (c == 'g') { + emu_dump_gpu_prompt(emu); + break; + } else if (c == 'm') { + emu_dump_mem_prompt(emu); + break; + } else { + printf("invalid option: '%c'\n", c); + break; + } + } while (true); +} + +static void +emu_write_prompt(struct emu *emu) +{ + do { + clear_line(); + printf(" write: GPR (r)egister, (c)ontrol register, (g)pu register, (m)emory: "); + + int c = readchar(); + printf("%c\n", c); + + if (c == 'r') { + emu_write_gpr_prompt(emu); + break; + } else if (c == 'c') { + emu_write_control_prompt(emu); + break; + } else if (c == 'g') { + emu_write_gpu_prompt(emu); + break; + } else if (c == 'm') { + emu_write_mem_prompt(emu); + break; + } else { + printf("invalid option: '%c'\n", c); + break; + } + } while (true); +} + +static void +emu_packet_prompt(struct emu *emu) +{ + clear_line(); + printf(" Enter packet (opc or register name), followed by payload: "); + fflush(stdout); + + char *p; + if (readline(&p) < 0) + return; + + printf("\n"); + + const char *name = extract_string(&p); + + /* Read the payload, so we can know the size to generate correct header: */ + uint32_t payload[0x7f]; + unsigned cnt = 0; + + do { + const char *val = extract_string(&p); + if (!val) + break; + + assert(cnt < ARRAY_SIZE(payload)); + payload[cnt++] = strtoul(val, NULL, 0); + } while (true); + + uint32_t hdr; + if (afuc_pm4_id(name) >= 0) { + unsigned opcode = afuc_pm4_id(name); + hdr = pm4_pkt7_hdr(opcode, cnt); + } else { + unsigned regindx = afuc_gpu_reg(name); + hdr = pm4_pkt4_hdr(regindx, cnt); + } + + ASSERTED bool ret = emu_queue_push(&emu->roq, hdr); + assert(ret); + + for (unsigned i = 0; i < cnt; i++) { + ASSERTED bool ret = emu_queue_push(&emu->roq, payload[i]); + assert(ret); + } +} + +void +emu_main_prompt(struct emu *emu) +{ + if (emu->run_mode) + return; + + do { + clear_line(); + printf("(s)tep, (r)un, (d)ump, (w)rite, (p)acket, (h)elp, (q)uit: "); + + int c = readchar(); + + printf("%c\n", c); + + if (c == 's') { + break; + } else if (c == 'r') { + emu->run_mode = true; + break; + } else if (c == 'd') { + emu_dump_prompt(emu); + } else if (c == 'w') { + emu_write_prompt(emu); + } else if (c == 'p') { + emu_packet_prompt(emu); + } else if (c == 'h') { + printf(" (s)tep - single step to next instruction\n"); + printf(" (r)un - run until next waitin\n"); + printf(" (d)ump - dump memory/register menu\n"); + printf(" (w)rite - write memory/register menu\n"); + printf(" (p)acket - inject a pm4 packet\n"); + printf(" (h)elp - show this usage message\n"); + printf(" (q)uit - exit emulator\n"); + } else if (c == 'q') { + printf("\n"); + exit(0); + } else { + printf("invalid option: '%c'\n", c); + } + } while (true); +} + +void +emu_clear_state_change(struct emu *emu) +{ + memset(emu->control_regs.written, 0, sizeof(emu->control_regs.written)); + memset(emu->pipe_regs.written, 0, sizeof(emu->pipe_regs.written)); + memset(emu->gpu_regs.written, 0, sizeof(emu->gpu_regs.written)); + memset(emu->gpr_regs.written, 0, sizeof(emu->gpr_regs.written)); + emu->gpumem_written = ~0; +} + +void +emu_dump_state_change(struct emu *emu) +{ + unsigned i; + + /* Print the GPRs that changed: */ + BITSET_FOREACH_SET (i, emu->gpr_regs.written, EMU_NUM_GPR_REGS) { + dump_gpr_register(emu, i); + } + + BITSET_FOREACH_SET (i, emu->gpu_regs.written, EMU_NUM_GPU_REGS) { + dump_gpu_register(emu, i); + } + + BITSET_FOREACH_SET (i, emu->pipe_regs.written, EMU_NUM_PIPE_REGS) { + dump_pipe_register(emu, i); + } + + BITSET_FOREACH_SET (i, emu->control_regs.written, EMU_NUM_CONTROL_REGS) { + dump_control_register(emu, i); + } + + if (emu->gpumem_written != ~0) { + dump_gpumem(emu, emu->gpumem_written); + } +} |