diff --git a/tools/arch/loongarch/include/asm/inst.h b/tools/arch/loongarch/include/asm/inst.h
index f0533fbc1e63..23d041cd76bf 100644
--- a/tools/arch/loongarch/include/asm/inst.h
+++ b/tools/arch/loongarch/include/asm/inst.h
@@ -56,6 +56,7 @@ enum reg2_op {
 	revbd_op	= 0x0f,
 	revh2w_op	= 0x10,
 	revhd_op	= 0x11,
+	ertn_op		= 0x1920e,
 };
 
 enum reg2i5_op {
diff --git a/tools/include/linux/bitops.h b/tools/include/linux/bitops.h
index f18683b95ea6..d81b52c070f5 100644
--- a/tools/include/linux/bitops.h
+++ b/tools/include/linux/bitops.h
@@ -87,4 +87,14 @@ static inline __u32 rol32(__u32 word, unsigned int shift)
 	return (word << shift) | (word >> ((-shift) & 31));
 }
 
+/**
+ * sign_extend64 - sign extend a 64-bit value using specified bit as sign-bit
+ * @value: value to sign extend
+ * @index: 0 based bit index (0<=index<64) to sign bit
+ */
+static __always_inline __s64 sign_extend64(__u64 value, int index)
+{
+	__u8 shift = 63 - index;
+	return (__s64)(value << shift) >> shift;
+}
 #endif
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 3f795f57e914..fc24efd6dba2 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
+#include <linux/bitops.h>
 #include <asm/inst.h>
 
 #include <objtool/check.h>
@@ -13,6 +14,8 @@
 #include <objtool/endianness.h>
 #include <arch/cfi_regs.h>
 
+#define to_cfi_reg(reg) (reg)
+
 int arch_ftrace_match(char *name)
 {
 	return !strcmp(name, "_mcount");
@@ -74,11 +77,18 @@ const char *arch_ret_insn(int len)
 	return (const char *)&ret;
 }
 
