1 // SPDX-License-Identifier: GPL-2.0-or-later 2 #include <string.h> 3 #include <objtool/check.h> 4 #include <objtool/warn.h> 5 #include <asm/inst.h> 6 #include <asm/orc_types.h> 7 #include <linux/objtool_types.h> 8 9 #ifndef EM_LOONGARCH 10 #define EM_LOONGARCH 258 11 #endif 12 13 int arch_ftrace_match(char *name) 14 { 15 return !strcmp(name, "_mcount"); 16 } 17 18 unsigned long arch_jump_destination(struct instruction *insn) 19 { 20 return insn->offset + (insn->immediate << 2); 21 } 22 23 unsigned long arch_dest_reloc_offset(int addend) 24 { 25 return addend; 26 } 27 28 bool arch_pc_relative_reloc(struct reloc *reloc) 29 { 30 return false; 31 } 32 33 bool arch_callee_saved_reg(unsigned char reg) 34 { 35 switch (reg) { 36 case CFI_RA: 37 case CFI_FP: 38 case CFI_S0 ... CFI_S8: 39 return true; 40 default: 41 return false; 42 } 43 } 44 45 int arch_decode_hint_reg(u8 sp_reg, int *base) 46 { 47 switch (sp_reg) { 48 case ORC_REG_UNDEFINED: 49 *base = CFI_UNDEFINED; 50 break; 51 case ORC_REG_SP: 52 *base = CFI_SP; 53 break; 54 case ORC_REG_FP: 55 *base = CFI_FP; 56 break; 57 default: 58 return -1; 59 } 60 61 return 0; 62 } 63 64 static bool is_loongarch(const struct elf *elf) 65 { 66 if (elf->ehdr.e_machine == EM_LOONGARCH) 67 return true; 68 69 WARN("unexpected ELF machine type %d", elf->ehdr.e_machine); 70 return false; 71 } 72 73 #define ADD_OP(op) \ 74 if (!(op = calloc(1, sizeof(*op)))) \ 75 return -1; \ 76 else for (*ops_list = op, ops_list = &op->next; op; op = NULL) 77 78 static bool decode_insn_reg0i26_fomat(union loongarch_instruction inst, 79 struct instruction *insn) 80 { 81 switch (inst.reg0i26_format.opcode) { 82 case b_op: 83 insn->type = INSN_JUMP_UNCONDITIONAL; 84 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 85 inst.reg0i26_format.immediate_l, 25); 86 break; 87 case bl_op: 88 insn->type = INSN_CALL; 89 insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 | 90 inst.reg0i26_format.immediate_l, 25); 91 break; 92 default: 93 return false; 94 } 95 96 return true; 97 } 98 99 static bool decode_insn_reg1i21_fomat(union loongarch_instruction inst, 100 struct instruction *insn) 101 { 102 switch (inst.reg1i21_format.opcode) { 103 case beqz_op: 104 case bnez_op: 105 case bceqz_op: 106 insn->type = INSN_JUMP_CONDITIONAL; 107 insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 | 108 inst.reg1i21_format.immediate_l, 20); 109 break; 110 default: 111 return false; 112 } 113 114 return true; 115 } 116 117 static bool decode_insn_reg2i12_fomat(union loongarch_instruction inst, 118 struct instruction *insn, 119 struct stack_op **ops_list, 120 struct stack_op *op) 121 { 122 switch (inst.reg2i12_format.opcode) { 123 case addid_op: 124 if ((inst.reg2i12_format.rd == CFI_SP) || (inst.reg2i12_format.rj == CFI_SP)) { 125 /* addi.d sp,sp,si12 or addi.d fp,sp,si12 */ 126 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 127 ADD_OP(op) { 128 op->src.type = OP_SRC_ADD; 129 op->src.reg = inst.reg2i12_format.rj; 130 op->src.offset = insn->immediate; 131 op->dest.type = OP_DEST_REG; 132 op->dest.reg = inst.reg2i12_format.rd; 133 } 134 } 135 break; 136 case ldd_op: 137 if (inst.reg2i12_format.rj == CFI_SP) { 138 /* ld.d rd,sp,si12 */ 139 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 140 ADD_OP(op) { 141 op->src.type = OP_SRC_REG_INDIRECT; 142 op->src.reg = CFI_SP; 143 op->src.offset = insn->immediate; 144 op->dest.type = OP_DEST_REG; 145 op->dest.reg = inst.reg2i12_format.rd; 146 } 147 } 148 break; 149 case std_op: 150 if (inst.reg2i12_format.rj == CFI_SP) { 151 /* st.d rd,sp,si12 */ 152 insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11); 153 ADD_OP(op) { 154 op->src.type = OP_SRC_REG; 155 op->src.reg = inst.reg2i12_format.rd; 156 op->dest.type = OP_DEST_REG_INDIRECT; 157 op->dest.reg = CFI_SP; 158 op->dest.offset = insn->immediate; 159 } 160 } 161 break; 162 case andi_op: 163 if (inst.reg2i12_format.rd == 0 && 164 inst.reg2i12_format.rj == 0 && 165 inst.reg2i12_format.immediate == 0) 166 /* andi r0,r0,0 */ 167 insn->type = INSN_NOP; 168 break; 169 default: 170 return false; 171 } 172 173 return true; 174 } 175 176 static bool decode_insn_reg2i14_fomat(union loongarch_instruction inst, 177 struct instruction *insn, 178 struct stack_op **ops_list, 179 struct stack_op *op) 180 { 181 switch (inst.reg2i14_format.opcode) { 182 case ldptrd_op: 183 if (inst.reg2i14_format.rj == CFI_SP) { 184 /* ldptr.d rd,sp,si14 */ 185 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 186 ADD_OP(op) { 187 op->src.type = OP_SRC_REG_INDIRECT; 188 op->src.reg = CFI_SP; 189 op->src.offset = insn->immediate; 190 op->dest.type = OP_DEST_REG; 191 op->dest.reg = inst.reg2i14_format.rd; 192 } 193 } 194 break; 195 case stptrd_op: 196 if (inst.reg2i14_format.rj == CFI_SP) { 197 /* stptr.d ra,sp,0 */ 198 if (inst.reg2i14_format.rd == LOONGARCH_GPR_RA && 199 inst.reg2i14_format.immediate == 0) 200 break; 201 202 /* stptr.d rd,sp,si14 */ 203 insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13); 204 ADD_OP(op) { 205 op->src.type = OP_SRC_REG; 206 op->src.reg = inst.reg2i14_format.rd; 207 op->dest.type = OP_DEST_REG_INDIRECT; 208 op->dest.reg = CFI_SP; 209 op->dest.offset = insn->immediate; 210 } 211 } 212 break; 213 default: 214 return false; 215 } 216 217 return true; 218 } 219 220 static bool decode_insn_reg2i16_fomat(union loongarch_instruction inst, 221 struct instruction *insn) 222 { 223 switch (inst.reg2i16_format.opcode) { 224 case jirl_op: 225 if (inst.reg2i16_format.rd == 0 && 226 inst.reg2i16_format.rj == CFI_RA && 227 inst.reg2i16_format.immediate == 0) { 228 /* jirl r0,ra,0 */ 229 insn->type = INSN_RETURN; 230 } else if (inst.reg2i16_format.rd == CFI_RA) { 231 /* jirl ra,rj,offs16 */ 232 insn->type = INSN_CALL_DYNAMIC; 233 } else if (inst.reg2i16_format.rd == CFI_A0 && 234 inst.reg2i16_format.immediate == 0) { 235 /* 236 * jirl a0,t0,0 237 * this is a special case in loongarch_suspend_enter, 238 * just treat it as a call instruction. 239 */ 240 insn->type = INSN_CALL_DYNAMIC; 241 } else if (inst.reg2i16_format.rd == 0 && 242 inst.reg2i16_format.immediate == 0) { 243 /* jirl r0,rj,0 */ 244 insn->type = INSN_JUMP_DYNAMIC; 245 } else if (inst.reg2i16_format.rd == 0 && 246 inst.reg2i16_format.immediate != 0) { 247 /* 248 * jirl r0,t0,12 249 * this is a rare case in JUMP_VIRT_ADDR, 250 * just ignore it due to it is harmless for tracing. 251 */ 252 break; 253 } else { 254 /* jirl rd,rj,offs16 */ 255 insn->type = INSN_JUMP_UNCONDITIONAL; 256 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 257 } 258 break; 259 case beq_op: 260 case bne_op: 261 case blt_op: 262 case bge_op: 263 case bltu_op: 264 case bgeu_op: 265 insn->type = INSN_JUMP_CONDITIONAL; 266 insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15); 267 break; 268 default: 269 return false; 270 } 271 272 return true; 273 } 274 275 int arch_decode_instruction(struct objtool_file *file, const struct section *sec, 276 unsigned long offset, unsigned int maxlen, 277 struct instruction *insn) 278 { 279 struct stack_op **ops_list = &insn->stack_ops; 280 const struct elf *elf = file->elf; 281 struct stack_op *op = NULL; 282 union loongarch_instruction inst; 283 284 if (!is_loongarch(elf)) 285 return -1; 286 287 if (maxlen < LOONGARCH_INSN_SIZE) 288 return 0; 289 290 insn->len = LOONGARCH_INSN_SIZE; 291 insn->type = INSN_OTHER; 292 insn->immediate = 0; 293 294 inst = *(union loongarch_instruction *)(sec->data->d_buf + offset); 295 296 if (decode_insn_reg0i26_fomat(inst, insn)) 297 return 0; 298 if (decode_insn_reg1i21_fomat(inst, insn)) 299 return 0; 300 if (decode_insn_reg2i12_fomat(inst, insn, ops_list, op)) 301 return 0; 302 if (decode_insn_reg2i14_fomat(inst, insn, ops_list, op)) 303 return 0; 304 if (decode_insn_reg2i16_fomat(inst, insn)) 305 return 0; 306 307 if (inst.word == 0) 308 insn->type = INSN_NOP; 309 else if (inst.reg0i15_format.opcode == break_op) { 310 /* break */ 311 insn->type = INSN_BUG; 312 } else if (inst.reg2_format.opcode == ertn_op) { 313 /* ertn */ 314 insn->type = INSN_RETURN; 315 } 316 317 return 0; 318 } 319 320 const char *arch_nop_insn(int len) 321 { 322 static u32 nop; 323 324 if (len != LOONGARCH_INSN_SIZE) 325 WARN("invalid NOP size: %d\n", len); 326 327 nop = LOONGARCH_INSN_NOP; 328 329 return (const char *)&nop; 330 } 331 332 const char *arch_ret_insn(int len) 333 { 334 static u32 ret; 335 336 if (len != LOONGARCH_INSN_SIZE) 337 WARN("invalid RET size: %d\n", len); 338 339 emit_jirl((union loongarch_instruction *)&ret, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); 340 341 return (const char *)&ret; 342 } 343 344 void arch_initial_func_cfi_state(struct cfi_init_state *state) 345 { 346 int i; 347 348 for (i = 0; i < CFI_NUM_REGS; i++) { 349 state->regs[i].base = CFI_UNDEFINED; 350 state->regs[i].offset = 0; 351 } 352 353 /* initial CFA (call frame address) */ 354 state->cfa.base = CFI_SP; 355 state->cfa.offset = 0; 356 } 357