diff --git a/configs/targets/loongarch64-linux-user.mak b/configs/targets/loongarch64-linux-user.mak
index dfded79dfa8531dfd0cb8928e47922810d4b7f41..ad3754dfdd0c39ebfa8c308326f744888e34c10e 100644
--- a/configs/targets/loongarch64-linux-user.mak
+++ b/configs/targets/loongarch64-linux-user.mak
@@ -4,3 +4,5 @@ TARGET_BASE_ARCH=loongarch
 TARGET_XML_FILES=gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml
 TARGET_SYSTBL=syscall.tbl
 TARGET_SYSTBL_ABI=common,64
+CONFIG_SEMIHOSTING=y
+CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c
index 73d7b6796a4261a77e73d8504b4ba9a722ae822d..426a772e6929e8e3c4b6e6f387b9c0ecf102b8c6 100644
--- a/linux-user/loongarch64/cpu_loop.c
+++ b/linux-user/loongarch64/cpu_loop.c
@@ -10,6 +10,7 @@
 #include "user-internals.h"
 #include "cpu_loop-common.h"
 #include "signal-common.h"
+#include "semihosting/common-semi.h"
 
 void cpu_loop(CPULoongArchState *env)
 {
@@ -84,6 +85,10 @@ void cpu_loop(CPULoongArchState *env)
         case EXCCODE_ASXD:
             env->CSR_EUEN |= R_CSR_EUEN_ASXE_MASK;
             break;
+        case EXCCODE_SEMIHOST:
+            do_common_semihosting(cs);
+            set_pc(env, env->pc + 4);
+            break;
 
         case EXCP_ATOMIC:
             cpu_exec_step_atomic(cs);
@@ -99,6 +104,9 @@ void cpu_loop(CPULoongArchState *env)
 
 void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
 {
+    CPUState *cpu = env_cpu(env);
+    TaskState *ts = get_task_state(cpu);
+    struct image_info *info = ts->info;
     int i;
 
     for (i = 0; i < 32; i++) {
@@ -106,4 +114,8 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs)
     }
     env->pc = regs->csr.era;
 
+    ts->stack_base = info->start_stack;
+    ts->heap_base = info->brk;
+    /* This will be filled in on the first SYS_HEAPINFO call.  */
+    ts->heap_limit = 0;
 }
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 67bc81b1499014739b002509a4e7a18afe977433..8ddbfa886881e5d7d02f45f8657d985efd863d96 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -103,7 +103,7 @@ struct TaskState {
     FPA11 fpa;
 # endif
 #endif
-#if defined(TARGET_ARM) || defined(TARGET_RISCV)
+#if defined(TARGET_ARM) || defined(TARGET_LOONGARCH) || defined(TARGET_RISCV)
     int swi_errno;
 #endif
 #if defined(TARGET_I386) && !defined(TARGET_X86_64)
@@ -121,7 +121,8 @@ struct TaskState {
 #ifdef TARGET_M68K
     abi_ulong tp_value;
 #endif
-#if defined(TARGET_ARM) || defined(TARGET_M68K) || defined(TARGET_RISCV)
+#if defined(TARGET_ARM) || defined(TARGET_LOONGARCH) || \
+    defined(TARGET_M68K) || defined(TARGET_RISCV)
     /* Extra fields for semihosted binaries.  */
     abi_ulong heap_base;
     abi_ulong heap_limit;
diff --git a/qemu-options.hx b/qemu-options.hx
index cc694d3b890c8ad9c5fad0a1f689781191d8e97a..0c4d599ab9e4e0e443aedc1b1c8733be1ab524ed 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -5011,10 +5011,11 @@ ERST
 DEF("semihosting", 0, QEMU_OPTION_semihosting,
     "-semihosting    semihosting mode\n",
     QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA |
-    QEMU_ARCH_MIPS | QEMU_ARCH_RISCV)
+    QEMU_ARCH_MIPS | QEMU_ARCH_RISCV | QEMU_ARCH_LOONGARCH)
 SRST
 ``-semihosting``
-    Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, RISC-V only).
+    Enable :ref:`Semihosting` mode (ARM, M68K, Xtensa, MIPS, RISC-V,
+    LoongArch only).
 
     .. warning::
       Note that this allows guest direct access to the host filesystem, so
