101e04c3fSmrg/* 201e04c3fSmrg * Copyright © 2016 Broadcom 301e04c3fSmrg * 401e04c3fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 501e04c3fSmrg * copy of this software and associated documentation files (the "Software"), 601e04c3fSmrg * to deal in the Software without restriction, including without limitation 701e04c3fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 801e04c3fSmrg * and/or sell copies of the Software, and to permit persons to whom the 901e04c3fSmrg * Software is furnished to do so, subject to the following conditions: 1001e04c3fSmrg * 1101e04c3fSmrg * The above copyright notice and this permission notice (including the next 1201e04c3fSmrg * paragraph) shall be included in all copies or substantial portions of the 1301e04c3fSmrg * Software. 1401e04c3fSmrg * 1501e04c3fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1601e04c3fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1701e04c3fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 1801e04c3fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1901e04c3fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 2001e04c3fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 2101e04c3fSmrg * IN THE SOFTWARE. 2201e04c3fSmrg */ 2301e04c3fSmrg 2401e04c3fSmrg#include <string.h> 2501e04c3fSmrg#include <stdio.h> 2601e04c3fSmrg#include "util/ralloc.h" 2701e04c3fSmrg 2801e04c3fSmrg#include "broadcom/common/v3d_device_info.h" 2901e04c3fSmrg#include "qpu_instr.h" 3001e04c3fSmrg#include "qpu_disasm.h" 3101e04c3fSmrg 3201e04c3fSmrgstruct disasm_state { 3301e04c3fSmrg const struct v3d_device_info *devinfo; 3401e04c3fSmrg char *string; 3501e04c3fSmrg size_t offset; 3601e04c3fSmrg}; 3701e04c3fSmrg 3801e04c3fSmrgstatic void 3901e04c3fSmrgappend(struct disasm_state *disasm, const char *fmt, ...) 4001e04c3fSmrg{ 4101e04c3fSmrg va_list args; 4201e04c3fSmrg va_start(args, fmt); 4301e04c3fSmrg ralloc_vasprintf_rewrite_tail(&disasm->string, 4401e04c3fSmrg &disasm->offset, 4501e04c3fSmrg fmt, args); 4601e04c3fSmrg va_end(args); 4701e04c3fSmrg} 4801e04c3fSmrg 4901e04c3fSmrgstatic void 5001e04c3fSmrgpad_to(struct disasm_state *disasm, int n) 5101e04c3fSmrg{ 5201e04c3fSmrg /* FIXME: Do a single append somehow. */ 5301e04c3fSmrg while (disasm->offset < n) 5401e04c3fSmrg append(disasm, " "); 5501e04c3fSmrg} 5601e04c3fSmrg 5701e04c3fSmrg 5801e04c3fSmrgstatic void 5901e04c3fSmrgv3d_qpu_disasm_raddr(struct disasm_state *disasm, 6001e04c3fSmrg const struct v3d_qpu_instr *instr, uint8_t mux) 6101e04c3fSmrg{ 6201e04c3fSmrg if (mux == V3D_QPU_MUX_A) { 6301e04c3fSmrg append(disasm, "rf%d", instr->raddr_a); 6401e04c3fSmrg } else if (mux == V3D_QPU_MUX_B) { 6501e04c3fSmrg if (instr->sig.small_imm) { 6601e04c3fSmrg uint32_t val; 677ec681f3Smrg ASSERTED bool ok = 6801e04c3fSmrg v3d_qpu_small_imm_unpack(disasm->devinfo, 6901e04c3fSmrg instr->raddr_b, 7001e04c3fSmrg &val); 7101e04c3fSmrg 7201e04c3fSmrg if ((int)val >= -16 && (int)val <= 15) 7301e04c3fSmrg append(disasm, "%d", val); 7401e04c3fSmrg else 7501e04c3fSmrg append(disasm, "0x%08x", val); 7601e04c3fSmrg assert(ok); 7701e04c3fSmrg } else { 7801e04c3fSmrg append(disasm, "rf%d", instr->raddr_b); 7901e04c3fSmrg } 8001e04c3fSmrg } else { 8101e04c3fSmrg append(disasm, "r%d", mux); 8201e04c3fSmrg } 8301e04c3fSmrg} 8401e04c3fSmrg 8501e04c3fSmrgstatic void 8601e04c3fSmrgv3d_qpu_disasm_waddr(struct disasm_state *disasm, uint32_t waddr, bool magic) 8701e04c3fSmrg{ 8801e04c3fSmrg if (!magic) { 8901e04c3fSmrg append(disasm, "rf%d", waddr); 9001e04c3fSmrg return; 9101e04c3fSmrg } 9201e04c3fSmrg 937ec681f3Smrg const char *name = v3d_qpu_magic_waddr_name(disasm->devinfo, waddr); 9401e04c3fSmrg if (name) 9501e04c3fSmrg append(disasm, "%s", name); 9601e04c3fSmrg else 9701e04c3fSmrg append(disasm, "waddr UNKNOWN %d", waddr); 9801e04c3fSmrg} 9901e04c3fSmrg 10001e04c3fSmrgstatic void 10101e04c3fSmrgv3d_qpu_disasm_add(struct disasm_state *disasm, 10201e04c3fSmrg const struct v3d_qpu_instr *instr) 10301e04c3fSmrg{ 10401e04c3fSmrg bool has_dst = v3d_qpu_add_op_has_dst(instr->alu.add.op); 10501e04c3fSmrg int num_src = v3d_qpu_add_op_num_src(instr->alu.add.op); 10601e04c3fSmrg 10701e04c3fSmrg append(disasm, "%s", v3d_qpu_add_op_name(instr->alu.add.op)); 10801e04c3fSmrg if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig)) 10901e04c3fSmrg append(disasm, "%s", v3d_qpu_cond_name(instr->flags.ac)); 11001e04c3fSmrg append(disasm, "%s", v3d_qpu_pf_name(instr->flags.apf)); 11101e04c3fSmrg append(disasm, "%s", v3d_qpu_uf_name(instr->flags.auf)); 11201e04c3fSmrg 11301e04c3fSmrg append(disasm, " "); 11401e04c3fSmrg 11501e04c3fSmrg if (has_dst) { 11601e04c3fSmrg v3d_qpu_disasm_waddr(disasm, instr->alu.add.waddr, 11701e04c3fSmrg instr->alu.add.magic_write); 11801e04c3fSmrg append(disasm, v3d_qpu_pack_name(instr->alu.add.output_pack)); 11901e04c3fSmrg } 12001e04c3fSmrg 12101e04c3fSmrg if (num_src >= 1) { 12201e04c3fSmrg if (has_dst) 12301e04c3fSmrg append(disasm, ", "); 12401e04c3fSmrg v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.a); 12501e04c3fSmrg append(disasm, "%s", 12601e04c3fSmrg v3d_qpu_unpack_name(instr->alu.add.a_unpack)); 12701e04c3fSmrg } 12801e04c3fSmrg 12901e04c3fSmrg if (num_src >= 2) { 13001e04c3fSmrg append(disasm, ", "); 13101e04c3fSmrg v3d_qpu_disasm_raddr(disasm, instr, instr->alu.add.b); 13201e04c3fSmrg append(disasm, "%s", 13301e04c3fSmrg v3d_qpu_unpack_name(instr->alu.add.b_unpack)); 13401e04c3fSmrg } 13501e04c3fSmrg} 13601e04c3fSmrg 13701e04c3fSmrgstatic void 13801e04c3fSmrgv3d_qpu_disasm_mul(struct disasm_state *disasm, 13901e04c3fSmrg const struct v3d_qpu_instr *instr) 14001e04c3fSmrg{ 14101e04c3fSmrg bool has_dst = v3d_qpu_mul_op_has_dst(instr->alu.mul.op); 14201e04c3fSmrg int num_src = v3d_qpu_mul_op_num_src(instr->alu.mul.op); 14301e04c3fSmrg 14401e04c3fSmrg pad_to(disasm, 21); 14501e04c3fSmrg append(disasm, "; "); 14601e04c3fSmrg 14701e04c3fSmrg append(disasm, "%s", v3d_qpu_mul_op_name(instr->alu.mul.op)); 14801e04c3fSmrg if (!v3d_qpu_sig_writes_address(disasm->devinfo, &instr->sig)) 14901e04c3fSmrg append(disasm, "%s", v3d_qpu_cond_name(instr->flags.mc)); 15001e04c3fSmrg append(disasm, "%s", v3d_qpu_pf_name(instr->flags.mpf)); 15101e04c3fSmrg append(disasm, "%s", v3d_qpu_uf_name(instr->flags.muf)); 15201e04c3fSmrg 15301e04c3fSmrg if (instr->alu.mul.op == V3D_QPU_M_NOP) 15401e04c3fSmrg return; 15501e04c3fSmrg 15601e04c3fSmrg append(disasm, " "); 15701e04c3fSmrg 15801e04c3fSmrg if (has_dst) { 15901e04c3fSmrg v3d_qpu_disasm_waddr(disasm, instr->alu.mul.waddr, 16001e04c3fSmrg instr->alu.mul.magic_write); 16101e04c3fSmrg append(disasm, v3d_qpu_pack_name(instr->alu.mul.output_pack)); 16201e04c3fSmrg } 16301e04c3fSmrg 16401e04c3fSmrg if (num_src >= 1) { 16501e04c3fSmrg if (has_dst) 16601e04c3fSmrg append(disasm, ", "); 16701e04c3fSmrg v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.a); 16801e04c3fSmrg append(disasm, "%s", 16901e04c3fSmrg v3d_qpu_unpack_name(instr->alu.mul.a_unpack)); 17001e04c3fSmrg } 17101e04c3fSmrg 17201e04c3fSmrg if (num_src >= 2) { 17301e04c3fSmrg append(disasm, ", "); 17401e04c3fSmrg v3d_qpu_disasm_raddr(disasm, instr, instr->alu.mul.b); 17501e04c3fSmrg append(disasm, "%s", 17601e04c3fSmrg v3d_qpu_unpack_name(instr->alu.mul.b_unpack)); 17701e04c3fSmrg } 17801e04c3fSmrg} 17901e04c3fSmrg 18001e04c3fSmrgstatic void 18101e04c3fSmrgv3d_qpu_disasm_sig_addr(struct disasm_state *disasm, 18201e04c3fSmrg const struct v3d_qpu_instr *instr) 18301e04c3fSmrg{ 18401e04c3fSmrg if (disasm->devinfo->ver < 41) 18501e04c3fSmrg return; 18601e04c3fSmrg 18701e04c3fSmrg if (!instr->sig_magic) 18801e04c3fSmrg append(disasm, ".rf%d", instr->sig_addr); 18901e04c3fSmrg else { 1907ec681f3Smrg const char *name = 1917ec681f3Smrg v3d_qpu_magic_waddr_name(disasm->devinfo, 1927ec681f3Smrg instr->sig_addr); 19301e04c3fSmrg if (name) 19401e04c3fSmrg append(disasm, ".%s", name); 19501e04c3fSmrg else 19601e04c3fSmrg append(disasm, ".UNKNOWN%d", instr->sig_addr); 19701e04c3fSmrg } 19801e04c3fSmrg} 19901e04c3fSmrg 20001e04c3fSmrgstatic void 20101e04c3fSmrgv3d_qpu_disasm_sig(struct disasm_state *disasm, 20201e04c3fSmrg const struct v3d_qpu_instr *instr) 20301e04c3fSmrg{ 20401e04c3fSmrg const struct v3d_qpu_sig *sig = &instr->sig; 20501e04c3fSmrg 20601e04c3fSmrg if (!sig->thrsw && 20701e04c3fSmrg !sig->ldvary && 20801e04c3fSmrg !sig->ldvpm && 20901e04c3fSmrg !sig->ldtmu && 2107ec681f3Smrg !sig->ldtlb && 2117ec681f3Smrg !sig->ldtlbu && 21201e04c3fSmrg !sig->ldunif && 21301e04c3fSmrg !sig->ldunifrf && 21401e04c3fSmrg !sig->ldunifa && 21501e04c3fSmrg !sig->ldunifarf && 21601e04c3fSmrg !sig->wrtmuc) { 21701e04c3fSmrg return; 21801e04c3fSmrg } 21901e04c3fSmrg 22001e04c3fSmrg pad_to(disasm, 41); 22101e04c3fSmrg 22201e04c3fSmrg if (sig->thrsw) 22301e04c3fSmrg append(disasm, "; thrsw"); 22401e04c3fSmrg if (sig->ldvary) { 22501e04c3fSmrg append(disasm, "; ldvary"); 22601e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 22701e04c3fSmrg } 22801e04c3fSmrg if (sig->ldvpm) 22901e04c3fSmrg append(disasm, "; ldvpm"); 23001e04c3fSmrg if (sig->ldtmu) { 23101e04c3fSmrg append(disasm, "; ldtmu"); 23201e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 23301e04c3fSmrg } 23401e04c3fSmrg if (sig->ldtlb) { 23501e04c3fSmrg append(disasm, "; ldtlb"); 23601e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 23701e04c3fSmrg } 23801e04c3fSmrg if (sig->ldtlbu) { 23901e04c3fSmrg append(disasm, "; ldtlbu"); 24001e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 24101e04c3fSmrg } 24201e04c3fSmrg if (sig->ldunif) 24301e04c3fSmrg append(disasm, "; ldunif"); 24401e04c3fSmrg if (sig->ldunifrf) { 24501e04c3fSmrg append(disasm, "; ldunifrf"); 24601e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 24701e04c3fSmrg } 24801e04c3fSmrg if (sig->ldunifa) 24901e04c3fSmrg append(disasm, "; ldunifa"); 25001e04c3fSmrg if (sig->ldunifarf) { 25101e04c3fSmrg append(disasm, "; ldunifarf"); 25201e04c3fSmrg v3d_qpu_disasm_sig_addr(disasm, instr); 25301e04c3fSmrg } 25401e04c3fSmrg if (sig->wrtmuc) 25501e04c3fSmrg append(disasm, "; wrtmuc"); 25601e04c3fSmrg} 25701e04c3fSmrg 25801e04c3fSmrgstatic void 25901e04c3fSmrgv3d_qpu_disasm_alu(struct disasm_state *disasm, 26001e04c3fSmrg const struct v3d_qpu_instr *instr) 26101e04c3fSmrg{ 26201e04c3fSmrg v3d_qpu_disasm_add(disasm, instr); 26301e04c3fSmrg v3d_qpu_disasm_mul(disasm, instr); 26401e04c3fSmrg v3d_qpu_disasm_sig(disasm, instr); 26501e04c3fSmrg} 26601e04c3fSmrg 26701e04c3fSmrgstatic void 26801e04c3fSmrgv3d_qpu_disasm_branch(struct disasm_state *disasm, 26901e04c3fSmrg const struct v3d_qpu_instr *instr) 27001e04c3fSmrg{ 27101e04c3fSmrg append(disasm, "b"); 27201e04c3fSmrg if (instr->branch.ub) 27301e04c3fSmrg append(disasm, "u"); 27401e04c3fSmrg append(disasm, "%s", v3d_qpu_branch_cond_name(instr->branch.cond)); 27501e04c3fSmrg append(disasm, "%s", v3d_qpu_msfign_name(instr->branch.msfign)); 27601e04c3fSmrg 27701e04c3fSmrg switch (instr->branch.bdi) { 27801e04c3fSmrg case V3D_QPU_BRANCH_DEST_ABS: 27901e04c3fSmrg append(disasm, " zero_addr+0x%08x", instr->branch.offset); 28001e04c3fSmrg break; 28101e04c3fSmrg 28201e04c3fSmrg case V3D_QPU_BRANCH_DEST_REL: 28301e04c3fSmrg append(disasm, " %d", instr->branch.offset); 28401e04c3fSmrg break; 28501e04c3fSmrg 28601e04c3fSmrg case V3D_QPU_BRANCH_DEST_LINK_REG: 28701e04c3fSmrg append(disasm, " lri"); 28801e04c3fSmrg break; 28901e04c3fSmrg 29001e04c3fSmrg case V3D_QPU_BRANCH_DEST_REGFILE: 29101e04c3fSmrg append(disasm, " rf%d", instr->branch.raddr_a); 29201e04c3fSmrg break; 29301e04c3fSmrg } 29401e04c3fSmrg 29501e04c3fSmrg if (instr->branch.ub) { 29601e04c3fSmrg switch (instr->branch.bdu) { 29701e04c3fSmrg case V3D_QPU_BRANCH_DEST_ABS: 29801e04c3fSmrg append(disasm, ", a:unif"); 29901e04c3fSmrg break; 30001e04c3fSmrg 30101e04c3fSmrg case V3D_QPU_BRANCH_DEST_REL: 30201e04c3fSmrg append(disasm, ", r:unif"); 30301e04c3fSmrg break; 30401e04c3fSmrg 30501e04c3fSmrg case V3D_QPU_BRANCH_DEST_LINK_REG: 30601e04c3fSmrg append(disasm, ", lri"); 30701e04c3fSmrg break; 30801e04c3fSmrg 30901e04c3fSmrg case V3D_QPU_BRANCH_DEST_REGFILE: 31001e04c3fSmrg append(disasm, ", rf%d", instr->branch.raddr_a); 31101e04c3fSmrg break; 31201e04c3fSmrg } 31301e04c3fSmrg } 31401e04c3fSmrg} 31501e04c3fSmrg 31601e04c3fSmrgconst char * 31701e04c3fSmrgv3d_qpu_decode(const struct v3d_device_info *devinfo, 31801e04c3fSmrg const struct v3d_qpu_instr *instr) 31901e04c3fSmrg{ 32001e04c3fSmrg struct disasm_state disasm = { 32101e04c3fSmrg .string = rzalloc_size(NULL, 1), 32201e04c3fSmrg .offset = 0, 32301e04c3fSmrg .devinfo = devinfo, 32401e04c3fSmrg }; 32501e04c3fSmrg 32601e04c3fSmrg switch (instr->type) { 32701e04c3fSmrg case V3D_QPU_INSTR_TYPE_ALU: 32801e04c3fSmrg v3d_qpu_disasm_alu(&disasm, instr); 32901e04c3fSmrg break; 33001e04c3fSmrg 33101e04c3fSmrg case V3D_QPU_INSTR_TYPE_BRANCH: 33201e04c3fSmrg v3d_qpu_disasm_branch(&disasm, instr); 33301e04c3fSmrg break; 33401e04c3fSmrg } 33501e04c3fSmrg 33601e04c3fSmrg return disasm.string; 33701e04c3fSmrg} 33801e04c3fSmrg 33901e04c3fSmrg/** 34001e04c3fSmrg * Returns a string containing the disassembled representation of the QPU 34101e04c3fSmrg * instruction. It is the caller's responsibility to free the return value 34201e04c3fSmrg * with ralloc_free(). 34301e04c3fSmrg */ 34401e04c3fSmrgconst char * 34501e04c3fSmrgv3d_qpu_disasm(const struct v3d_device_info *devinfo, uint64_t inst) 34601e04c3fSmrg{ 34701e04c3fSmrg struct v3d_qpu_instr instr; 34801e04c3fSmrg bool ok = v3d_qpu_instr_unpack(devinfo, inst, &instr); 34901e04c3fSmrg assert(ok); (void)ok; 35001e04c3fSmrg 35101e04c3fSmrg return v3d_qpu_decode(devinfo, &instr); 35201e04c3fSmrg} 35301e04c3fSmrg 35401e04c3fSmrgvoid 35501e04c3fSmrgv3d_qpu_dump(const struct v3d_device_info *devinfo, 35601e04c3fSmrg const struct v3d_qpu_instr *instr) 35701e04c3fSmrg{ 35801e04c3fSmrg const char *decoded = v3d_qpu_decode(devinfo, instr); 35901e04c3fSmrg fprintf(stderr, "%s", decoded); 36001e04c3fSmrg ralloc_free((char *)decoded); 36101e04c3fSmrg} 362