1bf215546Sopenharmony_ci/* 2bf215546Sopenharmony_ci * Copyright © 2016 Broadcom 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 20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21bf215546Sopenharmony_ci * IN THE SOFTWARE. 22bf215546Sopenharmony_ci */ 23bf215546Sopenharmony_ci 24bf215546Sopenharmony_ci#include <string.h> 25bf215546Sopenharmony_ci#include <stdio.h> 26bf215546Sopenharmony_ci#include "util/ralloc.h" 27bf215546Sopenharmony_ci 28bf215546Sopenharmony_ci#include "broadcom/common/v3d_device_info.h" 29bf215546Sopenharmony_ci#include "qpu_instr.h" 30bf215546Sopenharmony_ci#include "qpu_disasm.h" 31bf215546Sopenharmony_ci 32bf215546Sopenharmony_cistruct disasm_state { 33bf215546Sopenharmony_ci const struct v3d_device_info *devinfo; 34bf215546Sopenharmony_ci char *string; 35bf215546Sopenharmony_ci size_t offset; 36bf215546Sopenharmony_ci}; 37bf215546Sopenharmony_ci 38bf215546Sopenharmony_cistatic void 39bf215546Sopenharmony_ciappend(struct disasm_state *disasm, const char *fmt, ...) 40bf215546Sopenharmony_ci{ 41bf215546Sopenharmony_ci va_list args; 42bf215546Sopenharmony_ci va_start(args, fmt); 43bf215546Sopenharmony_ci ralloc_vasprintf_rewrite_tail(&disasm->string, 44bf215546Sopenharmony_ci &disasm->offset, 45bf215546Sopenharmony_ci fmt, args); 46bf215546Sopenharmony_ci va_end(args); 47bf215546Sopenharmony_ci} 48bf215546Sopenharmony_ci 49bf215546Sopenharmony_cistatic void 50bf215546Sopenharmony_cipad_to(struct disasm_state *disasm, int n) 51bf215546Sopenharmony_ci{ 52bf215546Sopenharmony_ci /* FIXME: Do a single append somehow. */ 53bf215546Sopenharmony_ci while (disasm->offset < n) 54bf215546Sopenharmony_ci append(disasm, " "); 55bf215546Sopenharmony_ci} 56bf215546Sopenharmony_ci 57bf215546Sopenharmony_ci 58bf215546Sopenharmony_cistatic void 59bf215546Sopenharmony_civ3d_qpu_disasm_raddr(struct disasm_state *disasm, 60bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr, uint8_t mux) 61bf215546Sopenharmony_ci{ 62bf215546Sopenharmony_ci if (mux == V3D_QPU_MUX_A) { 63bf215546Sopenharmony_ci append(disasm, "rf%d", instr->raddr_a); 64bf215546Sopenharmony_ci } else if (mux == V3D_QPU_MUX_B) { 65bf215546Sopenharmony_ci if (instr->sig.small_imm) { 66bf215546Sopenharmony_ci uint32_t val; 67bf215546Sopenharmony_ci ASSERTED bool ok = 68bf215546Sopenharmony_ci v3d_qpu_small_imm_unpack(disasm->devinfo, 69bf215546Sopenharmony_ci instr->raddr_b, 70bf215546Sopenharmony_ci &val); 71bf215546Sopenharmony_ci 72bf215546Sopenharmony_ci if ((int)val >= -16 && (int)val <= 15) 73bf215546Sopenharmony_ci append(disasm, "%d", val); 74bf215546Sopenharmony_ci else 75bf215546Sopenharmony_ci append(disasm, "0x%08x", val); 76bf215546Sopenharmony_ci assert(ok); 77bf215546Sopenharmony_ci } else { 78bf215546Sopenharmony_ci append(disasm, "rf%d", instr->raddr_b); 79bf215546Sopenharmony_ci } 80bf215546Sopenharmony_ci } else { 81bf215546Sopenharmony_ci append(disasm, "r%d", mux); 82bf215546Sopenharmony_ci } 83bf215546Sopenharmony_ci} 84bf215546Sopenharmony_ci 85bf215546Sopenharmony_cistatic void 86bf215546Sopenharmony_civ3d_qpu_disasm_waddr(struct disasm_state *disasm, uint32_t waddr, bool magic) 87bf215546Sopenharmony_ci{ 88bf215546Sopenharmony_ci if (!magic) { 89bf215546Sopenharmony_ci append(disasm, "rf%d", waddr); 90bf215546Sopenharmony_ci return; 91bf215546Sopenharmony_ci } 92bf215546Sopenharmony_ci 93bf215546Sopenharmony_ci const char *name = v3d_qpu_magic_waddr_name(disasm->devinfo, waddr); 94bf215546Sopenharmony_ci if (name) 95bf215546Sopenharmony_ci append(disasm, "%s", name); 96bf215546Sopenharmony_ci else 97bf215546Sopenharmony_ci append(disasm, "waddr UNKNOWN %d", waddr); 98bf215546Sopenharmony_ci} 99bf215546Sopenharmony_ci 100bf215546Sopenharmony_cistatic void 101bf215546Sopenharmony_civ3d_qpu_disasm_add(struct disasm_state *disasm, 102bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 103bf215546Sopenharmony_ci{ 104bf215546Sopenharmony_ci bool has_dst = v3d_qpu_add_op_has_dst(instr->alu.add.op); 105bf215546Sopenharmony_ci int num_src = v3d_qpu_add_op_num_src(instr->alu.add.op); 106bf215546Sopenharmony_ci 107bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_add_op_name(instr->alu.add.op)); 108bf215546Sopenharmony_ci if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig)) 109bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_cond_name(instr->flags.ac)); 110bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_pf_name(instr->flags.apf)); 111bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_uf_name(instr->flags.auf)); 112bf215546Sopenharmony_ci 113bf215546Sopenharmony_ci append(disasm, " "); 114bf215546Sopenharmony_ci 115bf215546Sopenharmony_ci if (has_dst) { 116bf215546Sopenharmony_ci v3d_qpu_disasm_waddr(disasm, instr->alu.add.waddr, 117bf215546Sopenharmony_ci instr->alu.add.magic_write); 118bf215546Sopenharmony_ci append(disasm, v3d_qpu_pack_name(instr->alu.add.output_pack)); 119bf215546Sopenharmony_ci } 120bf215546Sopenharmony_ci 121bf215546Sopenharmony_ci if (num_src >= 1) { 122bf215546Sopenharmony_ci if (has_dst) 123bf215546Sopenharmony_ci append(disasm, ", "); 124bf215546Sopenharmony_ci v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.a); 125bf215546Sopenharmony_ci append(disasm, "%s", 126bf215546Sopenharmony_ci v3d_qpu_unpack_name(instr->alu.add.a_unpack)); 127bf215546Sopenharmony_ci } 128bf215546Sopenharmony_ci 129bf215546Sopenharmony_ci if (num_src >= 2) { 130bf215546Sopenharmony_ci append(disasm, ", "); 131bf215546Sopenharmony_ci v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.b); 132bf215546Sopenharmony_ci append(disasm, "%s", 133bf215546Sopenharmony_ci v3d_qpu_unpack_name(instr->alu.add.b_unpack)); 134bf215546Sopenharmony_ci } 135bf215546Sopenharmony_ci} 136bf215546Sopenharmony_ci 137bf215546Sopenharmony_cistatic void 138bf215546Sopenharmony_civ3d_qpu_disasm_mul(struct disasm_state *disasm, 139bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 140bf215546Sopenharmony_ci{ 141bf215546Sopenharmony_ci bool has_dst = v3d_qpu_mul_op_has_dst(instr->alu.mul.op); 142bf215546Sopenharmony_ci int num_src = v3d_qpu_mul_op_num_src(instr->alu.mul.op); 143bf215546Sopenharmony_ci 144bf215546Sopenharmony_ci pad_to(disasm, 30); 145bf215546Sopenharmony_ci append(disasm, "; "); 146bf215546Sopenharmony_ci 147bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_mul_op_name(instr->alu.mul.op)); 148bf215546Sopenharmony_ci if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig)) 149bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_cond_name(instr->flags.mc)); 150bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_pf_name(instr->flags.mpf)); 151bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_uf_name(instr->flags.muf)); 152bf215546Sopenharmony_ci 153bf215546Sopenharmony_ci if (instr->alu.mul.op == V3D_QPU_M_NOP) 154bf215546Sopenharmony_ci return; 155bf215546Sopenharmony_ci 156bf215546Sopenharmony_ci append(disasm, " "); 157bf215546Sopenharmony_ci 158bf215546Sopenharmony_ci if (has_dst) { 159bf215546Sopenharmony_ci v3d_qpu_disasm_waddr(disasm, instr->alu.mul.waddr, 160bf215546Sopenharmony_ci instr->alu.mul.magic_write); 161bf215546Sopenharmony_ci append(disasm, v3d_qpu_pack_name(instr->alu.mul.output_pack)); 162bf215546Sopenharmony_ci } 163bf215546Sopenharmony_ci 164bf215546Sopenharmony_ci if (num_src >= 1) { 165bf215546Sopenharmony_ci if (has_dst) 166bf215546Sopenharmony_ci append(disasm, ", "); 167bf215546Sopenharmony_ci v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.a); 168bf215546Sopenharmony_ci append(disasm, "%s", 169bf215546Sopenharmony_ci v3d_qpu_unpack_name(instr->alu.mul.a_unpack)); 170bf215546Sopenharmony_ci } 171bf215546Sopenharmony_ci 172bf215546Sopenharmony_ci if (num_src >= 2) { 173bf215546Sopenharmony_ci append(disasm, ", "); 174bf215546Sopenharmony_ci v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.b); 175bf215546Sopenharmony_ci append(disasm, "%s", 176bf215546Sopenharmony_ci v3d_qpu_unpack_name(instr->alu.mul.b_unpack)); 177bf215546Sopenharmony_ci } 178bf215546Sopenharmony_ci} 179bf215546Sopenharmony_ci 180bf215546Sopenharmony_cistatic void 181bf215546Sopenharmony_civ3d_qpu_disasm_sig_addr(struct disasm_state *disasm, 182bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 183bf215546Sopenharmony_ci{ 184bf215546Sopenharmony_ci if (disasm->devinfo->ver < 41) 185bf215546Sopenharmony_ci return; 186bf215546Sopenharmony_ci 187bf215546Sopenharmony_ci if (!instr->sig_magic) 188bf215546Sopenharmony_ci append(disasm, ".rf%d", instr->sig_addr); 189bf215546Sopenharmony_ci else { 190bf215546Sopenharmony_ci const char *name = 191bf215546Sopenharmony_ci v3d_qpu_magic_waddr_name(disasm->devinfo, 192bf215546Sopenharmony_ci instr->sig_addr); 193bf215546Sopenharmony_ci if (name) 194bf215546Sopenharmony_ci append(disasm, ".%s", name); 195bf215546Sopenharmony_ci else 196bf215546Sopenharmony_ci append(disasm, ".UNKNOWN%d", instr->sig_addr); 197bf215546Sopenharmony_ci } 198bf215546Sopenharmony_ci} 199bf215546Sopenharmony_ci 200bf215546Sopenharmony_cistatic void 201bf215546Sopenharmony_civ3d_qpu_disasm_sig(struct disasm_state *disasm, 202bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 203bf215546Sopenharmony_ci{ 204bf215546Sopenharmony_ci const struct v3d_qpu_sig *sig = &instr->sig; 205bf215546Sopenharmony_ci 206bf215546Sopenharmony_ci if (!sig->thrsw && 207bf215546Sopenharmony_ci !sig->ldvary && 208bf215546Sopenharmony_ci !sig->ldvpm && 209bf215546Sopenharmony_ci !sig->ldtmu && 210bf215546Sopenharmony_ci !sig->ldtlb && 211bf215546Sopenharmony_ci !sig->ldtlbu && 212bf215546Sopenharmony_ci !sig->ldunif && 213bf215546Sopenharmony_ci !sig->ldunifrf && 214bf215546Sopenharmony_ci !sig->ldunifa && 215bf215546Sopenharmony_ci !sig->ldunifarf && 216bf215546Sopenharmony_ci !sig->wrtmuc) { 217bf215546Sopenharmony_ci return; 218bf215546Sopenharmony_ci } 219bf215546Sopenharmony_ci 220bf215546Sopenharmony_ci pad_to(disasm, 60); 221bf215546Sopenharmony_ci 222bf215546Sopenharmony_ci if (sig->thrsw) 223bf215546Sopenharmony_ci append(disasm, "; thrsw"); 224bf215546Sopenharmony_ci if (sig->ldvary) { 225bf215546Sopenharmony_ci append(disasm, "; ldvary"); 226bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 227bf215546Sopenharmony_ci } 228bf215546Sopenharmony_ci if (sig->ldvpm) 229bf215546Sopenharmony_ci append(disasm, "; ldvpm"); 230bf215546Sopenharmony_ci if (sig->ldtmu) { 231bf215546Sopenharmony_ci append(disasm, "; ldtmu"); 232bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 233bf215546Sopenharmony_ci } 234bf215546Sopenharmony_ci if (sig->ldtlb) { 235bf215546Sopenharmony_ci append(disasm, "; ldtlb"); 236bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 237bf215546Sopenharmony_ci } 238bf215546Sopenharmony_ci if (sig->ldtlbu) { 239bf215546Sopenharmony_ci append(disasm, "; ldtlbu"); 240bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 241bf215546Sopenharmony_ci } 242bf215546Sopenharmony_ci if (sig->ldunif) 243bf215546Sopenharmony_ci append(disasm, "; ldunif"); 244bf215546Sopenharmony_ci if (sig->ldunifrf) { 245bf215546Sopenharmony_ci append(disasm, "; ldunifrf"); 246bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 247bf215546Sopenharmony_ci } 248bf215546Sopenharmony_ci if (sig->ldunifa) 249bf215546Sopenharmony_ci append(disasm, "; ldunifa"); 250bf215546Sopenharmony_ci if (sig->ldunifarf) { 251bf215546Sopenharmony_ci append(disasm, "; ldunifarf"); 252bf215546Sopenharmony_ci v3d_qpu_disasm_sig_addr(disasm, instr); 253bf215546Sopenharmony_ci } 254bf215546Sopenharmony_ci if (sig->wrtmuc) 255bf215546Sopenharmony_ci append(disasm, "; wrtmuc"); 256bf215546Sopenharmony_ci} 257bf215546Sopenharmony_ci 258bf215546Sopenharmony_cistatic void 259bf215546Sopenharmony_civ3d_qpu_disasm_alu(struct disasm_state *disasm, 260bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 261bf215546Sopenharmony_ci{ 262bf215546Sopenharmony_ci v3d_qpu_disasm_add(disasm, instr); 263bf215546Sopenharmony_ci v3d_qpu_disasm_mul(disasm, instr); 264bf215546Sopenharmony_ci v3d_qpu_disasm_sig(disasm, instr); 265bf215546Sopenharmony_ci} 266bf215546Sopenharmony_ci 267bf215546Sopenharmony_cistatic void 268bf215546Sopenharmony_civ3d_qpu_disasm_branch(struct disasm_state *disasm, 269bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 270bf215546Sopenharmony_ci{ 271bf215546Sopenharmony_ci append(disasm, "b"); 272bf215546Sopenharmony_ci if (instr->branch.ub) 273bf215546Sopenharmony_ci append(disasm, "u"); 274bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond)); 275bf215546Sopenharmony_ci append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign)); 276bf215546Sopenharmony_ci 277bf215546Sopenharmony_ci switch (instr->branch.bdi) { 278bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_ABS: 279bf215546Sopenharmony_ci append(disasm, " zero_addr+0x%08x", instr->branch.offset); 280bf215546Sopenharmony_ci break; 281bf215546Sopenharmony_ci 282bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_REL: 283bf215546Sopenharmony_ci append(disasm, " %d", instr->branch.offset); 284bf215546Sopenharmony_ci break; 285bf215546Sopenharmony_ci 286bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_LINK_REG: 287bf215546Sopenharmony_ci append(disasm, " lri"); 288bf215546Sopenharmony_ci break; 289bf215546Sopenharmony_ci 290bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_REGFILE: 291bf215546Sopenharmony_ci append(disasm, " rf%d", instr->branch.raddr_a); 292bf215546Sopenharmony_ci break; 293bf215546Sopenharmony_ci } 294bf215546Sopenharmony_ci 295bf215546Sopenharmony_ci if (instr->branch.ub) { 296bf215546Sopenharmony_ci switch (instr->branch.bdu) { 297bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_ABS: 298bf215546Sopenharmony_ci append(disasm, ", a:unif"); 299bf215546Sopenharmony_ci break; 300bf215546Sopenharmony_ci 301bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_REL: 302bf215546Sopenharmony_ci append(disasm, ", r:unif"); 303bf215546Sopenharmony_ci break; 304bf215546Sopenharmony_ci 305bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_LINK_REG: 306bf215546Sopenharmony_ci append(disasm, ", lri"); 307bf215546Sopenharmony_ci break; 308bf215546Sopenharmony_ci 309bf215546Sopenharmony_ci case V3D_QPU_BRANCH_DEST_REGFILE: 310bf215546Sopenharmony_ci append(disasm, ", rf%d", instr->branch.raddr_a); 311bf215546Sopenharmony_ci break; 312bf215546Sopenharmony_ci } 313bf215546Sopenharmony_ci } 314bf215546Sopenharmony_ci} 315bf215546Sopenharmony_ci 316bf215546Sopenharmony_ciconst char * 317bf215546Sopenharmony_civ3d_qpu_decode(const struct v3d_device_info *devinfo, 318bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 319bf215546Sopenharmony_ci{ 320bf215546Sopenharmony_ci struct disasm_state disasm = { 321bf215546Sopenharmony_ci .string = rzalloc_size(NULL, 1), 322bf215546Sopenharmony_ci .offset = 0, 323bf215546Sopenharmony_ci .devinfo = devinfo, 324bf215546Sopenharmony_ci }; 325bf215546Sopenharmony_ci 326bf215546Sopenharmony_ci switch (instr->type) { 327bf215546Sopenharmony_ci case V3D_QPU_INSTR_TYPE_ALU: 328bf215546Sopenharmony_ci v3d_qpu_disasm_alu(&disasm, instr); 329bf215546Sopenharmony_ci break; 330bf215546Sopenharmony_ci 331bf215546Sopenharmony_ci case V3D_QPU_INSTR_TYPE_BRANCH: 332bf215546Sopenharmony_ci v3d_qpu_disasm_branch(&disasm, instr); 333bf215546Sopenharmony_ci break; 334bf215546Sopenharmony_ci } 335bf215546Sopenharmony_ci 336bf215546Sopenharmony_ci return disasm.string; 337bf215546Sopenharmony_ci} 338bf215546Sopenharmony_ci 339bf215546Sopenharmony_ci/** 340bf215546Sopenharmony_ci * Returns a string containing the disassembled representation of the QPU 341bf215546Sopenharmony_ci * instruction. It is the caller's responsibility to free the return value 342bf215546Sopenharmony_ci * with ralloc_free(). 343bf215546Sopenharmony_ci */ 344bf215546Sopenharmony_ciconst char * 345bf215546Sopenharmony_civ3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst) 346bf215546Sopenharmony_ci{ 347bf215546Sopenharmony_ci struct v3d_qpu_instr instr; 348bf215546Sopenharmony_ci bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr); 349bf215546Sopenharmony_ci assert(ok); (void)ok; 350bf215546Sopenharmony_ci 351bf215546Sopenharmony_ci return v3d_qpu_decode(devinfo, &instr); 352bf215546Sopenharmony_ci} 353bf215546Sopenharmony_ci 354bf215546Sopenharmony_civoid 355bf215546Sopenharmony_civ3d_qpu_dump(const struct v3d_device_info *devinfo, 356bf215546Sopenharmony_ci const struct v3d_qpu_instr *instr) 357bf215546Sopenharmony_ci{ 358bf215546Sopenharmony_ci const char *decoded = v3d_qpu_decode(devinfo, instr); 359bf215546Sopenharmony_ci fprintf(stderr, "%s", decoded); 360bf215546Sopenharmony_ci ralloc_free((char *)decoded); 361bf215546Sopenharmony_ci} 362