/* lm32 support for radare2 * 2-clause BSD * Copyright 2015 Felix Held */ #include #include #include #include #include "../arch/lm32/lm32_isa.h" #define LM32_UNUSED 0 //str has to be at least 8 chars elements long static int reg_number_to_string(ut8 reg, char *str) { ut8 match_idx = 0xff; int i; for (i = 0; i < RAsmLm32RegNumber; i++) { if (RAsmLm32Regs[i].number == reg) { match_idx = i; break; } } //register number not found in array. this shouldn't happen if (match_idx == 0xff) return -1; strcpy (str, RAsmLm32Regs[match_idx].name); return 0; } #if LM32_UNUSED static int string_to_reg_number(const char *str, ut8 *num) { ut8 match_idx = 0xff; int i; for (i = 0; i < RAsmLm32RegNumber; i++) { if (!strcmp (RAsmLm32Regs[i].name, str)) { match_idx = i; break; } } //register name string not found in array if (match_idx == 0xff) return -1; *num = RAsmLm32Regs[match_idx].number; return 0; } static int string_to_csr_number(const char *str, ut8 *num) { ut8 match_idx = 0xff; int i; for (i = 0; i < RAsmLm32CsrNumber; i++) { if (!strcmp (RAsmLm32Csrs[i].name, str)) { match_idx = i; break; } } //csr name string not found in array if (match_idx == 0xff) return -1; *num = RAsmLm32Csrs[match_idx].number; return 0; } static int string_to_opcode(const char *str, ut8 *num) { ut8 tmp_num = 0xff; int i; for (i = 0; i < RAsmLm32OpcodeNumber; i++) { if (!strcmp (RAsmLm32OpcodeList[i].name, str)) { tmp_num = i; } } //string not found in array if (tmp_num == 0xff) return -1; *num = tmp_num; return 0; } #endif //str has to be at least 8 chars elements long static int csr_number_to_string(ut8 csr, char *str) { ut8 match_idx = 0xff; int i; for (i = 0; i < RAsmLm32CsrNumber; i++) { if (RAsmLm32Csrs[i].number == csr) { match_idx = i; break; } } //csr number not found in array if (match_idx == 0xff) return -1; strcpy (str, RAsmLm32Csrs[match_idx].name); return 0; } //sign_loc is the location of the sign bit before the shift static st32 shift_and_signextend(ut8 shift, ut8 sign_loc, ut32 val) { ut32 tmp = val << shift; if (tmp & (1 << (shift + sign_loc))) { tmp |= ~((1 << (shift + sign_loc + 1)) - 1); } return tmp; } static bool is_invalid_imm5_instr(RAsmLm32Instruction *instr) { return instr->value & RAsmLm32InstrImm5InvalidBitsMask; } static bool is_invalid_one_reg_instr(RAsmLm32Instruction *instr) { return instr->value & RAsmLm32InstrOneRegInvalidBitsMask; } static bool is_invalid_two_reg_instr(RAsmLm32Instruction *instr) { return instr->value & RAsmLm32InstrTwoRegsInvalidBitsMask; } static bool is_invalid_wcsr_instr(RAsmLm32Instruction *instr) { return instr->value & RAsmLm32InstrWcsrInvalidBitsMask; } //ret == b ra static bool is_pseudo_instr_ret(RAsmLm32Instruction *instr) { //"ra" == 0x1d return (instr->op == lm32_op_b) && (instr->src0_reg == 0x1d); } //mv rX, rY == or rX, rY, r0 static bool is_pseudo_instr_mv(RAsmLm32Instruction *instr) { return (instr->op == lm32_op_or) && !instr->src1_reg; } //mvhi rX, imm16 == orhi rX, r0, imm16 static bool is_pseudo_instr_mvhi(RAsmLm32Instruction *instr) { return (instr->op == lm32_op_orhi) && !instr->src0_reg; } //not rX, rY == xnor rX, rY, r0 static bool is_pseudo_instr_not(RAsmLm32Instruction *instr) { return (instr->op == lm32_op_xnor) && !instr->src1_reg; } //mvi rX, imm16 == addi rX, r0, imm16 static bool is_pseudo_instr_mvi(RAsmLm32Instruction *instr) { return (instr->op == lm32_op_addi) && !instr->src0_reg; } //nop == addi r0, r0, 0 static bool is_pseudo_instr_nop(RAsmLm32Instruction *instr) { return (instr->op == lm32_op_addi) && !instr->dest_reg && !instr->src0_reg && !instr->immediate; } //raise instruction is used for break, scall static bool is_pseudo_instr_raise(RAsmLm32Instruction *instr) { return instr->op == raise_instr; } static int r_asm_lm32_decode(RAsmLm32Instruction *instr) { instr->op = extract_opcode (instr->value); if (instr->op >= RAsmLm32OpcodeNumber) return -1; instr->op_decode = RAsmLm32OpcodeList[instr->op]; switch (instr->op_decode.type) { case reg_imm16_signextend: instr->dest_reg = extract_reg_v (instr->value); instr->src0_reg = extract_reg_u (instr->value); instr->immediate = shift_and_signextend (0, RAsmLm32Imm16SignBitPos, extract_imm16 (instr->value)); break; case reg_imm16_shift2_signextend: instr->dest_reg = extract_reg_v (instr->value); instr->src0_reg = extract_reg_u (instr->value); instr->immediate = shift_and_signextend (2, RAsmLm32Imm16SignBitPos, extract_imm16 (instr->value)); break; case reg_imm16_zeroextend: instr->dest_reg = extract_reg_v (instr->value); instr->src0_reg = extract_reg_u (instr->value); instr->immediate = extract_imm16 (instr->value); break; case reg_imm5: if (is_invalid_imm5_instr (instr)) return -1; instr->dest_reg = extract_reg_v (instr->value); instr->src0_reg = extract_reg_u (instr->value); instr->immediate = extract_imm5 (instr->value); break; case raise_instr: if (is_invalid_imm5_instr (instr)) return -1; //might be less bits used, but this shouldn't hurt //invalid parameters are catched in print_pseudo_instruction anyway instr->immediate = extract_imm5 (instr->value); break; case one_reg: if (is_invalid_one_reg_instr (instr)) return -1; instr->src0_reg = extract_reg_u (instr->value); break; case two_regs: if (is_invalid_two_reg_instr (instr)) return -1; instr->dest_reg = extract_reg_w (instr->value); instr->src0_reg = extract_reg_u (instr->value); break; case three_regs: instr->dest_reg = extract_reg_w (instr->value); instr->src0_reg = extract_reg_v (instr->value); instr->src1_reg = extract_reg_u (instr->value); break; case reg_csr: //wcsr if (is_invalid_wcsr_instr (instr)) return -1; instr->src0_reg = extract_reg_v (instr->value); instr->csr = extract_reg_u (instr->value); break; case csr_reg: //rcsr //bitmask is the same as the two register one if (is_invalid_two_reg_instr (instr)) return -1; instr->dest_reg = extract_reg_w (instr->value); instr->csr = extract_reg_u (instr->value); break; case imm26: instr->immediate = shift_and_signextend (2, RAsmLm32Imm26SignBitPos, extract_imm26 (instr->value)); break; case reserved: default: return -1; } //see if the instruction corresponds to a pseudo-instruction instr->pseudoInstruction = is_pseudo_instr_ret (instr) || is_pseudo_instr_mv (instr) || is_pseudo_instr_mvhi (instr) || is_pseudo_instr_not (instr) || is_pseudo_instr_mvi (instr) || is_pseudo_instr_nop (instr) || is_pseudo_instr_raise (instr); return 0; } static int write_reg_names_to_struct(RAsmLm32Instruction *instr) { switch (instr->op_decode.type) { case reg_imm16_signextend: case reg_imm16_shift2_signextend: case reg_imm16_zeroextend: case reg_imm5: case two_regs: if (reg_number_to_string (instr->dest_reg, instr->dest_reg_str)) return -1; if (reg_number_to_string (instr->src0_reg, instr->src0_reg_str)) return -1; break; case one_reg: if (reg_number_to_string (instr->src0_reg, instr->src0_reg_str)) return -1; break; case three_regs: if (reg_number_to_string (instr->dest_reg, instr->dest_reg_str)) return -1; if (reg_number_to_string (instr->src0_reg, instr->src0_reg_str)) return -1; if (reg_number_to_string (instr->src1_reg, instr->src1_reg_str)) return -1; break; case reg_csr: if (reg_number_to_string (instr->src0_reg, instr->src0_reg_str)) return -1; if (csr_number_to_string (instr->csr, instr->csr_reg_str)) return -1; break; case csr_reg: if (reg_number_to_string (instr->dest_reg, instr->dest_reg_str)) return -1; if (csr_number_to_string (instr->csr, instr->csr_reg_str)) return -1; break; case raise_instr: case imm26: break; default: return -1; } return 0; } static int print_pseudo_instruction(RAsmLm32Instruction *instr, char *str) { if (!instr->pseudoInstruction) return -1; switch (instr->op) { //ret == b ra case lm32_op_b: strcpy (str, "ret"); break; //mv rX, rY == or rX, rY, r0 case lm32_op_or: sprintf (str, "mv %s, %s", instr->dest_reg_str, instr->src0_reg_str); break; //mvhi rX, imm16 == orhi rX, r0, imm16 case lm32_op_orhi: sprintf (str, "mvhi %s, 0x%x", instr->dest_reg_str, instr->immediate); break; //not rX, rY == xnor rX, rY, r0 case lm32_op_xnor: sprintf (str, "not %s, %s", instr->dest_reg_str, instr->src0_reg_str); break; //mvi rX, imm16 == addi rX, r0, imm16 //nop == addi r0, r0, 0 case lm32_op_addi: if (is_pseudo_instr_nop (instr)) { //nop strcpy (str, "nop"); } else { //mvi sprintf (str, "mvi %s, 0x%x", instr->dest_reg_str, instr->immediate); } break; //break, scall case lm32_op_raise: switch (instr->immediate) { case 0x2: //break strcpy (str, "break"); break; case 0x7: //scall strcpy (str, "scall"); break; default: return -1; } break; default: return -1; } return 0; } static int r_asm_lm32_stringify(RAsmLm32Instruction *instr, char *str) { if (write_reg_names_to_struct (instr)) return -1; //pseudo instructions need some special handling if (instr->pseudoInstruction) { //return after printing the decoded pseudo instruction, so it doesn't get overwritten return print_pseudo_instruction (instr, str); } //get opcode string strcpy (str, instr->op_decode.name); //get parameters (registers, immediate) string switch (instr->op_decode.type) { case reg_imm16_signextend: sprintf (str, "%s %s, %s, 0x%x", instr->op_decode.name, instr->dest_reg_str, instr->src0_reg_str, instr->immediate); break; case reg_imm16_zeroextend: case reg_imm5: sprintf (str, "%s %s, %s, 0x%x", instr->op_decode.name, instr->dest_reg_str, instr->src0_reg_str, instr->immediate); break; case reg_imm16_shift2_signextend: //print the branch/call destination address sprintf (str, "%s %s, %s, 0x%x", instr->op_decode.name, instr->dest_reg_str, instr->src0_reg_str, instr->immediate + instr->addr); break; case one_reg: sprintf (str, "%s %s", instr->op_decode.name, instr->src0_reg_str); break; case two_regs: sprintf (str, "%s %s, %s", instr->op_decode.name, instr->dest_reg_str, instr->src0_reg_str); break; case three_regs: sprintf (str, "%s %s, %s, %s", instr->op_decode.name, instr->dest_reg_str, instr->src0_reg_str, instr->src1_reg_str); break; case reg_csr: sprintf (str, "%s %s, %s", instr->op_decode.name, instr->csr_reg_str, instr->src0_reg_str); break; case csr_reg: sprintf (str, "%s %s, %s", instr->op_decode.name, instr->dest_reg_str, instr->csr_reg_str); break; case imm26: //print the branch/call destination address sprintf (str, "%s 0x%x", instr->op_decode.name, instr->immediate + instr->addr); break; //case raise_instr: //unneeded; handled as pseudo instruction default: return -1; } return 0; } #if 0 static int r_asm_lm32_destringify(const char *string, RAsmLm32Instruction *instr) { //TODO return -1; } static int r_asm_lm32_encode(RAsmLm32Instruction *instr, ut32 *val) { //TODO return -1; } static int assemble(RAsm *a, RAsmOp *ao, const char *str) { //TODO return -1; } #endif static int disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) { RAsmLm32Instruction instr; instr.value = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; instr.addr = a->pc; if (r_asm_lm32_decode (&instr)) { strcpy (op->buf_asm, "invalid"); a->invhex = 1; return -1; } //op->buf_asm is 256 chars long, which is more than sufficient if (r_asm_lm32_stringify (&instr, op->buf_asm)) { strcpy (op->buf_asm, "invalid"); a->invhex = 1; return -1; } return 4; } RAsmPlugin r_asm_plugin_lm32 = { .name = "lm32", .arch = "lm32", .desc = "disassembly plugin for Lattice Micro 32 ISA", .license = "BSD", .bits = 32, .endian = R_SYS_ENDIAN_BIG, .disassemble = &disassemble, }; #ifndef CORELIB struct r_lib_struct_t radare_plugin = { .type = R_LIB_TYPE_ASM, .data = &r_asm_plugin_lm32, .version = R2_VERSION }; #endif