1 /*
2 * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
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 <err.h>
25 #include <stdint.h>
26 #include <stdbool.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <getopt.h>
35
36 #include "afuc.h"
37 #include "rnn.h"
38 #include "rnndec.h"
39 #include "parser.h"
40 #include "asm.h"
41
42 int gpuver;
43
44
45 static struct rnndeccontext *ctx;
46 static struct rnndb *db;
47 static struct rnndomain *control_regs;
48 struct rnndomain *dom[2];
49
50
51 /* bit lame to hard-code max but fw sizes are small */
52 static struct asm_instruction instructions[0x2000];
53 static unsigned num_instructions;
54
55 static struct asm_label labels[0x512];
56 static unsigned num_labels;
57
next_instr(int tok)58 struct asm_instruction *next_instr(int tok)
59 {
60 struct asm_instruction *ai = &instructions[num_instructions++];
61 assert(num_instructions < ARRAY_SIZE(instructions));
62 ai->tok = tok;
63 return ai;
64 }
65
decl_label(const char * str)66 void decl_label(const char *str)
67 {
68 struct asm_label *label = &labels[num_labels++];
69
70 assert(num_labels < ARRAY_SIZE(labels));
71
72 label->offset = num_instructions;
73 label->label = str;
74 }
75
resolve_label(const char * str)76 static int resolve_label(const char *str)
77 {
78 int i;
79
80 for (i = 0; i < num_labels; i++) {
81 struct asm_label *label = &labels[i];
82
83 if (!strcmp(str, label->label)) {
84 return label->offset;
85 }
86 }
87
88 fprintf(stderr, "Undeclared label: %s\n", str);
89 exit(2);
90 }
91
tok2alu(int tok)92 static afuc_opc tok2alu(int tok)
93 {
94 switch (tok) {
95 case T_OP_ADD: return OPC_ADD;
96 case T_OP_ADDHI: return OPC_ADDHI;
97 case T_OP_SUB: return OPC_SUB;
98 case T_OP_SUBHI: return OPC_SUBHI;
99 case T_OP_AND: return OPC_AND;
100 case T_OP_OR: return OPC_OR;
101 case T_OP_XOR: return OPC_XOR;
102 case T_OP_NOT: return OPC_NOT;
103 case T_OP_SHL: return OPC_SHL;
104 case T_OP_USHR: return OPC_USHR;
105 case T_OP_ISHR: return OPC_ISHR;
106 case T_OP_ROT: return OPC_ROT;
107 case T_OP_MUL8: return OPC_MUL8;
108 case T_OP_MIN: return OPC_MIN;
109 case T_OP_MAX: return OPC_MAX;
110 case T_OP_CMP: return OPC_CMP;
111 case T_OP_MSB: return OPC_MSB;
112 default:
113 assert(0);
114 return -1;
115 }
116 }
117
emit_instructions(int outfd)118 static void emit_instructions(int outfd)
119 {
120 int i;
121
122 /* there is an extra 0x00000000 which kernel strips off.. we could
123 * perhaps use it for versioning.
124 */
125 i = 0;
126 write(outfd, &i, 4);
127
128 for (i = 0; i < num_instructions; i++) {
129 struct asm_instruction *ai = &instructions[i];
130 afuc_instr instr = {0};
131 afuc_opc opc;
132
133 /* special case, 2nd dword is patched up w/ # of instructions
134 * (ie. offset of jmptbl)
135 */
136 if (i == 1) {
137 assert(ai->is_literal);
138 ai->literal &= ~0xffff;
139 ai->literal |= num_instructions;
140 }
141
142 if (ai->is_literal) {
143 write(outfd, &ai->literal, 4);
144 continue;
145 }
146
147 switch (ai->tok) {
148 case T_OP_NOP:
149 opc = OPC_NOP;
150 if (gpuver >= 6)
151 instr.pad = 0x1000000;
152 break;
153 case T_OP_ADD:
154 case T_OP_ADDHI:
155 case T_OP_SUB:
156 case T_OP_SUBHI:
157 case T_OP_AND:
158 case T_OP_OR:
159 case T_OP_XOR:
160 case T_OP_NOT:
161 case T_OP_SHL:
162 case T_OP_USHR:
163 case T_OP_ISHR:
164 case T_OP_ROT:
165 case T_OP_MUL8:
166 case T_OP_MIN:
167 case T_OP_MAX:
168 case T_OP_CMP:
169 case T_OP_MSB:
170 if (ai->has_immed) {
171 /* MSB overlaps with STORE */
172 assert(ai->tok != T_OP_MSB);
173 if (ai->xmov) {
174 fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
175 exit(1);
176 }
177 opc = tok2alu(ai->tok);
178 instr.alui.dst = ai->dst;
179 instr.alui.src = ai->src1;
180 instr.alui.uimm = ai->immed;
181 } else {
182 opc = OPC_ALU;
183 instr.alu.dst = ai->dst;
184 instr.alu.src1 = ai->src1;
185 instr.alu.src2 = ai->src2;
186 instr.alu.xmov = ai->xmov;
187 instr.alu.alu = tok2alu(ai->tok);
188 }
189 break;
190 case T_OP_MOV:
191 /* move can either be encoded as movi (ie. move w/ immed) or
192 * an alu instruction
193 */
194 if ((ai->has_immed || ai->label) && ai->xmov) {
195 fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
196 exit(1);
197 }
198 if (ai->has_immed) {
199 opc = OPC_MOVI;
200 instr.movi.dst = ai->dst;
201 instr.movi.uimm = ai->immed;
202 instr.movi.shift = ai->shift;
203 } else if (ai->label) {
204 /* mov w/ a label is just an alias for an immediate, this
205 * is useful to load the address of a constant table into
206 * a register:
207 */
208 opc = OPC_MOVI;
209 instr.movi.dst = ai->dst;
210 instr.movi.uimm = resolve_label(ai->label);
211 instr.movi.shift = ai->shift;
212 } else {
213 /* encode as: or $dst, $00, $src */
214 opc = OPC_ALU;
215 instr.alu.dst = ai->dst;
216 instr.alu.src1 = 0x00; /* $00 reads-back 0 */
217 instr.alu.src2 = ai->src1;
218 instr.alu.xmov = ai->xmov;
219 instr.alu.alu = OPC_OR;
220 }
221 break;
222 case T_OP_CWRITE:
223 case T_OP_CREAD:
224 case T_OP_STORE:
225 case T_OP_LOAD:
226 if (gpuver >= 6) {
227 if (ai->tok == T_OP_CWRITE) {
228 opc = OPC_CWRITE6;
229 } else if (ai->tok == T_OP_CREAD) {
230 opc = OPC_CREAD6;
231 } else if (ai->tok == T_OP_STORE) {
232 opc = OPC_STORE6;
233 } else if (ai->tok == T_OP_LOAD) {
234 opc = OPC_LOAD6;
235 }
236 } else {
237 if (ai->tok == T_OP_CWRITE) {
238 opc = OPC_CWRITE5;
239 } else if (ai->tok == T_OP_CREAD) {
240 opc = OPC_CREAD5;
241 } else if (ai->tok == T_OP_STORE ||
242 ai->tok == T_OP_LOAD) {
243 fprintf(stderr, "load and store do not exist on a5xx\n");
244 exit(1);
245 }
246 }
247 instr.control.src1 = ai->src1;
248 instr.control.src2 = ai->src2;
249 instr.control.flags = ai->bit;
250 instr.control.uimm = ai->immed;
251 break;
252 case T_OP_BRNE:
253 case T_OP_BREQ:
254 if (ai->has_immed) {
255 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI;
256 instr.br.bit_or_imm = ai->immed;
257 } else {
258 opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB;
259 instr.br.bit_or_imm = ai->bit;
260 }
261 instr.br.src = ai->src1;
262 instr.br.ioff = resolve_label(ai->label) - i;
263 break;
264 case T_OP_RET:
265 opc = OPC_RET;
266 break;
267 case T_OP_IRET:
268 opc = OPC_RET;
269 instr.ret.interrupt = 1;
270 break;
271 case T_OP_CALL:
272 opc = OPC_CALL;
273 instr.call.uoff = resolve_label(ai->label);
274 break;
275 case T_OP_PREEMPTLEAVE:
276 opc = OPC_PREEMPTLEAVE6;
277 instr.call.uoff = resolve_label(ai->label);
278 break;
279 case T_OP_SETSECURE:
280 opc = OPC_SETSECURE;
281 if (resolve_label(ai->label) != i + 3) {
282 fprintf(stderr, "jump label %s is incorrect for setsecure\n", ai->label);
283 exit(1);
284 }
285 if (ai->src1 != 0x2) {
286 fprintf(stderr, "source for setsecure must be $02\n");
287 exit(1);
288 }
289 break;
290 case T_OP_JUMP:
291 /* encode jump as: brne $00, b0, #label */
292 opc = OPC_BRNEB;
293 instr.br.bit_or_imm = 0;
294 instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */
295 instr.br.ioff = resolve_label(ai->label) - i;
296 break;
297 case T_OP_WAITIN:
298 opc = OPC_WIN;
299 break;
300 default:
301 assert(0);
302 }
303
304 afuc_set_opc(&instr, opc, ai->rep);
305
306 write(outfd, &instr, 4);
307 }
308
309 }
310
find_enum_val(struct rnnenum * en,const char * name)311 static int find_enum_val(struct rnnenum *en, const char *name)
312 {
313 int i;
314
315 for (i = 0; i < en->valsnum; i++)
316 if (en->vals[i]->valvalid && !strcmp(name, en->vals[i]->name))
317 return en->vals[i]->value;
318
319 return -1;
320 }
321
find_reg(struct rnndomain * dom,const char * name)322 static int find_reg(struct rnndomain *dom, const char *name)
323 {
324 int i;
325
326 for (i = 0; i < dom->subelemsnum; i++)
327 if (!strcmp(name, dom->subelems[i]->name))
328 return dom->subelems[i]->offset;
329
330 return -1;
331 }
332
parse_control_reg(const char * name)333 unsigned parse_control_reg(const char *name)
334 {
335 /* skip leading "@" */
336 int val = find_reg(control_regs, name + 1);
337 if (val < 0) {
338 printf("invalid control reg: %s\n", name);
339 exit(2);
340 }
341 return (unsigned)val;
342 }
343
emit_jumptable(int outfd)344 static void emit_jumptable(int outfd)
345 {
346 struct rnnenum *en = rnn_findenum(ctx->db, "adreno_pm4_type3_packets");
347 uint32_t jmptable[0x80] = {0};
348 int i;
349
350 for (i = 0; i < num_labels; i++) {
351 struct asm_label *label = &labels[i];
352 int id = find_enum_val(en, label->label);
353
354 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
355 if (id < 0) {
356 if (sscanf(label->label, "UNKN%d", &id) != 1) {
357 /* if still not found, must not belong in jump-table: */
358 continue;
359 }
360 }
361
362 jmptable[id] = label->offset;
363 }
364
365 write(outfd, jmptable, sizeof(jmptable));
366 }
367
usage(void)368 static void usage(void)
369 {
370 fprintf(stderr, "Usage:\n"
371 "\tasm [-g GPUVER] filename.asm filename.fw\n"
372 "\t\t-g - specify GPU version (5, etc)\n"
373 );
374 exit(2);
375 }
376
main(int argc,char ** argv)377 int main(int argc, char **argv)
378 {
379 FILE *in;
380 char *file, *outfile, *name, *control_reg_name;
381 int c, ret, outfd;
382
383 /* Argument parsing: */
384 while ((c = getopt (argc, argv, "g:")) != -1) {
385 switch (c) {
386 case 'g':
387 gpuver = atoi(optarg);
388 break;
389 default:
390 usage();
391 }
392 }
393
394 if (optind >= (argc + 1)) {
395 fprintf(stderr, "no file specified!\n");
396 usage();
397 }
398
399 file = argv[optind];
400 outfile = argv[optind + 1];
401
402 outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
403 if (outfd < 0) {
404 fprintf(stderr, "could not open \"%s\"\n", outfile);
405 usage();
406 }
407
408 in = fopen(file, "r");
409 if (!in) {
410 fprintf(stderr, "could not open \"%s\"\n", file);
411 usage();
412 }
413
414 yyset_in(in);
415
416 /* if gpu version not specified, infer from filename: */
417 if (!gpuver) {
418 if (strstr(file, "a5")) {
419 gpuver = 5;
420 } else if (strstr(file, "a6")) {
421 gpuver = 6;
422 }
423 }
424
425 switch (gpuver) {
426 case 6:
427 name = "A6XX";
428 control_reg_name = "A6XX_CONTROL_REG";
429 break;
430 case 5:
431 name = "A5XX";
432 control_reg_name = "A5XX_CONTROL_REG";
433 break;
434 default:
435 fprintf(stderr, "unknown GPU version!\n");
436 usage();
437 }
438
439 rnn_init();
440 db = rnn_newdb();
441
442 ctx = rnndec_newcontext(db);
443
444 rnn_parsefile(db, "adreno.xml");
445 rnn_prepdb(db);
446 if (db->estatus)
447 errx(db->estatus, "failed to parse register database");
448 dom[0] = rnn_finddomain(db, name);
449 dom[1] = rnn_finddomain(db, "AXXX");
450 control_regs = rnn_finddomain(db, control_reg_name);
451
452 ret = yyparse();
453 if (ret) {
454 fprintf(stderr, "parse failed: %d\n", ret);
455 return ret;
456 }
457
458 emit_instructions(outfd);
459 emit_jumptable(outfd);
460
461 close(outfd);
462
463 return 0;
464 }
465