296 lines
6.7 KiB
C
296 lines
6.7 KiB
C
/*
|
|
* 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 <err.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "rnn.h"
|
|
#include "rnndec.h"
|
|
|
|
#include "afuc.h"
|
|
#include "util.h"
|
|
|
|
static struct rnndeccontext *ctx;
|
|
static struct rnndb *db;
|
|
static struct rnndomain *control_regs;
|
|
static struct rnndomain *pipe_regs;
|
|
struct rnndomain *dom[2];
|
|
static struct rnnenum *pm4_packets;
|
|
|
|
static int
|
|
find_reg(struct rnndomain *dom, const char *name)
|
|
{
|
|
for (int i = 0; i < dom->subelemsnum; i++)
|
|
if (!strcmp(name, dom->subelems[i]->name))
|
|
return dom->subelems[i]->offset;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static unsigned
|
|
reg(struct rnndomain *dom, const char *type, const char *name)
|
|
{
|
|
int val = find_reg(dom, name);
|
|
if (val < 0) {
|
|
char *endptr = NULL;
|
|
val = strtol(name, &endptr, 0);
|
|
if (endptr && *endptr) {
|
|
printf("invalid %s reg: %s\n", type, name);
|
|
exit(2);
|
|
}
|
|
}
|
|
return (unsigned)val;
|
|
}
|
|
|
|
static char *
|
|
reg_name(struct rnndomain *dom, unsigned id)
|
|
{
|
|
if (rnndec_checkaddr(ctx, dom, id, 0)) {
|
|
struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, dom, id, 0);
|
|
char *name = info->name;
|
|
free(info);
|
|
return name;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Map control reg name to offset.
|
|
*/
|
|
unsigned
|
|
afuc_control_reg(const char *name)
|
|
{
|
|
return reg(control_regs, "control", name);
|
|
}
|
|
|
|
/**
|
|
* Map offset to control reg name (or NULL), caller frees
|
|
*/
|
|
char *
|
|
afuc_control_reg_name(unsigned id)
|
|
{
|
|
return reg_name(control_regs, id);
|
|
}
|
|
|
|
/**
|
|
* Map pipe reg name to offset.
|
|
*/
|
|
unsigned
|
|
afuc_pipe_reg(const char *name)
|
|
{
|
|
return reg(pipe_regs, "pipe", name);
|
|
}
|
|
|
|
/**
|
|
* "void" pipe regs don't have a value written, the $addr right is
|
|
* enough to trigger what they do
|
|
*/
|
|
bool
|
|
afuc_pipe_reg_is_void(unsigned id)
|
|
{
|
|
if (rnndec_checkaddr(ctx, pipe_regs, id, 0)) {
|
|
struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, pipe_regs, id, 0);
|
|
free(info->name);
|
|
bool ret = !strcmp(info->typeinfo->name, "void");
|
|
free(info);
|
|
return ret;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Map offset to pipe reg name (or NULL), caller frees
|
|
*/
|
|
char *
|
|
afuc_pipe_reg_name(unsigned id)
|
|
{
|
|
return reg_name(pipe_regs, id);
|
|
}
|
|
|
|
/**
|
|
* Map GPU reg name to offset.
|
|
*/
|
|
unsigned
|
|
afuc_gpu_reg(const char *name)
|
|
{
|
|
int val = find_reg(dom[0], name);
|
|
if (val < 0)
|
|
val = find_reg(dom[1], name);
|
|
if (val < 0) {
|
|
char *endptr = NULL;
|
|
val = strtol(name, &endptr, 0);
|
|
if (endptr && *endptr) {
|
|
printf("invalid control reg: %s\n", name);
|
|
exit(2);
|
|
}
|
|
}
|
|
return (unsigned)val;
|
|
}
|
|
|
|
/**
|
|
* Map offset to gpu reg name (or NULL), caller frees
|
|
*/
|
|
char *
|
|
afuc_gpu_reg_name(unsigned id)
|
|
{
|
|
struct rnndomain *d = NULL;
|
|
|
|
if (rnndec_checkaddr(ctx, dom[0], id, 0)) {
|
|
d = dom[0];
|
|
} else if (rnndec_checkaddr(ctx, dom[1], id, 0)) {
|
|
d = dom[1];
|
|
}
|
|
|
|
if (d) {
|
|
struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, d, id, 0);
|
|
if (info) {
|
|
char *name = info->name;
|
|
free(info);
|
|
return name;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned
|
|
afuc_gpr_reg(const char *name)
|
|
{
|
|
/* If it starts with '$' just swallow it: */
|
|
if (name[0] == '$')
|
|
name++;
|
|
|
|
/* handle aliases: */
|
|
if (!strcmp(name, "rem")) {
|
|
return REG_REM;
|
|
} else if (!strcmp(name, "memdata")) {
|
|
return REG_MEMDATA;
|
|
} else if (!strcmp(name, "addr")) {
|
|
return REG_ADDR;
|
|
} else if (!strcmp(name, "regdata")) {
|
|
return REG_REGDATA;
|
|
} else if (!strcmp(name, "usraddr")) {
|
|
return REG_USRADDR;
|
|
} else if (!strcmp(name, "data")) {
|
|
return REG_DATA;
|
|
} else {
|
|
char *endptr = NULL;
|
|
unsigned val = strtol(name, &endptr, 16);
|
|
if (endptr && *endptr) {
|
|
printf("invalid gpr reg: %s\n", name);
|
|
exit(2);
|
|
}
|
|
return val;
|
|
}
|
|
}
|
|
|
|
static int
|
|
find_enum_val(struct rnnenum *en, const char *name)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < en->valsnum; i++)
|
|
if (en->vals[i]->valvalid && !strcmp(name, en->vals[i]->name))
|
|
return en->vals[i]->value;
|
|
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Map pm4 packet name to id
|
|
*/
|
|
int
|
|
afuc_pm4_id(const char *name)
|
|
{
|
|
return find_enum_val(pm4_packets, name);
|
|
}
|
|
|
|
const char *
|
|
afuc_pm_id_name(unsigned id)
|
|
{
|
|
return rnndec_decode_enum(ctx, "adreno_pm4_type3_packets", id);
|
|
}
|
|
|
|
void
|
|
afuc_printc(enum afuc_color c, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
if (c == AFUC_ERR) {
|
|
printf("%s", ctx->colors->err);
|
|
} else if (c == AFUC_LBL) {
|
|
printf("%s", ctx->colors->btarg);
|
|
}
|
|
va_start(args, fmt);
|
|
vprintf(fmt, args);
|
|
va_end(args);
|
|
printf("%s", ctx->colors->reset);
|
|
}
|
|
|
|
int afuc_util_init(int gpuver, bool colors)
|
|
{
|
|
char *name, *control_reg_name;
|
|
char *pipe_reg_name = NULL;
|
|
|
|
switch (gpuver) {
|
|
case 6:
|
|
name = "A6XX";
|
|
control_reg_name = "A6XX_CONTROL_REG";
|
|
pipe_reg_name = "A6XX_PIPE_REG";
|
|
break;
|
|
case 5:
|
|
name = "A5XX";
|
|
control_reg_name = "A5XX_CONTROL_REG";
|
|
pipe_reg_name = "A5XX_PIPE_REG";
|
|
break;
|
|
default:
|
|
fprintf(stderr, "unknown GPU version!\n");
|
|
return -1;
|
|
}
|
|
|
|
rnn_init();
|
|
db = rnn_newdb();
|
|
|
|
ctx = rnndec_newcontext(db);
|
|
ctx->colors = colors ? &envy_def_colors : &envy_null_colors;
|
|
|
|
rnn_parsefile(db, "adreno.xml");
|
|
rnn_prepdb(db);
|
|
if (db->estatus)
|
|
errx(db->estatus, "failed to parse register database");
|
|
dom[0] = rnn_finddomain(db, name);
|
|
dom[1] = rnn_finddomain(db, "AXXX");
|
|
control_regs = rnn_finddomain(db, control_reg_name);
|
|
pipe_regs = rnn_finddomain(db, pipe_reg_name);
|
|
|
|
rnndec_varadd(ctx, "chip", name);
|
|
|
|
pm4_packets = rnn_findenum(ctx->db, "adreno_pm4_type3_packets");
|
|
|
|
return 0;
|
|
}
|
|
|