Added few more stubs so that control reaches to DestroyDevice().
[mesa.git] / src / freedreno / afuc / disasm.c
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
40 static int gpuver;
41
42
43 static struct rnndeccontext *ctx;
44 static struct rnndb *db;
45 static struct rnndomain *control_regs;
46 struct rnndomain *dom[2];
47 const char *variant;
48
49 /* non-verbose mode should output something suitable to feed back into
50 * assembler.. verbose mode has additional output useful for debugging
51 * (like unexpected bits that are set)
52 */
53 static bool verbose = false;
54
55 static void print_gpu_reg(uint32_t regbase)
56 {
57 struct rnndomain *d = NULL;
58
59 if (regbase < 0x100)
60 return;
61
62 if (rnndec_checkaddr(ctx, dom[0], regbase, 0))
63 d = dom[0];
64 else if (rnndec_checkaddr(ctx, dom[1], regbase, 0))
65 d = dom[1];
66
67 if (d) {
68 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, d, regbase, 0);
69 if (info) {
70 printf("\t; %s", info->name);
71 free(info->name);
72 free(info);
73 return;
74 }
75 }
76 }
77
78 static void printc(const char *c, const char *fmt, ...)
79 {
80 va_list args;
81 printf("%s", c);
82 va_start(args, fmt);
83 vprintf(fmt, args);
84 va_end(args);
85 printf("%s", ctx->colors->reset);
86 }
87
88 #define printerr(fmt, ...) printc(ctx->colors->err, fmt, ##__VA_ARGS__)
89 #define printlbl(fmt, ...) printc(ctx->colors->btarg, fmt, ##__VA_ARGS__)
90
91 static void print_reg(unsigned reg)
92 {
93 // XXX seems like *reading* $00 --> literal zero??
94 // seems like read from $1c gives packet remaining len??
95 // $01 current packet header, writing to $01 triggers
96 // parsing header and jumping to appropriate handler.
97 if (reg == 0x1c)
98 printf("$rem"); /* remainding dwords in packet */
99 else if (reg == 0x1d)
100 printf("$addr");
101 else if (reg == 0x1e)
102 printf("$addr2"); // XXX
103 else if (reg == 0x1f)
104 printf("$data");
105 else
106 printf("$%02x", reg);
107 }
108
109 static void print_src(unsigned reg)
110 {
111 print_reg(reg);
112 }
113
114 static void print_dst(unsigned reg)
115 {
116 print_reg(reg);
117 }
118
119 static void print_alu_name(afuc_opc opc, uint32_t instr)
120 {
121 if (opc == OPC_ADD) {
122 printf("add ");
123 } else if (opc == OPC_ADDHI) {
124 printf("addhi ");
125 } else if (opc == OPC_SUB) {
126 printf("sub ");
127 } else if (opc == OPC_SUBHI) {
128 printf("subhi ");
129 } else if (opc == OPC_AND) {
130 printf("and ");
131 } else if (opc == OPC_OR) {
132 printf("or ");
133 } else if (opc == OPC_XOR) {
134 printf("xor ");
135 } else if (opc == OPC_NOT) {
136 printf("not ");
137 } else if (opc == OPC_SHL) {
138 printf("shl ");
139 } else if (opc == OPC_USHR) {
140 printf("ushr ");
141 } else if (opc == OPC_ISHR) {
142 printf("ishr ");
143 } else if (opc == OPC_ROT) {
144 printf("rot ");
145 } else if (opc == OPC_MUL8) {
146 printf("mul8 ");
147 } else if (opc == OPC_MIN) {
148 printf("min ");
149 } else if (opc == OPC_MAX) {
150 printf("max ");
151 } else if (opc == OPC_CMP) {
152 printf("cmp ");
153 } else if (opc == OPC_MSB) {
154 printf("msb ");
155 } else {
156 printerr("[%08x]", instr);
157 printf(" ; alu%02x ", opc);
158 }
159 }
160
161 static const char *getpm4(uint32_t id)
162 {
163 return rnndec_decode_enum(ctx, "adreno_pm4_type3_packets", id);
164 }
165
166 static inline unsigned
167 _odd_parity_bit(unsigned val)
168 {
169 /* See: http://graphics.stanford.edu/~seander/bithacks.html#ParityParallel
170 * note that we want odd parity so 0x6996 is inverted.
171 */
172 val ^= val >> 16;
173 val ^= val >> 8;
174 val ^= val >> 4;
175 val &= 0xf;
176 return (~0x6996 >> val) & 1;
177 }
178
179 static struct {
180 uint32_t offset;
181 uint32_t num_jump_labels;
182 uint32_t jump_labels[256];
183 } jump_labels[1024];
184 int num_jump_labels;
185
186 static void add_jump_table_entry(uint32_t n, uint32_t offset)
187 {
188 int i;
189
190 if (n > 128) /* can't possibly be a PM4 PKT3.. */
191 return;
192
193 for (i = 0; i < num_jump_labels; i++)
194 if (jump_labels[i].offset == offset)
195 goto add_label;
196
197 num_jump_labels = i + 1;
198 jump_labels[i].offset = offset;
199 jump_labels[i].num_jump_labels = 0;
200
201 add_label:
202 jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n;
203 assert(jump_labels[i].num_jump_labels < 256);
204 }
205
206 static int get_jump_table_entry(uint32_t offset)
207 {
208 int i;
209
210 for (i = 0; i < num_jump_labels; i++)
211 if (jump_labels[i].offset == offset)
212 return i;
213
214 return -1;
215 }
216
217 static uint32_t label_offsets[0x512];
218 static int num_label_offsets;
219
220 static int label_idx(uint32_t offset, bool create)
221 {
222 int i;
223 for (i = 0; i < num_label_offsets; i++)
224 if (offset == label_offsets[i])
225 return i;
226 if (!create)
227 return -1;
228 label_offsets[i] = offset;
229 num_label_offsets = i+1;
230 return i;
231 }
232
233 static const char *
234 label_name(uint32_t offset, bool allow_jt)
235 {
236 static char name[8];
237 int lidx;
238
239 if (allow_jt) {
240 lidx = get_jump_table_entry(offset);
241 if (lidx >= 0) {
242 int j;
243 for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) {
244 uint32_t jump_label = jump_labels[lidx].jump_labels[j];
245 const char *str = getpm4(jump_label);
246 if (str)
247 return str;
248 }
249 // if we don't find anything w/ known name, maybe we should
250 // return UNKN%d to at least make it clear that this is some
251 // sort of jump-table entry?
252 }
253 }
254
255 lidx = label_idx(offset, false);
256 if (lidx < 0)
257 return NULL;
258 sprintf(name, "l%03d", lidx);
259 return name;
260 }
261
262
263 static uint32_t fxn_offsets[0x512];
264 static int num_fxn_offsets;
265
266 static int fxn_idx(uint32_t offset, bool create)
267 {
268 int i;
269 for (i = 0; i < num_fxn_offsets; i++)
270 if (offset == fxn_offsets[i])
271 return i;
272 if (!create)
273 return -1;
274 fxn_offsets[i] = offset;
275 num_fxn_offsets = i+1;
276 return i;
277 }
278
279 static const char *
280 fxn_name(uint32_t offset)
281 {
282 static char name[8];
283 int fidx = fxn_idx(offset, false);
284 if (fidx < 0)
285 return NULL;
286 sprintf(name, "fxn%02d", fidx);
287 return name;
288 }
289
290 static void print_control_reg(uint32_t id)
291 {
292 if (rnndec_checkaddr(ctx, control_regs, id, 0)) {
293 struct rnndecaddrinfo *info = rnndec_decodeaddr(ctx, control_regs, id, 0);
294 printf("@%s", info->name);
295 free(info->name);
296 free(info);
297 } else {
298 printf("0x%03x", id);
299 }
300 }
301
302 static void disasm(uint32_t *buf, int sizedwords)
303 {
304 uint32_t *instrs = buf;
305 const int jmptbl_start = instrs[1] & 0xffff;
306 uint32_t *jmptbl = &buf[jmptbl_start];
307 afuc_opc opc;
308 bool rep;
309 int i;
310
311
312 /* parse jumptable: */
313 for (i = 0; i < 0x80; i++) {
314 unsigned offset = jmptbl[i];
315 unsigned n = i;// + CP_NOP;
316 add_jump_table_entry(n, offset);
317 }
318
319 /* do a pre-pass to find instructions that are potential branch targets,
320 * and add labels for them:
321 */
322 for (i = 0; i < jmptbl_start; i++) {
323 afuc_instr *instr = (void *)&instrs[i];
324
325 afuc_get_opc(instr, &opc, &rep);
326
327 switch (opc) {
328 case OPC_BRNEI:
329 case OPC_BREQI:
330 case OPC_BRNEB:
331 case OPC_BREQB:
332 label_idx(i + instr->br.ioff, true);
333 break;
334 case OPC_PREEMPTLEAVE6:
335 if (gpuver >= 6)
336 label_idx(instr->call.uoff, true);
337 break;
338 case OPC_CALL:
339 fxn_idx(instr->call.uoff, true);
340 break;
341 case OPC_SETSECURE:
342 /* this implicitly jumps to pc + 3 if successful */
343 label_idx(i + 3, true);
344 break;
345 default:
346 break;
347 }
348 }
349
350 /* print instructions: */
351 for (i = 0; i < jmptbl_start; i++) {
352 int jump_label_idx;
353 afuc_instr *instr = (void *)&instrs[i];
354 const char *fname, *lname;
355 afuc_opc opc;
356 bool rep;
357
358 afuc_get_opc(instr, &opc, &rep);
359
360 lname = label_name(i, false);
361 fname = fxn_name(i);
362 jump_label_idx = get_jump_table_entry(i);
363
364 if (jump_label_idx >= 0) {
365 int j;
366 printf("\n");
367 for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) {
368 uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j];
369 const char *name = getpm4(jump_label);
370 if (name) {
371 printlbl("%s", name);
372 } else {
373 printlbl("UNKN%d", jump_label);
374 }
375 printf(":\n");
376 }
377 }
378
379 if (fname) {
380 printlbl("%s", fname);
381 printf(":\n");
382 }
383
384 if (lname) {
385 printlbl(" %s", lname);
386 printf(":");
387 } else {
388 printf(" ");
389 }
390
391
392 if (verbose) {
393 printf("\t%04x: %08x ", i, instrs[i]);
394 } else {
395 printf(" ");
396 }
397
398 switch (opc) {
399 case OPC_NOP: {
400 /* a6xx changed the default immediate, and apparently 0
401 * is illegal now.
402 */
403 const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0;
404 if (instrs[i] != nop) {
405 printerr("[%08x]", instrs[i]);
406 printf(" ; ");
407 }
408 if (rep)
409 printf("(rep)");
410 printf("nop");
411 print_gpu_reg(instrs[i]);
412
413 break;
414 }
415 case OPC_ADD:
416 case OPC_ADDHI:
417 case OPC_SUB:
418 case OPC_SUBHI:
419 case OPC_AND:
420 case OPC_OR:
421 case OPC_XOR:
422 case OPC_NOT:
423 case OPC_SHL:
424 case OPC_USHR:
425 case OPC_ISHR:
426 case OPC_ROT:
427 case OPC_MUL8:
428 case OPC_MIN:
429 case OPC_MAX:
430 case OPC_CMP: {
431 bool src1 = true;
432
433 if (opc == OPC_NOT)
434 src1 = false;
435
436 if (rep)
437 printf("(rep)");
438
439 print_alu_name(opc, instrs[i]);
440 print_dst(instr->alui.dst);
441 printf(", ");
442 if (src1) {
443 print_src(instr->alui.src);
444 printf(", ");
445 }
446 printf("0x%04x", instr->alui.uimm);
447 print_gpu_reg(instr->alui.uimm);
448
449 /* print out unexpected bits: */
450 if (verbose) {
451 if (instr->alui.src && !src1)
452 printerr(" (src=%02x)", instr->alui.src);
453 }
454
455 break;
456 }
457 case OPC_MOVI: {
458 if (rep)
459 printf("(rep)");
460 printf("mov ");
461 print_dst(instr->movi.dst);
462 printf(", 0x%04x", instr->movi.uimm);
463 if (instr->movi.shift)
464 printf(" << %u", instr->movi.shift);
465
466 /* using mov w/ << 16 is popular way to construct a pkt7
467 * header to send (for ex, from PFP to ME), so check that
468 * case first
469 */
470 if ((instr->movi.shift == 16) &&
471 ((instr->movi.uimm & 0xff00) == 0x7000)) {
472 unsigned opc, p;
473
474 opc = instr->movi.uimm & 0x7f;
475 p = _odd_parity_bit(opc);
476
477 /* So, you'd think that checking the parity bit would be
478 * a good way to rule out false positives, but seems like
479 * ME doesn't really care.. at least it would filter out
480 * things that look like actual legit packets between
481 * PFP and ME..
482 */
483 if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) {
484 const char *name = getpm4(opc);
485 printf("\t; ");
486 if (name)
487 printlbl("%s", name);
488 else
489 printlbl("UNKN%u", opc);
490 break;
491 }
492 }
493
494 print_gpu_reg(instr->movi.uimm << instr->movi.shift);
495
496 break;
497 }
498 case OPC_ALU: {
499 bool src1 = true;
500
501 if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB)
502 src1 = false;
503
504 if (instr->alu.pad)
505 printf("[%08x] ; ", instrs[i]);
506
507 if (rep)
508 printf("(rep)");
509 if (instr->alu.xmov)
510 printf("(xmov%d)", instr->alu.xmov);
511
512 /* special case mnemonics:
513 * reading $00 seems to always yield zero, and so:
514 * or $dst, $00, $src -> mov $dst, $src
515 * Maybe add one for negate too, ie.
516 * sub $dst, $00, $src ???
517 */
518 if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) {
519 printf("mov ");
520 src1 = false;
521 } else {
522 print_alu_name(instr->alu.alu, instrs[i]);
523 }
524
525 print_dst(instr->alu.dst);
526 if (src1) {
527 printf(", ");
528 print_src(instr->alu.src1);
529 }
530 printf(", ");
531 print_src(instr->alu.src2);
532
533 /* print out unexpected bits: */
534 if (verbose) {
535 if (instr->alu.pad)
536 printerr(" (pad=%01x)", instr->alu.pad);
537 if (instr->alu.src1 && !src1)
538 printerr(" (src1=%02x)", instr->alu.src1);
539 }
540
541 /* xmov is a modifier that makes the processor execute up to 3
542 * extra mov's after the current instruction. Given an ALU
543 * instruction:
544 *
545 * (xmovN) alu $dst, $src1, $src2
546 *
547 * In all of the uses in the firmware blob, $dst and $src2 are one
548 * of the "special" registers $data, $addr, $addr2. I've observed
549 * that if $dst isn't "special" then it's replaced with $00
550 * instead of $data, but I haven't checked what happens if $src2
551 * isn't "special". Anyway, in the usual case, the HW produces a
552 * count M = min(N, $rem) and then does the following:
553 *
554 * M = 1:
555 * mov $data, $src2
556 *
557 * M = 2:
558 * mov $data, $src2
559 * mov $data, $src2
560 *
561 * M = 3:
562 * mov $data, $src2
563 * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH)
564 * mov $data, $src2
565 *
566 * It seems to be frequently used in combination with (rep) to
567 * provide a kind of hardware-based loop unrolling, and there's
568 * even a special case in the ISA to be able to do this with
569 * CP_CONTEXT_REG_BUNCH. However (rep) isn't required.
570 *
571 * This dumps the expected extra instructions, assuming that $rem
572 * isn't too small.
573 */
574 if (verbose && instr->alu.xmov) {
575 for (int i = 0; i < instr->alu.xmov; i++) {
576 printf("\n ; mov ");
577 if (instr->alu.dst < 0x1d)
578 printf("$00");
579 else if (instr->alu.xmov == 3 && i == 1)
580 print_dst(instr->alu.dst);
581 else
582 printf("$data");
583 printf(", ");
584 print_src(instr->alu.src2);
585 }
586 }
587
588 break;
589 }
590 case OPC_CWRITE6:
591 case OPC_CREAD6:
592 case OPC_STORE6:
593 case OPC_LOAD6: {
594 if (rep)
595 printf("(rep)");
596
597 bool is_control_reg = true;
598 if (gpuver >= 6) {
599 switch (opc) {
600 case OPC_CWRITE6:
601 printf("cwrite ");
602 break;
603 case OPC_CREAD6:
604 printf("cread ");
605 break;
606 case OPC_STORE6:
607 is_control_reg = false;
608 printf("store ");
609 break;
610 case OPC_LOAD6:
611 is_control_reg = false;
612 printf("load ");
613 break;
614 default:
615 assert(!"unreachable");
616 }
617 } else {
618 switch (opc) {
619 case OPC_CWRITE5:
620 printf("cwrite ");
621 break;
622 case OPC_CREAD5:
623 printf("cread ");
624 break;
625 default:
626 fprintf(stderr, "A6xx control opcode on A5xx?\n");
627 exit(1);
628 }
629 }
630
631 print_src(instr->control.src1);
632 printf(", [");
633 print_src(instr->control.src2);
634 printf(" + ");
635 if (is_control_reg && instr->control.flags != 0x4)
636 print_control_reg(instr->control.uimm);
637 else
638 printf("0x%03x", instr->control.uimm);
639 printf("], 0x%x", instr->control.flags);
640 break;
641 }
642 case OPC_BRNEI:
643 case OPC_BREQI:
644 case OPC_BRNEB:
645 case OPC_BREQB: {
646 unsigned off = i + instr->br.ioff;
647
648 assert(!rep);
649
650 /* Since $00 reads back zero, it can be used as src for
651 * unconditional branches. (This only really makes sense
652 * for the BREQB.. or possible BRNEI if imm==0.)
653 *
654 * If bit=0 then branch is taken if *all* bits are zero.
655 * Otherwise it is taken if bit (bit-1) is clear.
656 *
657 * Note the instruction after a jump/branch is executed
658 * regardless of whether branch is taken, so use nop or
659 * take that into account in code.
660 */
661 if (instr->br.src || (opc != OPC_BRNEB)) {
662 bool immed = false;
663
664 if (opc == OPC_BRNEI) {
665 printf("brne ");
666 immed = true;
667 } else if (opc == OPC_BREQI) {
668 printf("breq ");
669 immed = true;
670 } else if (opc == OPC_BRNEB) {
671 printf("brne ");
672 } else if (opc == OPC_BREQB) {
673 printf("breq ");
674 }
675 print_src(instr->br.src);
676 if (immed) {
677 printf(", 0x%x,", instr->br.bit_or_imm);
678 } else {
679 printf(", b%u,", instr->br.bit_or_imm);
680 }
681 } else {
682 printf("jump");
683 if (verbose && instr->br.bit_or_imm) {
684 printerr(" (src=%03x, bit=%03x) ",
685 instr->br.src, instr->br.bit_or_imm);
686 }
687 }
688
689 printf(" #");
690 printlbl("%s", label_name(off, true));
691 if (verbose)
692 printf(" (#%d, %04x)", instr->br.ioff, off);
693 break;
694 }
695 case OPC_CALL:
696 assert(!rep);
697 printf("call #");
698 printlbl("%s", fxn_name(instr->call.uoff));
699 if (verbose) {
700 printf(" (%04x)", instr->call.uoff);
701 if (instr->br.bit_or_imm || instr->br.src) {
702 printerr(" (src=%03x, bit=%03x) ",
703 instr->br.src, instr->br.bit_or_imm);
704 }
705 }
706 break;
707 case OPC_RET:
708 assert(!rep);
709 if (instr->ret.pad)
710 printf("[%08x] ; ", instrs[i]);
711 if (instr->ret.interrupt)
712 printf("iret");
713 else
714 printf("ret");
715 break;
716 case OPC_WIN:
717 assert(!rep);
718 if (instr->waitin.pad)
719 printf("[%08x] ; ", instrs[i]);
720 printf("waitin");
721 if (verbose && instr->waitin.pad)
722 printerr(" (pad=%x)", instr->waitin.pad);
723 break;
724 case OPC_PREEMPTLEAVE6:
725 if (gpuver < 6) {
726 printf("[%08x] ; op38", instrs[i]);
727 } else {
728 printf("preemptleave #");
729 printlbl("%s", label_name(instr->call.uoff, true));
730 }
731 break;
732 case OPC_SETSECURE:
733 /* Note: This seems to implicitly read the secure/not-secure state
734 * to set from the low bit of $02, and implicitly jumps to pc + 3
735 * (i.e. skipping the next two instructions) if it succeeds. We
736 * print these implicit parameters to make reading the disassembly
737 * easier.
738 */
739 if (instr->pad)
740 printf("[%08x] ; ", instrs[i]);
741 printf("setsecure $02, #");
742 printlbl("%s", label_name(i + 3, true));
743 break;
744 default:
745 printerr("[%08x]", instrs[i]);
746 printf(" ; op%02x ", opc);
747 print_dst(instr->alui.dst);
748 printf(", ");
749 print_src(instr->alui.src);
750 print_gpu_reg(instrs[i] & 0xffff);
751 break;
752 }
753 printf("\n");
754 }
755
756 /* print jumptable: */
757 if (verbose) {
758 printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n");
759 printf("; JUMP TABLE\n");
760 for (i = 0; i < 0x7f; i++) {
761 int n = i;// + CP_NOP;
762 uint32_t offset = jmptbl[i];
763 const char *name = getpm4(n);
764 printf("%3d %02x: ", n, n);
765 printf("%04x", offset);
766 if (name) {
767 printf(" ; %s", name);
768 } else {
769 printf(" ; UNKN%d", n);
770 }
771 printf("\n");
772 }
773 }
774 }
775
776 #define CHUNKSIZE 4096
777
778 static char * readfile(const char *path, int *sz)
779 {
780 char *buf = NULL;
781 int fd, ret, n = 0;
782
783 fd = open(path, O_RDONLY);
784 if (fd < 0)
785 return NULL;
786
787 while (1) {
788 buf = realloc(buf, n + CHUNKSIZE);
789 ret = read(fd, buf + n, CHUNKSIZE);
790 if (ret < 0) {
791 free(buf);
792 *sz = 0;
793 return NULL;
794 } else if (ret < CHUNKSIZE) {
795 n += ret;
796 *sz = n;
797 return buf;
798 } else {
799 n += CHUNKSIZE;
800 }
801 }
802 }
803
804 static void usage(void)
805 {
806 fprintf(stderr, "Usage:\n"
807 "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n"
808 "\t\t-g - specify GPU version (5, etc)\n"
809 "\t\t-c - use colors\n"
810 "\t\t-v - verbose output\n"
811 );
812 exit(2);
813 }
814
815 int main(int argc, char **argv)
816 {
817 uint32_t *buf;
818 char *file, *control_reg_name;
819 bool colors = false;
820 int sz, c;
821
822 /* Argument parsing: */
823 while ((c = getopt (argc, argv, "g:vc")) != -1) {
824 switch (c) {
825 case 'g':
826 gpuver = atoi(optarg);
827 break;
828 case 'v':
829 verbose = true;
830 break;
831 case 'c':
832 colors = true;
833 break;
834 default:
835 usage();
836 }
837 }
838
839 if (optind >= argc) {
840 fprintf(stderr, "no file specified!\n");
841 usage();
842 }
843
844 file = argv[optind];
845
846 /* if gpu version not specified, infer from filename: */
847 if (!gpuver) {
848 if (strstr(file, "a5")) {
849 gpuver = 5;
850 } else if (strstr(file, "a6")) {
851 gpuver = 6;
852 }
853 }
854
855 switch (gpuver) {
856 case 6:
857 printf("; a6xx microcode\n");
858 variant = "A6XX";
859 control_reg_name = "A6XX_CONTROL_REG";
860 break;
861 case 5:
862 printf("; a5xx microcode\n");
863 variant = "A5XX";
864 control_reg_name = "A5XX_CONTROL_REG";
865 break;
866 default:
867 fprintf(stderr, "unknown GPU version!\n");
868 usage();
869 }
870
871 rnn_init();
872 db = rnn_newdb();
873
874 ctx = rnndec_newcontext(db);
875 ctx->colors = colors ? &envy_def_colors : &envy_null_colors;
876
877 rnn_parsefile(db, "adreno.xml");
878 rnn_prepdb(db);
879 if (db->estatus)
880 errx(db->estatus, "failed to parse register database");
881 dom[0] = rnn_finddomain(db, variant);
882 dom[1] = rnn_finddomain(db, "AXXX");
883 control_regs = rnn_finddomain(db, control_reg_name);
884
885 rnndec_varadd(ctx, "chip", variant);
886
887 buf = (uint32_t *)readfile(file, &sz);
888
889 printf("; Disassembling microcode: %s\n", file);
890 printf("; Version: %08x\n\n", buf[1]);
891 disasm(&buf[1], sz/4 - 1);
892
893 return 0;
894 }