/*- * Copyright (c) 2016 Ruslan Bukin * All rights reserved. * * Portions of this software were developed by SRI International and the * University of Cambridge Computer Laboratory under DARPA/AFRL contract * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. * * Portions of this software were developed by the University of Cambridge * Computer Laboratory as part of the CTSRD Project, with support from the * UK Higher Education Innovation Fund (HEIF). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include struct riscv_op { char *name; char *type; char *fmt; int opcode; int funct3; int funct7; /* Or imm, depending on type. */ }; /* * Keep sorted by opcode, funct3, funct7 so some instructions * aliases will be supported (e.g. "mv" instruction alias) * Use same print format as binutils do. */ static struct riscv_op riscv_opcodes[] = { { "lb", "I", "d,o(s)", 3, 0, -1 }, { "lh", "I", "d,o(s)", 3, 1, -1 }, { "lw", "I", "d,o(s)", 3, 2, -1 }, { "ld", "I", "d,o(s)", 3, 3, -1 }, { "lbu", "I", "d,o(s)", 3, 4, -1 }, { "lhu", "I", "d,o(s)", 3, 5, -1 }, { "lwu", "I", "d,o(s)", 3, 6, -1 }, { "ldu", "I", "d,o(s)", 3, 7, -1 }, { "fence", "I", "", 15, 0, -1 }, { "fence.i", "I", "", 15, 1, -1 }, { "mv", "I", "d,s", 19, 0, 0 }, { "addi", "I", "d,s,j", 19, 0, -1 }, { "slli", "R2", "d,s,>", 19, 1, 0 }, { "slti", "I", "d,s,j", 19, 2, -1 }, { "sltiu", "I", "d,s,j", 19, 3, -1 }, { "xori", "I", "d,s,j", 19, 4, -1 }, { "srli", "R2", "d,s,>", 19, 5, 0 }, { "srai", "R2", "d,s,>", 19, 5, 0b010000 }, { "ori", "I", "d,s,j", 19, 6, -1 }, { "andi", "I", "d,s,j", 19, 7, -1 }, { "auipc", "U", "d,u", 23, -1, -1 }, { "sext.w", "I", "d,s", 27, 0, 0 }, { "addiw", "I", "d,s,j", 27, 0, -1 }, { "slliw", "R", "d,s,<", 27, 1, 0 }, { "srliw", "R", "d,s,<", 27, 5, 0 }, { "sraiw", "R", "d,s,<", 27, 5, 0b0100000 }, { "sb", "S", "t,q(s)", 35, 0, -1 }, { "sh", "S", "t,q(s)", 35, 1, -1 }, { "sw", "S", "t,q(s)", 35, 2, -1 }, { "sd", "S", "t,q(s)", 35, 3, -1 }, { "sbu", "S", "t,q(s)", 35, 4, -1 }, { "shu", "S", "t,q(s)", 35, 5, -1 }, { "swu", "S", "t,q(s)", 35, 6, -1 }, { "sdu", "S", "t,q(s)", 35, 7, -1 }, { "lr.w", "R", "d,t,0(s)", 47, 2, 0b0001000 }, { "sc.w", "R", "d,t,0(s)", 47, 2, 0b0001100 }, { "amoswap.w", "R", "d,t,0(s)", 47, 2, 0b0000100 }, { "amoadd.w", "R", "d,t,0(s)", 47, 2, 0b0000000 }, { "amoxor.w", "R", "d,t,0(s)", 47, 2, 0b0010000 }, { "amoand.w", "R", "d,t,0(s)", 47, 2, 0b0110000 }, { "amoor.w", "R", "d,t,0(s)", 47, 2, 0b0100000 }, { "amomin.w", "R", "d,t,0(s)", 47, 2, 0b1000000 }, { "amomax.w", "R", "d,t,0(s)", 47, 2, 0b1010000 }, { "amominu.w", "R", "d,t,0(s)", 47, 2, 0b1100000 }, { "amomaxu.w", "R", "d,t,0(s)", 47, 2, 0b1110000 }, { "lr.w.aq", "R", "d,t,0(s)", 47, 2, 0b0001000 }, { "sc.w.aq", "R", "d,t,0(s)", 47, 2, 0b0001100 }, { "amoswap.w.aq","R", "d,t,0(s)", 47, 2, 0b0000110 }, { "amoadd.w.aq","R", "d,t,0(s)", 47, 2, 0b0000010 }, { "amoxor.w.aq","R", "d,t,0(s)", 47, 2, 0b0010010 }, { "amoand.w.aq","R", "d,t,0(s)", 47, 2, 0b0110010 }, { "amoor.w.aq", "R", "d,t,0(s)", 47, 2, 0b0100010 }, { "amomin.w.aq","R", "d,t,0(s)", 47, 2, 0b1000010 }, { "amomax.w.aq","R", "d,t,0(s)", 47, 2, 0b1010010 }, { "amominu.w.aq","R", "d,t,0(s)", 47, 2, 0b1100010 }, { "amomaxu.w.aq","R", "d,t,0(s)", 47, 2, 0b1110010 }, { "amoswap.w.rl","R", "d,t,0(s)", 47, 2, 0b0000110 }, { "amoadd.w.rl","R", "d,t,0(s)", 47, 2, 0b0000001 }, { "amoxor.w.rl","R", "d,t,0(s)", 47, 2, 0b0010001 }, { "amoand.w.rl","R", "d,t,0(s)", 47, 2, 0b0110001 }, { "amoor.w.rl", "R", "d,t,0(s)", 47, 2, 0b0100001 }, { "amomin.w.rl","R", "d,t,0(s)", 47, 2, 0b1000001 }, { "amomax.w.rl","R", "d,t,0(s)", 47, 2, 0b1010001 }, { "amominu.w.rl","R", "d,t,0(s)", 47, 2, 0b1100001 }, { "amomaxu.w.rl","R", "d,t,0(s)", 47, 2, 0b1110001 }, { "amoswap.d", "R", "d,t,0(s)", 47, 3, 0b0000100 }, { "amoadd.d", "R", "d,t,0(s)", 47, 3, 0b0000000 }, { "amoxor.d", "R", "d,t,0(s)", 47, 3, 0b0010000 }, { "amoand.d", "R", "d,t,0(s)", 47, 3, 0b0110000 }, { "amoor.d", "R", "d,t,0(s)", 47, 3, 0b0100000 }, { "amomin.d", "R", "d,t,0(s)", 47, 3, 0b1000000 }, { "amomax.d", "R", "d,t,0(s)", 47, 3, 0b1010000 }, { "amominu.d", "R", "d,t,0(s)", 47, 3, 0b1100000 }, { "amomaxu.d", "R", "d,t,0(s)", 47, 3, 0b1110000 }, { "lr.d.aq", "R", "d,t,0(s)", 47, 3, 0b0001000 }, { "sc.d.aq", "R", "d,t,0(s)", 47, 3, 0b0001100 }, { "amoswap.d.aq","R", "d,t,0(s)", 47, 3, 0b0000110 }, { "amoadd.d.aq","R", "d,t,0(s)", 47, 3, 0b0000010 }, { "amoxor.d.aq","R", "d,t,0(s)", 47, 3, 0b0010010 }, { "amoand.d.aq","R", "d,t,0(s)", 47, 3, 0b0110010 }, { "amoor.d.aq", "R", "d,t,0(s)", 47, 3, 0b0100010 }, { "amomin.d.aq","R", "d,t,0(s)", 47, 3, 0b1000010 }, { "amomax.d.aq","R", "d,t,0(s)", 47, 3, 0b1010010 }, { "amominu.d.aq","R", "d,t,0(s)", 47, 3, 0b1100010 }, { "amomaxu.d.aq","R", "d,t,0(s)", 47, 3, 0b1110010 }, { "amoswap.d.rl","R", "d,t,0(s)", 47, 3, 0b0000110 }, { "amoadd.d.rl","R", "d,t,0(s)", 47, 3, 0b0000001 }, { "amoxor.d.rl","R", "d,t,0(s)", 47, 3, 0b0010001 }, { "amoand.d.rl","R", "d,t,0(s)", 47, 3, 0b0110001 }, { "amoor.d.rl", "R", "d,t,0(s)", 47, 3, 0b0100001 }, { "amomin.d.rl","R", "d,t,0(s)", 47, 3, 0b1000001 }, { "amomax.d.rl","R", "d,t,0(s)", 47, 3, 0b1010001 }, { "amominu.d.rl","R", "d,t,0(s)", 47, 3, 0b1100001 }, { "amomaxu.d.rl","R", "d,t,0(s)", 47, 3, 0b1110001 }, { "add", "R", "d,s,t", 51, 0, 0 }, { "sub", "R", "d,s,t", 51, 0, 0b0100000 }, { "mul", "R", "d,s,t", 51, 0, 0b0000001 }, { "sll", "R", "d,s,t", 51, 1, 0 }, { "slt", "R", "d,s,t", 51, 2, 0 }, { "sltu", "R", "d,s,t", 51, 3, 0 }, { "xor", "R", "d,s,t", 51, 4, 0 }, { "srl", "R", "d,s,t", 51, 5, 0 }, { "sra", "R", "d,s,t", 51, 5, 0b0100000 }, { "or", "R", "d,s,t", 51, 6, 0 }, { "and", "R", "d,s,t", 51, 7, 0 }, { "lui", "U", "d,u", 55, -1, -1 }, { "addw", "R", "d,s,t", 59, 0, 0 }, { "subw", "R", "d,s,t", 59, 0, 0b0100000 }, { "mulw", "R", "d,s,t", 59, 0, 1 }, { "sllw", "R", "d,s,t", 59, 1, 0 }, { "srlw", "R", "d,s,t", 59, 5, 0 }, { "sraw", "R", "d,s,t", 59, 5, 0b0100000 }, { "beq", "SB", "s,t,p", 99, 0, -1 }, { "bne", "SB", "s,t,p", 99, 1, -1 }, { "blt", "SB", "s,t,p", 99, 4, -1 }, { "bge", "SB", "s,t,p", 99, 5, -1 }, { "bltu", "SB", "s,t,p", 99, 6, -1 }, { "bgeu", "SB", "s,t,p", 99, 7, -1 }, { "jalr", "I", "d,s,j", 103, 0, -1 }, { "jal", "UJ", "a", 111, -1, -1 }, { "eret", "I", "", 115, 0, 0b000100000000 }, { "sfence.vm", "I", "", 115, 0, 0b000100000001 }, { "wfi", "I", "", 115, 0, 0b000100000010 }, { "csrrw", "I", "d,E,s", 115, 1, -1}, { "csrrs", "I", "d,E,s", 115, 2, -1}, { "csrrc", "I", "d,E,s", 115, 3, -1}, { "csrrwi", "I", "d,E,Z", 115, 5, -1}, { "csrrsi", "I", "d,E,Z", 115, 6, -1}, { "csrrci", "I", "d,E,Z", 115, 7, -1}, { NULL, NULL, NULL, 0, 0, 0 } }; struct csr_op { char *name; int imm; }; static struct csr_op csr_name[] = { { "fflags", 0x001 }, { "frm", 0x002 }, { "fcsr", 0x003 }, { "cycle", 0xc00 }, { "time", 0xc01 }, { "instret", 0xc02 }, { "stats", 0x0c0 }, { "uarch0", 0xcc0 }, { "uarch1", 0xcc1 }, { "uarch2", 0xcc2 }, { "uarch3", 0xcc3 }, { "uarch4", 0xcc4 }, { "uarch5", 0xcc5 }, { "uarch6", 0xcc6 }, { "uarch7", 0xcc7 }, { "uarch8", 0xcc8 }, { "uarch9", 0xcc9 }, { "uarch10", 0xcca }, { "uarch11", 0xccb }, { "uarch12", 0xccc }, { "uarch13", 0xccd }, { "uarch14", 0xcce }, { "uarch15", 0xccf }, { "sstatus", 0x100 }, { "stvec", 0x101 }, { "sie", 0x104 }, { "sscratch", 0x140 }, { "sepc", 0x141 }, { "sip", 0x144 }, { "sptbr", 0x180 }, { "sasid", 0x181 }, { "cyclew", 0x900 }, { "timew", 0x901 }, { "instretw", 0x902 }, { "stime", 0xd01 }, { "scause", 0xd42 }, { "sbadaddr", 0xd43 }, { "stimew", 0xa01 }, { "mstatus", 0x300 }, { "mtvec", 0x301 }, { "mtdeleg", 0x302 }, { "mie", 0x304 }, { "mtimecmp", 0x321 }, { "mscratch", 0x340 }, { "mepc", 0x341 }, { "mcause", 0x342 }, { "mbadaddr", 0x343 }, { "mip", 0x344 }, { "mtime", 0x701 }, { "mcpuid", 0xf00 }, { "mimpid", 0xf01 }, { "mhartid", 0xf10 }, { "mtohost", 0x780 }, { "mfromhost", 0x781 }, { "mreset", 0x782 }, { "send_ipi", 0x783 }, { "miobase", 0x784 }, { "cycleh", 0xc80 }, { "timeh", 0xc81 }, { "instreth", 0xc82 }, { "cyclehw", 0x980 }, { "timehw", 0x981 }, { "instrethw", 0x982 }, { "stimeh", 0xd81 }, { "stimehw", 0xa81 }, { "mtimecmph", 0x361 }, { "mtimeh", 0x741 }, { NULL, 0 } }; static char *reg_name[32] = { "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6" }; static int32_t get_imm(InstFmt i, char *type, uint32_t *val) { int imm; imm = 0; if (strcmp(type, "I") == 0) { imm = i.IType.imm; *val = imm; if (imm & (1 << 11)) imm |= (0xfffff << 12); /* sign extend */ } else if (strcmp(type, "S") == 0) { imm = i.SType.imm0_4; imm |= (i.SType.imm5_11 << 5); *val = imm; if (imm & (1 << 11)) imm |= (0xfffff << 12); /* sign extend */ } else if (strcmp(type, "U") == 0) { imm = i.UType.imm12_31; *val = imm; } else if (strcmp(type, "UJ") == 0) { imm = i.UJType.imm12_19 << 12; imm |= i.UJType.imm11 << 11; imm |= i.UJType.imm1_10 << 1; imm |= i.UJType.imm20 << 20; *val = imm; if (imm & (1 << 20)) imm |= (0xfff << 21); /* sign extend */ } else if (strcmp(type, "SB") == 0) { imm = i.SBType.imm11 << 11; imm |= i.SBType.imm1_4 << 1; imm |= i.SBType.imm5_10 << 5; imm |= i.SBType.imm12 << 12; *val = imm; if (imm & (1 << 12)) imm |= (0xfffff << 12); /* sign extend */ } return (imm); } static int oprint(struct riscv_op *op, vm_offset_t loc, int rd, int rs1, int rs2, uint32_t val, vm_offset_t imm) { char *p; int i; p = op->fmt; db_printf("%s\t", op->name); while (*p) { if (strncmp("d", p, 1) == 0) db_printf("%s", reg_name[rd]); else if (strncmp("s", p, 1) == 0) db_printf("%s", reg_name[rs1]); else if (strncmp("t", p, 1) == 0) db_printf("%s", reg_name[rs2]); else if (strncmp(">", p, 1) == 0) db_printf("0x%x", rs2); else if (strncmp("E", p, 1) == 0) { for (i = 0; csr_name[i].name != NULL; i++) if (csr_name[i].imm == val) db_printf("%s", csr_name[i].name); } else if (strncmp("Z", p, 1) == 0) db_printf("%d", rs1); else if (strncmp("<", p, 1) == 0) db_printf("0x%x", rs2); else if (strncmp("j", p, 1) == 0) db_printf("%d", imm); else if (strncmp("u", p, 1) == 0) db_printf("0x%x", imm); else if (strncmp("a", p, 1) == 0) db_printf("0x%016lx", imm); else if (strncmp("p", p, 1) == 0) db_printf("0x%016lx", (loc + imm)); else if (strlen(p) >= 4) { if (strncmp("o(s)", p, 4) == 0) db_printf("%d(%s)", imm, reg_name[rs1]); else if (strncmp("q(s)", p, 4) == 0) db_printf("%d(%s)", imm, reg_name[rs1]); else if (strncmp("0(s)", p, 4) == 0) db_printf("(%s)", reg_name[rs1]); } while (*p && strncmp(p, ",", 1) != 0) p++; if (*p) { db_printf(", "); p++; } } return (0); } static int match_type(InstFmt i, struct riscv_op *op, vm_offset_t loc) { uint32_t val; int found; int imm; val = 0; imm = get_imm(i, op->type, &val); if (strcmp(op->type, "U") == 0) { oprint(op, loc, i.UType.rd, 0, 0, val, imm); return (1); } if (strcmp(op->type, "UJ") == 0) { oprint(op, loc, 0, 0, 0, val, (loc + imm)); return (1); } if ((strcmp(op->type, "I") == 0) && \ (op->funct3 == i.IType.funct3)) { found = 0; if (op->funct7 != -1) { if (op->funct7 == i.IType.imm) found = 1; } else found = 1; if (found) { oprint(op, loc, i.IType.rd, i.IType.rs1, 0, val, imm); return (1); } } if ((strcmp(op->type, "S") == 0) && \ (op->funct3 == i.SType.funct3)) { oprint(op, loc, 0, i.SType.rs1, i.SType.rs2, val, imm); return (1); } if ((strcmp(op->type, "SB") == 0) && \ (op->funct3 == i.SBType.funct3)) { oprint(op, loc, 0, i.SBType.rs1, i.SBType.rs2, val, imm); return (1); } if ((strcmp(op->type, "R2") == 0) && \ (op->funct3 == i.R2Type.funct3) && \ (op->funct7 == i.R2Type.funct7)) { oprint(op, loc, i.R2Type.rd, i.R2Type.rs1, i.R2Type.rs2, val, imm); return (1); } if ((strcmp(op->type, "R") == 0) && \ (op->funct3 == i.RType.funct3) && \ (op->funct7 == i.RType.funct7)) { oprint(op, loc, i.RType.rd, i.RType.rs1, val, i.RType.rs2, imm); return (1); } return (0); } vm_offset_t db_disasm(vm_offset_t loc, bool altfmt) { struct riscv_op *op; InstFmt i; int j; i.word = db_get_value(loc, INSN_SIZE, 0); /* First match opcode */ for (j = 0; riscv_opcodes[j].name != NULL; j++) { op = &riscv_opcodes[j]; if (op->opcode == i.RType.opcode) { if (match_type(i, op, loc)) break; } } db_printf("\n"); return(loc + INSN_SIZE); }