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