+#define ADD_OP(op) \
+	if (!(op = calloc(1, sizeof(*op)))) \
+		return -1; \
+	else for (*ops_list = op, ops_list = &op->next; op; op = NULL)
+
 int arch_decode_instruction(struct objtool_file *file, const struct section *sec,
 			    unsigned long offset, unsigned int maxlen,
 			    struct instruction *insn)
 {
+	struct stack_op **ops_list = &insn->stack_ops;
 	const struct elf *elf = file->elf;
+	struct stack_op *op = NULL;
 	union loongarch_instruction inst;
 
 	if (!is_loongarch(elf))
@@ -97,6 +107,132 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
 	if (inst.word == 0)
 		insn->type = INSN_NOP;
 
+	switch (inst.reg2i12_format.opcode) {
+	case addid_op:
+		if ((inst.reg2i12_format.rj == CFI_SP) || (inst.reg2i12_format.rd == CFI_SP)) {
+			/* addi.d reg1,reg2,imm */
+			insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+			ADD_OP(op) {
+				op->src.type = OP_SRC_ADD;
+				op->src.reg = to_cfi_reg(inst.reg2i12_format.rj);
+				op->src.offset = insn->immediate;
+				op->dest.type = OP_DEST_REG;
+				op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd);
+			}
+		}
+		break;
+	case std_op:
+		if (inst.reg2i12_format.rj == CFI_SP) {
+			/* st.d reg,sp,imm */
+			insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+			ADD_OP(op) {
+				op->src.type = OP_SRC_REG;
+				op->src.reg = to_cfi_reg(inst.reg2i12_format.rd);
+				op->dest.type = OP_DEST_REG_INDIRECT;
+				op->dest.reg = CFI_SP;
+				op->dest.offset = insn->immediate;
+			}
+		}
+		break;
+	case ldd_op:
+		if (inst.reg2i12_format.rj == CFI_SP) {
+			/* ld.d reg,sp,imm */
+			insn->immediate = sign_extend64(inst.reg2i12_format.immediate, 11);
+			ADD_OP(op) {
+				op->src.type = OP_SRC_REG_INDIRECT;
+				op->src.reg = CFI_SP;
+				op->src.offset = insn->immediate;
+				op->dest.type = OP_DEST_REG;
+				op->dest.reg = to_cfi_reg(inst.reg2i12_format.rd);
+			}
+		}
+		break;
+	case andi_op:
+		if (inst.reg2i12_format.immediate == 0 &&
+		     inst.reg2i12_format.rj == 0 &&
+		     inst.reg2i12_format.rd == 0)
+			/* nop */
+			insn->type = INSN_NOP;
+		break;
+	default:
+		switch (inst.reg2i16_format.opcode) {
+		case jirl_op:
+			if (inst.reg2i16_format.rj == CFI_RA &&
+			     inst.reg2i16_format.rd == 0) {
+				/* jr ra */
+				insn->type = INSN_RETURN;
+			} else if (inst.reg2i16_format.rd == CFI_RA) {
+				/* jalr reg */
+				insn->type = INSN_CALL_DYNAMIC;
+			} else if (inst.reg2i16_format.rd == 0) {
+				/* jr reg */
+				insn->type = INSN_JUMP_DYNAMIC;
+			} else if (!inst.reg2i16_format.immediate) {
+				/* jirl  */
+				insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
+				insn->type = INSN_JUMP_UNCONDITIONAL;
+			}
+			break;
+		case beq_op:
+		case bne_op:
+		case blt_op:
+		case bge_op:
+		case bltu_op:
+		case bgeu_op:
+			insn->immediate = sign_extend64(inst.reg2i16_format.immediate, 15);
+			insn->type = INSN_JUMP_CONDITIONAL;
+			break;
+		case beqz_op:
+		case bnez_op:
+			insn->immediate = sign_extend64(inst.reg1i21_format.immediate_h << 16 |
+					     inst.reg1i21_format.immediate_l, 20);
+			insn->type = INSN_JUMP_CONDITIONAL;
+			break;
+		case bl_op:
+			insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
+					     inst.reg0i26_format.immediate_l, 25);
+			insn->type = INSN_CALL;
+			break;
+		case b_op:
+			insn->immediate = sign_extend64(inst.reg0i26_format.immediate_h << 16 |
+					     inst.reg0i26_format.immediate_l, 25);
+			insn->type = INSN_JUMP_UNCONDITIONAL;
+			break;
+		default:
+			if (inst.reg2i14_format.opcode == stptrd_op &&
+				inst.reg2i14_format.rj == CFI_SP) {
+				/* stptr.d reg,sp,imm */
+				insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
+				ADD_OP(op) {
+					op->src.type = OP_SRC_REG;
+					op->src.reg = to_cfi_reg(inst.reg2i14_format.rd);
+					op->dest.type = OP_DEST_REG_INDIRECT;
+					op->dest.reg = CFI_SP;
+					op->dest.offset = insn->immediate;
+				}
+			} else if (inst.reg2i14_format.opcode == ldptrd_op &&
+				inst.reg2i14_format.rj == CFI_SP) {
+				/* ldptr.d reg,sp,imm */
+				insn->immediate = sign_extend64(inst.reg2i14_format.immediate, 13);
+				ADD_OP(op) {
+					op->src.type = OP_SRC_REG_INDIRECT;
+					op->src.reg = CFI_SP;
+					op->src.offset = insn->immediate;
+					op->dest.type = OP_DEST_REG;
+					op->dest.reg = to_cfi_reg(inst.reg2i14_format.rd);
+				}
+			} else if (inst.reg0i15_format.opcode == break_op) {
+				/* break */
+				insn->type = INSN_BUG;
+			} else if (inst.reg2_format.opcode == ertn_op) {
+				/* ertn */
+				insn->type = INSN_RETURN;
+			}
+			break;
+		}
+		break;
+	}
+
 	return 0;
 }
 