@@ -5027,11 +5028,11 @@ DEF("semihosting-config", HAS_ARG, QEMU_OPTION_semihosting_config,
     "-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]\n" \
     "                semihosting configuration\n",
 QEMU_ARCH_ARM | QEMU_ARCH_M68K | QEMU_ARCH_XTENSA |
-QEMU_ARCH_MIPS | QEMU_ARCH_RISCV)
+QEMU_ARCH_MIPS | QEMU_ARCH_RISCV | QEMU_ARCH_LOONGARCH)
 SRST
 ``-semihosting-config [enable=on|off][,target=native|gdb|auto][,chardev=id][,userspace=on|off][,arg=str[,...]]``
-    Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V
-    only).
+    Enable and configure :ref:`Semihosting` (ARM, M68K, Xtensa, MIPS, RISC-V,
+    LoongArch only).
 
     .. warning::
       Note that this allows guest direct access to the host filesystem, so
diff --git a/target/loongarch/Kconfig b/target/loongarch/Kconfig
index 46b26b1a85715e779672bea93152a3c62c170fe2..ba8918d7045e9056107415612a7c756701923231 100644
--- a/target/loongarch/Kconfig
+++ b/target/loongarch/Kconfig
@@ -1,2 +1,3 @@
 config LOONGARCH64
     bool
+    select ARM_COMPATIBLE_SEMIHOSTING if TCG
diff --git a/target/loongarch/common-semi-target.h b/target/loongarch/common-semi-target.h
new file mode 100644
index 0000000000000000000000000000000000000000..066d2ea1af35861521beabfc4f31c496c9bc3e03
--- /dev/null
+++ b/target/loongarch/common-semi-target.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Target-specific parts of semihosting/arm-compat-semi.c.
+ *
+ * Copyright (c) 2005, 2007 CodeSourcery.
+ * Copyright (c) 2019, 2022 Linaro
+ * Copyright (c) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ */
+
+#ifndef TARGET_LOONGARCH_COMMON_SEMI_TARGET_H
+#define TARGET_LOONGARCH_COMMON_SEMI_TARGET_H
+
+static inline target_ulong common_semi_arg(CPUState *cs, int argno)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    return env->gpr[4 + argno];
+}
+
+static inline void common_semi_set_ret(CPUState *cs, target_ulong ret)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    env->gpr[4] = ret;
+}
+
+static inline bool common_semi_sys_exit_extended(CPUState *cs, int nr)
+{
+    return (nr == TARGET_SYS_EXIT_EXTENDED || sizeof(target_ulong) == 8);
+}
+
+static inline bool is_64bit_semihosting(CPUArchState *env)
+{
+    return !is_va32(env);
+}
+
+static inline target_ulong common_semi_stack_bottom(CPUState *cs)
+{
+    LoongArchCPU *cpu = LOONGARCH_CPU(cs);
+    CPULoongArchState *env = &cpu->env;
+    return env->gpr[3];
+}
+
+static inline bool common_semi_has_synccache(CPUArchState *env)
+{
+    return true;
+}
+
+#endif
diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c
index 57cc4f314bf707bea7f2c0eca5590841e68a2a97..9591f091f35eb001f255dc00b8a26276c9624a96 100644
--- a/target/loongarch/cpu.c
+++ b/target/loongarch/cpu.c
@@ -21,6 +21,7 @@
 #include "cpu-csr.h"
 #ifndef CONFIG_USER_ONLY
 #include "sysemu/reset.h"
+#include "semihosting/common-semi.h"
 #endif
 #include "vec.h"
 #ifdef CONFIG_KVM
@@ -71,6 +72,7 @@ static const struct TypeExcp excp_names[] = {
     {EXCCODE_BCE, "Bound Check Exception"},
     {EXCCODE_SXD, "128 bit vector instructions Disable exception"},
     {EXCCODE_ASXD, "256 bit vector instructions Disable exception"},
+    {EXCCODE_SEMIHOST, "Semihosting"},
     {EXCP_HLT, "EXCP_HLT"},
 };
 
@@ -179,6 +181,10 @@ static void loongarch_cpu_do_interrupt(CPUState *cs)
     }
 
     switch (cs->exception_index) {
+    case EXCCODE_SEMIHOST:
+        do_common_semihosting(cs);
+        set_pc(env, env->pc + 4);
+        return;
     case EXCCODE_DBP:
         env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, DCL, 1);
         env->CSR_DBG = FIELD_DP64(env->CSR_DBG, CSR_DBG, ECODE, 0xC);
diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h
index 86c86c6c958db1a215a3e76a27f379bd4a095fb6..2b466a046d552a8c62cb0c4f57c3ac50c64f21a7 100644
--- a/target/loongarch/cpu.h
+++ b/target/loongarch/cpu.h
@@ -115,6 +115,9 @@ FIELD(FCSR0, CAUSE, 24, 5)
 #define  EXCCODE_BTE                 EXCODE(21, 0)
 #define  EXCCODE_DBP                 EXCODE(26, 0) /* Reserved subcode used for debug */
 
+/* QEMU Internal Exceptions */
+#define  EXCCODE_SEMIHOST            EXCODE(63, 0)
+
 /* cpucfg[0] bits */
 FIELD(CPUCFG0, PRID, 0, 32)
 
diff --git a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
index 7e4ec93edb3c415268489014def22e85a0de6fb4..c9776081b8026b7aaafd49a0c55aab1779bc206b 100644
--- a/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
+++ b/target/loongarch/tcg/insn_trans/trans_privileged.c.inc
@@ -7,6 +7,28 @@
 
 #include "cpu-csr.h"
 
+static bool check_plv(DisasContext *ctx)
+{
+    if (ctx->plv == MMU_PLV_USER) {
+        generate_exception(ctx, EXCCODE_IPE);
+        return true;
+    }
+    return false;
+}
+
+static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
+{
+    if (semihosting_enabled(ctx->plv == MMU_PLV_USER) && a->imm == 0xab) {
+        generate_exception(ctx, EXCCODE_SEMIHOST);
+        return true;
+    }
+    if (check_plv(ctx)) {
+        return false;
+    }
+    generate_exception(ctx, EXCCODE_DBP);
+    return true;
+}
+
 #ifdef CONFIG_USER_ONLY
 
 #define GEN_FALSE_TRANS(name)   \
@@ -37,7 +59,6 @@ GEN_FALSE_TRANS(cacop)
 GEN_FALSE_TRANS(ldpte)
 GEN_FALSE_TRANS(lddir)
 GEN_FALSE_TRANS(ertn)
-GEN_FALSE_TRANS(dbcl)
 GEN_FALSE_TRANS(idle)
 
 #else
@@ -151,15 +172,6 @@ static const CSRInfo csr_info[] = {
     CSR_OFF(DSAVE),
 };
 
-static bool check_plv(DisasContext *ctx)
-{
-    if (ctx->plv == MMU_PLV_USER) {
-        generate_exception(ctx, EXCCODE_IPE);
-        return true;
-    }
-    return false;
-}
-
 static const CSRInfo *get_csr(unsigned csr_num)
 {
     const CSRInfo *csr;
@@ -475,15 +487,6 @@ static bool trans_ertn(DisasContext *ctx, arg_ertn *a)
     return true;
 }
 
-static bool trans_dbcl(DisasContext *ctx, arg_dbcl *a)
-{
-    if (check_plv(ctx)) {
-        return false;
-    }
-    generate_exception(ctx, EXCCODE_DBP);
-    return true;
-}
-
 static bool trans_idle(DisasContext *ctx, arg_idle *a)
 {
     if (check_plv(ctx)) {
diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c
index 1fca4afc731c048816618d87610a0cc0fe7579b1..9eaac98034fe7f3481007c3d69794a973d326f00 100644
--- a/target/loongarch/tcg/translate.c
+++ b/target/loongarch/tcg/translate.c
@@ -16,6 +16,7 @@
 #include "exec/log.h"
 #include "qemu/qemu-print.h"
 #include "fpu/softfloat.h"
+#include "semihosting/semihost.h"
 #include "translate.h"
 #include "internals.h"
 #include "vec.h"
diff --git a/tests/tcg/loongarch64/semicall.h b/tests/tcg/loongarch64/semicall.h
new file mode 100644
index 0000000000000000000000000000000000000000..0f29f7a52cee9b3273aba1c773e8f9d91fde7011
--- /dev/null
+++ b/tests/tcg/loongarch64/semicall.h
@@ -0,0 +1,19 @@
+/*
+ * Semihosting Tests - LoongArch Helper
+ *
+ * Copyright (c) 2024 Jiaxun Yang <jiaxun.yang@flygoat.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+uintptr_t __semi_call(uintptr_t type, uintptr_t arg0)
+{
+    register uintptr_t t asm("a0") = type;
+    register uintptr_t a0 asm("a1") = arg0;
+
+    asm("dbcl 0xab\n\t"
+        : "=r" (t)
+        : "r" (t), "r" (a0));
+
+    return t;
+}
