Path: blob/21.2-virgl/src/freedreno/afuc/emu-ui.c
4565 views
/*1* Copyright © 2021 Google, Inc.2*3* Permission is hereby granted, free of charge, to any person obtaining a4* copy of this software and associated documentation files (the "Software"),5* to deal in the Software without restriction, including without limitation6* the rights to use, copy, modify, merge, publish, distribute, sublicense,7* and/or sell copies of the Software, and to permit persons to whom the8* Software is furnished to do so, subject to the following conditions:9*10* The above copyright notice and this permission notice (including the next11* paragraph) shall be included in all copies or substantial portions of the12* Software.13*14* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR15* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,16* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL17* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER18* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,19* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE20* SOFTWARE.21*/2223#include <assert.h>24#include <ctype.h>25#include <inttypes.h>26#include <stdio.h>27#include <stdlib.h>28#include <termios.h>29#include <unistd.h>3031#include "freedreno_pm4.h"3233#include "emu.h"34#include "util.h"3536/*37* Emulator User Interface:38*39* Handles the user prompts and input parsing.40*/4142static void43clear_line(void)44{45if (!isatty(STDOUT_FILENO))46return;47printf("\r \r");48}4950static int51readchar(void)52{53static struct termios saved_termios, unbuffered_termios;54int c;5556fflush(stdout);5758tcgetattr(STDIN_FILENO, &saved_termios);59unbuffered_termios = saved_termios;60cfmakeraw(&unbuffered_termios);6162tcsetattr(STDIN_FILENO, TCSANOW, &unbuffered_termios);63do {64c = getchar();65} while (isspace(c));66tcsetattr(STDIN_FILENO, TCSANOW, &saved_termios);6768/* TODO, read from script until EOF and then read from stdin: */69if (c == -1)70exit(0);7172return c;73}7475static const char *76extract_string(char **buf)77{78char *p = *buf;7980/* eat any leading whitespace: */81while (*p && isspace(*p))82p++;8384if (!*p)85return NULL;8687char *ret = p;8889/* skip to next whitespace: */90while (*p && !isspace(*p))91p++;9293if (*p)94*p = '\0';9596*buf = ++p;9798return ret;99}100101static size_t102readline(char **p)103{104static char *buf;105static size_t n;106107ssize_t ret = getline(&buf, &n, stdin);108if (ret < 0)109return ret;110111*p = buf;112return 0;113}114115static ssize_t116read_two_values(const char **val1, const char **val2)117{118char *p;119120ssize_t ret = readline(&p);121if (ret < 0)122return ret;123124*val1 = extract_string(&p);125*val2 = extract_string(&p);126127return 0;128}129130static ssize_t131read_one_value(const char **val)132{133char *p;134135ssize_t ret = readline(&p);136if (ret < 0)137return ret;138139*val = extract_string(&p);140141return 0;142}143144static void145dump_gpr_register(struct emu *emu, unsigned n)146{147printf(" GPR: ");148print_dst(n);149printf(": ");150if (BITSET_TEST(emu->gpr_regs.written, n)) {151printdelta("%08x\n", emu->gpr_regs.val[n]);152} else {153printf("%08x\n", emu->gpr_regs.val[n]);154}155}156157static void158dump_gpr_registers(struct emu *emu)159{160for (unsigned i = 0; i < ARRAY_SIZE(emu->gpr_regs.val); i++) {161dump_gpr_register(emu, i);162}163}164165static void166dump_gpu_register(struct emu *emu, unsigned n)167{168printf(" GPU: ");169char *name = afuc_gpu_reg_name(n);170if (name) {171printf("%s", name);172free(name);173} else {174printf("0x%04x", n);175}176printf(": ");177if (BITSET_TEST(emu->gpu_regs.written, n)) {178printdelta("%08x\n", emu->gpu_regs.val[n]);179} else {180printf("%08x\n", emu->gpu_regs.val[n]);181}182}183184static void185dump_pipe_register(struct emu *emu, unsigned n)186{187printf(" PIPE: ");188print_pipe_reg(n);189printf(": ");190if (BITSET_TEST(emu->pipe_regs.written, n)) {191printdelta("%08x\n", emu->pipe_regs.val[n]);192} else {193printf("%08x\n", emu->pipe_regs.val[n]);194}195}196197static void198dump_control_register(struct emu *emu, unsigned n)199{200printf(" CTRL: ");201print_control_reg(n);202printf(": ");203if (BITSET_TEST(emu->control_regs.written, n)) {204printdelta("%08x\n", emu->control_regs.val[n]);205} else {206printf("%08x\n", emu->control_regs.val[n]);207}208}209210static void211dump_gpumem(struct emu *emu, uintptr_t addr)212{213uint32_t val = emu_mem_read_dword(emu, addr);214215printf(" MEM: 0x%016"PRIxPTR": ", addr);216if (addr == emu->gpumem_written) {217printdelta("0x%08x\n", val);218} else {219printf("0x%08x\n", val);220}221}222223static void224emu_write_gpr_prompt(struct emu *emu)225{226clear_line();227printf(" GPR register (name or offset) and value: ");228229const char *name;230const char *value;231232if (read_two_values(&name, &value))233return;234235unsigned offset = afuc_gpr_reg(name);236uint32_t val = strtoul(value, NULL, 0);237238emu_set_gpr_reg(emu, offset, val);239}240241static void242emu_write_control_prompt(struct emu *emu)243{244clear_line();245printf(" Control register (name or offset) and value: ");246247const char *name;248const char *value;249250if (read_two_values(&name, &value))251return;252253unsigned offset = afuc_control_reg(name);254uint32_t val = strtoul(value, NULL, 0);255256emu_set_control_reg(emu, offset, val);257}258259static void260emu_dump_control_prompt(struct emu *emu)261{262clear_line();263printf(" Control register (name or offset): ");264265const char *name;266267if (read_one_value(&name))268return;269270printf("\n");271272unsigned offset = afuc_control_reg(name);273dump_control_register(emu, offset);274}275276static void277emu_write_gpu_prompt(struct emu *emu)278{279clear_line();280printf(" GPU register (name or offset) and value: ");281282const char *name;283const char *value;284285if (read_two_values(&name, &value))286return;287288unsigned offset = afuc_gpu_reg(name);289uint32_t val = strtoul(value, NULL, 0);290291emu_set_gpu_reg(emu, offset, val);292}293294static void295emu_dump_gpu_prompt(struct emu *emu)296{297clear_line();298printf(" GPU register (name or offset): ");299300const char *name;301302if (read_one_value(&name))303return;304305printf("\n");306307unsigned offset = afuc_gpu_reg(name);308dump_gpu_register(emu, offset);309}310311static void312emu_write_mem_prompt(struct emu *emu)313{314clear_line();315printf(" GPU memory offset and value: ");316317const char *offset;318const char *value;319320if (read_two_values(&offset, &value))321return;322323uintptr_t addr = strtoull(offset, NULL, 0);324uint32_t val = strtoul(value, NULL, 0);325326emu_mem_write_dword(emu, addr, val);327}328329static void330emu_dump_mem_prompt(struct emu *emu)331{332clear_line();333printf(" GPU memory offset: ");334335const char *offset;336337if (read_one_value(&offset))338return;339340printf("\n");341342uintptr_t addr = strtoull(offset, NULL, 0);343dump_gpumem(emu, addr);344}345346static void347emu_dump_prompt(struct emu *emu)348{349do {350clear_line();351printf(" dump: GPR (r)egisters, (c)ontrol register, (g)pu register, (m)emory: ");352353int c = readchar();354printf("%c\n", c);355356if (c == 'r') {357/* Since there aren't too many GPR registers, just dump358* them all:359*/360dump_gpr_registers(emu);361break;362} else if (c == 'c') {363emu_dump_control_prompt(emu);364break;365} else if (c == 'g') {366emu_dump_gpu_prompt(emu);367break;368} else if (c == 'm') {369emu_dump_mem_prompt(emu);370break;371} else {372printf("invalid option: '%c'\n", c);373break;374}375} while (true);376}377378static void379emu_write_prompt(struct emu *emu)380{381do {382clear_line();383printf(" write: GPR (r)egister, (c)ontrol register, (g)pu register, (m)emory: ");384385int c = readchar();386printf("%c\n", c);387388if (c == 'r') {389emu_write_gpr_prompt(emu);390break;391} else if (c == 'c') {392emu_write_control_prompt(emu);393break;394} else if (c == 'g') {395emu_write_gpu_prompt(emu);396break;397} else if (c == 'm') {398emu_write_mem_prompt(emu);399break;400} else {401printf("invalid option: '%c'\n", c);402break;403}404} while (true);405}406407static void408emu_packet_prompt(struct emu *emu)409{410clear_line();411printf(" Enter packet (opc or register name), followed by payload: ");412fflush(stdout);413414char *p;415if (readline(&p) < 0)416return;417418printf("\n");419420const char *name = extract_string(&p);421422/* Read the payload, so we can know the size to generate correct header: */423uint32_t payload[0x7f];424unsigned cnt = 0;425426do {427const char *val = extract_string(&p);428if (!val)429break;430431assert(cnt < ARRAY_SIZE(payload));432payload[cnt++] = strtoul(val, NULL, 0);433} while (true);434435uint32_t hdr;436if (afuc_pm4_id(name) >= 0) {437unsigned opcode = afuc_pm4_id(name);438hdr = pm4_pkt7_hdr(opcode, cnt);439} else {440unsigned regindx = afuc_gpu_reg(name);441hdr = pm4_pkt4_hdr(regindx, cnt);442}443444ASSERTED bool ret = emu_queue_push(&emu->roq, hdr);445assert(ret);446447for (unsigned i = 0; i < cnt; i++) {448ASSERTED bool ret = emu_queue_push(&emu->roq, payload[i]);449assert(ret);450}451}452453void454emu_main_prompt(struct emu *emu)455{456if (emu->run_mode)457return;458459do {460clear_line();461printf("(s)tep, (r)un, (d)ump, (w)rite, (p)acket, (h)elp, (q)uit: ");462463int c = readchar();464465printf("%c\n", c);466467if (c == 's') {468break;469} else if (c == 'r') {470emu->run_mode = true;471break;472} else if (c == 'd') {473emu_dump_prompt(emu);474} else if (c == 'w') {475emu_write_prompt(emu);476} else if (c == 'p') {477emu_packet_prompt(emu);478} else if (c == 'h') {479printf(" (s)tep - single step to next instruction\n");480printf(" (r)un - run until next waitin\n");481printf(" (d)ump - dump memory/register menu\n");482printf(" (w)rite - write memory/register menu\n");483printf(" (p)acket - inject a pm4 packet\n");484printf(" (h)elp - show this usage message\n");485printf(" (q)uit - exit emulator\n");486} else if (c == 'q') {487printf("\n");488exit(0);489} else {490printf("invalid option: '%c'\n", c);491}492} while (true);493}494495void496emu_clear_state_change(struct emu *emu)497{498memset(emu->control_regs.written, 0, sizeof(emu->control_regs.written));499memset(emu->pipe_regs.written, 0, sizeof(emu->pipe_regs.written));500memset(emu->gpu_regs.written, 0, sizeof(emu->gpu_regs.written));501memset(emu->gpr_regs.written, 0, sizeof(emu->gpr_regs.written));502emu->gpumem_written = ~0;503}504505void506emu_dump_state_change(struct emu *emu)507{508unsigned i;509510if (emu->quiet)511return;512513/* Print the GPRs that changed: */514BITSET_FOREACH_SET (i, emu->gpr_regs.written, EMU_NUM_GPR_REGS) {515dump_gpr_register(emu, i);516}517518BITSET_FOREACH_SET (i, emu->gpu_regs.written, EMU_NUM_GPU_REGS) {519dump_gpu_register(emu, i);520}521522BITSET_FOREACH_SET (i, emu->pipe_regs.written, EMU_NUM_PIPE_REGS) {523dump_pipe_register(emu, i);524}525526BITSET_FOREACH_SET (i, emu->control_regs.written, EMU_NUM_CONTROL_REGS) {527dump_control_register(emu, i);528}529530if (emu->gpumem_written != ~0) {531dump_gpumem(emu, emu->gpumem_written);532}533}534535536