1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright (c) 2017 Rob Clark <robdclark@gmail.com> 3bf215546Sopenharmony_ci * 4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation 7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10bf215546Sopenharmony_ci * 11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next 12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13bf215546Sopenharmony_ci * Software. 14bf215546Sopenharmony_ci * 15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20bf215546Sopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21bf215546Sopenharmony_ci * SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include <assert.h> 25bf215546Sopenharmony_ci#include <err.h> 26bf215546Sopenharmony_ci#include <fcntl.h> 27bf215546Sopenharmony_ci#include <getopt.h> 28bf215546Sopenharmony_ci#include <stdarg.h> 29bf215546Sopenharmony_ci#include <stdbool.h> 30bf215546Sopenharmony_ci#include <stdint.h> 31bf215546Sopenharmony_ci#include <stdio.h> 32bf215546Sopenharmony_ci#include <stdlib.h> 33bf215546Sopenharmony_ci#include <string.h> 34bf215546Sopenharmony_ci#include <unistd.h> 35bf215546Sopenharmony_ci 36bf215546Sopenharmony_ci#include "util/os_file.h" 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_ci#include "freedreno_pm4.h" 39bf215546Sopenharmony_ci 40bf215546Sopenharmony_ci#include "afuc.h" 41bf215546Sopenharmony_ci#include "util.h" 42bf215546Sopenharmony_ci#include "emu.h" 43bf215546Sopenharmony_ci 44bf215546Sopenharmony_cistatic int gpuver; 45bf215546Sopenharmony_ci 46bf215546Sopenharmony_ci/* non-verbose mode should output something suitable to feed back into 47bf215546Sopenharmony_ci * assembler.. verbose mode has additional output useful for debugging 48bf215546Sopenharmony_ci * (like unexpected bits that are set) 49bf215546Sopenharmony_ci */ 50bf215546Sopenharmony_cistatic bool verbose = false; 51bf215546Sopenharmony_ci 52bf215546Sopenharmony_ci/* emulator mode: */ 53bf215546Sopenharmony_cistatic bool emulator = false; 54bf215546Sopenharmony_ci 55bf215546Sopenharmony_cistatic void 56bf215546Sopenharmony_ciprint_gpu_reg(uint32_t regbase) 57bf215546Sopenharmony_ci{ 58bf215546Sopenharmony_ci if (regbase < 0x100) 59bf215546Sopenharmony_ci return; 60bf215546Sopenharmony_ci 61bf215546Sopenharmony_ci char *name = afuc_gpu_reg_name(regbase); 62bf215546Sopenharmony_ci if (name) { 63bf215546Sopenharmony_ci printf("\t; %s", name); 64bf215546Sopenharmony_ci free(name); 65bf215546Sopenharmony_ci } 66bf215546Sopenharmony_ci} 67bf215546Sopenharmony_ci 68bf215546Sopenharmony_ci#define printerr(fmt, ...) afuc_printc(AFUC_ERR, fmt, ##__VA_ARGS__) 69bf215546Sopenharmony_ci#define printlbl(fmt, ...) afuc_printc(AFUC_LBL, fmt, ##__VA_ARGS__) 70bf215546Sopenharmony_ci 71bf215546Sopenharmony_civoid 72bf215546Sopenharmony_ciprint_src(unsigned reg) 73bf215546Sopenharmony_ci{ 74bf215546Sopenharmony_ci if (reg == REG_REM) 75bf215546Sopenharmony_ci printf("$rem"); /* remainding dwords in packet */ 76bf215546Sopenharmony_ci else if (reg == REG_MEMDATA) 77bf215546Sopenharmony_ci printf("$memdata"); 78bf215546Sopenharmony_ci else if (reg == REG_REGDATA) 79bf215546Sopenharmony_ci printf("$regdata"); 80bf215546Sopenharmony_ci else if (reg == REG_DATA) 81bf215546Sopenharmony_ci printf("$data"); 82bf215546Sopenharmony_ci else 83bf215546Sopenharmony_ci printf("$%02x", reg); 84bf215546Sopenharmony_ci} 85bf215546Sopenharmony_ci 86bf215546Sopenharmony_civoid 87bf215546Sopenharmony_ciprint_dst(unsigned reg) 88bf215546Sopenharmony_ci{ 89bf215546Sopenharmony_ci if (reg == REG_REM) 90bf215546Sopenharmony_ci printf("$rem"); /* remainding dwords in packet */ 91bf215546Sopenharmony_ci else if (reg == REG_ADDR) 92bf215546Sopenharmony_ci printf("$addr"); 93bf215546Sopenharmony_ci else if (reg == REG_USRADDR) 94bf215546Sopenharmony_ci printf("$usraddr"); 95bf215546Sopenharmony_ci else if (reg == REG_DATA) 96bf215546Sopenharmony_ci printf("$data"); 97bf215546Sopenharmony_ci else 98bf215546Sopenharmony_ci printf("$%02x", reg); 99bf215546Sopenharmony_ci} 100bf215546Sopenharmony_ci 101bf215546Sopenharmony_cistatic void 102bf215546Sopenharmony_ciprint_alu_name(afuc_opc opc, uint32_t instr) 103bf215546Sopenharmony_ci{ 104bf215546Sopenharmony_ci if (opc == OPC_ADD) { 105bf215546Sopenharmony_ci printf("add "); 106bf215546Sopenharmony_ci } else if (opc == OPC_ADDHI) { 107bf215546Sopenharmony_ci printf("addhi "); 108bf215546Sopenharmony_ci } else if (opc == OPC_SUB) { 109bf215546Sopenharmony_ci printf("sub "); 110bf215546Sopenharmony_ci } else if (opc == OPC_SUBHI) { 111bf215546Sopenharmony_ci printf("subhi "); 112bf215546Sopenharmony_ci } else if (opc == OPC_AND) { 113bf215546Sopenharmony_ci printf("and "); 114bf215546Sopenharmony_ci } else if (opc == OPC_OR) { 115bf215546Sopenharmony_ci printf("or "); 116bf215546Sopenharmony_ci } else if (opc == OPC_XOR) { 117bf215546Sopenharmony_ci printf("xor "); 118bf215546Sopenharmony_ci } else if (opc == OPC_NOT) { 119bf215546Sopenharmony_ci printf("not "); 120bf215546Sopenharmony_ci } else if (opc == OPC_SHL) { 121bf215546Sopenharmony_ci printf("shl "); 122bf215546Sopenharmony_ci } else if (opc == OPC_USHR) { 123bf215546Sopenharmony_ci printf("ushr "); 124bf215546Sopenharmony_ci } else if (opc == OPC_ISHR) { 125bf215546Sopenharmony_ci printf("ishr "); 126bf215546Sopenharmony_ci } else if (opc == OPC_ROT) { 127bf215546Sopenharmony_ci printf("rot "); 128bf215546Sopenharmony_ci } else if (opc == OPC_MUL8) { 129bf215546Sopenharmony_ci printf("mul8 "); 130bf215546Sopenharmony_ci } else if (opc == OPC_MIN) { 131bf215546Sopenharmony_ci printf("min "); 132bf215546Sopenharmony_ci } else if (opc == OPC_MAX) { 133bf215546Sopenharmony_ci printf("max "); 134bf215546Sopenharmony_ci } else if (opc == OPC_CMP) { 135bf215546Sopenharmony_ci printf("cmp "); 136bf215546Sopenharmony_ci } else if (opc == OPC_MSB) { 137bf215546Sopenharmony_ci printf("msb "); 138bf215546Sopenharmony_ci } else { 139bf215546Sopenharmony_ci printerr("[%08x]", instr); 140bf215546Sopenharmony_ci printf(" ; alu%02x ", opc); 141bf215546Sopenharmony_ci } 142bf215546Sopenharmony_ci} 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_cistatic const char * 145bf215546Sopenharmony_cigetpm4(uint32_t id) 146bf215546Sopenharmony_ci{ 147bf215546Sopenharmony_ci return afuc_pm_id_name(id); 148bf215546Sopenharmony_ci} 149bf215546Sopenharmony_ci 150bf215546Sopenharmony_cistatic struct { 151bf215546Sopenharmony_ci uint32_t offset; 152bf215546Sopenharmony_ci uint32_t num_jump_labels; 153bf215546Sopenharmony_ci uint32_t jump_labels[256]; 154bf215546Sopenharmony_ci} jump_labels[1024]; 155bf215546Sopenharmony_ciint num_jump_labels; 156bf215546Sopenharmony_ci 157bf215546Sopenharmony_cistatic void 158bf215546Sopenharmony_ciadd_jump_table_entry(uint32_t n, uint32_t offset) 159bf215546Sopenharmony_ci{ 160bf215546Sopenharmony_ci int i; 161bf215546Sopenharmony_ci 162bf215546Sopenharmony_ci if (n > 128) /* can't possibly be a PM4 PKT3.. */ 163bf215546Sopenharmony_ci return; 164bf215546Sopenharmony_ci 165bf215546Sopenharmony_ci for (i = 0; i < num_jump_labels; i++) 166bf215546Sopenharmony_ci if (jump_labels[i].offset == offset) 167bf215546Sopenharmony_ci goto add_label; 168bf215546Sopenharmony_ci 169bf215546Sopenharmony_ci num_jump_labels = i + 1; 170bf215546Sopenharmony_ci jump_labels[i].offset = offset; 171bf215546Sopenharmony_ci jump_labels[i].num_jump_labels = 0; 172bf215546Sopenharmony_ci 173bf215546Sopenharmony_ciadd_label: 174bf215546Sopenharmony_ci jump_labels[i].jump_labels[jump_labels[i].num_jump_labels++] = n; 175bf215546Sopenharmony_ci assert(jump_labels[i].num_jump_labels < 256); 176bf215546Sopenharmony_ci} 177bf215546Sopenharmony_ci 178bf215546Sopenharmony_cistatic int 179bf215546Sopenharmony_ciget_jump_table_entry(uint32_t offset) 180bf215546Sopenharmony_ci{ 181bf215546Sopenharmony_ci int i; 182bf215546Sopenharmony_ci 183bf215546Sopenharmony_ci for (i = 0; i < num_jump_labels; i++) 184bf215546Sopenharmony_ci if (jump_labels[i].offset == offset) 185bf215546Sopenharmony_ci return i; 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci return -1; 188bf215546Sopenharmony_ci} 189bf215546Sopenharmony_ci 190bf215546Sopenharmony_cistatic uint32_t label_offsets[0x512]; 191bf215546Sopenharmony_cistatic int num_label_offsets; 192bf215546Sopenharmony_ci 193bf215546Sopenharmony_cistatic int 194bf215546Sopenharmony_cilabel_idx(uint32_t offset, bool create) 195bf215546Sopenharmony_ci{ 196bf215546Sopenharmony_ci int i; 197bf215546Sopenharmony_ci for (i = 0; i < num_label_offsets; i++) 198bf215546Sopenharmony_ci if (offset == label_offsets[i]) 199bf215546Sopenharmony_ci return i; 200bf215546Sopenharmony_ci if (!create) 201bf215546Sopenharmony_ci return -1; 202bf215546Sopenharmony_ci label_offsets[i] = offset; 203bf215546Sopenharmony_ci num_label_offsets = i + 1; 204bf215546Sopenharmony_ci return i; 205bf215546Sopenharmony_ci} 206bf215546Sopenharmony_ci 207bf215546Sopenharmony_cistatic const char * 208bf215546Sopenharmony_cilabel_name(uint32_t offset, bool allow_jt) 209bf215546Sopenharmony_ci{ 210bf215546Sopenharmony_ci static char name[12]; 211bf215546Sopenharmony_ci int lidx; 212bf215546Sopenharmony_ci 213bf215546Sopenharmony_ci if (allow_jt) { 214bf215546Sopenharmony_ci lidx = get_jump_table_entry(offset); 215bf215546Sopenharmony_ci if (lidx >= 0) { 216bf215546Sopenharmony_ci int j; 217bf215546Sopenharmony_ci for (j = 0; j < jump_labels[lidx].num_jump_labels; j++) { 218bf215546Sopenharmony_ci uint32_t jump_label = jump_labels[lidx].jump_labels[j]; 219bf215546Sopenharmony_ci const char *str = getpm4(jump_label); 220bf215546Sopenharmony_ci if (str) 221bf215546Sopenharmony_ci return str; 222bf215546Sopenharmony_ci } 223bf215546Sopenharmony_ci // if we don't find anything w/ known name, maybe we should 224bf215546Sopenharmony_ci // return UNKN%d to at least make it clear that this is some 225bf215546Sopenharmony_ci // sort of jump-table entry? 226bf215546Sopenharmony_ci } 227bf215546Sopenharmony_ci } 228bf215546Sopenharmony_ci 229bf215546Sopenharmony_ci lidx = label_idx(offset, false); 230bf215546Sopenharmony_ci if (lidx < 0) 231bf215546Sopenharmony_ci return NULL; 232bf215546Sopenharmony_ci sprintf(name, "l%03d", lidx); 233bf215546Sopenharmony_ci return name; 234bf215546Sopenharmony_ci} 235bf215546Sopenharmony_ci 236bf215546Sopenharmony_cistatic uint32_t fxn_offsets[0x512]; 237bf215546Sopenharmony_cistatic int num_fxn_offsets; 238bf215546Sopenharmony_ci 239bf215546Sopenharmony_cistatic int 240bf215546Sopenharmony_cifxn_idx(uint32_t offset, bool create) 241bf215546Sopenharmony_ci{ 242bf215546Sopenharmony_ci int i; 243bf215546Sopenharmony_ci for (i = 0; i < num_fxn_offsets; i++) 244bf215546Sopenharmony_ci if (offset == fxn_offsets[i]) 245bf215546Sopenharmony_ci return i; 246bf215546Sopenharmony_ci if (!create) 247bf215546Sopenharmony_ci return -1; 248bf215546Sopenharmony_ci fxn_offsets[i] = offset; 249bf215546Sopenharmony_ci num_fxn_offsets = i + 1; 250bf215546Sopenharmony_ci return i; 251bf215546Sopenharmony_ci} 252bf215546Sopenharmony_ci 253bf215546Sopenharmony_cistatic const char * 254bf215546Sopenharmony_cifxn_name(uint32_t offset) 255bf215546Sopenharmony_ci{ 256bf215546Sopenharmony_ci static char name[14]; 257bf215546Sopenharmony_ci int fidx = fxn_idx(offset, false); 258bf215546Sopenharmony_ci if (fidx < 0) 259bf215546Sopenharmony_ci return NULL; 260bf215546Sopenharmony_ci sprintf(name, "fxn%02d", fidx); 261bf215546Sopenharmony_ci return name; 262bf215546Sopenharmony_ci} 263bf215546Sopenharmony_ci 264bf215546Sopenharmony_civoid 265bf215546Sopenharmony_ciprint_control_reg(uint32_t id) 266bf215546Sopenharmony_ci{ 267bf215546Sopenharmony_ci char *name = afuc_control_reg_name(id); 268bf215546Sopenharmony_ci if (name) { 269bf215546Sopenharmony_ci printf("@%s", name); 270bf215546Sopenharmony_ci free(name); 271bf215546Sopenharmony_ci } else { 272bf215546Sopenharmony_ci printf("0x%03x", id); 273bf215546Sopenharmony_ci } 274bf215546Sopenharmony_ci} 275bf215546Sopenharmony_ci 276bf215546Sopenharmony_civoid 277bf215546Sopenharmony_ciprint_pipe_reg(uint32_t id) 278bf215546Sopenharmony_ci{ 279bf215546Sopenharmony_ci char *name = afuc_pipe_reg_name(id); 280bf215546Sopenharmony_ci if (name) { 281bf215546Sopenharmony_ci printf("|%s", name); 282bf215546Sopenharmony_ci free(name); 283bf215546Sopenharmony_ci } else { 284bf215546Sopenharmony_ci printf("0x%03x", id); 285bf215546Sopenharmony_ci } 286bf215546Sopenharmony_ci} 287bf215546Sopenharmony_ci 288bf215546Sopenharmony_cistatic void 289bf215546Sopenharmony_cidisasm_instr(uint32_t *instrs, unsigned pc) 290bf215546Sopenharmony_ci{ 291bf215546Sopenharmony_ci int jump_label_idx; 292bf215546Sopenharmony_ci afuc_instr *instr = (void *)&instrs[pc]; 293bf215546Sopenharmony_ci const char *fname, *lname; 294bf215546Sopenharmony_ci afuc_opc opc; 295bf215546Sopenharmony_ci bool rep; 296bf215546Sopenharmony_ci 297bf215546Sopenharmony_ci afuc_get_opc(instr, &opc, &rep); 298bf215546Sopenharmony_ci 299bf215546Sopenharmony_ci lname = label_name(pc, false); 300bf215546Sopenharmony_ci fname = fxn_name(pc); 301bf215546Sopenharmony_ci jump_label_idx = get_jump_table_entry(pc); 302bf215546Sopenharmony_ci 303bf215546Sopenharmony_ci if (jump_label_idx >= 0) { 304bf215546Sopenharmony_ci int j; 305bf215546Sopenharmony_ci printf("\n"); 306bf215546Sopenharmony_ci for (j = 0; j < jump_labels[jump_label_idx].num_jump_labels; j++) { 307bf215546Sopenharmony_ci uint32_t jump_label = jump_labels[jump_label_idx].jump_labels[j]; 308bf215546Sopenharmony_ci const char *name = getpm4(jump_label); 309bf215546Sopenharmony_ci if (name) { 310bf215546Sopenharmony_ci printlbl("%s", name); 311bf215546Sopenharmony_ci } else { 312bf215546Sopenharmony_ci printlbl("UNKN%d", jump_label); 313bf215546Sopenharmony_ci } 314bf215546Sopenharmony_ci printf(":\n"); 315bf215546Sopenharmony_ci } 316bf215546Sopenharmony_ci } 317bf215546Sopenharmony_ci 318bf215546Sopenharmony_ci if (fname) { 319bf215546Sopenharmony_ci printlbl("%s", fname); 320bf215546Sopenharmony_ci printf(":\n"); 321bf215546Sopenharmony_ci } 322bf215546Sopenharmony_ci 323bf215546Sopenharmony_ci if (lname) { 324bf215546Sopenharmony_ci printlbl(" %s", lname); 325bf215546Sopenharmony_ci printf(":"); 326bf215546Sopenharmony_ci } else { 327bf215546Sopenharmony_ci printf(" "); 328bf215546Sopenharmony_ci } 329bf215546Sopenharmony_ci 330bf215546Sopenharmony_ci if (verbose) { 331bf215546Sopenharmony_ci printf("\t%04x: %08x ", pc, instrs[pc]); 332bf215546Sopenharmony_ci } else { 333bf215546Sopenharmony_ci printf(" "); 334bf215546Sopenharmony_ci } 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci switch (opc) { 337bf215546Sopenharmony_ci case OPC_NOP: { 338bf215546Sopenharmony_ci /* a6xx changed the default immediate, and apparently 0 339bf215546Sopenharmony_ci * is illegal now. 340bf215546Sopenharmony_ci */ 341bf215546Sopenharmony_ci const uint32_t nop = gpuver >= 6 ? 0x1000000 : 0x0; 342bf215546Sopenharmony_ci if (instrs[pc] != nop) { 343bf215546Sopenharmony_ci printerr("[%08x]", instrs[pc]); 344bf215546Sopenharmony_ci printf(" ; "); 345bf215546Sopenharmony_ci } 346bf215546Sopenharmony_ci if (rep) 347bf215546Sopenharmony_ci printf("(rep)"); 348bf215546Sopenharmony_ci printf("nop"); 349bf215546Sopenharmony_ci print_gpu_reg(instrs[pc]); 350bf215546Sopenharmony_ci 351bf215546Sopenharmony_ci break; 352bf215546Sopenharmony_ci } 353bf215546Sopenharmony_ci case OPC_ADD: 354bf215546Sopenharmony_ci case OPC_ADDHI: 355bf215546Sopenharmony_ci case OPC_SUB: 356bf215546Sopenharmony_ci case OPC_SUBHI: 357bf215546Sopenharmony_ci case OPC_AND: 358bf215546Sopenharmony_ci case OPC_OR: 359bf215546Sopenharmony_ci case OPC_XOR: 360bf215546Sopenharmony_ci case OPC_NOT: 361bf215546Sopenharmony_ci case OPC_SHL: 362bf215546Sopenharmony_ci case OPC_USHR: 363bf215546Sopenharmony_ci case OPC_ISHR: 364bf215546Sopenharmony_ci case OPC_ROT: 365bf215546Sopenharmony_ci case OPC_MUL8: 366bf215546Sopenharmony_ci case OPC_MIN: 367bf215546Sopenharmony_ci case OPC_MAX: 368bf215546Sopenharmony_ci case OPC_CMP: { 369bf215546Sopenharmony_ci bool src1 = true; 370bf215546Sopenharmony_ci 371bf215546Sopenharmony_ci if (opc == OPC_NOT) 372bf215546Sopenharmony_ci src1 = false; 373bf215546Sopenharmony_ci 374bf215546Sopenharmony_ci if (rep) 375bf215546Sopenharmony_ci printf("(rep)"); 376bf215546Sopenharmony_ci 377bf215546Sopenharmony_ci print_alu_name(opc, instrs[pc]); 378bf215546Sopenharmony_ci print_dst(instr->alui.dst); 379bf215546Sopenharmony_ci printf(", "); 380bf215546Sopenharmony_ci if (src1) { 381bf215546Sopenharmony_ci print_src(instr->alui.src); 382bf215546Sopenharmony_ci printf(", "); 383bf215546Sopenharmony_ci } 384bf215546Sopenharmony_ci printf("0x%04x", instr->alui.uimm); 385bf215546Sopenharmony_ci print_gpu_reg(instr->alui.uimm); 386bf215546Sopenharmony_ci 387bf215546Sopenharmony_ci /* print out unexpected bits: */ 388bf215546Sopenharmony_ci if (verbose) { 389bf215546Sopenharmony_ci if (instr->alui.src && !src1) 390bf215546Sopenharmony_ci printerr(" (src=%02x)", instr->alui.src); 391bf215546Sopenharmony_ci } 392bf215546Sopenharmony_ci 393bf215546Sopenharmony_ci break; 394bf215546Sopenharmony_ci } 395bf215546Sopenharmony_ci case OPC_MOVI: { 396bf215546Sopenharmony_ci if (rep) 397bf215546Sopenharmony_ci printf("(rep)"); 398bf215546Sopenharmony_ci printf("mov "); 399bf215546Sopenharmony_ci print_dst(instr->movi.dst); 400bf215546Sopenharmony_ci printf(", 0x%04x", instr->movi.uimm); 401bf215546Sopenharmony_ci if (instr->movi.shift) 402bf215546Sopenharmony_ci printf(" << %u", instr->movi.shift); 403bf215546Sopenharmony_ci 404bf215546Sopenharmony_ci if ((instr->movi.dst == REG_ADDR) && (instr->movi.shift >= 16)) { 405bf215546Sopenharmony_ci uint32_t val = (uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift; 406bf215546Sopenharmony_ci val &= ~0x40000; /* b18 seems to be a flag */ 407bf215546Sopenharmony_ci 408bf215546Sopenharmony_ci if ((val & 0x00ffffff) == 0) { 409bf215546Sopenharmony_ci printf("\t; "); 410bf215546Sopenharmony_ci print_pipe_reg(val >> 24); 411bf215546Sopenharmony_ci break; 412bf215546Sopenharmony_ci } 413bf215546Sopenharmony_ci } 414bf215546Sopenharmony_ci /* using mov w/ << 16 is popular way to construct a pkt7 415bf215546Sopenharmony_ci * header to send (for ex, from PFP to ME), so check that 416bf215546Sopenharmony_ci * case first 417bf215546Sopenharmony_ci */ 418bf215546Sopenharmony_ci if ((instr->movi.shift == 16) && 419bf215546Sopenharmony_ci ((instr->movi.uimm & 0xff00) == 0x7000)) { 420bf215546Sopenharmony_ci unsigned opc, p; 421bf215546Sopenharmony_ci 422bf215546Sopenharmony_ci opc = instr->movi.uimm & 0x7f; 423bf215546Sopenharmony_ci p = pm4_odd_parity_bit(opc); 424bf215546Sopenharmony_ci 425bf215546Sopenharmony_ci /* So, you'd think that checking the parity bit would be 426bf215546Sopenharmony_ci * a good way to rule out false positives, but seems like 427bf215546Sopenharmony_ci * ME doesn't really care.. at least it would filter out 428bf215546Sopenharmony_ci * things that look like actual legit packets between 429bf215546Sopenharmony_ci * PFP and ME.. 430bf215546Sopenharmony_ci */ 431bf215546Sopenharmony_ci if (1 || p == ((instr->movi.uimm >> 7) & 0x1)) { 432bf215546Sopenharmony_ci const char *name = getpm4(opc); 433bf215546Sopenharmony_ci printf("\t; "); 434bf215546Sopenharmony_ci if (name) 435bf215546Sopenharmony_ci printlbl("%s", name); 436bf215546Sopenharmony_ci else 437bf215546Sopenharmony_ci printlbl("UNKN%u", opc); 438bf215546Sopenharmony_ci break; 439bf215546Sopenharmony_ci } 440bf215546Sopenharmony_ci } 441bf215546Sopenharmony_ci 442bf215546Sopenharmony_ci print_gpu_reg((uint32_t)instr->movi.uimm << (uint32_t)instr->movi.shift); 443bf215546Sopenharmony_ci 444bf215546Sopenharmony_ci break; 445bf215546Sopenharmony_ci } 446bf215546Sopenharmony_ci case OPC_ALU: { 447bf215546Sopenharmony_ci bool src1 = true; 448bf215546Sopenharmony_ci 449bf215546Sopenharmony_ci if (instr->alu.alu == OPC_NOT || instr->alu.alu == OPC_MSB) 450bf215546Sopenharmony_ci src1 = false; 451bf215546Sopenharmony_ci 452bf215546Sopenharmony_ci if (instr->alu.pad) 453bf215546Sopenharmony_ci printf("[%08x] ; ", instrs[pc]); 454bf215546Sopenharmony_ci 455bf215546Sopenharmony_ci if (rep) 456bf215546Sopenharmony_ci printf("(rep)"); 457bf215546Sopenharmony_ci if (instr->alu.xmov) 458bf215546Sopenharmony_ci printf("(xmov%d)", instr->alu.xmov); 459bf215546Sopenharmony_ci 460bf215546Sopenharmony_ci /* special case mnemonics: 461bf215546Sopenharmony_ci * reading $00 seems to always yield zero, and so: 462bf215546Sopenharmony_ci * or $dst, $00, $src -> mov $dst, $src 463bf215546Sopenharmony_ci * Maybe add one for negate too, ie. 464bf215546Sopenharmony_ci * sub $dst, $00, $src ??? 465bf215546Sopenharmony_ci */ 466bf215546Sopenharmony_ci if ((instr->alu.alu == OPC_OR) && !instr->alu.src1) { 467bf215546Sopenharmony_ci printf("mov "); 468bf215546Sopenharmony_ci src1 = false; 469bf215546Sopenharmony_ci } else { 470bf215546Sopenharmony_ci print_alu_name(instr->alu.alu, instrs[pc]); 471bf215546Sopenharmony_ci } 472bf215546Sopenharmony_ci 473bf215546Sopenharmony_ci print_dst(instr->alu.dst); 474bf215546Sopenharmony_ci if (src1) { 475bf215546Sopenharmony_ci printf(", "); 476bf215546Sopenharmony_ci print_src(instr->alu.src1); 477bf215546Sopenharmony_ci } 478bf215546Sopenharmony_ci printf(", "); 479bf215546Sopenharmony_ci print_src(instr->alu.src2); 480bf215546Sopenharmony_ci 481bf215546Sopenharmony_ci /* print out unexpected bits: */ 482bf215546Sopenharmony_ci if (verbose) { 483bf215546Sopenharmony_ci if (instr->alu.pad) 484bf215546Sopenharmony_ci printerr(" (pad=%01x)", instr->alu.pad); 485bf215546Sopenharmony_ci if (instr->alu.src1 && !src1) 486bf215546Sopenharmony_ci printerr(" (src1=%02x)", instr->alu.src1); 487bf215546Sopenharmony_ci } 488bf215546Sopenharmony_ci 489bf215546Sopenharmony_ci /* xmov is a modifier that makes the processor execute up to 3 490bf215546Sopenharmony_ci * extra mov's after the current instruction. Given an ALU 491bf215546Sopenharmony_ci * instruction: 492bf215546Sopenharmony_ci * 493bf215546Sopenharmony_ci * (xmovN) alu $dst, $src1, $src2 494bf215546Sopenharmony_ci * 495bf215546Sopenharmony_ci * In all of the uses in the firmware blob, $dst and $src2 are one 496bf215546Sopenharmony_ci * of the "special" registers $data, $addr, $addr2. I've observed 497bf215546Sopenharmony_ci * that if $dst isn't "special" then it's replaced with $00 498bf215546Sopenharmony_ci * instead of $data, but I haven't checked what happens if $src2 499bf215546Sopenharmony_ci * isn't "special". Anyway, in the usual case, the HW produces a 500bf215546Sopenharmony_ci * count M = min(N, $rem) and then does the following: 501bf215546Sopenharmony_ci * 502bf215546Sopenharmony_ci * M = 1: 503bf215546Sopenharmony_ci * mov $data, $src2 504bf215546Sopenharmony_ci * 505bf215546Sopenharmony_ci * M = 2: 506bf215546Sopenharmony_ci * mov $data, $src2 507bf215546Sopenharmony_ci * mov $data, $src2 508bf215546Sopenharmony_ci * 509bf215546Sopenharmony_ci * M = 3: 510bf215546Sopenharmony_ci * mov $data, $src2 511bf215546Sopenharmony_ci * mov $dst, $src2 (special case for CP_CONTEXT_REG_BUNCH) 512bf215546Sopenharmony_ci * mov $data, $src2 513bf215546Sopenharmony_ci * 514bf215546Sopenharmony_ci * It seems to be frequently used in combination with (rep) to 515bf215546Sopenharmony_ci * provide a kind of hardware-based loop unrolling, and there's 516bf215546Sopenharmony_ci * even a special case in the ISA to be able to do this with 517bf215546Sopenharmony_ci * CP_CONTEXT_REG_BUNCH. However (rep) isn't required. 518bf215546Sopenharmony_ci * 519bf215546Sopenharmony_ci * This dumps the expected extra instructions, assuming that $rem 520bf215546Sopenharmony_ci * isn't too small. 521bf215546Sopenharmony_ci */ 522bf215546Sopenharmony_ci if (verbose && instr->alu.xmov) { 523bf215546Sopenharmony_ci for (int i = 0; i < instr->alu.xmov; i++) { 524bf215546Sopenharmony_ci printf("\n ; mov "); 525bf215546Sopenharmony_ci if (instr->alu.dst < 0x1d) 526bf215546Sopenharmony_ci printf("$00"); 527bf215546Sopenharmony_ci else if (instr->alu.xmov == 3 && i == 1) 528bf215546Sopenharmony_ci print_dst(instr->alu.dst); 529bf215546Sopenharmony_ci else 530bf215546Sopenharmony_ci printf("$data"); 531bf215546Sopenharmony_ci printf(", "); 532bf215546Sopenharmony_ci print_src(instr->alu.src2); 533bf215546Sopenharmony_ci } 534bf215546Sopenharmony_ci } 535bf215546Sopenharmony_ci 536bf215546Sopenharmony_ci break; 537bf215546Sopenharmony_ci } 538bf215546Sopenharmony_ci case OPC_CWRITE6: 539bf215546Sopenharmony_ci case OPC_CREAD6: 540bf215546Sopenharmony_ci case OPC_STORE6: 541bf215546Sopenharmony_ci case OPC_LOAD6: { 542bf215546Sopenharmony_ci if (rep) 543bf215546Sopenharmony_ci printf("(rep)"); 544bf215546Sopenharmony_ci 545bf215546Sopenharmony_ci bool is_control_reg = true; 546bf215546Sopenharmony_ci bool is_store = true; 547bf215546Sopenharmony_ci if (gpuver >= 6) { 548bf215546Sopenharmony_ci switch (opc) { 549bf215546Sopenharmony_ci case OPC_CWRITE6: 550bf215546Sopenharmony_ci printf("cwrite "); 551bf215546Sopenharmony_ci break; 552bf215546Sopenharmony_ci case OPC_CREAD6: 553bf215546Sopenharmony_ci is_store = false; 554bf215546Sopenharmony_ci printf("cread "); 555bf215546Sopenharmony_ci break; 556bf215546Sopenharmony_ci case OPC_STORE6: 557bf215546Sopenharmony_ci is_control_reg = false; 558bf215546Sopenharmony_ci printf("store "); 559bf215546Sopenharmony_ci break; 560bf215546Sopenharmony_ci case OPC_LOAD6: 561bf215546Sopenharmony_ci is_control_reg = false; 562bf215546Sopenharmony_ci is_store = false; 563bf215546Sopenharmony_ci printf("load "); 564bf215546Sopenharmony_ci break; 565bf215546Sopenharmony_ci default: 566bf215546Sopenharmony_ci assert(!"unreachable"); 567bf215546Sopenharmony_ci } 568bf215546Sopenharmony_ci } else { 569bf215546Sopenharmony_ci switch (opc) { 570bf215546Sopenharmony_ci case OPC_CWRITE5: 571bf215546Sopenharmony_ci printf("cwrite "); 572bf215546Sopenharmony_ci break; 573bf215546Sopenharmony_ci case OPC_CREAD5: 574bf215546Sopenharmony_ci is_store = false; 575bf215546Sopenharmony_ci printf("cread "); 576bf215546Sopenharmony_ci break; 577bf215546Sopenharmony_ci default: 578bf215546Sopenharmony_ci fprintf(stderr, "A6xx control opcode on A5xx?\n"); 579bf215546Sopenharmony_ci exit(1); 580bf215546Sopenharmony_ci } 581bf215546Sopenharmony_ci } 582bf215546Sopenharmony_ci 583bf215546Sopenharmony_ci if (is_store) 584bf215546Sopenharmony_ci print_src(instr->control.src1); 585bf215546Sopenharmony_ci else 586bf215546Sopenharmony_ci print_dst(instr->control.src1); 587bf215546Sopenharmony_ci printf(", ["); 588bf215546Sopenharmony_ci print_src(instr->control.src2); 589bf215546Sopenharmony_ci printf(" + "); 590bf215546Sopenharmony_ci if (is_control_reg && instr->control.flags != 0x4) 591bf215546Sopenharmony_ci print_control_reg(instr->control.uimm); 592bf215546Sopenharmony_ci else 593bf215546Sopenharmony_ci printf("0x%03x", instr->control.uimm); 594bf215546Sopenharmony_ci printf("], 0x%x", instr->control.flags); 595bf215546Sopenharmony_ci break; 596bf215546Sopenharmony_ci } 597bf215546Sopenharmony_ci case OPC_BRNEI: 598bf215546Sopenharmony_ci case OPC_BREQI: 599bf215546Sopenharmony_ci case OPC_BRNEB: 600bf215546Sopenharmony_ci case OPC_BREQB: { 601bf215546Sopenharmony_ci unsigned off = pc + instr->br.ioff; 602bf215546Sopenharmony_ci 603bf215546Sopenharmony_ci assert(!rep); 604bf215546Sopenharmony_ci 605bf215546Sopenharmony_ci /* Since $00 reads back zero, it can be used as src for 606bf215546Sopenharmony_ci * unconditional branches. (This only really makes sense 607bf215546Sopenharmony_ci * for the BREQB.. or possible BRNEI if imm==0.) 608bf215546Sopenharmony_ci * 609bf215546Sopenharmony_ci * If bit=0 then branch is taken if *all* bits are zero. 610bf215546Sopenharmony_ci * Otherwise it is taken if bit (bit-1) is clear. 611bf215546Sopenharmony_ci * 612bf215546Sopenharmony_ci * Note the instruction after a jump/branch is executed 613bf215546Sopenharmony_ci * regardless of whether branch is taken, so use nop or 614bf215546Sopenharmony_ci * take that into account in code. 615bf215546Sopenharmony_ci */ 616bf215546Sopenharmony_ci if (instr->br.src || (opc != OPC_BRNEB)) { 617bf215546Sopenharmony_ci bool immed = false; 618bf215546Sopenharmony_ci 619bf215546Sopenharmony_ci if (opc == OPC_BRNEI) { 620bf215546Sopenharmony_ci printf("brne "); 621bf215546Sopenharmony_ci immed = true; 622bf215546Sopenharmony_ci } else if (opc == OPC_BREQI) { 623bf215546Sopenharmony_ci printf("breq "); 624bf215546Sopenharmony_ci immed = true; 625bf215546Sopenharmony_ci } else if (opc == OPC_BRNEB) { 626bf215546Sopenharmony_ci printf("brne "); 627bf215546Sopenharmony_ci } else if (opc == OPC_BREQB) { 628bf215546Sopenharmony_ci printf("breq "); 629bf215546Sopenharmony_ci } 630bf215546Sopenharmony_ci print_src(instr->br.src); 631bf215546Sopenharmony_ci if (immed) { 632bf215546Sopenharmony_ci printf(", 0x%x,", instr->br.bit_or_imm); 633bf215546Sopenharmony_ci } else { 634bf215546Sopenharmony_ci printf(", b%u,", instr->br.bit_or_imm); 635bf215546Sopenharmony_ci } 636bf215546Sopenharmony_ci } else { 637bf215546Sopenharmony_ci printf("jump"); 638bf215546Sopenharmony_ci if (verbose && instr->br.bit_or_imm) { 639bf215546Sopenharmony_ci printerr(" (src=%03x, bit=%03x) ", instr->br.src, 640bf215546Sopenharmony_ci instr->br.bit_or_imm); 641bf215546Sopenharmony_ci } 642bf215546Sopenharmony_ci } 643bf215546Sopenharmony_ci 644bf215546Sopenharmony_ci printf(" #"); 645bf215546Sopenharmony_ci printlbl("%s", label_name(off, true)); 646bf215546Sopenharmony_ci if (verbose) 647bf215546Sopenharmony_ci printf(" (#%d, %04x)", instr->br.ioff, off); 648bf215546Sopenharmony_ci break; 649bf215546Sopenharmony_ci } 650bf215546Sopenharmony_ci case OPC_CALL: 651bf215546Sopenharmony_ci assert(!rep); 652bf215546Sopenharmony_ci printf("call #"); 653bf215546Sopenharmony_ci printlbl("%s", fxn_name(instr->call.uoff)); 654bf215546Sopenharmony_ci if (verbose) { 655bf215546Sopenharmony_ci printf(" (%04x)", instr->call.uoff); 656bf215546Sopenharmony_ci if (instr->br.bit_or_imm || instr->br.src) { 657bf215546Sopenharmony_ci printerr(" (src=%03x, bit=%03x) ", instr->br.src, 658bf215546Sopenharmony_ci instr->br.bit_or_imm); 659bf215546Sopenharmony_ci } 660bf215546Sopenharmony_ci } 661bf215546Sopenharmony_ci break; 662bf215546Sopenharmony_ci case OPC_RET: 663bf215546Sopenharmony_ci assert(!rep); 664bf215546Sopenharmony_ci if (instr->ret.pad) 665bf215546Sopenharmony_ci printf("[%08x] ; ", instrs[pc]); 666bf215546Sopenharmony_ci if (instr->ret.interrupt) 667bf215546Sopenharmony_ci printf("iret"); 668bf215546Sopenharmony_ci else 669bf215546Sopenharmony_ci printf("ret"); 670bf215546Sopenharmony_ci break; 671bf215546Sopenharmony_ci case OPC_WIN: 672bf215546Sopenharmony_ci assert(!rep); 673bf215546Sopenharmony_ci if (instr->waitin.pad) 674bf215546Sopenharmony_ci printf("[%08x] ; ", instrs[pc]); 675bf215546Sopenharmony_ci printf("waitin"); 676bf215546Sopenharmony_ci if (verbose && instr->waitin.pad) 677bf215546Sopenharmony_ci printerr(" (pad=%x)", instr->waitin.pad); 678bf215546Sopenharmony_ci break; 679bf215546Sopenharmony_ci case OPC_PREEMPTLEAVE6: 680bf215546Sopenharmony_ci if (gpuver < 6) { 681bf215546Sopenharmony_ci printf("[%08x] ; op38", instrs[pc]); 682bf215546Sopenharmony_ci } else { 683bf215546Sopenharmony_ci printf("preemptleave #"); 684bf215546Sopenharmony_ci printlbl("%s", label_name(instr->call.uoff, true)); 685bf215546Sopenharmony_ci } 686bf215546Sopenharmony_ci break; 687bf215546Sopenharmony_ci case OPC_SETSECURE: 688bf215546Sopenharmony_ci /* Note: This seems to implicitly read the secure/not-secure state 689bf215546Sopenharmony_ci * to set from the low bit of $02, and implicitly jumps to pc + 3 690bf215546Sopenharmony_ci * (i.e. skipping the next two instructions) if it succeeds. We 691bf215546Sopenharmony_ci * print these implicit parameters to make reading the disassembly 692bf215546Sopenharmony_ci * easier. 693bf215546Sopenharmony_ci */ 694bf215546Sopenharmony_ci if (instr->pad) 695bf215546Sopenharmony_ci printf("[%08x] ; ", instrs[pc]); 696bf215546Sopenharmony_ci printf("setsecure $02, #"); 697bf215546Sopenharmony_ci printlbl("%s", label_name(pc + 3, true)); 698bf215546Sopenharmony_ci break; 699bf215546Sopenharmony_ci default: 700bf215546Sopenharmony_ci printerr("[%08x]", instrs[pc]); 701bf215546Sopenharmony_ci printf(" ; op%02x ", opc); 702bf215546Sopenharmony_ci print_dst(instr->alui.dst); 703bf215546Sopenharmony_ci printf(", "); 704bf215546Sopenharmony_ci print_src(instr->alui.src); 705bf215546Sopenharmony_ci print_gpu_reg(instrs[pc] & 0xffff); 706bf215546Sopenharmony_ci break; 707bf215546Sopenharmony_ci } 708bf215546Sopenharmony_ci printf("\n"); 709bf215546Sopenharmony_ci} 710bf215546Sopenharmony_ci 711bf215546Sopenharmony_cistatic void 712bf215546Sopenharmony_cisetup_packet_table(uint32_t *jmptbl, uint32_t sizedwords) 713bf215546Sopenharmony_ci{ 714bf215546Sopenharmony_ci num_jump_labels = 0; 715bf215546Sopenharmony_ci 716bf215546Sopenharmony_ci for (unsigned i = 0; i < sizedwords; i++) { 717bf215546Sopenharmony_ci unsigned offset = jmptbl[i]; 718bf215546Sopenharmony_ci unsigned n = i; // + CP_NOP; 719bf215546Sopenharmony_ci add_jump_table_entry(n, offset); 720bf215546Sopenharmony_ci } 721bf215546Sopenharmony_ci} 722bf215546Sopenharmony_ci 723bf215546Sopenharmony_cistatic void 724bf215546Sopenharmony_cisetup_labels(uint32_t *instrs, uint32_t sizedwords) 725bf215546Sopenharmony_ci{ 726bf215546Sopenharmony_ci afuc_opc opc; 727bf215546Sopenharmony_ci bool rep; 728bf215546Sopenharmony_ci 729bf215546Sopenharmony_ci num_label_offsets = 0; 730bf215546Sopenharmony_ci 731bf215546Sopenharmony_ci for (unsigned i = 0; i < sizedwords; i++) { 732bf215546Sopenharmony_ci afuc_instr *instr = (void *)&instrs[i]; 733bf215546Sopenharmony_ci 734bf215546Sopenharmony_ci afuc_get_opc(instr, &opc, &rep); 735bf215546Sopenharmony_ci 736bf215546Sopenharmony_ci switch (opc) { 737bf215546Sopenharmony_ci case OPC_BRNEI: 738bf215546Sopenharmony_ci case OPC_BREQI: 739bf215546Sopenharmony_ci case OPC_BRNEB: 740bf215546Sopenharmony_ci case OPC_BREQB: 741bf215546Sopenharmony_ci label_idx(i + instr->br.ioff, true); 742bf215546Sopenharmony_ci break; 743bf215546Sopenharmony_ci case OPC_PREEMPTLEAVE6: 744bf215546Sopenharmony_ci if (gpuver >= 6) 745bf215546Sopenharmony_ci label_idx(instr->call.uoff, true); 746bf215546Sopenharmony_ci break; 747bf215546Sopenharmony_ci case OPC_CALL: 748bf215546Sopenharmony_ci fxn_idx(instr->call.uoff, true); 749bf215546Sopenharmony_ci break; 750bf215546Sopenharmony_ci case OPC_SETSECURE: 751bf215546Sopenharmony_ci /* this implicitly jumps to pc + 3 if successful */ 752bf215546Sopenharmony_ci label_idx(i + 3, true); 753bf215546Sopenharmony_ci break; 754bf215546Sopenharmony_ci default: 755bf215546Sopenharmony_ci break; 756bf215546Sopenharmony_ci } 757bf215546Sopenharmony_ci } 758bf215546Sopenharmony_ci} 759bf215546Sopenharmony_ci 760bf215546Sopenharmony_cistatic void 761bf215546Sopenharmony_cidisasm(struct emu *emu) 762bf215546Sopenharmony_ci{ 763bf215546Sopenharmony_ci uint32_t sizedwords = emu->sizedwords; 764bf215546Sopenharmony_ci uint32_t lpac_offset = 0; 765bf215546Sopenharmony_ci 766bf215546Sopenharmony_ci EMU_GPU_REG(CP_SQE_INSTR_BASE); 767bf215546Sopenharmony_ci EMU_GPU_REG(CP_LPAC_SQE_INSTR_BASE); 768bf215546Sopenharmony_ci 769bf215546Sopenharmony_ci emu_init(emu); 770bf215546Sopenharmony_ci 771bf215546Sopenharmony_ci#ifdef BOOTSTRAP_DEBUG 772bf215546Sopenharmony_ci while (true) { 773bf215546Sopenharmony_ci disasm_instr(emu->instrs, emu->gpr_regs.pc); 774bf215546Sopenharmony_ci emu_step(emu); 775bf215546Sopenharmony_ci } 776bf215546Sopenharmony_ci#endif 777bf215546Sopenharmony_ci 778bf215546Sopenharmony_ci emu_run_bootstrap(emu); 779bf215546Sopenharmony_ci 780bf215546Sopenharmony_ci /* Figure out if we have LPAC SQE appended: */ 781bf215546Sopenharmony_ci if (emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE)) { 782bf215546Sopenharmony_ci lpac_offset = emu_get_reg64(emu, &CP_LPAC_SQE_INSTR_BASE) - 783bf215546Sopenharmony_ci emu_get_reg64(emu, &CP_SQE_INSTR_BASE); 784bf215546Sopenharmony_ci lpac_offset /= 4; 785bf215546Sopenharmony_ci sizedwords = lpac_offset; 786bf215546Sopenharmony_ci } 787bf215546Sopenharmony_ci 788bf215546Sopenharmony_ci setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); 789bf215546Sopenharmony_ci setup_labels(emu->instrs, emu->sizedwords); 790bf215546Sopenharmony_ci 791bf215546Sopenharmony_ci /* TODO add option to emulate LPAC SQE instead: */ 792bf215546Sopenharmony_ci if (emulator) { 793bf215546Sopenharmony_ci /* Start from clean slate: */ 794bf215546Sopenharmony_ci emu_fini(emu); 795bf215546Sopenharmony_ci emu_init(emu); 796bf215546Sopenharmony_ci 797bf215546Sopenharmony_ci while (true) { 798bf215546Sopenharmony_ci disasm_instr(emu->instrs, emu->gpr_regs.pc); 799bf215546Sopenharmony_ci emu_step(emu); 800bf215546Sopenharmony_ci } 801bf215546Sopenharmony_ci } 802bf215546Sopenharmony_ci 803bf215546Sopenharmony_ci /* print instructions: */ 804bf215546Sopenharmony_ci for (int i = 0; i < sizedwords; i++) { 805bf215546Sopenharmony_ci disasm_instr(emu->instrs, i); 806bf215546Sopenharmony_ci } 807bf215546Sopenharmony_ci 808bf215546Sopenharmony_ci if (!lpac_offset) 809bf215546Sopenharmony_ci return; 810bf215546Sopenharmony_ci 811bf215546Sopenharmony_ci printf(";\n"); 812bf215546Sopenharmony_ci printf("; LPAC microcode:\n"); 813bf215546Sopenharmony_ci printf(";\n"); 814bf215546Sopenharmony_ci 815bf215546Sopenharmony_ci emu_fini(emu); 816bf215546Sopenharmony_ci 817bf215546Sopenharmony_ci emu->lpac = true; 818bf215546Sopenharmony_ci emu->instrs += lpac_offset; 819bf215546Sopenharmony_ci emu->sizedwords -= lpac_offset; 820bf215546Sopenharmony_ci 821bf215546Sopenharmony_ci emu_init(emu); 822bf215546Sopenharmony_ci emu_run_bootstrap(emu); 823bf215546Sopenharmony_ci 824bf215546Sopenharmony_ci setup_packet_table(emu->jmptbl, ARRAY_SIZE(emu->jmptbl)); 825bf215546Sopenharmony_ci setup_labels(emu->instrs, emu->sizedwords); 826bf215546Sopenharmony_ci 827bf215546Sopenharmony_ci /* print instructions: */ 828bf215546Sopenharmony_ci for (int i = 0; i < emu->sizedwords; i++) { 829bf215546Sopenharmony_ci disasm_instr(emu->instrs, i); 830bf215546Sopenharmony_ci } 831bf215546Sopenharmony_ci} 832bf215546Sopenharmony_ci 833bf215546Sopenharmony_ci 834bf215546Sopenharmony_cistatic void 835bf215546Sopenharmony_cidisasm_legacy(uint32_t *buf, int sizedwords) 836bf215546Sopenharmony_ci{ 837bf215546Sopenharmony_ci uint32_t *instrs = buf; 838bf215546Sopenharmony_ci const int jmptbl_start = instrs[1] & 0xffff; 839bf215546Sopenharmony_ci uint32_t *jmptbl = &buf[jmptbl_start]; 840bf215546Sopenharmony_ci int i; 841bf215546Sopenharmony_ci 842bf215546Sopenharmony_ci /* parse jumptable: */ 843bf215546Sopenharmony_ci setup_packet_table(jmptbl, 0x80); 844bf215546Sopenharmony_ci 845bf215546Sopenharmony_ci /* do a pre-pass to find instructions that are potential branch targets, 846bf215546Sopenharmony_ci * and add labels for them: 847bf215546Sopenharmony_ci */ 848bf215546Sopenharmony_ci setup_labels(instrs, jmptbl_start); 849bf215546Sopenharmony_ci 850bf215546Sopenharmony_ci /* print instructions: */ 851bf215546Sopenharmony_ci for (i = 0; i < jmptbl_start; i++) { 852bf215546Sopenharmony_ci disasm_instr(instrs, i); 853bf215546Sopenharmony_ci } 854bf215546Sopenharmony_ci 855bf215546Sopenharmony_ci /* print jumptable: */ 856bf215546Sopenharmony_ci if (verbose) { 857bf215546Sopenharmony_ci printf(";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;\n"); 858bf215546Sopenharmony_ci printf("; JUMP TABLE\n"); 859bf215546Sopenharmony_ci for (i = 0; i < 0x7f; i++) { 860bf215546Sopenharmony_ci int n = i; // + CP_NOP; 861bf215546Sopenharmony_ci uint32_t offset = jmptbl[i]; 862bf215546Sopenharmony_ci const char *name = getpm4(n); 863bf215546Sopenharmony_ci printf("%3d %02x: ", n, n); 864bf215546Sopenharmony_ci printf("%04x", offset); 865bf215546Sopenharmony_ci if (name) { 866bf215546Sopenharmony_ci printf(" ; %s", name); 867bf215546Sopenharmony_ci } else { 868bf215546Sopenharmony_ci printf(" ; UNKN%d", n); 869bf215546Sopenharmony_ci } 870bf215546Sopenharmony_ci printf("\n"); 871bf215546Sopenharmony_ci } 872bf215546Sopenharmony_ci } 873bf215546Sopenharmony_ci} 874bf215546Sopenharmony_ci 875bf215546Sopenharmony_cistatic void 876bf215546Sopenharmony_ciusage(void) 877bf215546Sopenharmony_ci{ 878bf215546Sopenharmony_ci fprintf(stderr, "Usage:\n" 879bf215546Sopenharmony_ci "\tdisasm [-g GPUVER] [-v] [-c] filename.asm\n" 880bf215546Sopenharmony_ci "\t\t-g - specify GPU version (5, etc)\n" 881bf215546Sopenharmony_ci "\t\t-c - use colors\n" 882bf215546Sopenharmony_ci "\t\t-v - verbose output\n" 883bf215546Sopenharmony_ci "\t\t-e - emulator mode\n"); 884bf215546Sopenharmony_ci exit(2); 885bf215546Sopenharmony_ci} 886bf215546Sopenharmony_ci 887bf215546Sopenharmony_ciint 888bf215546Sopenharmony_cimain(int argc, char **argv) 889bf215546Sopenharmony_ci{ 890bf215546Sopenharmony_ci uint32_t *buf; 891bf215546Sopenharmony_ci char *file; 892bf215546Sopenharmony_ci bool colors = false; 893bf215546Sopenharmony_ci uint32_t gpu_id = 0; 894bf215546Sopenharmony_ci size_t sz; 895bf215546Sopenharmony_ci int c, ret; 896bf215546Sopenharmony_ci bool unit_test = false; 897bf215546Sopenharmony_ci 898bf215546Sopenharmony_ci /* Argument parsing: */ 899bf215546Sopenharmony_ci while ((c = getopt(argc, argv, "g:vceu")) != -1) { 900bf215546Sopenharmony_ci switch (c) { 901bf215546Sopenharmony_ci case 'g': 902bf215546Sopenharmony_ci gpu_id = atoi(optarg); 903bf215546Sopenharmony_ci break; 904bf215546Sopenharmony_ci case 'v': 905bf215546Sopenharmony_ci verbose = true; 906bf215546Sopenharmony_ci break; 907bf215546Sopenharmony_ci case 'c': 908bf215546Sopenharmony_ci colors = true; 909bf215546Sopenharmony_ci break; 910bf215546Sopenharmony_ci case 'e': 911bf215546Sopenharmony_ci emulator = true; 912bf215546Sopenharmony_ci verbose = true; 913bf215546Sopenharmony_ci break; 914bf215546Sopenharmony_ci case 'u': 915bf215546Sopenharmony_ci unit_test = true; 916bf215546Sopenharmony_ci break; 917bf215546Sopenharmony_ci default: 918bf215546Sopenharmony_ci usage(); 919bf215546Sopenharmony_ci } 920bf215546Sopenharmony_ci } 921bf215546Sopenharmony_ci 922bf215546Sopenharmony_ci if (optind >= argc) { 923bf215546Sopenharmony_ci fprintf(stderr, "no file specified!\n"); 924bf215546Sopenharmony_ci usage(); 925bf215546Sopenharmony_ci } 926bf215546Sopenharmony_ci 927bf215546Sopenharmony_ci file = argv[optind]; 928bf215546Sopenharmony_ci 929bf215546Sopenharmony_ci /* if gpu version not specified, infer from filename: */ 930bf215546Sopenharmony_ci if (!gpu_id) { 931bf215546Sopenharmony_ci char *str = strstr(file, "a5"); 932bf215546Sopenharmony_ci if (!str) 933bf215546Sopenharmony_ci str = strstr(file, "a6"); 934bf215546Sopenharmony_ci if (str) 935bf215546Sopenharmony_ci gpu_id = atoi(str + 1); 936bf215546Sopenharmony_ci } 937bf215546Sopenharmony_ci 938bf215546Sopenharmony_ci if (gpu_id < 500) { 939bf215546Sopenharmony_ci printf("invalid gpu_id: %d\n", gpu_id); 940bf215546Sopenharmony_ci return -1; 941bf215546Sopenharmony_ci } 942bf215546Sopenharmony_ci 943bf215546Sopenharmony_ci gpuver = gpu_id / 100; 944bf215546Sopenharmony_ci 945bf215546Sopenharmony_ci /* a6xx is *mostly* a superset of a5xx, but some opcodes shuffle 946bf215546Sopenharmony_ci * around, and behavior of special regs is a bit different. Right 947bf215546Sopenharmony_ci * now we only bother to support the a6xx variant. 948bf215546Sopenharmony_ci */ 949bf215546Sopenharmony_ci if (emulator && (gpuver != 6)) { 950bf215546Sopenharmony_ci fprintf(stderr, "Emulator only supported on a6xx!\n"); 951bf215546Sopenharmony_ci return 1; 952bf215546Sopenharmony_ci } 953bf215546Sopenharmony_ci 954bf215546Sopenharmony_ci ret = afuc_util_init(gpuver, colors); 955bf215546Sopenharmony_ci if (ret < 0) { 956bf215546Sopenharmony_ci usage(); 957bf215546Sopenharmony_ci } 958bf215546Sopenharmony_ci 959bf215546Sopenharmony_ci printf("; a%dxx microcode\n", gpuver); 960bf215546Sopenharmony_ci 961bf215546Sopenharmony_ci buf = (uint32_t *)os_read_file(file, &sz); 962bf215546Sopenharmony_ci 963bf215546Sopenharmony_ci if (!unit_test) 964bf215546Sopenharmony_ci printf("; Disassembling microcode: %s\n", file); 965bf215546Sopenharmony_ci printf("; Version: %08x\n\n", buf[1]); 966bf215546Sopenharmony_ci 967bf215546Sopenharmony_ci if (gpuver < 6) { 968bf215546Sopenharmony_ci disasm_legacy(&buf[1], sz / 4 - 1); 969bf215546Sopenharmony_ci } else { 970bf215546Sopenharmony_ci struct emu emu = { 971bf215546Sopenharmony_ci .instrs = &buf[1], 972bf215546Sopenharmony_ci .sizedwords = sz / 4 - 1, 973bf215546Sopenharmony_ci .gpu_id = gpu_id, 974bf215546Sopenharmony_ci }; 975bf215546Sopenharmony_ci 976bf215546Sopenharmony_ci disasm(&emu); 977bf215546Sopenharmony_ci } 978bf215546Sopenharmony_ci 979bf215546Sopenharmony_ci return 0; 980bf215546Sopenharmony_ci} 981