• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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