17ec681f3Smrg/* 27ec681f3Smrg * Copyright (c) 2017 Rob Clark <robdclark@gmail.com> 37ec681f3Smrg * 47ec681f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a 57ec681f3Smrg * copy of this software and associated documentation files (the "Software"), 67ec681f3Smrg * to deal in the Software without restriction, including without limitation 77ec681f3Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 87ec681f3Smrg * and/or sell copies of the Software, and to permit persons to whom the 97ec681f3Smrg * Software is furnished to do so, subject to the following conditions: 107ec681f3Smrg * 117ec681f3Smrg * The above copyright notice and this permission notice (including the next 127ec681f3Smrg * paragraph) shall be included in all copies or substantial portions of the 137ec681f3Smrg * Software. 147ec681f3Smrg * 157ec681f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 167ec681f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 177ec681f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 187ec681f3Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 197ec681f3Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 207ec681f3Smrg * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 217ec681f3Smrg * SOFTWARE. 227ec681f3Smrg */ 237ec681f3Smrg 247ec681f3Smrg#include <assert.h> 257ec681f3Smrg#include <err.h> 267ec681f3Smrg#include <fcntl.h> 277ec681f3Smrg#include <getopt.h> 287ec681f3Smrg#include <stdarg.h> 297ec681f3Smrg#include <stdbool.h> 307ec681f3Smrg#include <stdint.h> 317ec681f3Smrg#include <stdio.h> 327ec681f3Smrg#include <stdlib.h> 337ec681f3Smrg#include <string.h> 347ec681f3Smrg#include <unistd.h> 357ec681f3Smrg 367ec681f3Smrg#include "util/os_file.h" 377ec681f3Smrg 387ec681f3Smrg#include "freedreno_pm4.h" 397ec681f3Smrg 407ec681f3Smrg#include "afuc.h" 417ec681f3Smrg#include "util.h" 427ec681f3Smrg#include "emu.h" 437ec681f3Smrg 447ec681f3Smrgstatic int gpuver; 457ec681f3Smrg 467ec681f3Smrg/* non-verbose mode should output something suitable to feed back into 477ec681f3Smrg * assembler.. verbose mode has additional output useful for debugging 487ec681f3Smrg * (like unexpected bits that are set) 497ec681f3Smrg */ 507ec681f3Smrgstatic bool verbose = false; 517ec681f3Smrg 527ec681f3Smrg/* emulator mode: */ 537ec681f3Smrgstatic bool emulator = false; 547ec681f3Smrg 557ec681f3Smrgstatic void 567ec681f3Smrgprint_gpu_reg(uint32_t regbase) 577ec681f3Smrg{ 587ec681f3Smrg if (regbase < 0x100) 597ec681f3Smrg return; 607ec681f3Smrg 617ec681f3Smrg char *name = afuc_gpu_reg_name(regbase); 627ec681f3Smrg if (name) { 637ec681f3Smrg printf("\t; %s", name); 647ec681f3Smrg free(name); 657ec681f3Smrg } 667ec681f3Smrg} 677ec681f3Smrg 687ec681f3Smrg#define printerr(fmt, ...) afuc_printc(AFUC_ERR, fmt, ##__VA_ARGS__) 697ec681f3Smrg#define printlbl(fmt, ...) afuc_printc(AFUC_LBL, fmt, ##__VA_ARGS__) 707ec681f3Smrg 717ec681f3Smrgvoid 727ec681f3Smrgprint_src(unsigned reg) 737ec681f3Smrg{ 747ec681f3Smrg if (reg == REG_REM) 757ec681f3Smrg printf("$rem"); /* remainding dwords in packet */ 767ec681f3Smrg else if (reg == REG_MEMDATA) 777ec681f3Smrg printf("$memdata"); 787ec681f3Smrg else if (reg == REG_REGDATA) 797ec681f3Smrg printf("$regdata"); 807ec681f3Smrg else if (reg == REG_DATA) 817ec681f3Smrg printf("$data"); 827ec681f3Smrg else 837ec681f3Smrg printf("$%02x", reg); 847ec681f3Smrg} 857ec681f3Smrg 867ec681f3Smrgvoid 877ec681f3Smrgprint_dst(unsigned reg) 887ec681f3Smrg{ 897ec681f3Smrg if (reg == REG_REM) 907ec681f3Smrg printf("$rem"); /* remainding dwords in packet */ 917ec681f3Smrg else if (reg == REG_ADDR) 927ec681f3Smrg printf("$addr"); 937ec681f3Smrg else if (reg == REG_USRADDR) 947ec681f3Smrg printf("$usraddr"); 957ec681f3Smrg else if (reg == REG_DATA) 967ec681f3Smrg printf("$data"); 977ec681f3Smrg else 987ec681f3Smrg printf("$%02x", reg); 997ec681f3Smrg} 1007ec681f3Smrg 1017ec681f3Smrgstatic void 1027ec681f3Smrgprint_alu_name(afuc_opc opc, uint32_t instr) 1037ec681f3Smrg{ 1047ec681f3Smrg if (opc == OPC_ADD) { 1057ec681f3Smrg printf("add "); 1067ec681f3Smrg } else if (opc == OPC_ADDHI) { 1077ec681f3Smrg printf("addhi "); 1087ec681f3Smrg } else if (opc == OPC_SUB) { 1097ec681f3Smrg printf("sub "); 1107ec681f3Smrg } else if (opc == OPC_SUBHI) { 1117ec681f3Smrg printf("subhi "); 1127ec681f3Smrg } else if (opc == OPC_AND) { 1137ec681f3Smrg printf("and "); 1147ec681f3Smrg } else if (opc == OPC_OR) { 1157ec681f3Smrg printf("or "); 1167ec681f3Smrg } else if (opc == OPC_XOR) { 1177ec681f3Smrg printf("xor "); 1187ec681f3Smrg } else if (opc == OPC_NOT) { 1197ec681f3Smrg printf("not "); 1207ec681f3Smrg } else if (opc == OPC_SHL) { 1217ec681f3Smrg printf("shl "); 1227ec681f3Smrg } else if (opc == OPC_USHR) { 1237ec681f3Smrg printf("ushr "); 1247ec681f3Smrg } else if (opc == OPC_ISHR) { 1257ec681f3Smrg printf("ishr "); 1267ec681f3Smrg } else if (opc == OPC_ROT) { 1277ec681f3Smrg printf("rot "); 1287ec681f3Smrg } else if (opc == OPC_MUL8) { 1297ec681f3Smrg printf("mul8 "); 1307ec681f3Smrg } else if (opc == OPC_MIN) { 1317ec681f3Smrg printf("min "); 1327ec681f3Smrg } else if (opc == OPC_MAX) { 1337ec681f3Smrg printf("max "); 1347ec681f3Smrg } else if (opc == OPC_CMP) { 1357ec681f3Smrg printf("cmp "); 1367ec681f3Smrg } else if (opc == OPC_MSB) { 1377ec681f3Smrg printf("msb "); 1387ec681f3Smrg } else { 1397ec681f3Smrg printerr("[%08x]", instr); 1407ec681f3Smrg printf(" ; alu%02x ", opc); 1417ec681f3Smrg } 1427ec681f3Smrg} 1437ec681f3Smrg 1447ec681f3Smrgstatic const char * 1457ec681f3Smrggetpm4(uint32_t id) 1467ec681f3Smrg{ 1477ec681f3Smrg return afuc_pm_id_name(id); 1487ec681f3Smrg} 1497ec681f3Smrg 1507ec681f3Smrgstatic struct { 1517ec681f3Smrg uint32_t offset; 1527ec681f3Smrg uint32_t num_jump_labels; 1537ec681f3Smrg uint32_t jump_labels[256]; 1547ec681f3Smrg} jump_labels[1024]; 1557ec681f3Smrgint num_jump_labels; 1567ec681f3Smrg 1577ec681f3Smrgstatic void 1587ec681f3Smrgadd_jump_table_entry(uint32_t n, uint32_t offset) 1597ec681f3Smrg{ 1607ec681f3Smrg int i; 1617ec681f3Smrg 1627ec681f3Smrg if (n > 128) /* can't possibly be a PM4 PKT3.. */ 1637ec681f3Smrg return; 1647ec681f3Smrg 1657ec681f3Smrg for (i = 0; i < num_jump_labels; i++) 1667ec681f3Smrg if (jump_labels[i].offset == offset) 1677ec681f3Smrg goto add_label; 1687ec681f3Smrg 1697ec681f3Smrg num_jump_labels = i + 1; 1707ec681f3Smrg jump_labels[i].offset = offset; 1717ec681f3Smrg jump_labels[i].num_jump_labels = 0; 1727ec681f3Smrg 1737ec681f3Smrgadd_label: 1747ec681f3Smrg jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n; 1757ec681f3Smrg assert(jump_labels[i].num_jump_labels < 256); 1767ec681f3Smrg} 1777ec681f3Smrg 1787ec681f3Smrgstatic int 1797ec681f3Smrgget_jump_table_entry(uint32_t offset) 1807ec681f3Smrg{ 1817ec681f3Smrg int i; 1827ec681f3Smrg 1837ec681f3Smrg for (i = 0; i < num_jump_labels; i++) 1847ec681f3Smrg if (jump_labels[i].offset == offset) 1857ec681f3Smrg return i; 1867ec681f3Smrg 1877ec681f3Smrg return -1; 1887ec681f3Smrg} 1897ec681f3Smrg 1907ec681f3Smrgstatic uint32_t label_offsets[0x512]; 1917ec681f3Smrgstatic int num_label_offsets; 1927ec681f3Smrg 1937ec681f3Smrgstatic int 1947ec681f3Smrglabel_idx(uint32_t offset, bool create) 1957ec681f3Smrg{ 1967ec681f3Smrg int i; 1977ec681f3Smrg for (i = 0; i < num_label_offsets; i++) 1987ec681f3Smrg if (offset == label_offsets[i]) 1997ec681f3Smrg return i; 2007ec681f3Smrg if (!create) 2017ec681f3Smrg return -1; 2027ec681f3Smrg label_offsets[i] = offset; 2037ec681f3Smrg num_label_offsets = i + 1; 2047ec681f3Smrg return i; 2057ec681f3Smrg} 2067ec681f3Smrg 2077ec681f3Smrgstatic const char * 2087ec681f3Smrglabel_name(uint32_t offset, bool allow_jt) 2097ec681f3Smrg{ 2107ec681f3Smrg static char name[12]; 2117ec681f3Smrg int lidx; 2127ec681f3Smrg 2137ec681f3Smrg if (allow_jt) { 2147ec681f3Smrg lidx = get_jump_table_entry(offset); 2157ec681f3Smrg if (lidx >= 0) { 2167ec681f3Smrg int j; 2177ec681f3Smrg for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) { 2187ec681f3Smrg uint32_t jump_label = jump_labels[lidx].jump_labels[j]; 2197ec681f3Smrg const char *str = getpm4(jump_label); 2207ec681f3Smrg if (str) 2217ec681f3Smrg return str; 2227ec681f3Smrg } 2237ec681f3Smrg // if we don't find anything w/ known name, maybe we should 2247ec681f3Smrg // return UNKN%d to at least make it clear that this is some 2257ec681f3Smrg // sort of jump-table entry? 2267ec681f3Smrg } 2277ec681f3Smrg } 2287ec681f3Smrg 2297ec681f3Smrg lidx = label_idx(offset, false); 2307ec681f3Smrg if (lidx < 0) 2317ec681f3Smrg return NULL; 2327ec681f3Smrg sprintf(name, "l%03d", lidx); 2337ec681f3Smrg return name; 2347ec681f3Smrg} 2357ec681f3Smrg 2367ec681f3Smrgstatic uint32_t fxn_offsets[0x512]; 2377ec681f3Smrgstatic int num_fxn_offsets; 2387ec681f3Smrg 2397ec681f3Smrgstatic int 2407ec681f3Smrgfxn_idx(uint32_t offset, bool create) 2417ec681f3Smrg{ 2427ec681f3Smrg int i; 2437ec681f3Smrg for (i = 0; i < num_fxn_offsets; i++) 2447ec681f3Smrg if (offset == fxn_offsets[i]) 2457ec681f3Smrg return i; 2467ec681f3Smrg if (!create) 2477ec681f3Smrg return -1; 2487ec681f3Smrg fxn_offsets[i] = offset; 2497ec681f3Smrg num_fxn_offsets = i + 1; 2507ec681f3Smrg return i; 2517ec681f3Smrg} 2527ec681f3Smrg 2537ec681f3Smrgstatic const char * 2547ec681f3Smrgfxn_name(uint32_t offset) 2557ec681f3Smrg{ 2567ec681f3Smrg static char name[14]; 2577ec681f3Smrg int fidx = fxn_idx(offset, false); 2587ec681f3Smrg if (fidx < 0) 2597ec681f3Smrg return NULL; 2607ec681f3Smrg sprintf(name, "fxn%02d", fidx); 2617ec681f3Smrg return name; 2627ec681f3Smrg} 2637ec681f3Smrg 2647ec681f3Smrgvoid 2657ec681f3Smrgprint_control_reg(uint32_t id) 2667ec681f3Smrg{ 2677ec681f3Smrg char *name = afuc_control_reg_name(id); 2687ec681f3Smrg if (name) { 2697ec681f3Smrg printf("@%s", name); 2707ec681f3Smrg free(name); 2717ec681f3Smrg } else { 2727ec681f3Smrg printf("0x%03x", id); 2737ec681f3Smrg } 2747ec681f3Smrg} 2757ec681f3Smrg 2767ec681f3Smrgvoid 2777ec681f3Smrgprint_pipe_reg(uint32_t id) 2787ec681f3Smrg{ 2797ec681f3Smrg char *name = afuc_pipe_reg_name(id); 2807ec681f3Smrg if (name) { 2817ec681f3Smrg printf("|%s", name); 2827ec681f3Smrg free(name); 2837ec681f3Smrg } else { 2847ec681f3Smrg printf("0x%03x", id); 2857ec681f3Smrg } 2867ec681f3Smrg} 2877ec681f3Smrg 2887ec681f3Smrgstatic void 2897ec681f3Smrgdisasm_instr(uint32_t *instrs, unsigned pc) 2907ec681f3Smrg{ 2917ec681f3Smrg int jump_label_idx; 2927ec681f3Smrg afuc_instr *instr = (void *)&instrs[pc]; 2937ec681f3Smrg const char *fname, *lname; 2947ec681f3Smrg afuc_opc opc; 2957ec681f3Smrg bool rep; 2967ec681f3Smrg 2977ec681f3Smrg afuc_get_opc(instr, &opc, &rep); 2987ec681f3Smrg 2997ec681f3Smrg lname = label_name(pc, false); 3007ec681f3Smrg fname = fxn_name(pc); 3017ec681f3Smrg jump_label_idx = get_jump_table_entry(pc); 3027ec681f3Smrg 3037ec681f3Smrg if (jump_label_idx >= 0) { 3047ec681f3Smrg int j; 3057ec681f3Smrg printf("\n"); 3067ec681f3Smrg for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) { 3077ec681f3Smrg uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j]; 3087ec681f3Smrg const char *name = getpm4(jump_label); 3097ec681f3Smrg if (name) { 3107ec681f3Smrg printlbl("%s", name); 3117ec681f3Smrg } else { 3127ec681f3Smrg printlbl("UNKN%d", jump_label); 3137ec681f3Smrg } 3147ec681f3Smrg printf(":\n"); 3157ec681f3Smrg } 3167ec681f3Smrg } 3177ec681f3Smrg 3187ec681f3Smrg if (fname) { 3197ec681f3Smrg printlbl("%s", fname); 3207ec681f3Smrg printf(":\n"); 3217ec681f3Smrg } 3227ec681f3Smrg 3237ec681f3Smrg if (lname) { 3247ec681f3Smrg printlbl(" %s", lname); 3257ec681f3Smrg printf(":"); 3267ec681f3Smrg } else { 3277ec681f3Smrg printf(" "); 3287ec681f3Smrg } 3297ec681f3Smrg 3307ec681f3Smrg if (verbose) { 3317ec681f3Smrg printf("\t%04x: %08x ", pc, instrs[pc]); 3327ec681f3Smrg } else { 3337ec681f3Smrg printf(" "); 3347ec681f3Smrg } 3357ec681f3Smrg 3367ec681f3Smrg switch (opc) { 3377ec681f3Smrg case OPC_NOP: { 3387ec681f3Smrg /* a6xx changed the default immediate, and apparently 0 3397ec681f3Smrg * is illegal now. 3407ec681f3Smrg */ 3417ec681f3Smrg const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0; 3427ec681f3Smrg if (instrs[pc] != nop) { 3437ec681f3Smrg printerr("[%08x]", instrs[pc]); 3447ec681f3Smrg printf(" ; "); 3457ec681f3Smrg } 3467ec681f3Smrg if (rep) 3477ec681f3Smrg printf("(rep)"); 3487ec681f3Smrg printf("nop"); 3497ec681f3Smrg print_gpu_reg(instrs[pc]); 3507ec681f3Smrg 3517ec681f3Smrg break; 3527ec681f3Smrg } 3537ec681f3Smrg case OPC_ADD: 3547ec681f3Smrg case OPC_ADDHI: 3557ec681f3Smrg case OPC_SUB: 3567ec681f3Smrg case OPC_SUBHI: 3577ec681f3Smrg case OPC_AND: 3587ec681f3Smrg case OPC_OR: 3597ec681f3Smrg case OPC_XOR: 3607ec681f3Smrg case OPC_NOT: 3617ec681f3Smrg case OPC_SHL: 3627ec681f3Smrg case OPC_USHR: 3637ec681f3Smrg case OPC_ISHR: 3647ec681f3Smrg case OPC_ROT: 3657ec681f3Smrg case OPC_MUL8: 3667ec681f3Smrg case OPC_MIN: 3677ec681f3Smrg case OPC_MAX: 3687ec681f3Smrg case OPC_CMP: { 3697ec681f3Smrg bool src1 = true; 3707ec681f3Smrg 3717ec681f3Smrg if (opc == OPC_NOT) 3727ec681f3Smrg src1 = false; 3737ec681f3Smrg 3747ec681f3Smrg if (rep) 3757ec681f3Smrg printf("(rep)"); 3767ec681f3Smrg 3777ec681f3Smrg print_alu_name(opc, instrs[pc]); 3787ec681f3Smrg print_dst(instr->alui.dst); 3797ec681f3Smrg printf(", "); 3807ec681f3Smrg if (src1) { 3817ec681f3Smrg print_src(instr->alui.src); 3827ec681f3Smrg printf(", "); 3837ec681f3Smrg } 3847ec681f3Smrg printf("0x%04x", instr->alui.uimm); 3857ec681f3Smrg print_gpu_reg(instr->alui.uimm); 3867ec681f3Smrg 3877ec681f3Smrg /* print out unexpected bits: */ 3887ec681f3Smrg if (verbose) { 3897ec681f3Smrg if (instr->alui.src && !src1) 3907ec681f3Smrg printerr(" (src=%02x)", instr->alui.src); 3917ec681f3Smrg } 3927ec681f3Smrg 3937ec681f3Smrg break; 3947ec681f3Smrg } 3957ec681f3Smrg case OPC_MOVI: { 3967ec681f3Smrg if (rep) 3977ec681f3Smrg printf("(rep)"); 3987ec681f3Smrg printf("mov "); 3997ec681f3Smrg print_dst(instr->movi.dst); 4007ec681f3Smrg printf(", 0x%04x", instr->movi.uimm); 4017ec681f3Smrg if (instr->movi.shift) 4027ec681f3Smrg printf(" << %u", instr->movi.shift); 4037ec681f3Smrg 4047ec681f3Smrg if ((instr->movi.dst == REG_ADDR) && (instr->movi.shift >= 16)) { 4057ec681f3Smrg uint32_t val = (uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift; 4067ec681f3Smrg val &= ~0x40000; /* b18 seems to be a flag */ 4077ec681f3Smrg 4087ec681f3Smrg if ((val & 0x00ffffff) == 0) { 4097ec681f3Smrg printf("\t; "); 4107ec681f3Smrg print_pipe_reg(val >> 24); 4117ec681f3Smrg break; 4127ec681f3Smrg } 4137ec681f3Smrg } 4147ec681f3Smrg /* using mov w/ << 16 is popular way to construct a pkt7 4157ec681f3Smrg * header to send (for ex, from PFP to ME), so check that 4167ec681f3Smrg * case first 4177ec681f3Smrg */ 4187ec681f3Smrg if ((instr->movi.shift == 16) && 4197ec681f3Smrg ((instr->movi.uimm & 0xff00) == 0x7000)) { 4207ec681f3Smrg unsigned opc, p; 4217ec681f3Smrg 4227ec681f3Smrg opc = instr->movi.uimm & 0x7f; 4237ec681f3Smrg p = pm4_odd_parity_bit(opc); 4247ec681f3Smrg 4257ec681f3Smrg /* So, you'd think that checking the parity bit would be 4267ec681f3Smrg * a good way to rule out false positives, but seems like 4277ec681f3Smrg * ME doesn't really care.. at least it would filter out 4287ec681f3Smrg * things that look like actual legit packets between 4297ec681f3Smrg * PFP and ME.. 4307ec681f3Smrg */ 4317ec681f3Smrg if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) { 4327ec681f3Smrg const char *name = getpm4(opc); 4337ec681f3Smrg printf("\t; "); 4347ec681f3Smrg if (name) 4357ec681f3Smrg printlbl("%s", name); 4367ec681f3Smrg else 4377ec681f3Smrg printlbl("UNKN%u", opc); 4387ec681f3Smrg break; 4397ec681f3Smrg } 4407ec681f3Smrg } 4417ec681f3Smrg 4427ec681f3Smrg print_gpu_reg((uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift); 4437ec681f3Smrg 4447ec681f3Smrg break; 4457ec681f3Smrg } 4467ec681f3Smrg case OPC_ALU: { 4477ec681f3Smrg bool src1 = true; 4487ec681f3Smrg 4497ec681f3Smrg if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB) 4507ec681f3Smrg src1 = false; 4517ec681f3Smrg 4527ec681f3Smrg if (instr->alu.pad) 4537ec681f3Smrg printf("[%08x] ; ", instrs[pc]); 4547ec681f3Smrg 4557ec681f3Smrg if (rep) 4567ec681f3Smrg printf("(rep)"); 4577ec681f3Smrg if (instr->alu.xmov) 4587ec681f3Smrg printf("(xmov%d)", instr->alu.xmov); 4597ec681f3Smrg 4607ec681f3Smrg /* special case mnemonics: 4617ec681f3Smrg * reading $00 seems to always yield zero, and so: 4627ec681f3Smrg * or $dst, $00, $src -> mov $dst, $src 4637ec681f3Smrg * Maybe add one for negate too, ie. 4647ec681f3Smrg * sub $dst, $00, $src ??? 4657ec681f3Smrg */ 4667ec681f3Smrg if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) { 4677ec681f3Smrg printf("mov "); 4687ec681f3Smrg src1 = false; 4697ec681f3Smrg } else { 4707ec681f3Smrg print_alu_name(instr->alu.alu, instrs[pc]); 4717ec681f3Smrg } 4727ec681f3Smrg 4737ec681f3Smrg print_dst(instr->alu.dst); 4747ec681f3Smrg if (src1) { 4757ec681f3Smrg printf(", "); 4767ec681f3Smrg print_src(instr->alu.src1); 4777ec681f3Smrg } 4787ec681f3Smrg printf(", "); 4797ec681f3Smrg print_src(instr->alu.src2); 4807ec681f3Smrg 4817ec681f3Smrg /* print out unexpected bits: */ 4827ec681f3Smrg if (verbose) { 4837ec681f3Smrg if (instr->alu.pad) 4847ec681f3Smrg printerr(" (pad=%01x)", instr->alu.pad); 4857ec681f3Smrg if (instr->alu.src1 && !src1) 4867ec681f3Smrg printerr(" (src1=%02x)", instr->alu.src1); 4877ec681f3Smrg } 4887ec681f3Smrg 4897ec681f3Smrg /* xmov is a modifier that makes the processor execute up to 3 4907ec681f3Smrg * extra mov's after the current instruction. Given an ALU 4917ec681f3Smrg * instruction: 4927ec681f3Smrg * 4937ec681f3Smrg * (xmovN) alu $dst, $src1, $src2 4947ec681f3Smrg * 4957ec681f3Smrg * In all of the uses in the firmware blob, $dst and $src2 are one 4967ec681f3Smrg * of the "special" registers $data, $addr, $addr2. I've observed 4977ec681f3Smrg * that if $dst isn't "special" then it's replaced with $00 4987ec681f3Smrg * instead of $data, but I haven't checked what happens if $src2 4997ec681f3Smrg * isn't "special". Anyway, in the usual case, the HW produces a 5007ec681f3Smrg * count M = min(N, $rem) and then does the following: 5017ec681f3Smrg * 5027ec681f3Smrg * M = 1: 5037ec681f3Smrg * mov $data, $src2 5047ec681f3Smrg * 5057ec681f3Smrg * M = 2: 5067ec681f3Smrg * mov $data, $src2 5077ec681f3Smrg * mov $data, $src2 5087ec681f3Smrg * 5097ec681f3Smrg * M = 3: 5107ec681f3Smrg * mov $data, $src2 5117ec681f3Smrg * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH) 5127ec681f3Smrg * mov $data, $src2 5137ec681f3Smrg * 5147ec681f3Smrg * It seems to be frequently used in combination with (rep) to 5157ec681f3Smrg * provide a kind of hardware-based loop unrolling, and there's 5167ec681f3Smrg * even a special case in the ISA to be able to do this with 5177ec681f3Smrg * CP_CONTEXT_REG_BUNCH. However (rep) isn't required. 5187ec681f3Smrg * 5197ec681f3Smrg * This dumps the expected extra instructions, assuming that $rem 5207ec681f3Smrg * isn't too small. 5217ec681f3Smrg */ 5227ec681f3Smrg if (verbose && instr->alu.xmov) { 5237ec681f3Smrg for (int i = 0; i < instr->alu.xmov; i++) { 5247ec681f3Smrg printf("\n ; mov "); 5257ec681f3Smrg if (instr->alu.dst < 0x1d) 5267ec681f3Smrg printf("$00"); 5277ec681f3Smrg else if (instr->alu.xmov == 3 && i == 1) 5287ec681f3Smrg print_dst(instr->alu.dst); 5297ec681f3Smrg else 5307ec681f3Smrg printf("$data"); 5317ec681f3Smrg printf(", "); 5327ec681f3Smrg print_src(instr->alu.src2); 5337ec681f3Smrg } 5347ec681f3Smrg } 5357ec681f3Smrg 5367ec681f3Smrg break; 5377ec681f3Smrg } 5387ec681f3Smrg case OPC_CWRITE6: 5397ec681f3Smrg case OPC_CREAD6: 5407ec681f3Smrg case OPC_STORE6: 5417ec681f3Smrg case OPC_LOAD6: { 5427ec681f3Smrg if (rep) 5437ec681f3Smrg printf("(rep)"); 5447ec681f3Smrg 5457ec681f3Smrg bool is_control_reg = true; 5467ec681f3Smrg bool is_store = true; 5477ec681f3Smrg if (gpuver >= 6) { 5487ec681f3Smrg switch (opc) { 5497ec681f3Smrg case OPC_CWRITE6: 5507ec681f3Smrg printf("cwrite "); 5517ec681f3Smrg break; 5527ec681f3Smrg case OPC_CREAD6: 5537ec681f3Smrg is_store = false; 5547ec681f3Smrg printf("cread "); 5557ec681f3Smrg break; 5567ec681f3Smrg case OPC_STORE6: 5577ec681f3Smrg is_control_reg = false; 5587ec681f3Smrg printf("store "); 5597ec681f3Smrg break; 5607ec681f3Smrg case OPC_LOAD6: 5617ec681f3Smrg is_control_reg = false; 5627ec681f3Smrg is_store = false; 5637ec681f3Smrg printf("load "); 5647ec681f3Smrg break; 5657ec681f3Smrg default: 5667ec681f3Smrg assert(!"unreachable"); 5677ec681f3Smrg } 5687ec681f3Smrg } else { 5697ec681f3Smrg switch (opc) { 5707ec681f3Smrg case OPC_CWRITE5: 5717ec681f3Smrg printf("cwrite "); 5727ec681f3Smrg break; 5737ec681f3Smrg case OPC_CREAD5: 5747ec681f3Smrg is_store = false; 5757ec681f3Smrg printf("cread "); 5767ec681f3Smrg break; 5777ec681f3Smrg default: 5787ec681f3Smrg fprintf(stderr, "A6xx control opcode on A5xx?\n"); 5797ec681f3Smrg exit(1); 5807ec681f3Smrg } 5817ec681f3Smrg } 5827ec681f3Smrg 5837ec681f3Smrg if (is_store) 5847ec681f3Smrg print_src(instr->control.src1); 5857ec681f3Smrg else 5867ec681f3Smrg print_dst(instr->control.src1); 5877ec681f3Smrg printf(", ["); 5887ec681f3Smrg print_src(instr->control.src2); 5897ec681f3Smrg printf(" + "); 5907ec681f3Smrg if (is_control_reg && instr->control.flags != 0x4) 5917ec681f3Smrg print_control_reg(instr->control.uimm); 5927ec681f3Smrg else 5937ec681f3Smrg printf("0x%03x", instr->control.uimm); 5947ec681f3Smrg printf("], 0x%x", instr->control.flags); 5957ec681f3Smrg break; 5967ec681f3Smrg } 5977ec681f3Smrg case OPC_BRNEI: 5987ec681f3Smrg case OPC_BREQI: 5997ec681f3Smrg case OPC_BRNEB: 6007ec681f3Smrg case OPC_BREQB: { 6017ec681f3Smrg unsigned off = pc + instr->br.ioff; 6027ec681f3Smrg 6037ec681f3Smrg assert(!rep); 6047ec681f3Smrg 6057ec681f3Smrg /* Since $00 reads back zero, it can be used as src for 6067ec681f3Smrg * unconditional branches. (This only really makes sense 6077ec681f3Smrg * for the BREQB.. or possible BRNEI if imm==0.) 6087ec681f3Smrg * 6097ec681f3Smrg * If bit=0 then branch is taken if *all* bits are zero. 6107ec681f3Smrg * Otherwise it is taken if bit (bit-1) is clear. 6117ec681f3Smrg * 6127ec681f3Smrg * Note the instruction after a jump/branch is executed 6137ec681f3Smrg * regardless of whether branch is taken, so use nop or 6147ec681f3Smrg * take that into account in code. 6157ec681f3Smrg */ 6167ec681f3Smrg if (instr->br.src || (opc != OPC_BRNEB)) { 6177ec681f3Smrg bool immed = false; 6187ec681f3Smrg 6197ec681f3Smrg if (opc == OPC_BRNEI) { 6207ec681f3Smrg printf("brne "); 6217ec681f3Smrg immed = true; 6227ec681f3Smrg } else if (opc == OPC_BREQI) { 6237ec681f3Smrg printf("breq "); 6247ec681f3Smrg immed = true; 6257ec681f3Smrg } else if (opc == OPC_BRNEB) { 6267ec681f3Smrg printf("brne "); 6277ec681f3Smrg } else if (opc == OPC_BREQB) { 6287ec681f3Smrg printf("breq "); 6297ec681f3Smrg } 6307ec681f3Smrg print_src(instr->br.src); 6317ec681f3Smrg if (immed) { 6327ec681f3Smrg printf(", 0x%x,", instr->br.bit_or_imm); 6337ec681f3Smrg } else { 6347ec681f3Smrg printf(", b%u,", instr->br.bit_or_imm); 6357ec681f3Smrg } 6367ec681f3Smrg } else { 6377ec681f3Smrg printf("jump"); 6387ec681f3Smrg if (verbose && instr->br.bit_or_imm) { 6397ec681f3Smrg printerr(" (src=%03x, bit=%03x) ", instr->br.src, 6407ec681f3Smrg instr->br.bit_or_imm); 6417ec681f3Smrg } 6427ec681f3Smrg } 6437ec681f3Smrg 6447ec681f3Smrg printf(" #"); 6457ec681f3Smrg printlbl("%s", label_name(off, true)); 6467ec681f3Smrg if (verbose) 6477ec681f3Smrg printf(" (#%d, %04x)", instr->br.ioff, off); 6487ec681f3Smrg break; 6497ec681f3Smrg } 6507ec681f3Smrg case OPC_CALL: 6517ec681f3Smrg assert(!rep); 6527ec681f3Smrg printf("call #"); 6537ec681f3Smrg printlbl("%s", fxn_name(instr->call.uoff)); 6547ec681f3Smrg if (verbose) { 6557ec681f3Smrg printf(" (%04x)", instr->call.uoff); 6567ec681f3Smrg if (instr->br.bit_or_imm || instr->br.src) { 6577ec681f3Smrg printerr(" (src=%03x, bit=%03x) ", instr->br.src, 6587ec681f3Smrg instr->br.bit_or_imm); 6597ec681f3Smrg } 6607ec681f3Smrg } 6617ec681f3Smrg break; 6627ec681f3Smrg case OPC_RET: 6637ec681f3Smrg assert(!rep); 6647ec681f3Smrg if (instr->ret.pad) 6657ec681f3Smrg printf("[%08x] ; ", instrs[pc]); 6667ec681f3Smrg if (instr->ret.interrupt) 6677ec681f3Smrg printf("iret"); 6687ec681f3Smrg else 6697ec681f3Smrg printf("ret"); 6707ec681f3Smrg break; 6717ec681f3Smrg case OPC_WIN: 6727ec681f3Smrg assert(!rep); 6737ec681f3Smrg if (instr->waitin.pad) 6747ec681f3Smrg printf("[%08x] ; ", instrs[pc]); 6757ec681f3Smrg printf("waitin"); 6767ec681f3Smrg if (verbose && instr->waitin.pad) 6777ec681f3Smrg printerr(" (pad=%x)", instr->waitin.pad); 6787ec681f3Smrg break; 6797ec681f3Smrg case OPC_PREEMPTLEAVE6: 6807ec681f3Smrg if (gpuver < 6) { 6817ec681f3Smrg printf("[%08x] ; op38", instrs[pc]); 6827ec681f3Smrg } else { 6837ec681f3Smrg printf("preemptleave #"); 6847ec681f3Smrg printlbl("%s", label_name(instr->call.uoff, true)); 6857ec681f3Smrg } 6867ec681f3Smrg break; 6877ec681f3Smrg case OPC_SETSECURE: 6887ec681f3Smrg /* Note: This seems to implicitly read the secure/not-secure state 6897ec681f3Smrg * to set from the low bit of $02, and implicitly jumps to pc + 3 6907ec681f3Smrg * (i.e. skipping the next two instructions) if it succeeds. We 6917ec681f3Smrg * print these implicit parameters to make reading the disassembly 6927ec681f3Smrg * easier. 6937ec681f3Smrg */ 6947ec681f3Smrg if (instr->pad) 6957ec681f3Smrg printf("[%08x] ; ", instrs[pc]); 6967ec681f3Smrg printf("setsecure $02, #"); 6977ec681f3Smrg printlbl("%s", label_name(pc + 3, true)); 6987ec681f3Smrg break; 6997ec681f3Smrg default: 7007ec681f3Smrg printerr("[%08x]", instrs[pc]); 7017ec681f3Smrg printf(" ; op%02x ", opc); 7027ec681f3Smrg print_dst(instr->alui.dst); 7037ec681f3Smrg printf(", "); 7047ec681f3Smrg print_src(instr->alui.src); 7057ec681f3Smrg print_gpu_reg(instrs[pc] & 0xffff); 7067ec681f3Smrg break; 7077ec681f3Smrg } 7087ec681f3Smrg printf("\n"); 7097ec681f3Smrg} 7107ec681f3Smrg 7117ec681f3Smrgstatic void 7127ec681f3Smrgsetup_packet_table(uint32_t *jmptbl, uint32_t sizedwords) 7137ec681f3Smrg{ 7147ec681f3Smrg num_jump_labels = 0; 7157ec681f3Smrg 7167ec681f3Smrg for (unsigned i = 0; i < sizedwords; i++) { 7177ec681f3Smrg unsigned offset = jmptbl[i]; 7187ec681f3Smrg unsigned n = i; // + CP_NOP; 7197ec681f3Smrg add_jump_table_entry(n, offset); 7207ec681f3Smrg } 7217ec681f3Smrg} 7227ec681f3Smrg 7237ec681f3Smrgstatic void 7247ec681f3Smrgsetup_labels(uint32_t *instrs, uint32_t sizedwords) 7257ec681f3Smrg{ 7267ec681f3Smrg afuc_opc opc; 7277ec681f3Smrg bool rep; 7287ec681f3Smrg 7297ec681f3Smrg num_label_offsets = 0; 7307ec681f3Smrg 7317ec681f3Smrg for (unsigned i = 0; i < sizedwords; i++) { 7327ec681f3Smrg afuc_instr *instr = (void *)&instrs[i]; 7337ec681f3Smrg 7347ec681f3Smrg afuc_get_opc(instr, &opc, &rep); 7357ec681f3Smrg 7367ec681f3Smrg switch (opc) { 7377ec681f3Smrg case OPC_BRNEI: 7387ec681f3Smrg case OPC_BREQI: 7397ec681f3Smrg case OPC_BRNEB: 7407ec681f3Smrg case OPC_BREQB: 7417ec681f3Smrg label_idx(i + instr->br.ioff, true); 7427ec681f3Smrg break; 7437ec681f3Smrg case OPC_PREEMPTLEAVE6: 7447ec681f3Smrg if (gpuver >= 6) 7457ec681f3Smrg label_idx(instr->call.uoff, true); 7467ec681f3Smrg break; 7477ec681f3Smrg case OPC_CALL: 7487ec681f3Smrg fxn_idx(instr->call.uoff, true); 7497ec681f3Smrg break; 7507ec681f3Smrg case OPC_SETSECURE: 7517ec681f3Smrg /* this implicitly jumps to pc + 3 if successful */ 7527ec681f3Smrg label_idx(i + 3, true); 7537ec681f3Smrg break; 7547ec681f3Smrg default: 7557ec681f3Smrg break; 7567ec681f3Smrg } 7577ec681f3Smrg } 7587ec681f3Smrg} 7597ec681f3Smrg 7607ec681f3Smrgstatic void 7617ec681f3Smrgdisasm(struct emu *emu) 7627ec681f3Smrg{ 7637ec681f3Smrg uint32_t sizedwords = emu->sizedwords; 7647ec681f3Smrg uint32_t lpac_offset = 0; 7657ec681f3Smrg 7667ec681f3Smrg EMU_GPU_REG(CP_SQE_INSTR_BASE); 7677ec681f3Smrg EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE); 7687ec681f3Smrg 7697ec681f3Smrg emu_init(emu); 7707ec681f3Smrg 7717ec681f3Smrg#ifdef BOOTSTRAP_DEBUG 7727ec681f3Smrg while (true) { 7737ec681f3Smrg disasm_instr(emu->instrs, emu->gpr_regs.pc); 7747ec681f3Smrg emu_step(emu); 7757ec681f3Smrg } 7767ec681f3Smrg#endif 7777ec681f3Smrg 7787ec681f3Smrg emu_run_bootstrap(emu); 7797ec681f3Smrg 7807ec681f3Smrg /* Figure out if we have LPAC SQE appended: */ 7817ec681f3Smrg if (emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE)) { 7827ec681f3Smrg lpac_offset = emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE) - 7837ec681f3Smrg emu_get_reg64(emu, &CP_SQE_INSTR_BASE); 7847ec681f3Smrg lpac_offset /= 4; 7857ec681f3Smrg sizedwords = lpac_offset; 7867ec681f3Smrg } 7877ec681f3Smrg 7887ec681f3Smrg setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); 7897ec681f3Smrg setup_labels(emu->instrs, emu->sizedwords); 7907ec681f3Smrg 7917ec681f3Smrg /* TODO add option to emulate LPAC SQE instead: */ 7927ec681f3Smrg if (emulator) { 7937ec681f3Smrg /* Start from clean slate: */ 7947ec681f3Smrg emu_fini(emu); 7957ec681f3Smrg emu_init(emu); 7967ec681f3Smrg 7977ec681f3Smrg while (true) { 7987ec681f3Smrg disasm_instr(emu->instrs, emu->gpr_regs.pc); 7997ec681f3Smrg emu_step(emu); 8007ec681f3Smrg } 8017ec681f3Smrg } 8027ec681f3Smrg 8037ec681f3Smrg /* print instructions: */ 8047ec681f3Smrg for (int i = 0; i < sizedwords; i++) { 8057ec681f3Smrg disasm_instr(emu->instrs, i); 8067ec681f3Smrg } 8077ec681f3Smrg 8087ec681f3Smrg if (!lpac_offset) 8097ec681f3Smrg return; 8107ec681f3Smrg 8117ec681f3Smrg printf(";\n"); 8127ec681f3Smrg printf("; LPAC microcode:\n"); 8137ec681f3Smrg printf(";\n"); 8147ec681f3Smrg 8157ec681f3Smrg emu_fini(emu); 8167ec681f3Smrg 8177ec681f3Smrg emu->lpac = true; 8187ec681f3Smrg emu->instrs += lpac_offset; 8197ec681f3Smrg emu->sizedwords -= lpac_offset; 8207ec681f3Smrg 8217ec681f3Smrg emu_init(emu); 8227ec681f3Smrg emu_run_bootstrap(emu); 8237ec681f3Smrg 8247ec681f3Smrg setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); 8257ec681f3Smrg setup_labels(emu->instrs, emu->sizedwords); 8267ec681f3Smrg 8277ec681f3Smrg /* print instructions: */ 8287ec681f3Smrg for (int i = 0; i < emu->sizedwords; i++) { 8297ec681f3Smrg disasm_instr(emu->instrs, i); 8307ec681f3Smrg } 8317ec681f3Smrg} 8327ec681f3Smrg 8337ec681f3Smrg 8347ec681f3Smrgstatic void 8357ec681f3Smrgdisasm_legacy(uint32_t *buf, int sizedwords) 8367ec681f3Smrg{ 8377ec681f3Smrg uint32_t *instrs = buf; 8387ec681f3Smrg const int jmptbl_start = instrs[1] & 0xffff; 8397ec681f3Smrg uint32_t *jmptbl = &buf[jmptbl_start]; 8407ec681f3Smrg int i; 8417ec681f3Smrg 8427ec681f3Smrg /* parse jumptable: */ 8437ec681f3Smrg setup_packet_table(jmptbl, 0x80); 8447ec681f3Smrg 8457ec681f3Smrg /* do a pre-pass to find instructions that are potential branch targets, 8467ec681f3Smrg * and add labels for them: 8477ec681f3Smrg */ 8487ec681f3Smrg setup_labels(instrs, jmptbl_start); 8497ec681f3Smrg 8507ec681f3Smrg /* print instructions: */ 8517ec681f3Smrg for (i = 0; i < jmptbl_start; i++) { 8527ec681f3Smrg disasm_instr(instrs, i); 8537ec681f3Smrg } 8547ec681f3Smrg 8557ec681f3Smrg /* print jumptable: */ 8567ec681f3Smrg if (verbose) { 8577ec681f3Smrg printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"); 8587ec681f3Smrg printf("; JUMP TABLE\n"); 8597ec681f3Smrg for (i = 0; i < 0x7f; i++) { 8607ec681f3Smrg int n = i; // + CP_NOP; 8617ec681f3Smrg uint32_t offset = jmptbl[i]; 8627ec681f3Smrg const char *name = getpm4(n); 8637ec681f3Smrg printf("%3d %02x: ", n, n); 8647ec681f3Smrg printf("%04x", offset); 8657ec681f3Smrg if (name) { 8667ec681f3Smrg printf(" ; %s", name); 8677ec681f3Smrg } else { 8687ec681f3Smrg printf(" ; UNKN%d", n); 8697ec681f3Smrg } 8707ec681f3Smrg printf("\n"); 8717ec681f3Smrg } 8727ec681f3Smrg } 8737ec681f3Smrg} 8747ec681f3Smrg 8757ec681f3Smrgstatic void 8767ec681f3Smrgusage(void) 8777ec681f3Smrg{ 8787ec681f3Smrg fprintf(stderr, "Usage:\n" 8797ec681f3Smrg "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n" 8807ec681f3Smrg "\t\t-g - specify GPU version (5, etc)\n" 8817ec681f3Smrg "\t\t-c - use colors\n" 8827ec681f3Smrg "\t\t-v - verbose output\n" 8837ec681f3Smrg "\t\t-e - emulator mode\n"); 8847ec681f3Smrg exit(2); 8857ec681f3Smrg} 8867ec681f3Smrg 8877ec681f3Smrgint 8887ec681f3Smrgmain(int argc, char **argv) 8897ec681f3Smrg{ 8907ec681f3Smrg uint32_t *buf; 8917ec681f3Smrg char *file; 8927ec681f3Smrg bool colors = false; 8937ec681f3Smrg uint32_t gpu_id = 0; 8947ec681f3Smrg size_t sz; 8957ec681f3Smrg int c, ret; 8967ec681f3Smrg bool unit_test = false; 8977ec681f3Smrg 8987ec681f3Smrg /* Argument parsing: */ 8997ec681f3Smrg while ((c = getopt(argc, argv, "g:vceu")) != -1) { 9007ec681f3Smrg switch (c) { 9017ec681f3Smrg case 'g': 9027ec681f3Smrg gpu_id = atoi(optarg); 9037ec681f3Smrg break; 9047ec681f3Smrg case 'v': 9057ec681f3Smrg verbose = true; 9067ec681f3Smrg break; 9077ec681f3Smrg case 'c': 9087ec681f3Smrg colors = true; 9097ec681f3Smrg break; 9107ec681f3Smrg case 'e': 9117ec681f3Smrg emulator = true; 9127ec681f3Smrg verbose = true; 9137ec681f3Smrg break; 9147ec681f3Smrg case 'u': 9157ec681f3Smrg unit_test = true; 9167ec681f3Smrg break; 9177ec681f3Smrg default: 9187ec681f3Smrg usage(); 9197ec681f3Smrg } 9207ec681f3Smrg } 9217ec681f3Smrg 9227ec681f3Smrg if (optind >= argc) { 9237ec681f3Smrg fprintf(stderr, "no file specified!\n"); 9247ec681f3Smrg usage(); 9257ec681f3Smrg } 9267ec681f3Smrg 9277ec681f3Smrg file = argv[optind]; 9287ec681f3Smrg 9297ec681f3Smrg /* if gpu version not specified, infer from filename: */ 9307ec681f3Smrg if (!gpu_id) { 9317ec681f3Smrg char *str = strstr(file, "a5"); 9327ec681f3Smrg if (!str) 9337ec681f3Smrg str = strstr(file, "a6"); 9347ec681f3Smrg if (str) 9357ec681f3Smrg gpu_id = atoi(str + 1); 9367ec681f3Smrg } 9377ec681f3Smrg 9387ec681f3Smrg if (gpu_id < 500) { 9397ec681f3Smrg printf("invalid gpu_id: %d\n", gpu_id); 9407ec681f3Smrg return -1; 9417ec681f3Smrg } 9427ec681f3Smrg 9437ec681f3Smrg gpuver = gpu_id / 100; 9447ec681f3Smrg 9457ec681f3Smrg /* a6xx is *mostly* a superset of a5xx, but some opcodes shuffle 9467ec681f3Smrg * around, and behavior of special regs is a bit different. Right 9477ec681f3Smrg * now we only bother to support the a6xx variant. 9487ec681f3Smrg */ 9497ec681f3Smrg if (emulator && (gpuver != 6)) { 9507ec681f3Smrg fprintf(stderr, "Emulator only supported on a6xx!\n"); 9517ec681f3Smrg return 1; 9527ec681f3Smrg } 9537ec681f3Smrg 9547ec681f3Smrg ret = afuc_util_init(gpuver, colors); 9557ec681f3Smrg if (ret < 0) { 9567ec681f3Smrg usage(); 9577ec681f3Smrg } 9587ec681f3Smrg 9597ec681f3Smrg printf("; a%dxx microcode\n", gpuver); 9607ec681f3Smrg 9617ec681f3Smrg buf = (uint32_t *)os_read_file(file, &sz); 9627ec681f3Smrg 9637ec681f3Smrg if (!unit_test) 9647ec681f3Smrg printf("; Disassembling microcode: %s\n", file); 9657ec681f3Smrg printf("; Version: %08x\n\n", buf[1]); 9667ec681f3Smrg 9677ec681f3Smrg if (gpuver < 6) { 9687ec681f3Smrg disasm_legacy(&buf[1], sz / 4 - 1); 9697ec681f3Smrg } else { 9707ec681f3Smrg struct emu emu = { 9717ec681f3Smrg .instrs = &buf[1], 9727ec681f3Smrg .sizedwords = sz / 4 - 1, 9737ec681f3Smrg .gpu_id = gpu_id, 9747ec681f3Smrg }; 9757ec681f3Smrg 9767ec681f3Smrg disasm(&emu); 9777ec681f3Smrg } 9787ec681f3Smrg 9797ec681f3Smrg return 0; 9807ec681f3Smrg} 981