1 /*
2 * Copyright © 2021 Google, Inc.
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include <err.h>
7 #include <stdarg.h>
8 #include <stdio.h>
9 #include <string.h>
10
11 #include "rnn.h"
12 #include "rnndec.h"
13
14 #include "afuc.h"
15 #include "util.h"
16
17 static struct rnndeccontext *ctx;
18 static struct rnndb *db;
19 static struct rnndomain *control_regs;
20 static struct rnndomain *sqe_regs;
21 static struct rnndomain *pipe_regs;
22 struct rnndomain *dom[2];
23 static struct rnnenum *pm4_packets;
24
25 static int
find_reg(struct rnndomain * dom,const char * name)26 find_reg(struct rnndomain *dom, const char *name)
27 {
28 for (int i = 0; i < dom->subelemsnum; i++)
29 if (!strcmp(name, dom->subelems[i]->name))
30 return dom->subelems[i]->offset;
31
32 return -1;
33 }
34
35 static unsigned
reg(struct rnndomain * dom,const char * type,const char * name)36 reg(struct rnndomain *dom, const char *type, const char *name)
37 {
38 int val = find_reg(dom, name);
39 if (val < 0) {
40 char *endptr = NULL;
41 val = strtol(name, &endptr, 0);
42 if (endptr && *endptr) {
43 printf("invalid %s reg: %s\n", type, name);
44 exit(2);
45 }
46 }
47 return (unsigned)val;
48 }
49
50 static char *
reg_name(struct rnndomain * dom,unsigned id)51 reg_name(struct rnndomain *dom, unsigned id)
52 {
53 if (rnndec_checkaddr(ctx, dom, id, 0)) {
54 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, dom, id, 0);
55 char *name = info->name;
56 free(info);
57 return name;
58 } else {
59 return NULL;
60 }
61 }
62
63 /**
64 * Map control reg name to offset.
65 */
66 unsigned
afuc_control_reg(const char * name)67 afuc_control_reg(const char *name)
68 {
69 return reg(control_regs, "control", name);
70 }
71
72 /**
73 * Map offset to SQE reg name (or NULL), caller frees
74 */
75 char *
afuc_sqe_reg_name(unsigned id)76 afuc_sqe_reg_name(unsigned id)
77 {
78 return reg_name(sqe_regs, id);
79 }
80
81 /**
82 * Map SQE reg name to offset.
83 */
84 unsigned
afuc_sqe_reg(const char * name)85 afuc_sqe_reg(const char *name)
86 {
87 return reg(sqe_regs, "SQE", name);
88 }
89
90 /**
91 * Map offset to control reg name (or NULL), caller frees
92 */
93 char *
afuc_control_reg_name(unsigned id)94 afuc_control_reg_name(unsigned id)
95 {
96 return reg_name(control_regs, id);
97 }
98
99 /**
100 * Map pipe reg name to offset.
101 */
102 unsigned
afuc_pipe_reg(const char * name)103 afuc_pipe_reg(const char *name)
104 {
105 return reg(pipe_regs, "pipe", name);
106 }
107
108 /**
109 * "void" pipe regs don't have a value written, the $addr right is
110 * enough to trigger what they do
111 */
112 bool
afuc_pipe_reg_is_void(unsigned id)113 afuc_pipe_reg_is_void(unsigned id)
114 {
115 if (rnndec_checkaddr(ctx, pipe_regs, id, 0)) {
116 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, pipe_regs, id, 0);
117 free(info->name);
118 bool ret = !strcmp(info->typeinfo->name, "void");
119 free(info);
120 return ret;
121 } else {
122 return false;
123 }
124 }
125
126 /**
127 * Map offset to pipe reg name (or NULL), caller frees
128 */
129 char *
afuc_pipe_reg_name(unsigned id)130 afuc_pipe_reg_name(unsigned id)
131 {
132 return reg_name(pipe_regs, id);
133 }
134
135 /**
136 * Map GPU reg name to offset.
137 */
138 unsigned
afuc_gpu_reg(const char * name)139 afuc_gpu_reg(const char *name)
140 {
141 int val = find_reg(dom[0], name);
142 if (val < 0)
143 val = find_reg(dom[1], name);
144 if (val < 0) {
145 char *endptr = NULL;
146 val = strtol(name, &endptr, 0);
147 if (endptr && *endptr) {
148 printf("invalid control reg: %s\n", name);
149 exit(2);
150 }
151 }
152 return (unsigned)val;
153 }
154
155 /**
156 * Map offset to gpu reg name (or NULL), caller frees
157 */
158 char *
afuc_gpu_reg_name(unsigned id)159 afuc_gpu_reg_name(unsigned id)
160 {
161 struct rnndomain *d = NULL;
162
163 if (rnndec_checkaddr(ctx, dom[0], id, 0)) {
164 d = dom[0];
165 } else if (rnndec_checkaddr(ctx, dom[1], id, 0)) {
166 d = dom[1];
167 }
168
169 if (d) {
170 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, d, id, 0);
171 if (info) {
172 char *name = info->name;
173 free(info);
174 return name;
175 }
176 }
177
178 return NULL;
179 }
180
181 unsigned
afuc_gpr_reg(const char * name)182 afuc_gpr_reg(const char *name)
183 {
184 /* If it starts with '$' just swallow it: */
185 if (name[0] == '$')
186 name++;
187
188 /* handle aliases: */
189 if (!strcmp(name, "rem")) {
190 return REG_REM;
191 } else if (!strcmp(name, "memdata")) {
192 return REG_MEMDATA;
193 } else if (!strcmp(name, "addr")) {
194 return REG_ADDR;
195 } else if (!strcmp(name, "regdata")) {
196 return REG_REGDATA;
197 } else if (!strcmp(name, "usraddr")) {
198 return REG_USRADDR;
199 } else if (!strcmp(name, "data")) {
200 return REG_DATA;
201 } else {
202 char *endptr = NULL;
203 unsigned val = strtol(name, &endptr, 16);
204 if (endptr && *endptr) {
205 printf("invalid gpr reg: %s\n", name);
206 exit(2);
207 }
208 return val;
209 }
210 }
211
212 static int
find_enum_val(struct rnnenum * en,const char * name)213 find_enum_val(struct rnnenum *en, const char *name)
214 {
215 int i;
216
217 for (i = 0; i < en->valsnum; i++)
218 if (en->vals[i]->valvalid && !strcmp(name, en->vals[i]->name))
219 return en->vals[i]->value;
220
221 return -1;
222 }
223
224 /**
225 * Map pm4 packet name to id
226 */
227 int
afuc_pm4_id(const char * name)228 afuc_pm4_id(const char *name)
229 {
230 return find_enum_val(pm4_packets, name);
231 }
232
233 const char *
afuc_pm_id_name(unsigned id)234 afuc_pm_id_name(unsigned id)
235 {
236 return rnndec_decode_enum(ctx, "adreno_pm4_type3_packets", id);
237 }
238
239 void
afuc_printc(enum afuc_color c,const char * fmt,...)240 afuc_printc(enum afuc_color c, const char *fmt, ...)
241 {
242 va_list args;
243 if (c == AFUC_ERR) {
244 printf("%s", ctx->colors->err);
245 } else if (c == AFUC_LBL) {
246 printf("%s", ctx->colors->btarg);
247 }
248 va_start(args, fmt);
249 vprintf(fmt, args);
250 va_end(args);
251 printf("%s", ctx->colors->reset);
252 }
253
afuc_util_init(enum afuc_fwid fw_id,int * gpuver_out,bool colors)254 int afuc_util_init(enum afuc_fwid fw_id, int *gpuver_out, bool colors)
255 {
256 char *name, *control_reg_name, *variant;
257 char *pipe_reg_name = NULL;
258
259 switch (fw_id) {
260 case AFUC_A750:
261 name = "A6XX";
262 variant = "A7XX";
263 control_reg_name = "A7XX_GEN3_CONTROL_REG";
264 pipe_reg_name = "A7XX_PIPE_REG";
265 *gpuver_out = 7;
266 break;
267 case AFUC_A730:
268 case AFUC_A740:
269 name = "A6XX";
270 variant = "A7XX";
271 control_reg_name = "A7XX_CONTROL_REG";
272 pipe_reg_name = "A7XX_PIPE_REG";
273 *gpuver_out = 7;
274 break;
275 case AFUC_A630:
276 case AFUC_A650:
277 case AFUC_A660:
278 name = "A6XX";
279 variant = "A6XX";
280 control_reg_name = "A6XX_CONTROL_REG";
281 pipe_reg_name = "A6XX_PIPE_REG";
282 *gpuver_out = 6;
283 break;
284 case AFUC_A530:
285 name = "A5XX";
286 variant = "A5XX";
287 control_reg_name = "A5XX_CONTROL_REG";
288 pipe_reg_name = "A5XX_PIPE_REG";
289 *gpuver_out = 5;
290 break;
291 default:
292 fprintf(stderr, "unknown GPU version %03x!\n", fw_id);
293 return -1;
294 }
295
296 rnn_init();
297 db = rnn_newdb();
298
299 ctx = rnndec_newcontext(db);
300 ctx->colors = colors ? &envy_def_colors : &envy_null_colors;
301
302 rnn_parsefile(db, "adreno.xml");
303 rnn_prepdb(db);
304 if (db->estatus)
305 errx(db->estatus, "failed to parse register database");
306 dom[0] = rnn_finddomain(db, name);
307 dom[1] = rnn_finddomain(db, "AXXX");
308 control_regs = rnn_finddomain(db, control_reg_name);
309 sqe_regs = rnn_finddomain(db, "A6XX_SQE_REG");
310 pipe_regs = rnn_finddomain(db, pipe_reg_name);
311
312 rnndec_varadd(ctx, "chip", variant);
313
314 pm4_packets = rnn_findenum(ctx->db, "adreno_pm4_type3_packets");
315
316 return 0;
317 }
318
319