2 * Copyright (c) 2017 Rob Clark <robdclark@gmail.com>
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:
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
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
45 static struct rnndeccontext
*ctx
;
46 static struct rnndb
*db
;
47 static struct rnndomain
*control_regs
;
48 struct rnndomain
*dom
[2];
51 /* bit lame to hard-code max but fw sizes are small */
52 static struct asm_instruction instructions
[0x2000];
53 static unsigned num_instructions
;
55 static struct asm_label labels
[0x512];
56 static unsigned num_labels
;
58 struct asm_instruction
*next_instr(int tok
)
60 struct asm_instruction
*ai
= &instructions
[num_instructions
++];
61 assert(num_instructions
< ARRAY_SIZE(instructions
));
66 void decl_label(const char *str
)
68 struct asm_label
*label
= &labels
[num_labels
++];
70 assert(num_labels
< ARRAY_SIZE(labels
));
72 label
->offset
= num_instructions
;
76 static int resolve_label(const char *str
)
80 for (i
= 0; i
< num_labels
; i
++) {
81 struct asm_label
*label
= &labels
[i
];
83 if (!strcmp(str
, label
->label
)) {
88 fprintf(stderr
, "Undeclared label: %s\n", str
);
92 static afuc_opc
tok2alu(int 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
;
118 static void emit_instructions(int outfd
)
122 /* there is an extra 0x00000000 which kernel strips off.. we could
123 * perhaps use it for versioning.
128 for (i
= 0; i
< num_instructions
; i
++) {
129 struct asm_instruction
*ai
= &instructions
[i
];
130 afuc_instr instr
= {0};
133 /* special case, 2nd dword is patched up w/ # of instructions
134 * (ie. offset of jmptbl)
137 assert(ai
->is_literal
);
138 ai
->literal
&= ~0xffff;
139 ai
->literal
|= num_instructions
;
142 if (ai
->is_literal
) {
143 write(outfd
, &ai
->literal
, 4);
151 instr
.pad
= 0x1000000;
171 /* MSB overlaps with STORE */
172 assert(ai
->tok
!= T_OP_MSB
);
174 fprintf(stderr
, "ALU instruction cannot have immediate and xmov\n");
177 opc
= tok2alu(ai
->tok
);
178 instr
.alui
.dst
= ai
->dst
;
179 instr
.alui
.src
= ai
->src1
;
180 instr
.alui
.uimm
= ai
->immed
;
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
);
191 /* move can either be encoded as movi (ie. move w/ immed) or
194 if ((ai
->has_immed
|| ai
->label
) && ai
->xmov
) {
195 fprintf(stderr
, "ALU instruction cannot have immediate and xmov\n");
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
209 instr
.movi
.dst
= ai
->dst
;
210 instr
.movi
.uimm
= resolve_label(ai
->label
);
211 instr
.movi
.shift
= ai
->shift
;
213 /* encode as: or $dst, $00, $src */
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
;
227 if (ai
->tok
== T_OP_CWRITE
) {
229 } else if (ai
->tok
== T_OP_CREAD
) {
231 } else if (ai
->tok
== T_OP_STORE
) {
233 } else if (ai
->tok
== T_OP_LOAD
) {
237 if (ai
->tok
== T_OP_CWRITE
) {
239 } else if (ai
->tok
== T_OP_CREAD
) {
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");
247 instr
.control
.src1
= ai
->src1
;
248 instr
.control
.src2
= ai
->src2
;
249 instr
.control
.flags
= ai
->bit
;
250 instr
.control
.uimm
= ai
->immed
;
255 opc
= (ai
->tok
== T_OP_BRNE
) ? OPC_BRNEI
: OPC_BREQI
;
256 instr
.br
.bit_or_imm
= ai
->immed
;
258 opc
= (ai
->tok
== T_OP_BRNE
) ? OPC_BRNEB
: OPC_BREQB
;
259 instr
.br
.bit_or_imm
= ai
->bit
;
261 instr
.br
.src
= ai
->src1
;
262 instr
.br
.ioff
= resolve_label(ai
->label
) - i
;
269 instr
.ret
.interrupt
= 1;
273 instr
.call
.uoff
= resolve_label(ai
->label
);
275 case T_OP_PREEMPTLEAVE
:
276 opc
= OPC_PREEMPTLEAVE6
;
277 instr
.call
.uoff
= resolve_label(ai
->label
);
281 if (resolve_label(ai
->label
) != i
+ 3) {
282 fprintf(stderr
, "jump label %s is incorrect for setsecure\n", ai
->label
);
285 if (ai
->src1
!= 0x2) {
286 fprintf(stderr
, "source for setsecure must be $02\n");
291 /* encode jump as: brne $00, b0, #label */
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
;
304 afuc_set_opc(&instr
, opc
, ai
->rep
);
306 write(outfd
, &instr
, 4);
311 static int find_enum_val(struct rnnenum
*en
, const char *name
)
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
;
322 static int find_reg(struct rnndomain
*dom
, const char *name
)
326 for (i
= 0; i
< dom
->subelemsnum
; i
++)
327 if (!strcmp(name
, dom
->subelems
[i
]->name
))
328 return dom
->subelems
[i
]->offset
;
333 unsigned parse_control_reg(const char *name
)
335 /* skip leading "@" */
336 int val
= find_reg(control_regs
, name
+ 1);
338 printf("invalid control reg: %s\n", name
);
341 return (unsigned)val
;
344 static void emit_jumptable(int outfd
)
346 struct rnnenum
*en
= rnn_findenum(ctx
->db
, "adreno_pm4_type3_packets");
347 uint32_t jmptable
[0x80] = {0};
350 for (i
= 0; i
< num_labels
; i
++) {
351 struct asm_label
*label
= &labels
[i
];
352 int id
= find_enum_val(en
, label
->label
);
354 /* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
356 if (sscanf(label
->label
, "UNKN%d", &id
) != 1) {
357 /* if still not found, must not belong in jump-table: */
362 jmptable
[id
] = label
->offset
;
365 write(outfd
, jmptable
, sizeof(jmptable
));
368 static void usage(void)
370 fprintf(stderr
, "Usage:\n"
371 "\tasm [-g GPUVER] filename.asm filename.fw\n"
372 "\t\t-g - specify GPU version (5, etc)\n"
377 int main(int argc
, char **argv
)
380 char *file
, *outfile
, *name
, *control_reg_name
;
383 /* Argument parsing: */
384 while ((c
= getopt (argc
, argv
, "g:")) != -1) {
387 gpuver
= atoi(optarg
);
394 if (optind
>= (argc
+ 1)) {
395 fprintf(stderr
, "no file specified!\n");
400 outfile
= argv
[optind
+ 1];
402 outfd
= open(outfile
, O_WRONLY
| O_CREAT
| O_TRUNC
, 0644);
404 fprintf(stderr
, "could not open \"%s\"\n", outfile
);
408 in
= fopen(file
, "r");
410 fprintf(stderr
, "could not open \"%s\"\n", file
);
416 /* if gpu version not specified, infer from filename: */
418 if (strstr(file
, "a5")) {
420 } else if (strstr(file
, "a6")) {
428 control_reg_name
= "A6XX_CONTROL_REG";
432 control_reg_name
= "A5XX_CONTROL_REG";
435 fprintf(stderr
, "unknown GPU version!\n");
442 ctx
= rnndec_newcontext(db
);
444 rnn_parsefile(db
, "adreno.xml");
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
);
454 fprintf(stderr
, "parse failed: %d\n", ret
);
458 emit_instructions(outfd
);
459 emit_jumptable(outfd
);