public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 1/2] gdb: LoongArch GDB Port.
@ 2021-11-12  3:18 liuzhensong
  2021-11-12  3:18 ` [PATCH v2 2/2] gdbserver: LoongArch GDBServer Port liuzhensong
  0 siblings, 1 reply; 2+ messages in thread
From: liuzhensong @ 2021-11-12  3:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: xuchenghua, liuzhensong

Hi all:

This is a series patch set to support LoongArch.

LoongArch has the typical characteristics of RISC. LoongArch
instructions are of fixed size and have regular instruction formats.
Most of the instructions have two source operands and one destination
operand. LoongArch is a load-store architecture; this means only the
load/store instructions can access memory the operands of the other
instructions are within the processor core or the immediate number in
the instruction opcode.
LoongArch is divided into two versions, the 32-bit version (LA32) and
the 64-bit version (LA64). LA64 applications are “application-level
backward binary compatibility” with LA32 applications. That means LA32
applications can run directly on the machine compatible with LA64, but
the behavior of system software (such as the kernel) on the machine
compatible with LA32 is not guaranteed to be the same as on the
machine compatible with LA64.
LoongArch is composed of a basic part (Loongson Base) and an expanded
part, as shown in the figure. The expansion part includes Loongson
Binary Translation (LBT), Loongson VirtualiZation (LVZ), Loongson SIMD
EXtension (LSX), and Loongson Advanced SIMD EXtension(LASX).

The documents are on https://github.com/loongson/LoongArch-Documentation 
The ELF ABI Documents are on https://github.com/loongson/LoongArch-Documentation/blob/main/LoongArch-ELF-ABI-EN.adoc 
The GCC code based on GCC trunk: https://github.com/loongson/gcc/tree/loongarch_upstream 
The GLIBC code based on GLIBC trunk: https://github.com/loongson/glibc/commits/loongarch_2_34_for_upstream 
We will send those patches for review soon.
Thanks.


gdb/Makefile.in
gdb/arch/loongarch-linux-nat.c
gdb/arch/loongarch-linux-nat.h
gdb/arch/loongarch.c
gdb/arch/loongarch.h
gdb/configure.host
gdb/configure.nat
gdb/configure.tgt
gdb/corelow.c
gdb/features/Makefile
gdb/features/loongarch/base32.c
gdb/features/loongarch/base32.xml
gdb/features/loongarch/base64.c
gdb/features/loongarch/base64.xml
gdb/features/loongarch/fpu32.c
gdb/features/loongarch/fpu32.xml
gdb/features/loongarch/fpu64.c
gdb/features/loongarch/fpu64.xml
gdb/features/loongarch/lasx.c
gdb/features/loongarch/lasx.xml
gdb/features/loongarch/lbt32.c
gdb/features/loongarch/lbt32.xml
gdb/features/loongarch/lbt64.c
gdb/features/loongarch/lbt64.xml
gdb/features/loongarch/lsx.c
gdb/features/loongarch/lsx.xml
gdb/loongarch-linux-nat.c
gdb/loongarch-linux-tdep.c
gdb/loongarch-linux-tdep.h
gdb/loongarch-tdep.c
gdb/loongarch-tdep.h
gdb/nat/loongarch-linux-watch.c
gdb/nat/loongarch-linux-watch.h
gdb/remote.c
gdb/target.h
gdb/testsuite/gdb.base/dump.exp
gdb/testsuite/gdb.base/float.exp
gdb/testsuite/gdb.trace/entry-values.exp
gdb/testsuite/gdb.xml/tdesc-regs.exp
---
 gdb/Makefile.in                          |   13 +
 gdb/arch/loongarch-linux-nat.c           |   94 ++
 gdb/arch/loongarch-linux-nat.h           |   35 +
 gdb/arch/loongarch.c                     |   84 +
 gdb/arch/loongarch.h                     |   37 +
 gdb/configure.host                       |    3 +
 gdb/configure.nat                        |    4 +
 gdb/configure.tgt                        |    8 +
 gdb/corelow.c                            |   39 +
 gdb/features/Makefile                    |   10 +
 gdb/features/loongarch/base32.c          |   47 +
 gdb/features/loongarch/base32.xml        |   45 +
 gdb/features/loongarch/base64.c          |   47 +
 gdb/features/loongarch/base64.xml        |   45 +
 gdb/features/loongarch/fpu32.c           |   54 +
 gdb/features/loongarch/fpu32.xml         |   53 +
 gdb/features/loongarch/fpu64.c           |   62 +
 gdb/features/loongarch/fpu64.xml         |   58 +
 gdb/features/loongarch/lasx.c            |   80 +
 gdb/features/loongarch/lasx.xml          |   59 +
 gdb/features/loongarch/lbt32.c           |   19 +
 gdb/features/loongarch/lbt32.xml         |   17 +
 gdb/features/loongarch/lbt64.c           |   19 +
 gdb/features/loongarch/lbt64.xml         |   17 +
 gdb/features/loongarch/lsx.c             |   80 +
 gdb/features/loongarch/lsx.xml           |   59 +
 gdb/loongarch-linux-nat.c                |  877 ++++++++++
 gdb/loongarch-linux-tdep.c               |  677 ++++++++
 gdb/loongarch-linux-tdep.h               |   48 +
 gdb/loongarch-tdep.c                     | 1864 ++++++++++++++++++++++
 gdb/loongarch-tdep.h                     |   55 +
 gdb/nat/loongarch-linux-watch.c          |  330 ++++
 gdb/nat/loongarch-linux-watch.h          |  132 ++
 gdb/remote.c                             |   25 +
 gdb/target.h                             |    3 +
 gdb/testsuite/gdb.base/dump.exp          |    4 +
 gdb/testsuite/gdb.base/float.exp         |    2 +
 gdb/testsuite/gdb.trace/entry-values.exp |    2 +
 gdb/testsuite/gdb.xml/tdesc-regs.exp     |    5 +
 39 files changed, 5112 insertions(+)
 create mode 100644 gdb/arch/loongarch-linux-nat.c
 create mode 100644 gdb/arch/loongarch-linux-nat.h
 create mode 100644 gdb/arch/loongarch.c
 create mode 100644 gdb/arch/loongarch.h
 create mode 100644 gdb/features/loongarch/base32.c
 create mode 100644 gdb/features/loongarch/base32.xml
 create mode 100644 gdb/features/loongarch/base64.c
 create mode 100644 gdb/features/loongarch/base64.xml
 create mode 100644 gdb/features/loongarch/fpu32.c
 create mode 100644 gdb/features/loongarch/fpu32.xml
 create mode 100644 gdb/features/loongarch/fpu64.c
 create mode 100644 gdb/features/loongarch/fpu64.xml
 create mode 100644 gdb/features/loongarch/lasx.c
 create mode 100644 gdb/features/loongarch/lasx.xml
 create mode 100644 gdb/features/loongarch/lbt32.c
 create mode 100644 gdb/features/loongarch/lbt32.xml
 create mode 100644 gdb/features/loongarch/lbt64.c
 create mode 100644 gdb/features/loongarch/lbt64.xml
 create mode 100644 gdb/features/loongarch/lsx.c
 create mode 100644 gdb/features/loongarch/lsx.xml
 create mode 100644 gdb/loongarch-linux-nat.c
 create mode 100644 gdb/loongarch-linux-tdep.c
 create mode 100644 gdb/loongarch-linux-tdep.h
 create mode 100644 gdb/loongarch-tdep.c
 create mode 100644 gdb/loongarch-tdep.h
 create mode 100644 gdb/nat/loongarch-linux-watch.c
 create mode 100644 gdb/nat/loongarch-linux-watch.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 4201f65e68..df86e3d212 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -732,6 +732,8 @@ ALL_TARGET_OBS = \
 	arch/i386.o \
 	arch/ppc-linux-common.o \
 	arch/riscv.o \
+	arch/loongarch.o \
+	arch/loongarch-linux-nat.o \
 	arm-bsd-tdep.o \
 	arm-fbsd-tdep.o \
 	arm-linux-tdep.o \
@@ -780,6 +782,9 @@ ALL_TARGET_OBS = \
 	linux-record.o \
 	linux-tdep.o \
 	lm32-tdep.o \
+	loongarch-tdep.o \
+	loongarch-linux-tdep.o \
+	loongarch-linux-nat.o \
 	m32c-tdep.o \
 	m32r-linux-tdep.o \
 	m32r-tdep.o \
@@ -1356,6 +1361,8 @@ HFILES_NO_SRCDIR = \
 	linux-record.h \
 	linux-tdep.h \
 	location.h \
+	loongarch-tdep.h \
+	loongarch-linux-tdep.h \
 	m2-lang.h \
 	m32r-tdep.h \
 	m68k-tdep.h \
@@ -1490,6 +1497,8 @@ HFILES_NO_SRCDIR = \
 	arch/arc.h \
 	arch/arm.h \
 	arch/i386.h \
+	arch/loongarch.h \
+	arch/loongarch-linux-nat.h \
 	arch/ppc-linux-common.h \
 	arch/ppc-linux-tdesc.h \
 	arch/riscv.h \
@@ -1535,6 +1544,7 @@ HFILES_NO_SRCDIR = \
 	nat/linux-personality.h \
 	nat/linux-ptrace.h \
 	nat/linux-waitpid.h \
+	nat/loongarch-linux-watch.h \
 	nat/mips-linux-watch.h \
 	nat/ppc-linux.h \
 	nat/x86-cpuid.h \
@@ -2223,6 +2233,9 @@ ALLDEPFILES = \
 	linux-record.c \
 	linux-tdep.c \
 	lm32-tdep.c \
+	loongarch-tdep.c \
+	loongarch-linux-tdep.c \
+	loongarch-linux-nat.c \
 	m32r-linux-nat.c \
 	m32r-linux-tdep.c \
 	m32r-tdep.c \
diff --git a/gdb/arch/loongarch-linux-nat.c b/gdb/arch/loongarch-linux-nat.c
new file mode 100644
index 0000000000..70bf74260a
--- /dev/null
+++ b/gdb/arch/loongarch-linux-nat.c
@@ -0,0 +1,94 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* For external ptrace. */
+#ifdef GDBSERVER
+#include "server.h"
+#include "nat/gdb_ptrace.h"
+#else
+#include "defs.h"
+#include "nat/gdb_ptrace.h"
+#endif
+
+#include "arch/loongarch.h"
+#include "arch/loongarch-linux-nat.h"
+#include "loongarch-linux-tdep.h"
+#include "elf/common.h"
+#include <sys/uio.h>
+
+static uint32_t
+loongarch_cpucfg_may_ptrace (uint64_t rj, int tid)
+{
+  char t_buf[rj * 4 + 4];
+  struct iovec iovec = { .iov_base = &t_buf, .iov_len = sizeof (t_buf) };
+  if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_CPUCFG, &iovec) < 0)
+    ((uint32_t *) t_buf)[rj] = loongarch_cpucfg (rj);
+  return ((uint32_t *) t_buf)[rj];
+}
+
+struct target_desc *
+loongarch_linux_read_description_runtime (int tid)
+{
+  int rlen, fpu32, fpu64, lbt, lsx, lasx;
+
+  uint32_t cpucfg1 = loongarch_cpucfg_may_ptrace (1, tid);
+  rlen = cpucfg1 & 0x2 /* LA64 */ ? 64 : 32;
+
+  uint32_t cpucfg2 = loongarch_cpucfg_may_ptrace (2, tid);
+  fpu32 = 0, fpu64 = 0;
+  if (cpucfg2 & 0x4 /* FP_DP */)
+    fpu64 = 1;
+  else if (cpucfg2 & 0x2 /* FP_SP */)
+    fpu32 = 1;
+  if (fpu32 || fpu64)
+    {
+      loongarch_elf_fpregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec) < 0)
+	fpu32 = 0, fpu64 = 0;
+    }
+
+  lbt = 0;
+  if (cpucfg2 & 0x1c0000 /* LBT_X86 || LBT_ARM || LBT_MIPS */)
+    {
+      loongarch_elf_lbtregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, &iovec) == 0)
+	lbt = 1;
+    }
+
+  lsx = 0;
+  if (cpucfg2 & 0x40 /* LSX */)
+    {
+      loongarch_elf_lsxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, &iovec) == 0)
+	lsx = 1;
+    }
+
+  lasx = 0;
+  if (cpucfg2 & 0x80 /* LASX */)
+    {
+      loongarch_elf_lasxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, &iovec) == 0)
+	lasx = 1;
+    }
+
+  return loongarch_create_target_description (rlen, fpu32, fpu64, lbt, lsx,
+					      lasx);
+}
diff --git a/gdb/arch/loongarch-linux-nat.h b/gdb/arch/loongarch-linux-nat.h
new file mode 100644
index 0000000000..a9cd453734
--- /dev/null
+++ b/gdb/arch/loongarch-linux-nat.h
@@ -0,0 +1,35 @@
+/*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_LINUX_NAT_H
+#define LOONGARCH_LINUX_NAT_H
+#include <stdint.h>
+
+static inline uint32_t
+loongarch_cpucfg (uint64_t rj)
+{
+  uint32_t ret;
+  asm ("cpucfg %0,%1" : "=r"(ret) : "r"(rj));
+  return ret;
+}
+
+struct target_desc;
+
+extern struct target_desc *loongarch_linux_read_description_runtime (int tid);
+
+#endif
diff --git a/gdb/arch/loongarch.c b/gdb/arch/loongarch.c
new file mode 100644
index 0000000000..007c638cdb
--- /dev/null
+++ b/gdb/arch/loongarch.c
@@ -0,0 +1,84 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/common-regcache.h"
+#include "arch/loongarch.h"
+
+const char *loongarch_expedite_regs[] = { "r3", "pc", NULL };
+
+unsigned int loongarch_debug = 0;
+
+#include <../features/loongarch/base32.c>
+#include <../features/loongarch/base64.c>
+#include <../features/loongarch/fpu32.c>
+#include <../features/loongarch/fpu64.c>
+#include <../features/loongarch/lbt32.c>
+#include <../features/loongarch/lbt64.c>
+#include <../features/loongarch/lsx.c>
+#include <../features/loongarch/lasx.c>
+
+target_desc *
+loongarch_create_target_description (int rlen, int fpu32, int fpu64, int lbt,
+				     int lsx, int lasx)
+{
+  gdb_assert (rlen == 32 || rlen == 64);
+
+  target_desc_up tdesc = allocate_target_description ();
+
+  set_tdesc_architecture (tdesc.get (),
+			  rlen == 64 ? "loongarch64" : "loongarch32");
+
+  int regnum = 0;
+
+  if (rlen == 64)
+    regnum = create_feature_loongarch_base64 (tdesc.get (), regnum);
+  else if (rlen == 32)
+    regnum = create_feature_loongarch_base32 (tdesc.get (), regnum);
+  else
+    gdb_assert_not_reached ("rlen unknown");
+
+  if (fpu32)
+    regnum = create_feature_loongarch_fpu32 (tdesc.get (), regnum);
+  else if (fpu64)
+    regnum = create_feature_loongarch_fpu64 (tdesc.get (), regnum);
+
+  if (lbt && rlen == 32)
+    regnum = create_feature_loongarch_lbt32 (tdesc.get (), regnum);
+  else if (lbt && rlen == 64)
+    regnum = create_feature_loongarch_lbt64 (tdesc.get (), regnum);
+
+  if (lsx)
+    regnum = create_feature_loongarch_lsx (tdesc.get (), regnum);
+
+  if (lasx)
+    regnum = create_feature_loongarch_lasx (tdesc.get (), regnum);
+
+  return tdesc.release ();
+}
+
+target_desc *
+loongarch_get_base_target_description (int rlen)
+{
+  if (rlen == 64)
+    return loongarch_create_target_description (64, 0, 0, 0, 0, 0);
+  else if (rlen == 32)
+    return loongarch_create_target_description (32, 0, 0, 0, 0, 0);
+  else
+    gdb_assert_not_reached ("rlen unknown");
+  return NULL;
+}
diff --git a/gdb/arch/loongarch.h b/gdb/arch/loongarch.h
new file mode 100644
index 0000000000..9d9f65b3e7
--- /dev/null
+++ b/gdb/arch/loongarch.h
@@ -0,0 +1,37 @@
+/*
+   Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_LOONGARCH_H
+#define ARCH_LOONGARCH_H
+
+#include "elf/loongarch.h"
+#include "opcode/loongarch.h"
+
+extern unsigned int loongarch_debug;
+
+struct target_desc;
+
+extern const char *loongarch_expedite_regs[];
+
+extern struct target_desc *loongarch_get_base_target_description (int rlen);
+
+extern struct target_desc *
+loongarch_create_target_description (int rlen, int fpu32, int fpu64, int lbt,
+				     int lsx, int lasx);
+
+#endif
diff --git a/gdb/configure.host b/gdb/configure.host
index 1fc83601e0..20fa4ccd81 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -58,6 +58,7 @@ arc*)			gdb_host_cpu=arc ;;
 arm*)			gdb_host_cpu=arm ;;
 hppa*)			gdb_host_cpu=pa ;;
 i[34567]86*)		gdb_host_cpu=i386 ;;
+loongarch*)		gdb_host_cpu=loongarch ;;
 m68*)			gdb_host_cpu=m68k ;;
 mips*)			gdb_host_cpu=mips ;;
 powerpc* | rs6000)	gdb_host_cpu=powerpc ;;
@@ -117,6 +118,8 @@ i[34567]86-*-cygwin*)	gdb_host=cygwin ;;
 
 ia64-*-linux*)		gdb_host=linux ;;
 
+loongarch*-linux*)		gdb_host=linux ;;
+
 m68*-*-linux*)		gdb_host=linux ;;
 m68*-*-netbsd* | m68*-*-knetbsd*-gnu)
 			gdb_host=nbsdelf ;;
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 655c75dd1a..150e02751d 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -258,6 +258,10 @@ case ${gdb_host} in
 		# Host: Intel IA-64 running GNU/Linux
 		NATDEPFILES="${NATDEPFILES} ia64-linux-nat.o"
 		;;
+	    loongarch)
+		# Host: LoongArch, running GNU/Linux.
+		NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o arch/loongarch-linux-nat.o linux-nat-trad.o nat/loongarch-linux-watch.o"
+		;;
 	    m32r)
 		# Host: M32R based machine running GNU/Linux
 		NATDEPFILES="${NATDEPFILES} m32r-linux-nat.o"
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 56cdfef937..e58ed6eaa5 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -103,6 +103,9 @@ xtensa*)
 	cpu_obs="xtensa-tdep.o xtensa-config.o solib-svr4.o"
 	;;
 
+loongarch*)
+	cpu_obs="arch/loongarch.o";;
+
 esac
 
 # 2. Get the objects per os in $TARG.
@@ -341,6 +344,11 @@ lm32-*-*)
 	gdb_target_obs="lm32-tdep.o" 
 	;;
 
+loongarch*-linux*)
+	gdb_target_obs="loongarch-tdep.o loongarch-linux-tdep.o glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o"
+	build_gdbserver=yes
+	;;
+
 m32c-*-*)
 	# Target: Renesas M32C family
 	gdb_target_obs="m32c-tdep.o"
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 5f48d96aa1..53320587d7 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -966,6 +966,45 @@ core_target::xfer_partial (enum target_object object, const char *annex,
 	}
       /* FALL THROUGH */
 
+    case TARGET_OBJECT_LARCH:
+      if (strcmp (annex, "cpucfg") == 0)
+	{
+	  struct bfd_section *section;
+	  bfd_size_type size;
+
+	  if (writebuf)
+	    return TARGET_XFER_E_IO;
+	  section = bfd_get_section_by_name (core_bfd, ".reg-loongarch-cpucfg");
+	  if (section == NULL)
+	    return TARGET_XFER_E_IO;
+
+	  size = bfd_section_size (section);
+	  if (offset >= size)
+	    return TARGET_XFER_EOF;
+	  size -= offset;
+	  if (size > len)
+	    size = len;
+
+	  if (size == 0)
+	    return TARGET_XFER_EOF;
+	  if (!bfd_get_section_contents (core_bfd, section, readbuf,
+					 (file_ptr) offset, size))
+	    {
+	      warning (_("Couldn't read .reg-cpucfg section in core file."));
+	      return TARGET_XFER_E_IO;
+	    }
+
+	  *xfered_len = (ULONGEST) size;
+	  return TARGET_XFER_OK;
+	}
+
+      if (strcmp (annex, "csr") == 0)
+	{
+	  return TARGET_XFER_UNAVAILABLE;
+	}
+
+      return TARGET_XFER_E_IO;
+
     case TARGET_OBJECT_SIGNAL_INFO:
       if (readbuf)
 	{
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index e478bf838b..ed8b274b61 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -68,6 +68,7 @@ WHICH = mips-linux mips-dsp-linux \
 	tic6x-c64xp-linux tic6x-c64x-linux tic6x-c62x-linux
 
 # Record which registers should be sent to GDB by default after stop.
+loongarch-expedite = r3,pc
 mips-expedite = r29,pc
 mips-dsp-expedite = r29,pc
 mips64-expedite = r29,pc
@@ -172,6 +173,7 @@ GDB = false
 aarch64-feature = 1
 arm-feature = 1
 i386-feature = 1
+loongarch-feature = 1
 riscv-feature = 1
 tic6x-feature = 1
 
@@ -225,6 +227,14 @@ FEATURE_XMLFILES = aarch64-core.xml \
 	i386/64bit-pkeys.xml \
 	i386/64bit-sse.xml \
 	i386/x32-core.xml \
+	loongarch/base32.xml \
+	loongarch/base64.xml \
+	loongarch/fpu32.xml \
+	loongarch/fpu64.xml \
+	loongarch/lbt32.xml \
+	loongarch/lbt64.xml \
+	loongarch/lsx.xml \
+	loongarch/lasx.xml \
 	riscv/rv32e-xregs.xml \
 	riscv/32bit-cpu.xml \
 	riscv/32bit-fpu.xml \
diff --git a/gdb/features/loongarch/base32.c b/gdb/features/loongarch/base32.c
new file mode 100644
index 0000000000..4fcdb21a13
--- /dev/null
+++ b/gdb/features/loongarch/base32.c
@@ -0,0 +1,47 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: base32.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_base32 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.base");
+  tdesc_create_reg (feature, "r0", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r1", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r2", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r3", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r4", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r5", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r6", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r7", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r8", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r9", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r10", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r11", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r12", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r13", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r14", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r15", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r16", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r17", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r18", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r19", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r20", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r21", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r22", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r23", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r24", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r25", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r26", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r27", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r28", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r29", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r30", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "r31", regnum++, 1, "general", 32, "uint32");
+  tdesc_create_reg (feature, "pc", regnum++, 1, "general", 32, "code_ptr");
+  tdesc_create_reg (feature, "badvaddr", regnum++, 1, "general", 32, "code_ptr");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/base32.xml b/gdb/features/loongarch/base32.xml
new file mode 100644
index 0000000000..8eacfdd3e7
--- /dev/null
+++ b/gdb/features/loongarch/base32.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.base">
+  <reg name="r0" bitsize="32" type="uint32" group="general"/>
+  <reg name="r1" bitsize="32" type="uint32" group="general"/>
+  <reg name="r2" bitsize="32" type="uint32" group="general"/>
+  <reg name="r3" bitsize="32" type="uint32" group="general"/>
+  <reg name="r4" bitsize="32" type="uint32" group="general"/>
+  <reg name="r5" bitsize="32" type="uint32" group="general"/>
+  <reg name="r6" bitsize="32" type="uint32" group="general"/>
+  <reg name="r7" bitsize="32" type="uint32" group="general"/>
+  <reg name="r8" bitsize="32" type="uint32" group="general"/>
+  <reg name="r9" bitsize="32" type="uint32" group="general"/>
+  <reg name="r10" bitsize="32" type="uint32" group="general"/>
+  <reg name="r11" bitsize="32" type="uint32" group="general"/>
+  <reg name="r12" bitsize="32" type="uint32" group="general"/>
+  <reg name="r13" bitsize="32" type="uint32" group="general"/>
+  <reg name="r14" bitsize="32" type="uint32" group="general"/>
+  <reg name="r15" bitsize="32" type="uint32" group="general"/>
+  <reg name="r16" bitsize="32" type="uint32" group="general"/>
+  <reg name="r17" bitsize="32" type="uint32" group="general"/>
+  <reg name="r18" bitsize="32" type="uint32" group="general"/>
+  <reg name="r19" bitsize="32" type="uint32" group="general"/>
+  <reg name="r20" bitsize="32" type="uint32" group="general"/>
+  <reg name="r21" bitsize="32" type="uint32" group="general"/>
+  <reg name="r22" bitsize="32" type="uint32" group="general"/>
+  <reg name="r23" bitsize="32" type="uint32" group="general"/>
+  <reg name="r24" bitsize="32" type="uint32" group="general"/>
+  <reg name="r25" bitsize="32" type="uint32" group="general"/>
+  <reg name="r26" bitsize="32" type="uint32" group="general"/>
+  <reg name="r27" bitsize="32" type="uint32" group="general"/>
+  <reg name="r28" bitsize="32" type="uint32" group="general"/>
+  <reg name="r29" bitsize="32" type="uint32" group="general"/>
+  <reg name="r30" bitsize="32" type="uint32" group="general"/>
+  <reg name="r31" bitsize="32" type="uint32" group="general"/>
+  <reg name="pc" bitsize="32" type="code_ptr" group="general"/>
+  <reg name="badvaddr" bitsize="32" type="code_ptr" group="general"/>
+</feature>
diff --git a/gdb/features/loongarch/base64.c b/gdb/features/loongarch/base64.c
new file mode 100644
index 0000000000..5212c16b60
--- /dev/null
+++ b/gdb/features/loongarch/base64.c
@@ -0,0 +1,47 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: base64.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_base64 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.base");
+  tdesc_create_reg (feature, "r0", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r1", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r2", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r3", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r4", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r5", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r6", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r7", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r8", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r9", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r10", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r11", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r12", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r13", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r14", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r15", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r16", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r17", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r18", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r19", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r20", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r21", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r22", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r23", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r24", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r25", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r26", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r27", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r28", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r29", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r30", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "r31", regnum++, 1, "general", 64, "uint64");
+  tdesc_create_reg (feature, "pc", regnum++, 1, "general", 64, "code_ptr");
+  tdesc_create_reg (feature, "badvaddr", regnum++, 1, "general", 64, "code_ptr");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/base64.xml b/gdb/features/loongarch/base64.xml
new file mode 100644
index 0000000000..afbbb63147
--- /dev/null
+++ b/gdb/features/loongarch/base64.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.base">
+  <reg name="r0" bitsize="64" type="uint64" group="general"/>
+  <reg name="r1" bitsize="64" type="uint64" group="general"/>
+  <reg name="r2" bitsize="64" type="uint64" group="general"/>
+  <reg name="r3" bitsize="64" type="uint64" group="general"/>
+  <reg name="r4" bitsize="64" type="uint64" group="general"/>
+  <reg name="r5" bitsize="64" type="uint64" group="general"/>
+  <reg name="r6" bitsize="64" type="uint64" group="general"/>
+  <reg name="r7" bitsize="64" type="uint64" group="general"/>
+  <reg name="r8" bitsize="64" type="uint64" group="general"/>
+  <reg name="r9" bitsize="64" type="uint64" group="general"/>
+  <reg name="r10" bitsize="64" type="uint64" group="general"/>
+  <reg name="r11" bitsize="64" type="uint64" group="general"/>
+  <reg name="r12" bitsize="64" type="uint64" group="general"/>
+  <reg name="r13" bitsize="64" type="uint64" group="general"/>
+  <reg name="r14" bitsize="64" type="uint64" group="general"/>
+  <reg name="r15" bitsize="64" type="uint64" group="general"/>
+  <reg name="r16" bitsize="64" type="uint64" group="general"/>
+  <reg name="r17" bitsize="64" type="uint64" group="general"/>
+  <reg name="r18" bitsize="64" type="uint64" group="general"/>
+  <reg name="r19" bitsize="64" type="uint64" group="general"/>
+  <reg name="r20" bitsize="64" type="uint64" group="general"/>
+  <reg name="r21" bitsize="64" type="uint64" group="general"/>
+  <reg name="r22" bitsize="64" type="uint64" group="general"/>
+  <reg name="r23" bitsize="64" type="uint64" group="general"/>
+  <reg name="r24" bitsize="64" type="uint64" group="general"/>
+  <reg name="r25" bitsize="64" type="uint64" group="general"/>
+  <reg name="r26" bitsize="64" type="uint64" group="general"/>
+  <reg name="r27" bitsize="64" type="uint64" group="general"/>
+  <reg name="r28" bitsize="64" type="uint64" group="general"/>
+  <reg name="r29" bitsize="64" type="uint64" group="general"/>
+  <reg name="r30" bitsize="64" type="uint64" group="general"/>
+  <reg name="r31" bitsize="64" type="uint64" group="general"/>
+  <reg name="pc" bitsize="64" type="code_ptr" group="general"/>
+  <reg name="badvaddr" bitsize="64" type="code_ptr" group="general"/>
+</feature>
diff --git a/gdb/features/loongarch/fpu32.c b/gdb/features/loongarch/fpu32.c
new file mode 100644
index 0000000000..bf8964a3b3
--- /dev/null
+++ b/gdb/features/loongarch/fpu32.c
@@ -0,0 +1,54 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: fpu32.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_fpu32 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu");
+  tdesc_create_reg (feature, "f0", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f1", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f2", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f3", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f4", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f5", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f6", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f7", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f8", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f9", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f10", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f11", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f12", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f13", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f14", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f15", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f16", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f17", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f18", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f19", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f20", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f21", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f22", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f23", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f24", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f25", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f26", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f27", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f28", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f29", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f30", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "f31", regnum++, 1, "float", 32, "ieee_single");
+  tdesc_create_reg (feature, "fcc0", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc1", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc2", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc3", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc4", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc5", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc6", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc7", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/fpu32.xml b/gdb/features/loongarch/fpu32.xml
new file mode 100644
index 0000000000..8421730e96
--- /dev/null
+++ b/gdb/features/loongarch/fpu32.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.fpu">
+
+  <reg name="f0" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f1" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f2" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f3" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f4" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f5" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f6" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f7" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f8" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f9" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f10" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f11" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f12" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f13" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f14" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f15" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f16" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f17" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f18" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f19" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f20" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f21" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f22" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f23" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f24" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f25" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f26" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f27" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f28" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f29" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f30" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="f31" bitsize="32" type="ieee_single" group="float"/>
+  <reg name="fcc0" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc1" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc2" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc3" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc4" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc5" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc6" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc7" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
+</feature>
diff --git a/gdb/features/loongarch/fpu64.c b/gdb/features/loongarch/fpu64.c
new file mode 100644
index 0000000000..f9e24c30f3
--- /dev/null
+++ b/gdb/features/loongarch/fpu64.c
@@ -0,0 +1,62 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: fpu64.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_fpu64 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu");
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_union (feature, "fpu64type");
+  tdesc_type *field_type;
+  field_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_add_field (type_with_fields, "f", field_type);
+  field_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_add_field (type_with_fields, "d", field_type);
+
+  tdesc_create_reg (feature, "f0", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f1", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f2", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f3", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f4", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f5", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f6", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f7", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f8", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f9", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f10", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f11", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f12", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f13", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f14", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f15", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f16", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f17", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f18", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f19", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f20", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f21", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f22", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f23", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f24", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f25", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f26", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f27", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f28", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f29", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f30", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "f31", regnum++, 1, "float", 64, "fpu64type");
+  tdesc_create_reg (feature, "fcc0", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc1", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc2", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc3", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc4", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc5", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc6", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcc7", regnum++, 1, "float", 8, "uint8");
+  tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/fpu64.xml b/gdb/features/loongarch/fpu64.xml
new file mode 100644
index 0000000000..420be8bdb3
--- /dev/null
+++ b/gdb/features/loongarch/fpu64.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.fpu">
+
+  <union id="fpu64type">
+    <field name="f" type="ieee_single"/>
+    <field name="d" type="ieee_double"/>
+  </union>
+
+  <reg name="f0" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f1" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f2" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f3" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f4" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f5" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f6" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f7" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f8" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f9" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f10" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f11" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f12" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f13" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f14" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f15" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f16" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f17" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f18" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f19" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f20" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f21" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f22" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f23" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f24" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f25" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f26" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f27" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f28" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f29" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f30" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="f31" bitsize="64" type="fpu64type" group="float"/>
+  <reg name="fcc0" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc1" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc2" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc3" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc4" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc5" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc6" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcc7" bitsize="8" type="uint8" group="float"/>
+  <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
+</feature>
diff --git a/gdb/features/loongarch/lasx.c b/gdb/features/loongarch/lasx.c
new file mode 100644
index 0000000000..96e3ea9000
--- /dev/null
+++ b/gdb/features/loongarch/lasx.c
@@ -0,0 +1,80 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: lasx.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_lasx (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lasx");
+  tdesc_type *element_type;
+  element_type = tdesc_named_type (feature, "int8");
+  tdesc_create_vector (feature, "v32i8", element_type, 32);
+
+  element_type = tdesc_named_type (feature, "int16");
+  tdesc_create_vector (feature, "v16i16", element_type, 16);
+
+  element_type = tdesc_named_type (feature, "int32");
+  tdesc_create_vector (feature, "v8i32", element_type, 8);
+
+  element_type = tdesc_named_type (feature, "int64");
+  tdesc_create_vector (feature, "v4i64", element_type, 4);
+
+  element_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_create_vector (feature, "v8f32", element_type, 8);
+
+  element_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_create_vector (feature, "v4f64", element_type, 4);
+
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_union (feature, "lasxv");
+  tdesc_type *field_type;
+  field_type = tdesc_named_type (feature, "v32i8");
+  tdesc_add_field (type_with_fields, "v32i8", field_type);
+  field_type = tdesc_named_type (feature, "v16i16");
+  tdesc_add_field (type_with_fields, "v16i16", field_type);
+  field_type = tdesc_named_type (feature, "v8i32");
+  tdesc_add_field (type_with_fields, "v8i32", field_type);
+  field_type = tdesc_named_type (feature, "v4i64");
+  tdesc_add_field (type_with_fields, "v4i64", field_type);
+  field_type = tdesc_named_type (feature, "v8f32");
+  tdesc_add_field (type_with_fields, "v8f32", field_type);
+  field_type = tdesc_named_type (feature, "v4f64");
+  tdesc_add_field (type_with_fields, "v4f64", field_type);
+
+  tdesc_create_reg (feature, "xr0", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr1", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr2", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr3", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr4", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr5", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr6", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr7", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr8", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr9", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr10", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr11", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr12", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr13", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr14", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr15", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr16", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr17", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr18", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr19", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr20", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr21", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr22", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr23", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr24", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr25", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr26", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr27", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr28", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr29", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr30", regnum++, 1, "lasx", 256, "lasxv");
+  tdesc_create_reg (feature, "xr31", regnum++, 1, "lasx", 256, "lasxv");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/lasx.xml b/gdb/features/loongarch/lasx.xml
new file mode 100644
index 0000000000..6f73df04d2
--- /dev/null
+++ b/gdb/features/loongarch/lasx.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.lasx">
+  <vector id="v32i8" type="int8" count="32"/>
+  <vector id="v16i16" type="int16" count="16"/>
+  <vector id="v8i32" type="int32" count="8"/>
+  <vector id="v4i64" type="int64" count="4"/>
+  <vector id="v8f32" type="ieee_single" count="8"/>
+  <vector id="v4f64" type="ieee_double" count="4"/>
+
+  <union id="lasxv">
+    <field name="v32i8" type="v32i8"/>
+    <field name="v16i16" type="v16i16"/>
+    <field name="v8i32" type="v8i32"/>
+    <field name="v4i64" type="v4i64"/>
+    <field name="v8f32" type="v8f32"/>
+    <field name="v4f64" type="v4f64"/>
+  </union>
+
+  <reg name="xr0" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr1" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr2" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr3" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr4" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr5" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr6" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr7" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr8" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr9" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr10" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr11" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr12" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr13" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr14" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr15" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr16" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr17" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr18" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr19" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr20" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr21" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr22" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr23" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr24" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr25" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr26" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr27" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr28" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr29" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr30" bitsize="256" type="lasxv" group="lasx"/>
+  <reg name="xr31" bitsize="256" type="lasxv" group="lasx"/>
+</feature>
diff --git a/gdb/features/loongarch/lbt32.c b/gdb/features/loongarch/lbt32.c
new file mode 100644
index 0000000000..d245c75004
--- /dev/null
+++ b/gdb/features/loongarch/lbt32.c
@@ -0,0 +1,19 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: lbt32.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_lbt32 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lbt");
+  tdesc_create_reg (feature, "scr0", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "scr1", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "scr2", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "scr3", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "EFLAG", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "x86_top", regnum++, 1, "lbt", 8, "uint8");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/lbt32.xml b/gdb/features/loongarch/lbt32.xml
new file mode 100644
index 0000000000..1c0133e415
--- /dev/null
+++ b/gdb/features/loongarch/lbt32.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.lbt">
+  <reg name="scr0" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="scr1" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="scr2" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="scr3" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="EFLAG" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="x86_top" bitsize="8" type="uint8" group="lbt"/>
+</feature>
diff --git a/gdb/features/loongarch/lbt64.c b/gdb/features/loongarch/lbt64.c
new file mode 100644
index 0000000000..ecef3307b7
--- /dev/null
+++ b/gdb/features/loongarch/lbt64.c
@@ -0,0 +1,19 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: lbt64.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_lbt64 (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lbt");
+  tdesc_create_reg (feature, "scr0", regnum++, 1, "lbt", 64, "uint64");
+  tdesc_create_reg (feature, "scr1", regnum++, 1, "lbt", 64, "uint64");
+  tdesc_create_reg (feature, "scr2", regnum++, 1, "lbt", 64, "uint64");
+  tdesc_create_reg (feature, "scr3", regnum++, 1, "lbt", 64, "uint64");
+  tdesc_create_reg (feature, "EFLAG", regnum++, 1, "lbt", 32, "uint32");
+  tdesc_create_reg (feature, "x86_top", regnum++, 1, "lbt", 8, "uint8");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/lbt64.xml b/gdb/features/loongarch/lbt64.xml
new file mode 100644
index 0000000000..1df26f5171
--- /dev/null
+++ b/gdb/features/loongarch/lbt64.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.lbt">
+  <reg name="scr0" bitsize="64" type="uint64" group="lbt"/>
+  <reg name="scr1" bitsize="64" type="uint64" group="lbt"/>
+  <reg name="scr2" bitsize="64" type="uint64" group="lbt"/>
+  <reg name="scr3" bitsize="64" type="uint64" group="lbt"/>
+  <reg name="EFLAG" bitsize="32" type="uint32" group="lbt"/>
+  <reg name="x86_top" bitsize="8" type="uint8" group="lbt"/>
+</feature>
diff --git a/gdb/features/loongarch/lsx.c b/gdb/features/loongarch/lsx.c
new file mode 100644
index 0000000000..dd253f3c76
--- /dev/null
+++ b/gdb/features/loongarch/lsx.c
@@ -0,0 +1,80 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: lsx.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_lsx (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lsx");
+  tdesc_type *element_type;
+  element_type = tdesc_named_type (feature, "int8");
+  tdesc_create_vector (feature, "v16i8", element_type, 16);
+
+  element_type = tdesc_named_type (feature, "int16");
+  tdesc_create_vector (feature, "v8i16", element_type, 8);
+
+  element_type = tdesc_named_type (feature, "int32");
+  tdesc_create_vector (feature, "v4i32", element_type, 4);
+
+  element_type = tdesc_named_type (feature, "int64");
+  tdesc_create_vector (feature, "v2i64", element_type, 2);
+
+  element_type = tdesc_named_type (feature, "ieee_single");
+  tdesc_create_vector (feature, "v4f32", element_type, 4);
+
+  element_type = tdesc_named_type (feature, "ieee_double");
+  tdesc_create_vector (feature, "v2f64", element_type, 2);
+
+  tdesc_type_with_fields *type_with_fields;
+  type_with_fields = tdesc_create_union (feature, "lsxv");
+  tdesc_type *field_type;
+  field_type = tdesc_named_type (feature, "v16i8");
+  tdesc_add_field (type_with_fields, "v16i8", field_type);
+  field_type = tdesc_named_type (feature, "v8i16");
+  tdesc_add_field (type_with_fields, "v8i16", field_type);
+  field_type = tdesc_named_type (feature, "v4i32");
+  tdesc_add_field (type_with_fields, "v4i32", field_type);
+  field_type = tdesc_named_type (feature, "v2i64");
+  tdesc_add_field (type_with_fields, "v2i64", field_type);
+  field_type = tdesc_named_type (feature, "v4f32");
+  tdesc_add_field (type_with_fields, "v4f32", field_type);
+  field_type = tdesc_named_type (feature, "v2f64");
+  tdesc_add_field (type_with_fields, "v2f64", field_type);
+
+  tdesc_create_reg (feature, "vr0", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr1", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr2", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr3", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr4", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr5", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr6", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr7", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr8", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr9", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr10", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr11", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr12", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr13", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr14", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr15", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr16", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr17", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr18", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr19", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr20", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr21", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr22", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr23", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr24", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr25", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr26", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr27", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr28", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr29", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr30", regnum++, 1, "lsx", 128, "lsxv");
+  tdesc_create_reg (feature, "vr31", regnum++, 1, "lsx", 128, "lsxv");
+  return regnum;
+}
diff --git a/gdb/features/loongarch/lsx.xml b/gdb/features/loongarch/lsx.xml
new file mode 100644
index 0000000000..a0a6ba49e4
--- /dev/null
+++ b/gdb/features/loongarch/lsx.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+     Contributed by Loongson Ltd.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.lsx">
+  <vector id="v16i8" type="int8" count="16"/>
+  <vector id="v8i16" type="int16" count="8"/>
+  <vector id="v4i32" type="int32" count="4"/>
+  <vector id="v2i64" type="int64" count="2"/>
+  <vector id="v4f32" type="ieee_single" count="4"/>
+  <vector id="v2f64" type="ieee_double" count="2"/>
+
+  <union id="lsxv">
+    <field name="v16i8" type="v16i8"/>
+    <field name="v8i16" type="v8i16"/>
+    <field name="v4i32" type="v4i32"/>
+    <field name="v2i64" type="v2i64"/>
+    <field name="v4f32" type="v4f32"/>
+    <field name="v2f64" type="v2f64"/>
+  </union>
+
+  <reg name="vr0" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr1" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr2" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr3" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr4" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr5" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr6" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr7" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr8" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr9" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr10" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr11" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr12" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr13" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr14" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr15" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr16" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr17" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr18" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr19" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr20" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr21" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr22" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr23" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr24" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr25" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr26" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr27" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr28" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr29" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr30" bitsize="128" type="lsxv" group="lsx"/>
+  <reg name="vr31" bitsize="128" type="lsxv" group="lsx"/>
+</feature>
diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c
new file mode 100644
index 0000000000..484ed05692
--- /dev/null
+++ b/gdb/loongarch-linux-nat.c
@@ -0,0 +1,877 @@
+/* Target-dependent code for GNU/Linux LoongArch.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "command.h"
+#include "gdbcmd.h"
+#include "inferior.h"
+#include "loongarch-tdep.h"
+#include "target.h"
+#include "regcache.h"
+#include "linux-nat-trad.h"
+#include "loongarch-linux-tdep.h"
+#include "target-descriptions.h"
+
+#include "gdb_proc_service.h"
+#include "gregset.h"
+
+#include "nat/gdb_ptrace.h"
+#include <asm/ptrace.h>
+#include "inf-ptrace.h"
+#include "arch/loongarch-linux-nat.h"
+#include "elf/common.h"
+
+#include "nat/loongarch-linux-watch.h"
+
+class loongarch_linux_nat_target final : public linux_nat_trad_target
+{
+public:
+  void fetch_registers (struct regcache *, int) override;
+  void store_registers (struct regcache *, int) override;
+
+  void close () override;
+
+  int can_use_hw_breakpoint (enum bptype, int, int) override;
+
+  int insert_hw_breakpoint (struct gdbarch *,
+			    struct bp_target_info *) override;
+
+  int remove_hw_breakpoint (struct gdbarch *,
+			    struct bp_target_info *) override;
+
+  int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+			 struct expression *) override;
+
+  int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
+			 struct expression *) override;
+
+  bool stopped_by_watchpoint () override;
+
+  bool stopped_data_address (CORE_ADDR *) override;
+
+  int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
+
+  const struct target_desc *read_description () override;
+
+  enum target_xfer_status xfer_partial (enum target_object object,
+					const char *annex, gdb_byte *readbuf,
+					const gdb_byte *writebuf,
+					ULONGEST offset, ULONGEST len,
+					ULONGEST *xfered_len) override;
+
+protected:
+  CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regno,
+			       int store_p) override;
+
+  void low_new_thread (struct lwp_info *lp) override;
+
+private:
+  void loongarch_fetch_regs (struct regcache *regcache, int regno);
+  void loongarch_store_regs (struct regcache *regcache, int regno);
+};
+
+static void
+ensure_ptrace_getregset ()
+{
+  have_ptrace_getregset = TRIBOOL_TRUE;
+}
+
+/* Fetch REGNO (or all registers if REGNO == -1) from the target
+   using any working method.  */
+
+void
+loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
+					     int regnum)
+{
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    ensure_ptrace_getregset ();
+
+  if (sizeof (void *) == 4)
+    gdb_assert (have_ptrace_getregset == TRIBOOL_TRUE
+		|| register_size (regcache->arch (),
+		   gdbarch_tdep (regcache->arch ())->regs.r) != 32);
+
+  if (have_ptrace_getregset == TRIBOOL_TRUE)
+    loongarch_fetch_regs (regcache, regnum);
+  else
+    linux_nat_trad_target::fetch_registers (regcache, regnum);
+}
+
+/* Store REGNO (or all registers if REGNO == -1) to the target
+   using any working method.  */
+
+void
+loongarch_linux_nat_target::store_registers (struct regcache *regcache,
+					     int regnum)
+{
+  if (have_ptrace_getregset == TRIBOOL_UNKNOWN)
+    ensure_ptrace_getregset ();
+
+  if (sizeof (void *) == 4)
+    gdb_assert (have_ptrace_getregset == TRIBOOL_TRUE
+		|| register_size (regcache->arch (),
+		   gdbarch_tdep (regcache->arch ())->regs.r) != 32);
+
+  if (have_ptrace_getregset == TRIBOOL_TRUE)
+    loongarch_store_regs (regcache, regnum);
+  else
+    linux_nat_trad_target::store_registers (regcache, regnum);
+}
+
+const struct target_desc *
+loongarch_linux_nat_target::read_description ()
+{
+  ensure_ptrace_getregset ();
+  return loongarch_linux_read_description_runtime (inferior_ptid.pid ());
+}
+
+void
+loongarch_linux_nat_target::loongarch_fetch_regs (struct regcache *regcache,
+						  int regno)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  pid_t tid = get_ptrace_pid (regcache->ptid ());
+
+  do
+    {
+      if (regs->r < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->r <= regno && regno < regs->r + 32)
+	;
+      else if (regs->pc == regno)
+	;
+      else if (regs->badvaddr == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_gregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec));
+      loongarch_elf_gregset.supply_regset (NULL, regcache, regno, &regset,
+					   sizeof (regset));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->f < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->f <= regno && regno < regs->f + 32)
+	;
+      else if (regs->fcc <= regno && regno < regs->fcc + 8)
+	;
+      else if (regs->fcsr == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_fpregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec));
+      loongarch_elf_fpregset.supply_regset (NULL, regcache, regno, &regset,
+					    sizeof (regset));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->scr < 0)
+	break;
+      else if (regno == -1)
+	;
+      else if (regs->scr <= regno && regno < regs->scr + 4)
+	;
+      else if (regs->EFLAG == regno)
+	;
+      else if (regs->x86_top == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_lbtregset_t regset = { 0 };
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, &iovec));
+      loongarch_elf_lbtregset.supply_regset (NULL, regcache, regno, &regset,
+					     sizeof (regset));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->vr < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->vr <= regno && regno < regs->vr + 32)
+	;
+      else
+	break;
+
+      loongarch_elf_lsxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, &iovec));
+      loongarch_elf_lsxregset.supply_regset (NULL, regcache, regno, &regset,
+					     sizeof (regset));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->xr < 0)
+	break;
+      else if (regno == -1)
+	;
+      else if (regs->xr <= regno && regno < regs->xr + 32)
+	;
+      else
+	break;
+
+      loongarch_elf_lasxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, &iovec));
+      loongarch_elf_lasxregset.supply_regset (NULL, regcache, regno, &regset,
+					      sizeof (regset));
+    }
+  while (0);
+}
+
+void
+loongarch_linux_nat_target::loongarch_store_regs (struct regcache *regcache,
+						  int regno)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  pid_t tid = get_ptrace_pid (regcache->ptid ());
+
+  do
+    {
+      if (regs->r < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->r <= regno && regno < regs->r + 32)
+	;
+      else if (regs->pc == regno || regs->badvaddr == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_gregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec));
+      loongarch_elf_gregset.collect_regset (NULL, regcache, regno, &regset,
+					    sizeof (regset));
+      gdb_assert (0 == ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, &iovec));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->f < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->f <= regno && regno < regs->f + 32)
+	;
+      else if (regs->fcc <= regno && regno < regs->fcc + 8)
+	;
+      else if (regs->fcsr == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_fpregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec));
+      loongarch_elf_fpregset.collect_regset (NULL, regcache, regno, &regset,
+					     sizeof (regset));
+      gdb_assert (0 == ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->scr < 0)
+	break;
+      else if (regno == -1)
+	;
+      else if (regs->scr <= regno && regno < regs->scr + 4)
+	;
+      else if (regs->EFLAG == regno)
+	;
+      else if (regs->x86_top == regno)
+	;
+      else
+	break;
+
+      loongarch_elf_lbtregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, &iovec));
+      loongarch_elf_lbtregset.collect_regset (NULL, regcache, regno, &regset,
+					      sizeof (regset));
+      gdb_assert (0 == ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, &iovec));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->vr < 0)
+	break;
+      if (regno == -1)
+	;
+      else if (regs->vr <= regno && regno < regs->vr + 32)
+	;
+      else
+	break;
+
+      loongarch_elf_lsxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, &iovec));
+      loongarch_elf_lsxregset.collect_regset (NULL, regcache, regno, &regset,
+					      sizeof (regset));
+      gdb_assert (0 == ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, &iovec));
+    }
+  while (0);
+
+  do
+    {
+      if (regs->xr < 0)
+	break;
+      else if (regno == -1)
+	;
+      else if (regs->xr <= regno && regno < regs->xr + 32)
+	;
+      else
+	break;
+
+      loongarch_elf_lasxregset_t regset;
+      struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+      gdb_assert (0 == ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, &iovec));
+      loongarch_elf_lasxregset.collect_regset (NULL, regcache, regno, &regset,
+					       sizeof (regset));
+      gdb_assert (0 == ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LASX, &iovec));
+    }
+  while (0);
+}
+
+/* Return the address in the core dump or inferior of register REGNO.  */
+
+CORE_ADDR
+loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch,
+					       int regno, int store_p)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  /* according to <asm/ptrace.h> */
+  if (0 <= regs->r && regs->r <= regno && regno < regs->r + GPR_NUM)
+    return GPR_BASE + regno - regs->r;
+  else if (regs->pc == regno)
+    return PC;
+  return -1;
+}
+
+/* Implement the to_xfer_partial target_ops method.  */
+
+enum target_xfer_status
+loongarch_linux_nat_target::xfer_partial (enum target_object object,
+					  const char *annex, gdb_byte *readbuf,
+					  const gdb_byte *writebuf,
+					  ULONGEST offset, ULONGEST len,
+					  ULONGEST *xfered_len)
+{
+  pid_t pid = inferior_ptid.pid ();
+
+  if (object != TARGET_OBJECT_LARCH)
+    return linux_nat_trad_target::xfer_partial (
+      object, annex, readbuf, writebuf, offset, len, xfered_len);
+
+  if (strcmp (annex, "cpucfg") == 0)
+    {
+      if (writebuf)
+	return TARGET_XFER_E_IO;
+      if (offset % 4 != 0 || offset % 4 != 0)
+	return TARGET_XFER_E_IO;
+      char t_buf[offset + len];
+      struct iovec iovec = { .iov_base = &t_buf, .iov_len = sizeof (t_buf) };
+      if (ptrace (PTRACE_GETREGSET, pid, NT_LARCH_CPUCFG, &iovec) < 0)
+	{
+	  size_t i;
+	  for (i = offset / 4; i < (offset + len) / 4 + 1; i++)
+	    ((uint32_t *) t_buf)[i] = loongarch_cpucfg (i);
+	}
+      memcpy (readbuf, t_buf + offset, len);
+      *xfered_len = len;
+      return TARGET_XFER_OK;
+    }
+
+  return TARGET_XFER_E_IO;
+}
+
+static loongarch_linux_nat_target the_loongarch_linux_nat_target;
+
+/* Wrapper functions.  These are only used by libthread_db.  */
+
+void
+supply_gregset (struct regcache *regcache,
+		const gdb_gregset_t /* elf_gregset_t  */ *gregset)
+{
+  loongarch_elf_gregset.supply_regset (NULL, regcache, -1, gregset,
+				       sizeof (gdb_gregset_t));
+}
+
+void
+fill_gregset (const struct regcache *regcache,
+	      gdb_gregset_t /* elf_gregset_t  */ *gregset, int regno)
+{
+  loongarch_elf_gregset.collect_regset (NULL, regcache, regno, gregset,
+					sizeof (gdb_gregset_t));
+}
+
+void
+supply_fpregset (struct regcache *regcache,
+		 const gdb_fpregset_t /* elf_fpregset_t  */ *fpregset)
+{
+  loongarch_elf_fpregset.supply_regset (NULL, regcache, -1, fpregset,
+					sizeof (gdb_fpregset_t));
+}
+
+void
+fill_fpregset (const struct regcache *regcache,
+	       gdb_fpregset_t /* elf_fpregset_t  */ *fpregset, int regno)
+{
+  loongarch_elf_fpregset.collect_regset (NULL, regcache, regno, fpregset,
+					 sizeof (gdb_fpregset_t));
+}
+
+/* -1 if the kernel and/or CPU do not support watch registers.
+   1 if watch_readback is valid and we can read style, num_valid
+   and the masks.
+   0 if we need to read the watch_readback.  */
+
+static int watch_readback_valid;
+
+/* Cached watch register read values.  */
+
+static struct pt_watch_regs watch_readback =
+{
+  .max_valid = MAX_DEBUG_REGISTER,
+};
+
+static struct loongarch_watchpoint *current_watches;
+
+/*  The current set of watch register values for writing the
+    registers.  */
+
+static struct pt_watch_regs watch_mirror =
+{
+  .max_valid = MAX_DEBUG_REGISTER,
+};
+
+static void
+loongarch_show_dr (const char *func, CORE_ADDR addr, int len,
+		   enum target_hw_bp_type type)
+{
+  int i;
+
+  puts_unfiltered (func);
+  if (addr || len)
+    printf_unfiltered (
+      " (addr=%s, len=%d, type=%s)", paddress (target_gdbarch (), addr), len,
+      type == hw_write
+      ? "data-write"
+      : (type == hw_read
+	 ? "data-read"
+	 : (type == hw_access ? "data-read/write"
+	    : (type == hw_execute ? "instruction-execute"
+	       : "??unknown??"))));
+  puts_unfiltered (":\n");
+
+  for (i = 0; i < MAX_DEBUG_REGISTER; i++)
+    printf_unfiltered (
+      "\tDR%d: addr=%s, mask=%s\n", i,
+      paddress (target_gdbarch (),
+		loongarch_linux_watch_get_addr (&watch_mirror, i)),
+      paddress (target_gdbarch (),
+		loongarch_linux_watch_get_mask (&watch_mirror, i)));
+}
+
+/* Target to_can_use_hw_breakpoint implementation.  Return 1 if we can
+   handle the specified watch type.  */
+
+int
+loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt,
+						   int ot)
+{
+  int i;
+  uint32_t wanted_mask, irw_mask;
+  long lwp = inferior_ptid.lwp ();
+
+  if (!loongarch_linux_read_watch_registers (lwp, &watch_readback,
+					     &watch_readback_valid, 0))
+    return 0;
+
+  switch (type)
+    {
+    case bp_hardware_watchpoint:
+      wanted_mask = W_MASK;
+      break;
+    case bp_read_watchpoint:
+      wanted_mask = R_MASK;
+      break;
+    case bp_access_watchpoint:
+      wanted_mask = R_MASK | W_MASK;
+      break;
+    case bp_hardware_breakpoint:
+      wanted_mask = I_MASK;
+      break;
+    default:
+      return 0;
+    }
+
+  for (i = 0; i < loongarch_linux_watch_get_num_valid (&watch_readback) && cnt;
+       i++)
+    {
+      irw_mask = loongarch_linux_watch_get_irwmask (&watch_readback, i);
+      if ((irw_mask & wanted_mask) == wanted_mask)
+	cnt--;
+    }
+  return (cnt == 0) ? 1 : 0;
+}
+
+/* Target to_stopped_by_watchpoint implementation.  Return 1 if
+   stopped by watchpoint.  The watchhi R and W bits indicate the watch
+   register triggered.  */
+
+bool
+loongarch_linux_nat_target::stopped_by_watchpoint ()
+{
+  int n;
+  int num_valid;
+
+  if (!loongarch_linux_read_watch_registers (
+	inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 1))
+    return false;
+
+  num_valid = loongarch_linux_watch_get_num_valid (&watch_readback);
+
+  for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
+    if (loongarch_linux_watch_get_irwstat (&watch_readback, n)
+	& (R_MASK | W_MASK))
+      return true;
+
+  return false;
+}
+
+/* Target to_stopped_data_address implementation.  Set the address
+   where the watch triggered (if known).  Return 1 if the address was
+   known.  */
+
+bool
+loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *paddr)
+{
+  int num_valid, n;
+  if (!loongarch_linux_read_watch_registers (
+	inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 1))
+    return false;
+
+  num_valid = loongarch_linux_watch_get_num_valid (&watch_readback);
+
+  for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
+    if (loongarch_linux_watch_get_irwstat (&watch_readback, n)
+	& (R_MASK | W_MASK))
+      {
+	CORE_ADDR t_addr, t_mask;
+	int t_irw;
+	struct loongarch_watchpoint *watch;
+
+	t_addr = loongarch_linux_watch_get_addr (&watch_readback, n);
+	t_irw = loongarch_linux_watch_get_irw (&watch_readback, n) & IRW_MASK;
+	t_mask = loongarch_linux_watch_get_mask (&watch_readback, n);
+
+	for (watch = current_watches; watch != NULL; watch = watch->next)
+	  {
+	    CORE_ADDR addr = watch->addr;
+	    CORE_ADDR last_byte = addr + watch->len - 1;
+
+	    if ((t_irw & loongarch_linux_watch_type_to_irw (watch->type)) == 0)
+	      {
+		/* Different type.  */
+		continue;
+	      }
+	    /* Check for overlap of even a single byte.  */
+	    if (last_byte >= t_addr && addr <= t_addr + t_mask)
+	      {
+		*paddr = addr;
+		return true;
+	      }
+	  }
+      }
+  return false;
+}
+
+/* Target to_region_ok_for_hw_watchpoint implementation.  Return 1 if
+   the specified region can be covered by the watch registers.  */
+
+int
+loongarch_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr,
+							 int len)
+{
+  struct pt_watch_regs dummy_regs;
+  int i;
+
+  if (!loongarch_linux_read_watch_registers (
+	inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 0))
+    return 0;
+
+  dummy_regs = watch_readback;
+  /* Clear them out.  */
+  for (i = 0; i < loongarch_linux_watch_get_num_valid (&dummy_regs); i++)
+    {
+      loongarch_linux_watch_set_addr (&dummy_regs, i, 0);
+      loongarch_linux_watch_set_mask (&dummy_regs, i, 0);
+      loongarch_linux_watch_set_irw (&dummy_regs, i, 0);
+    }
+  return loongarch_linux_watch_try_one_watch (&dummy_regs, addr, len, 0);
+}
+
+/* Write the mirrored watch register values for each thread.  */
+
+static int
+write_watchpoint_regs (void)
+{
+  int tid;
+
+  for (const lwp_info *lp : all_lwps ())
+    {
+      tid = lp->ptid.lwp ();
+      if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror, NULL) == -1)
+        perror_with_name (_ ("Couldn't write debug register"));
+    }
+  return 0;
+}
+
+/* linux_nat_target::low_new_thread implementation.  */
+
+void
+loongarch_linux_nat_target::low_new_thread (struct lwp_info *lp)
+{
+  long tid = lp->ptid.lwp ();
+
+  if (!loongarch_linux_read_watch_registers (tid, &watch_readback,
+					     &watch_readback_valid, 0))
+    return;
+
+  if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror, NULL) == -1)
+    perror_with_name (_ ("Couldn't write debug register"));
+}
+
+/* Target to_insert_watchpoint implementation.  Try to insert a new
+   watch.  Return zero on success.  */
+
+int
+loongarch_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
+					       enum target_hw_bp_type type,
+					       struct expression *cond)
+{
+  struct pt_watch_regs regs =
+  {
+    .max_valid = MAX_DEBUG_REGISTER,
+  };
+  struct loongarch_watchpoint *new_watch;
+  struct loongarch_watchpoint **pw;
+
+  int retval;
+
+  if (!loongarch_linux_read_watch_registers (
+	inferior_ptid.lwp (), &watch_readback, &watch_readback_valid, 0))
+    return -1;
+
+  if (len <= 0)
+    return -1;
+
+  regs = watch_readback;
+  /* Add the current watches.  */
+  loongarch_linux_watch_populate_regs (current_watches, &regs);
+
+  /* Now try to add the new watch.  */
+  if (!loongarch_linux_watch_try_one_watch (
+	&regs, addr, len, loongarch_linux_watch_type_to_irw (type)))
+    return -1;
+
+  /* It fit.  Stick it on the end of the list.  */
+  new_watch = XNEW (struct loongarch_watchpoint);
+  new_watch->addr = addr;
+  new_watch->len = len;
+  new_watch->type = type;
+  new_watch->next = NULL;
+
+  pw = &current_watches;
+  while (*pw != NULL)
+    pw = &(*pw)->next;
+  *pw = new_watch;
+
+  watch_mirror = regs;
+  retval = write_watchpoint_regs ();
+
+  if (show_debug_regs)
+    loongarch_show_dr ("insert_watchpoint", addr, len, type);
+
+  return retval;
+}
+
+/* Target to_remove_watchpoint implementation.  Try to remove a watch.
+   Return zero on success.  */
+
+int
+loongarch_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
+					       enum target_hw_bp_type type,
+					       struct expression *cond)
+{
+  int retval;
+  int deleted_one;
+
+  struct loongarch_watchpoint **pw;
+  struct loongarch_watchpoint *w;
+
+  /* Search for a known watch that matches.  Then unlink and free
+     it.  */
+  deleted_one = 0;
+  pw = &current_watches;
+  while ((w = *pw))
+    {
+      if (w->addr == addr && w->len == len && w->type == type)
+	{
+	  *pw = w->next;
+	  xfree (w);
+	  deleted_one = 1;
+	  break;
+	}
+      pw = &(w->next);
+    }
+
+  if (!deleted_one)
+    return -1; /* We don't know about it, fail doing nothing.  */
+
+  /* At this point watch_readback is known to be valid because we
+     could not have added the watch without reading it.  */
+  gdb_assert (watch_readback_valid == 1);
+
+  watch_mirror = watch_readback;
+  loongarch_linux_watch_populate_regs (current_watches, &watch_mirror);
+
+  retval = write_watchpoint_regs ();
+
+  if (show_debug_regs)
+    loongarch_show_dr ("remove_watchpoint", addr, len, type);
+
+  return retval;
+}
+
+/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::insert_hw_breakpoint (
+  struct gdbarch *gdbarch, struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
+  int len = 4;
+  const enum target_hw_bp_type type = hw_execute;
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    fprintf_unfiltered (
+      gdb_stdlog, "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+      (unsigned long) addr, len);
+
+  ret = insert_watchpoint (addr, len, type, NULL);
+  return ret;
+}
+
+/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::remove_hw_breakpoint (
+  struct gdbarch *gdbarch, struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address;
+  int len = 4;
+  const enum target_hw_bp_type type = hw_execute;
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    fprintf_unfiltered (
+      gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+      (unsigned long) addr, len);
+
+  ret = remove_watchpoint (addr, len, type, NULL);
+
+  return ret;
+}
+
+/* Target to_close implementation.  Free any watches and call the
+   super implementation.  */
+
+void
+loongarch_linux_nat_target::close ()
+{
+  struct loongarch_watchpoint *w;
+  struct loongarch_watchpoint *nw;
+
+  /* Clean out the current_watches list.  */
+  w = current_watches;
+  while (w)
+    {
+      nw = w->next;
+      xfree (w);
+      w = nw;
+    }
+  current_watches = NULL;
+
+  linux_nat_trad_target::close ();
+}
+
+void _initialize_loongarch_linux_nat ();
+void
+_initialize_loongarch_linux_nat ()
+{
+  add_setshow_boolean_cmd (
+    "show-debug-regs", class_maintenance, &show_debug_regs, _ ("\
+Set whether to show variables that mirror the LoongArch debug registers."),
+    _ ("\
+Show whether to show variables that mirror the LoongArch debug registers."),
+    _ ("\
+Use \"on\" to enable, \"off\" to disable.\n\
+If enabled, the debug registers values are shown when GDB inserts\n\
+or removes a hardware breakpoint or watchpoint, and when the inferior\n\
+triggers a breakpoint or watchpoint."),
+    NULL, NULL, &maintenance_set_cmdlist, &maintenance_show_cmdlist);
+
+  linux_target = &the_loongarch_linux_nat_target;
+  add_inf_child_target (&the_loongarch_linux_nat_target);
+}
diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c
new file mode 100644
index 0000000000..dc59f288b3
--- /dev/null
+++ b/gdb/loongarch-linux-tdep.c
@@ -0,0 +1,677 @@
+/* Target-dependent code for GNU/Linux LoongArch.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "inferior.h"
+#include "gdbcore.h"
+#include "target.h"
+#include "solib-svr4.h"
+#include "osabi.h"
+#include "loongarch-tdep.h"
+#include "frame.h"
+#include "regcache.h"
+#include "trad-frame.h"
+#include "tramp-frame.h"
+#include "gdbtypes.h"
+#include "objfiles.h"
+#include "solib.h"
+#include "solist.h"
+#include "symtab.h"
+#include "target-descriptions.h"
+#include "loongarch-linux-tdep.h"
+#include "glibc-tdep.h"
+#include "linux-tdep.h"
+#include "xml-syscall.h"
+#include "gdbsupport/gdb_signals.h"
+
+static void
+loongarch_supply_elf_gregset (const struct regset *r,
+			      struct regcache *regcache, int regno,
+			      const void *gprs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->r && sizeof (loongarch_elf_gregset_t) <= len);
+
+  int regsize = register_size (regcache->arch (), regs->r);
+  const gdb_byte *buf = NULL;
+
+  if (regno == -1)
+    {
+      /* Set $r0 = 0.  */
+      regcache->raw_supply_zeroed (regs->r);
+
+      for (int i = 1; i < 32; i++)
+	{
+	buf = (const gdb_byte*)gprs + regsize * i;
+	regcache->raw_supply (regs->r + i, (const void *)buf);
+	}
+
+      /* Size base (pc) = regsize * regs->pc.  */
+      buf = (const gdb_byte*)gprs + regsize * regs->pc;
+      regcache->raw_supply (regs->pc, (const void *)buf);
+
+      /* Size base (badvaddr) = regsize * regs->badvaddr.  */
+      buf = (const gdb_byte*)gprs + regsize * regs->badvaddr;
+      regcache->raw_supply (regs->badvaddr, (const void *)buf);
+    }
+  else if (regs->r == regno)
+    regcache->raw_supply_zeroed (regno);
+  else if ((regs->r < regno && regno < regs->r + 32)
+	  || (regs->pc == regno)
+	  || (regs->badvaddr == regno))
+    {
+    /* Offset offset (regno) = regsize * (regno - regs->r).  */
+    buf = (const gdb_byte*)gprs + regsize * (regno - regs->r);
+    regcache->raw_supply (regno, (const void *)buf);
+    }
+}
+
+static void
+loongarch_fill_elf_gregset (const struct regset *r,
+			    const struct regcache *regcache, int regno,
+			    void *gprs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->r && sizeof (loongarch_elf_gregset_t) <= len);
+  int regsize = register_size (regcache->arch (), regs->r);
+  gdb_byte *buf = NULL;
+
+  if (regno == -1)
+    {
+      for (int i = 0; i < 32; i++)
+	{
+	buf = (gdb_byte *)gprs + regsize * i;
+	regcache->raw_collect (regs->r + i, (void *)buf);
+	}
+
+      /* Size base (pc) = regsize * regs->pc.  */
+      buf = (gdb_byte *)gprs + regsize * regs->pc;
+      regcache->raw_collect (regs->pc, (void *)buf);
+
+      /* Size base (badvaddr) = regsize * regs->badvaddr.  */
+      buf = (gdb_byte *)gprs + regsize * regs->badvaddr;
+      regcache->raw_collect (regs->badvaddr, (void *)buf);
+    }
+  else if ((regs->r <= regno && regno < regs->r + 32)
+	  ||(regs->pc == regno)
+	  ||(regs->badvaddr == regno))
+    {
+    /* Offset offset (regno) = regsize * (regno - regs->r).  */
+    buf = (gdb_byte *)gprs + regsize * (regno - regs->r);
+    regcache->raw_collect (regno, (void *)buf);
+    }
+}
+
+const struct regset loongarch_elf_gregset =
+{
+  NULL,
+  loongarch_supply_elf_gregset,
+  loongarch_fill_elf_gregset,
+};
+
+static void
+loongarch_supply_elf_fpregset (const struct regset *r,
+			       struct regcache *regcache, int regno,
+			       const void *fprs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->f && sizeof (loongarch_elf_fpregset_t) <= len);
+
+  const gdb_byte *buf = NULL;
+  int fprsize = register_size (regcache->arch (), regs->f);
+  int fccsize = register_size (regcache->arch (), regs->fcc);
+
+  if (regno == -1)
+    {
+    /* 32 fprs.  */
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (const gdb_byte *)fprs + fprsize * i;
+      regcache->raw_supply (regs->f + i, (const void *)buf);
+      }
+
+    /* 8 fccs , base (fcc) = 32 * sizeof (fpr).  */
+    buf = (const gdb_byte *)fprs + fprsize * 32;
+    for (int i = 0; i < 8; i++)
+      {
+      regcache->raw_supply (regs->fcc + i, (const void *)buf);
+      buf += fccsize;
+      }
+
+    /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc).  */
+    buf = (const gdb_byte *)fprs + 32 * fprsize + 8 * fccsize;
+    regno = regs->fcsr;
+    }
+  else if (regs->f <= regno && regno < regs->f + 32)
+    {
+    /* Offset offset (regno - f) = (regno - regs->f) * sizeof (fpr).  */
+    buf = (const gdb_byte *)fprs + fprsize * (regno - regs->f);
+    }
+  else if (regs->fcc <= regno && regno < regs->fcc + 8)
+    {
+    /* Size base (fcc) + offset (regno - fcc)
+       = 32 * sizeof (fpr) + (regno - regs->fcc) * sizeof (fcc).  */
+    buf = (const gdb_byte *)fprs + 32 * fprsize
+	  + (regno - regs->fcc) * fccsize;
+    }
+  else if (regs->fcsr == regno)
+    {
+    /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc).  */
+    buf = (const gdb_byte *)fprs + 32 * fprsize + 8 * fccsize;
+    }
+  else
+    {
+    return;
+    }
+
+    /* Supply register.  */
+    regcache->raw_supply (regno, (const void *)buf);
+}
+
+static void
+loongarch_fill_elf_fpregset (const struct regset *r,
+			     const struct regcache *regcache, int regno,
+			     void *fprs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->f && sizeof (loongarch_elf_fpregset_t) <= len);
+  gdb_byte *buf = NULL;
+  int fprsize = register_size (regcache->arch (), regs->f);
+  int fccsize = register_size (regcache->arch (), regs->fcc);
+
+  if (regno == -1)
+    {
+    /* 32 fprs.  */
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (gdb_byte *)fprs + fprsize * i;
+      regcache->raw_collect (regs->f + i, (void *)buf);
+      }
+
+      /* 8 fccs , base (fcc) = 32 * sizeof (fpr).  */
+      buf = (gdb_byte *)fprs + fprsize * 32;
+      for (int i = 0; i < 8; i++)
+	{
+	regcache->raw_collect (regs->fcc + i, (void *)buf);
+	buf += fccsize;
+	}
+
+      /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc).  */
+      buf = (gdb_byte *)fprs + fprsize * 32 + fccsize * 8;
+      regno = regs->fcsr;
+    }
+  else if (regs->f <= regno && regno < regs->f + 32)
+    {
+    /* Offset offset (regno - f) = (regno - regs->f) * sizeof (fpr).  */
+    buf = (gdb_byte *)fprs + fprsize * (regno - regs->f);
+    }
+  else if (regs->fcc <= regno && regno < regs->fcc + 8)
+    {
+    /* Size base (fcc) + offset (regno - fcc)
+       = 32 * sizeof (fpr) + (regno - regs->fcc) * sizeof (fcc).  */
+    buf = (gdb_byte *)fprs + 32 * fprsize + (regno - regs->fcc) * fccsize;
+    }
+  else if (regs->fcsr == regno)
+    {
+    /* Size base (fcsr) = 32 * sizeof (fpr) + 8 * sizeof (fcc).  */
+    buf = (gdb_byte *)fprs + 32 * fprsize + 8 * fccsize;
+    }
+  else
+    {
+    return;
+    }
+
+    /* Supply register.  */
+    regcache->raw_collect (regno, (void *)buf);
+}
+
+const struct regset loongarch_elf_fpregset =
+{
+  NULL,
+  loongarch_supply_elf_fpregset,
+  loongarch_fill_elf_fpregset,
+};
+
+static void
+loongarch_supply_elf_cpucfgregset (const struct regset *r,
+				   struct regcache *regcache, int regno,
+				   const void *cpucfgs, size_t len)
+{
+}
+
+static void
+loongarch_fill_elf_cpucfgregset (const struct regset *r,
+				 const struct regcache *regcache, int regno,
+				 void *cpucfgs, size_t len)
+{
+  ULONGEST xfered_len;
+  target_xfer_partial (current_inferior ()->top_target (),
+		       /* current_top_target (),*/ TARGET_OBJECT_LARCH,
+		       "cpucfg", (gdb_byte *) cpucfgs, NULL, 0, len,
+		       &xfered_len);
+  memset ((gdb_byte *) cpucfgs + xfered_len, 0, len - xfered_len);
+}
+
+const struct regset loongarch_elf_cpucfgregset =
+{
+  NULL,
+  loongarch_supply_elf_cpucfgregset,
+  loongarch_fill_elf_cpucfgregset,
+};
+
+static void
+loongarch_supply_elf_lbtregset (const struct regset *r,
+				struct regcache *regcache, int regno,
+				const void *lbtrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->scr && sizeof (loongarch_elf_lbtregset_t) <= len);
+
+  const gdb_byte *buf = NULL;
+  int scrsize = register_size (regcache->arch (), regs->scr);
+  int efsize = register_size (regcache->arch (), regs->EFLAG);
+
+  if (regno == -1)
+    {
+    /* 4 scrs.  */
+    for (int i = 0; i < 4; i++)
+      {
+      buf = (const gdb_byte *)lbtrs + scrsize * i;
+      regcache->raw_supply (regs->scr + i, (const void *)buf);
+      }
+
+      /* Size base (EFLAG) = 4 * sizeof (scr).  */
+      buf = (const gdb_byte *)lbtrs + scrsize * 4;
+      regcache->raw_supply (regs->EFLAG, (const void *)buf);
+
+      /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG).  */
+      buf = (const gdb_byte *)lbtrs + scrsize * 4 + efsize;
+      regno = regs->x86_top;
+    }
+  else if (regs->scr <= regno && regno < regs->scr + 4)
+    /* Offset offset (EFLAG) = sizeof (scr) * (regno - regs->scr).  */
+    buf = (const gdb_byte *)lbtrs + scrsize * (regno - regs->scr);
+  else if (regs->EFLAG == regno)
+    /* Size base (EFLAG) = 4 * sizeof (scr).  */
+    buf = (const gdb_byte *)lbtrs + scrsize * 4;
+  else if (regs->x86_top == regno)
+    {
+    /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG).  */
+    buf = (const gdb_byte *)lbtrs + scrsize * 4 + efsize;
+    }
+  else
+    {
+    return;
+    }
+
+  regcache->raw_supply (regno, (const void *)buf);
+}
+
+static void
+loongarch_fill_elf_lbtregset (const struct regset *r,
+			      const struct regcache *regcache, int regno,
+			      void *lbtrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->scr && sizeof (loongarch_elf_lbtregset_t) <= len);
+
+  gdb_byte *buf = NULL;
+  int scrsize = register_size (regcache->arch (), regs->scr);
+  int efsize = register_size (regcache->arch (), regs->EFLAG);
+
+  if (regno == -1)
+    {
+
+    /* 4 scrs.  */
+    for (int i = 0; i < 4; i++)
+      {
+      buf = (gdb_byte *)lbtrs + scrsize * i;
+      regcache->raw_collect (regs->scr + i, (void *)buf);
+      }
+
+      /* Size base (EFLAG) = 4 * sizeof (scr).  */
+      buf = (gdb_byte *)lbtrs + scrsize * 4;
+      regcache->raw_collect (regs->EFLAG, (void *)buf);
+
+      /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG).  */
+      buf = (gdb_byte *)lbtrs + scrsize * 4 + efsize;
+      regno = regs->x86_top;
+    }
+  else if (regs->scr <= regno && regno < regs->scr + 4)
+    /* Offset offset (EFLAG) = sizeof (scr) * (regno - regs->scr).  */
+    buf = (gdb_byte *)lbtrs + scrsize * (regno - regs->scr);
+  else if (regs->EFLAG == regno)
+    /* Size base (EFLAG) = 4 * sizeof (scr).  */
+    buf = (gdb_byte *)lbtrs + scrsize * 4;
+  else if (regs->x86_top == regno)
+    /* Size base (x86_top) = 4 * sizeof (scr) + sizeof (EFLAG).  */
+    buf = (gdb_byte *)lbtrs + scrsize * 4 + efsize;
+  else
+    {
+    return;
+    }
+
+  regcache->raw_collect (regno, (void *)buf);
+}
+
+const struct regset loongarch_elf_lbtregset =
+{
+  NULL,
+  loongarch_supply_elf_lbtregset,
+  loongarch_fill_elf_lbtregset,
+};
+
+static void
+loongarch_supply_elf_lsxregset (const struct regset *r,
+				struct regcache *regcache, int regno,
+				const void *lsxrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->vr && sizeof (loongarch_elf_lsxregset_t) <= len);
+
+  const gdb_byte *buf = NULL;
+  int regsize = register_size (regcache->arch (), regs->vr);
+
+  if (regno == -1)
+    {
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (const gdb_byte *)lsxrs + regsize * i;
+      regcache->raw_supply (regs->vr + i, (const void *)buf);
+      }
+    }
+  else if (regs->vr <= regno && regno < regs->vr + 32)
+    {
+      buf = (const gdb_byte *)lsxrs + regsize * (regno - regs->vr);
+      regcache->raw_supply (regno, (const void *)buf);
+    }
+}
+
+static void
+loongarch_fill_elf_lsxregset (const struct regset *r,
+			      const struct regcache *regcache, int regno,
+			      void *lsxrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->vr && sizeof (loongarch_elf_lsxregset_t) <= len);
+
+  gdb_byte *buf = NULL;
+  int regsize = register_size (regcache->arch (), regs->vr);
+
+  if (regno == -1)
+    {
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (gdb_byte *)lsxrs + regsize * i;
+      regcache->raw_collect (regs->vr + i, (void *)buf);
+      }
+    }
+  else if (regs->vr <= regno && regno < regs->vr + 32)
+    {
+    buf = (gdb_byte *)lsxrs + regsize * (regno - regs->vr);
+    regcache->raw_collect (regno, (void *)buf);
+    }
+}
+
+const struct regset loongarch_elf_lsxregset =
+{
+  NULL,
+  loongarch_supply_elf_lsxregset,
+  loongarch_fill_elf_lsxregset,
+};
+
+static void
+loongarch_supply_elf_lasxregset (const struct regset *r,
+				 struct regcache *regcache, int regno,
+				 const void *lasxrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->xr && sizeof (loongarch_elf_lasxregset_t) <= len);
+
+  const gdb_byte *buf = NULL;
+  int regsize = register_size (regcache->arch (), regs->xr);
+
+  if (regno == -1)
+    {
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (const gdb_byte *)lasxrs + regsize * i;
+      regcache->raw_supply (regs->xr + i, (const void *)buf);
+      }
+    }
+  else if (regs->xr <= regno && regno < regs->xr + 32)
+    {
+    buf = (const gdb_byte *)lasxrs + regsize * (regno - regs->xr);
+    regcache->raw_supply (regno, (const void *)buf);
+    }
+}
+
+static void
+loongarch_fill_elf_lasxregset (const struct regset *r,
+			       const struct regcache *regcache, int regno,
+			       void *lasxrs, size_t len)
+{
+  auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+  gdb_assert (0 <= regs->xr && sizeof (loongarch_elf_lasxregset_t) <= len);
+
+  gdb_byte *buf = NULL;
+  int regsize = register_size (regcache->arch (), regs->xr);
+
+  if (regno == -1)
+    {
+    for (int i = 0; i < 32; i++)
+      {
+      buf = (gdb_byte *)lasxrs + regsize * i;
+      regcache->raw_collect (regs->xr + i, (void *)buf);
+      }
+    }
+  else if (regs->xr <= regno && regno < regs->xr + 32)
+    {
+    buf = (gdb_byte *)lasxrs + regsize * (regno - regs->xr);
+    regcache->raw_collect (regno, (void *)buf);
+    }
+}
+
+const struct regset loongarch_elf_lasxregset =
+{
+  NULL,
+  loongarch_supply_elf_lasxregset,
+  loongarch_fill_elf_lasxregset,
+};
+
+static void
+loongarch_linux_iterate_over_regset_sections (
+  struct gdbarch *gdbarch, iterate_over_regset_sections_cb *cb, void *cb_data,
+  const struct regcache *regcache)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  if (0 <= regs->r)
+    cb (".reg", sizeof (loongarch_elf_gregset_t),
+	sizeof (loongarch_elf_gregset_t), &loongarch_elf_gregset, NULL,
+	cb_data);
+  if (0 <= regs->f)
+    cb (".reg2", sizeof (loongarch_elf_fpregset_t),
+	sizeof (loongarch_elf_fpregset_t), &loongarch_elf_fpregset, NULL,
+	cb_data);
+  do
+    {
+      uint32_t t;
+      ULONGEST xfered_len;
+      if (target_xfer_partial (current_inferior ()->top_target (),
+			       /* current_top_target (),*/ TARGET_OBJECT_LARCH,
+			       "cpucfg", (gdb_byte *) &t, NULL, 0, sizeof (t),
+			       &xfered_len) != TARGET_XFER_OK)
+	break;
+      cb (".reg-loongarch-cpucfg", 64 * 4, 64 * 4, &loongarch_elf_cpucfgregset,
+	  "LoongArch CPU config", cb_data);
+    }
+  while (0);
+  if (0 <= regs->scr)
+    cb (".reg-loongarch-lbt", sizeof (loongarch_elf_lbtregset_t),
+	sizeof (loongarch_elf_lbtregset_t), &loongarch_elf_lbtregset,
+	"LoongArch Binary Translation", cb_data);
+  if (0 <= regs->vr)
+    cb (".reg-loongarch-lsx", sizeof (loongarch_elf_lsxregset_t),
+	sizeof (loongarch_elf_lsxregset_t), &loongarch_elf_lsxregset,
+	"LoongArch SIMD Extension", cb_data);
+  if (0 <= regs->xr)
+    cb (".reg-loongarch-lasx", sizeof (loongarch_elf_lasxregset_t),
+	sizeof (loongarch_elf_lasxregset_t), &loongarch_elf_lasxregset,
+	"LoongArch Advanced SIMD Extension", cb_data);
+}
+
+static const struct target_desc *
+loongarch_linux_core_read_description (struct gdbarch *gdbarch,
+				       struct target_ops *target, bfd *abfd)
+{
+  int rlen, fpu32, fpu64, lbt, lsx, lasx;
+
+  rlen = 64;
+  fpu32 = 0;
+
+  fpu64 = !!bfd_get_section_by_name (abfd, ".reg2");
+  lbt = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lbt");
+  lsx = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lsx");
+  lasx = !!bfd_get_section_by_name (abfd, ".reg-loongarch-lasx");
+
+  return loongarch_create_target_description (rlen, fpu32, fpu64, lbt, lsx,
+					      lasx);
+}
+
+static void
+loongarch_linux_lp64_sigframe_init (const struct tramp_frame *self,
+				    struct frame_info *this_frame,
+				    struct trad_frame_cache *this_cache,
+				    CORE_ADDR func)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  CORE_ADDR frame_sp = get_frame_sp (this_frame);
+
+  CORE_ADDR sigcontext_base = frame_sp + 224;
+  int i;
+
+  trad_frame_set_reg_addr (this_cache, regs->pc, sigcontext_base);
+  for (i = 0; i < 32; i++)
+    trad_frame_set_reg_addr (this_cache, regs->r + i,
+			     sigcontext_base + 8 + i * 8);
+
+  trad_frame_set_id (this_cache, frame_id_build (frame_sp, func));
+}
+
+static const struct tramp_frame loongarch_linux_lp64_rt_sigframe =
+{
+  SIGTRAMP_FRAME,
+  4,
+  { /* From $kernel/arch/loongarch/vdso/sigreturn.S.  */
+    /* ori	$r11, $r0, 0x8b(__NR_rt_sigreturn)  */
+    { 0x03822c0b, ULONGEST_MAX },
+    { 0x002b0000, ULONGEST_MAX }, /* syscall	0  */
+    { TRAMP_SENTINEL_INSN, ULONGEST_MAX } },
+  loongarch_linux_lp64_sigframe_init,
+  NULL
+};
+
+/* Return the current system call's number present in the
+   a7 register.  When the function fails, it returns -1.  */
+
+static LONGEST
+loongarch_linux_get_syscall_number (struct gdbarch *gdbarch,
+				    thread_info *thread)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  auto regs = &tdep->regs;
+  struct regcache *regcache = get_thread_regcache (thread);
+  LONGEST ret;
+
+  if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+    && (REG_VALID == regcache_cooked_read_signed (regcache, regs->r + 11, &ret)))
+      return ret;
+
+  return -1;
+}
+
+static CORE_ADDR
+loongarch_linux_syscall_next_pc (struct frame_info *frame)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  auto regs = &tdep->regs;
+  ULONGEST a7 = get_frame_register_unsigned (frame, regs->r + 11);
+
+  /* If we are about to make a sigreturn syscall, use the unwinder to
+     decode the signal frame.  */
+  if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+    && (a7 == 0x8b /* LP64: __NR_rt_sigreturn.  */))
+    return frame_unwind_caller_pc (get_current_frame ());
+
+  return -1;
+}
+
+static void
+loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  linux_init_abi (info, gdbarch, 0); /* FIXME displaced step support.  */
+
+  if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi))
+      set_solib_svr4_fetch_link_map_offsets (
+	gdbarch, svr4_ilp32_fetch_link_map_offsets);
+
+  else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+    {
+      set_solib_svr4_fetch_link_map_offsets (gdbarch,
+					     svr4_lp64_fetch_link_map_offsets);
+      tramp_frame_prepend_unwinder (gdbarch,
+				    &loongarch_linux_lp64_rt_sigframe);
+      tdep->syscall_next_pc = loongarch_linux_syscall_next_pc;
+
+      set_gdbarch_get_syscall_number (gdbarch,
+				      loongarch_linux_get_syscall_number);
+    }
+
+  /* GNU/Linux uses the dynamic linker included in the GNU C Library.  */
+  set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+  /* Enable TLS support.  */
+  set_gdbarch_fetch_tls_load_module_address (gdbarch,
+					     svr4_fetch_objfile_link_map);
+
+  set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT);
+
+  /* Core file support.  */
+  set_gdbarch_iterate_over_regset_sections (
+    gdbarch, loongarch_linux_iterate_over_regset_sections);
+  set_gdbarch_core_read_description (gdbarch,
+				     loongarch_linux_core_read_description);
+}
+
+void _initialize_loongarch_linux_tdep ();
+void
+_initialize_loongarch_linux_tdep ()
+{
+  gdbarch_register_osabi (
+    bfd_arch_loongarch,
+    bfd_mach_loongarch32 /* GDB may not care what arch variant is this.
+     So we specify DEFAULT_BFD_ARCH.  */
+    ,
+    GDB_OSABI_LINUX, loongarch_linux_init_abi);
+}
diff --git a/gdb/loongarch-linux-tdep.h b/gdb/loongarch-linux-tdep.h
new file mode 100644
index 0000000000..e3456a8fa4
--- /dev/null
+++ b/gdb/loongarch-linux-tdep.h
@@ -0,0 +1,48 @@
+/* GNU/Linux on LoongArch target support, prototypes.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_LINUX_TDEP_H
+#define LOONGARCH_LINUX_TDEP_H
+
+#include <regset.h>
+
+#define ELF_NGREG   45
+#define ELF_NFPREG  34
+
+typedef uint64_t loongarch_elf_gregset_t[ELF_NGREG];
+extern const struct regset loongarch_elf_gregset;
+
+typedef uint64_t loongarch_elf_fpregset_t[ELF_NFPREG];
+extern const struct regset loongarch_elf_fpregset;
+
+/* Regset variable size.  */
+extern const struct regset loongarch_elf_cpucfg;
+
+/* 4 SCRs + 4-byte EFLAG + 1-byte x86_top.  */
+typedef uint64_t loongarch_elf_lbtregset_t[5];
+extern const struct regset loongarch_elf_lbtregset;
+
+typedef uint64_t loongarch_elf_lsxregset_t[32 * 2];
+extern const struct regset loongarch_elf_lsxregset;
+
+typedef uint64_t loongarch_elf_lasxregset_t[32 * 4];
+extern const struct regset loongarch_elf_lasxregset;
+
+#endif
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
new file mode 100644
index 0000000000..a5ce3caced
--- /dev/null
+++ b/gdb/loongarch-tdep.c
@@ -0,0 +1,1864 @@
+/* Target-dependent code for GNU/Linux LoongArch.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+#include "frame.h"
+#include "inferior.h"
+#include "symtab.h"
+#include "value.h"
+#include "gdbcmd.h"
+#include "language.h"
+#include "gdbcore.h"
+#include "symfile.h"
+#include "objfiles.h"
+#include "gdbtypes.h"
+#include "target.h"
+#include "arch-utils.h"
+#include "regcache.h"
+#include "osabi.h"
+#include "block.h"
+#include "reggroups.h"
+#include "elf-bfd.h"
+#include "symcat.h"
+#include "dis-asm.h"
+#include "frame-unwind.h"
+#include "frame-base.h"
+#include "trad-frame.h"
+#include "infcall.h"
+#include "floatformat.h"
+#include "remote.h"
+#include "target-descriptions.h"
+#include "dwarf2/frame.h"
+#include "user-regs.h"
+#include "valprint.h"
+#include "gdbsupport/common-defs.h"
+#include "cli/cli-decode.h"
+#include "observable.h"
+#include "loongarch-tdep.h"
+#include "arch/loongarch.h"
+
+#include <algorithm>
+
+static int
+loongarch_rlen (struct gdbarch *gdbarch)
+{
+  if (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi))
+    return 64;
+  else if (EF_LOONGARCH_IS_ILP32 (gdbarch_tdep (gdbarch)->ef_abi))
+      return 32;
+  else
+      gdb_assert_not_reached ("unknown ABI");
+  return 0;
+}
+
+static insn_t
+loongarch_fetch_instruction (CORE_ADDR addr, int *errp)
+{
+  size_t insnlen = loongarch_insn_length (0);
+  gdb_byte buf[insnlen];
+  int err;
+  ULONGEST ret;
+
+  err = target_read_memory (addr, buf, insnlen);
+  if (errp != NULL)
+    *errp = err;
+  if (err != 0)
+    {
+      if (errp == NULL)
+	memory_error (TARGET_XFER_E_IO, addr);
+      return 0;
+    }
+  ret = extract_unsigned_integer (buf, insnlen, BFD_ENDIAN_LITTLE);
+  return ret;
+}
+
+static int
+loongarch_insn_is_branch_and_must_branch (insn_t insn)
+{
+  if ((insn & 0xfc000000) == 0x4c000000	    /* jirl r0:5,r5:5,s10:16<<2 */
+      || (insn & 0xfc000000) == 0x50000000  /* b sb0:10|10:16<<2 */
+      || (insn & 0xfc000000) == 0x54000000) /* bl sb0:10|10:16<<2 */
+    return 1;
+  return 0;
+}
+
+static int
+loongarch_insn_is_branch (insn_t insn)
+{
+  if (loongarch_insn_is_branch_and_must_branch (insn)
+      || (insn & 0xfc000000) == 0x40000000  /* beqz r5:5,sb0:5|10:16<<2 */
+      || (insn & 0xfc000000) == 0x44000000  /* bnez r5:5,sb0:5|10:16<<2 */
+      || (insn & 0xfc000300) == 0x48000000  /* bceqz c5:3,sb0:5|10:16<<2 */
+      || (insn & 0xfc000300) == 0x48000100  /* bcnez c5:3,sb0:5|10:16<<2 */
+      || (insn & 0xfc000000) == 0x58000000  /* beq r5:5,r0:5,sb10:16<<2 */
+      || (insn & 0xfc000000) == 0x5c000000  /* bne r5:5,r0:5,sb10:16<<2 */
+      || (insn & 0xfc000000) == 0x60000000  /* blt r5:5,r0:5,sb10:16<<2 */
+      || (insn & 0xfc000000) == 0x64000000  /* bge r5:5,r0:5,sb10:16<<2 */
+      || (insn & 0xfc000000) == 0x68000000  /* bltu r5:5,r0:5,sb10:16<<2 */
+      || (insn & 0xfc000000) == 0x6c000000) /* bgeu r5:5,r0:5,sb10:16<<2 */
+    return 1;
+  return 0;
+}
+
+static CORE_ADDR
+loongarch_next_pc_if_branch (struct regcache *regcache, CORE_ADDR cur_pc,
+			     insn_t insn)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  CORE_ADDR next_pc;
+
+  if ((insn & 0xfc000000) == 0x40000000	    /* beqz r5:5,sb0:5|10:16<<2 */
+      || (insn & 0xfc000000) == 0x44000000  /* bnez r5:5,sb0:5|10:16<<2 */
+      || (insn & 0xfc000300) == 0x48000000  /* bceqz c5:3,sb0:5|10:16<<2 */
+      || (insn & 0xfc000300) == 0x48000100) /* bcnez c5:3,sb0:5|10:16<<2 */
+    next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1);
+  else if ((insn & 0xfc000000) == 0x4c000000) /* jirl r0:5,r5:5,s10:16<<2 */
+    next_pc = regcache_raw_get_signed (
+		regcache, regs->r + loongarch_decode_imm ("5:5", insn, 0))
+	      + loongarch_decode_imm ("10:16<<2", insn, 1);
+  else if ((insn & 0xfc000000) == 0x50000000	 /* b sb0:10|10:16<<2 */
+	   || (insn & 0xfc000000) == 0x54000000) /* bl sb0:10|10:16<<2 */
+    next_pc = cur_pc + loongarch_decode_imm ("0:10|10:16<<2", insn, 1);
+  else if ((insn & 0xfc000000) == 0x58000000	/* beq r5:5,r0:5,sb10:16<<2 */
+	   || (insn & 0xfc000000) == 0x5c000000 /* bne r5:5,r0:5,sb10:16<<2 */
+	   || (insn & 0xfc000000) == 0x60000000 /* blt r5:5,r0:5,sb10:16<<2 */
+	   || (insn & 0xfc000000) == 0x64000000 /* bge r5:5,r0:5,sb10:16<<2 */
+	   || (insn & 0xfc000000) == 0x68000000 /* bltu r5:5,r0:5,sb10:16<<2 */
+	   || (insn & 0xfc000000)
+		== 0x6c000000) /* bgeu r5:5,r0:5,sb10:16<<2 */
+    next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1);
+  else
+    gdb_assert_not_reached ("I don't know what branch is this");
+
+  return next_pc;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LL/LLD
+   instruction and ending with a SC/SCD instruction.  If such a sequence
+   is found, attempt to step through it.  A breakpoint is placed at the end of
+   the sequence.  */
+
+static std::vector<CORE_ADDR>
+loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR pc)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  CORE_ADDR next_pc;
+  std::vector<CORE_ADDR> next_pcs;
+  insn_t insn = loongarch_fetch_instruction (pc, NULL);
+  size_t insnlen = loongarch_insn_length (insn);
+  int i, atomic_sequence_length, found_atomic_sequence_endpoint;
+
+  if ((insn & 0xff000000) != 0x20000000	    /* ll.w */
+      && (insn & 0xff000000) != 0x22000000) /* ll.d */
+    return {};
+
+  if (loongarch_debug)
+    fprintf_unfiltered (gdb_stdlog,
+			"Single step: PC: %s OK, I found ll\\.[wd] here. It's "
+			"atomic sequence?\n",
+			paddress (gdbarch, pc));
+
+  atomic_sequence_length = 30; /* Magic.  */
+  found_atomic_sequence_endpoint = 0;
+  for (pc += insnlen, i = 0; i < atomic_sequence_length; pc += insnlen, i++)
+    {
+      insn = loongarch_fetch_instruction (pc, NULL);
+      insnlen = loongarch_insn_length (insn);
+
+      if (loongarch_insn_is_branch_and_must_branch (insn))
+	{
+	  if (loongarch_debug)
+	    fprintf_unfiltered (
+	      gdb_stdlog,
+	      "Single step: PC: %s Must branch here. Treat it normally.\n",
+	      paddress (gdbarch, pc));
+	  break;
+	}
+      else if (loongarch_insn_is_branch (insn))
+	{
+	  next_pc = loongarch_next_pc_if_branch (regcache, pc, insn);
+
+	  if (loongarch_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Single step: PC: %s May branch inside and "
+				"target is %s. Breakpoint there.\n",
+				paddress (gdbarch, pc),
+				paddress (gdbarch, next_pc));
+
+	  next_pcs.push_back (next_pc);
+	}
+      else if ((insn & 0xff000000) == 0x21000000     /* sc.w */
+	       || (insn & 0xff000000) == 0x23000000) /* sc.d */
+	{
+	  found_atomic_sequence_endpoint = 1;
+	  next_pc = pc + insnlen;
+
+	  if (loongarch_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Single step: PC: %s I found sc\\.[wd] and "
+				"atomic sequence ends at here.\n"
+				"Breakpoint next pc: %s.\n",
+				paddress (gdbarch, pc),
+				paddress (gdbarch, next_pc));
+
+	  next_pcs.push_back (next_pc);
+	  break;
+	}
+    }
+
+  if (!found_atomic_sequence_endpoint)
+    {
+      if (loongarch_debug)
+	fprintf_unfiltered (
+	  gdb_stdlog,
+	  "Single step: PC: %s Not ends with sc\\.[wd] in %d insns?\n"
+	  "Treat it as not atomic sequence.\n",
+	  paddress (gdbarch, pc), atomic_sequence_length);
+
+      return {};
+    }
+
+  return next_pcs;
+}
+
+/* Implement LoongArch software single step.  */
+
+std::vector<CORE_ADDR>
+loongarch_software_single_step (struct regcache *regcache);
+std::vector<CORE_ADDR>
+loongarch_software_single_step (struct regcache *regcache)
+{
+  struct gdbarch *gdbarch = regcache->arch ();
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  CORE_ADDR pc = regcache_read_pc (regcache);
+  std::vector<CORE_ADDR> next_pcs
+    = loongarch_deal_with_atomic_sequence (regcache, pc);
+
+  if (!next_pcs.empty ())
+    return next_pcs;
+
+  insn_t insn = loongarch_fetch_instruction (pc, NULL);
+  size_t insnlen = loongarch_insn_length (insn);
+  CORE_ADDR next = pc + insnlen;
+
+  if ((insn & 0xffff8000) == 0x002b0000 && tdep->syscall_next_pc)
+    {
+      CORE_ADDR syscall_next = tdep->syscall_next_pc (get_current_frame ());
+      if (syscall_next != -1)
+	{
+	  if (loongarch_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"PC: %s Syscall found. Next pc is %s.\n",
+				paddress (gdbarch, pc),
+				paddress (gdbarch, syscall_next));
+	  return {syscall_next};
+	}
+    }
+
+  if (loongarch_insn_is_branch (insn))
+    {
+      CORE_ADDR branch_tgt = loongarch_next_pc_if_branch (regcache, pc, insn);
+      if (loongarch_debug)
+	fprintf_unfiltered (
+	  gdb_stdlog, "PC: %s Next pc is %s if branch, %s for non-branch.\n",
+	  paddress (gdbarch, pc), paddress (gdbarch, branch_tgt),
+	  paddress (gdbarch, next));
+      return {next, branch_tgt};
+    }
+  else
+    {
+      if (loongarch_debug)
+	fprintf_unfiltered (gdb_stdlog, "PC: %s Next pc is %s.\n",
+			    paddress (gdbarch, pc), paddress (gdbarch, next));
+      return {next};
+    }
+}
+
+/* Callback function for user_reg_add.  */
+
+static struct value *
+value_of_loongarch_user_reg (struct frame_info *frame, const void *baton)
+{
+  return value_of_register ((long long) baton, frame);
+}
+
+/* Implement the register_name gdbarch method.  */
+
+static const char *
+loongarch_register_name (struct gdbarch *gdbarch, int regnum)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+
+  if ((0 <= regs->r && regs->r <= regnum && regnum < regs->r + 32)
+    && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi)))
+	return loongarch_r_lp64_name[regnum - regs->r] + 1;
+
+  else if ((0 <= regs->f && regs->f <= regnum && regnum < regs->f + 32)
+    && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi)))
+	return loongarch_f_lp64_name[regnum - regs->f] + 1;
+
+  return tdesc_register_name (gdbarch, regnum);
+}
+
+/* Analyze the function prologue from START_PC to LIMIT_PC.  Builds
+   the associated FRAME_CACHE if not null.
+   Return the address of the first instruction past the prologue.  */
+
+static CORE_ADDR
+loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc,
+			 CORE_ADDR limit_pc, struct frame_info *this_frame,
+			 struct trad_frame_cache *this_cache)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  int rlen_is_64b = (loongarch_rlen (gdbarch) == 64);
+
+  CORE_ADDR cur_pc, prologue_end = 0;
+  insn_t insn;
+  size_t insnlen;
+
+  int sp = regs->sp - regs->r;
+
+  int fp = sp; /* Frame pointer.  */
+  long frame_offset = 0;
+  int non_prologue_insns = 0;
+  int cfa_unknown = 0;
+
+  /* Try to trace li.  */
+  int64_t r_value[32] = {0};
+  int r_value_known[32] = {1, 0};
+
+  long r_cfa_offset[32] = {0};
+  int r_cfa_offset_p[32] = {0};
+
+  long f_cfa_offset[32] = {0};
+  int f_cfa_offset_p[32] = {0};
+
+  if (start_pc + 80 < limit_pc)
+    limit_pc = start_pc + 80;
+
+  for (cur_pc = start_pc; cur_pc < limit_pc; cur_pc += insnlen)
+    {
+      int rd, rj, rk;
+      int64_t si12, si20, si14;
+
+      insn = loongarch_fetch_instruction (cur_pc, NULL);
+      insnlen = loongarch_insn_length (insn);
+
+      rd = loongarch_decode_imm ("0:5", insn, 0);
+      rj = loongarch_decode_imm ("5:5", insn, 0);
+      rk = loongarch_decode_imm ("10:5", insn, 0);
+      si12 = loongarch_decode_imm ("10:12", insn, 1);
+      si20 = loongarch_decode_imm ("5:20", insn, 1);
+      si14 = loongarch_decode_imm ("10:14<<2", insn, 1);
+
+      if ((((insn & 0xffc00000) == 0x02800000 /* addi.w fp,fp,si12 */
+	    && !rlen_is_64b)
+	   || ((insn & 0xffc00000) == 0x02c00000 /* addi.d fp,fp,si12 */
+	       && rlen_is_64b))
+	  && rd == fp && rj == fp)
+	{
+	  if (si12 < 0)
+	    frame_offset -= si12;
+	  else
+	    /* Exit loop if a positive stack adjustment is found, which
+	       usually means that the stack cleanup code in the function
+	       epilogue is reached.  */
+	    break;
+	  prologue_end = cur_pc + insnlen;
+	}
+      else if ((((insn & 0xffc00000) == 0x29800000 /* st.w rd,fp,si12 */
+		 && !rlen_is_64b)
+		|| ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,fp,si12 */
+		    && rlen_is_64b))
+	       && rj == fp)
+	{
+	  if (!r_cfa_offset_p[rd] && !r_value_known[rd])
+	    r_cfa_offset[rd] = si12 - frame_offset, r_cfa_offset_p[rd] = 1;
+	  prologue_end = cur_pc + insnlen;
+	}
+      else if ((((insn & 0xff000000) == 0x25000000 /* stptr.w rd,fp,si14 */
+		 && !rlen_is_64b)
+		|| ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,fp,si14 */
+		    && rlen_is_64b))
+	       && rj == fp)
+	{
+	  if (!r_cfa_offset_p[rd] && !r_value_known[rd])
+	    r_cfa_offset[rd] = si14 - frame_offset, r_cfa_offset_p[rd] = 1;
+	  prologue_end = cur_pc + insnlen;
+	}
+      else if (((insn & 0xffc00000) == 0x2b400000     /* fst.s fd,fp,si12 */
+		|| (insn & 0xffc00000) == 0x2bc00000) /* fst.d fd,fp,si12 */
+	       && rj == fp)
+	{
+	  if (!f_cfa_offset_p[rd])
+	    f_cfa_offset[rd] = si12 - frame_offset, f_cfa_offset_p[rd] = 1;
+	}
+      else if ((((insn & 0xffff8000) == 0x00110000 /* sub.w fp,fp,rk */
+		 && !rlen_is_64b)
+		|| ((insn & 0xffff8000) == 0x00118000 /* sub.d fp,fp,rk */
+		    && rlen_is_64b))
+	       && rd == fp && rj == fp)
+	{
+	  if (r_value_known[rk])
+	    {
+	      frame_offset += r_value[rk];
+	      prologue_end = cur_pc + insnlen;
+	    }
+	  else
+	    cfa_unknown = 1;
+	}
+      else if ((insn & 0xffff8000) == 0x00150000 /* or rd,fp,$r0 */
+	       && rj == fp && rk == 0)
+	{
+	  fp = rd;
+	  prologue_end = cur_pc + insnlen;
+	}
+      else if ((insn & 0xffc00000) == 0x02800000) /* addi.w rd,rj,si12 */
+	{
+	  if (r_value_known[rj] && rd != 0)
+	    r_value[rd] = (int32_t) (r_value[rj] + si12),
+	    r_value_known[rd] = 1;
+	}
+      else if ((insn & 0xffc00000) == 0x03800000) /* ori rd,rj,si12 */
+	{
+	  if (r_value_known[rj] && rd != 0)
+	    r_value[rd] = r_value[rj] | (si12 & 0xfff), r_value_known[rd] = 1;
+	}
+      else if ((insn & 0xfe000000) == 0x14000000) /* lu12i.w rd,si20 */
+	{
+	  if (rd != 0)
+	    r_value[rd] = si20 << 12, r_value_known[rd] = 1;
+	}
+      else if ((insn & 0xfe000000) == 0x16000000) /* lu32i.d rd,si20 */
+	{
+	  if (r_value_known[rd] && rd != 0)
+	    r_value[rd] = (r_value[rd] & 0xffffffff) | (si20 << 32),
+	    r_value_known[rd] = 1;
+	}
+      else if ((insn & 0xffc00000) == 0x03000000) /* lu52i.d rd,rj,si12 */
+	{
+	  if (r_value_known[rj] && rd != 0)
+	    r_value[rd] = (r_value[rj] & 0xfffffffffffff) | (si12 << 52),
+	    r_value_known[rd] = 1;
+	}
+      else if (loongarch_insn_is_branch (insn))
+	break; /* Shrink-wrap or end of prologue in a basic block.  */
+      else
+	non_prologue_insns++;
+
+      /* 4 INSNs for 'la' and one for some other.  */
+      if (5 < non_prologue_insns)
+	break;
+    }
+
+  if (loongarch_debug)
+    {
+      const char *fun_name;
+      find_pc_partial_function (start_pc, &fun_name, NULL, NULL);
+      fprintf_unfiltered (gdb_stdlog,
+			  "Prologue Analyze: -- Start -- Callee [%s] %s\n",
+			  fun_name ? fun_name : "<unknown>",
+			  paddress (gdbarch, start_pc));
+    }
+
+  do
+    {
+      int i;
+      CORE_ADDR cfa = -1;
+
+      if (!(this_frame && this_cache))
+	break;
+
+      if (!cfa_unknown)
+	{
+	  try
+	    {
+	      cfa = get_frame_register_signed (this_frame, regs->r + fp)
+		    + frame_offset;
+	    }
+	  catch (const gdb_exception_error &ex)
+	    {
+	      cfa_unknown = 1;
+	      if (ex.error != NOT_AVAILABLE_ERROR)
+		throw;
+	    }
+
+	  if (loongarch_debug)
+	    fprintf_unfiltered (
+	      gdb_stdlog,
+	      "Prologue Analyze: CFA is (frame pointer $%s + 0x%lx) = %s\n",
+	      gdbarch_register_name (gdbarch, regs->r + fp),
+	      (long) frame_offset,
+	      cfa_unknown ? "<unknown>" : paddress (gdbarch, cfa));
+	}
+      else if (loongarch_debug)
+	fprintf_unfiltered (gdb_stdlog,
+			    "Prologue Analyze: Unknown stack frame size, so "
+			    "can't get known CFA\n");
+
+      if (r_cfa_offset_p[1] && !cfa_unknown)
+	{
+	  CORE_ADDR ret_saved = cfa + r_cfa_offset[1];
+	  trad_frame_set_reg_addr (this_cache, gdbarch_pc_regnum (gdbarch),
+				   ret_saved);
+	  if (loongarch_debug)
+	    fprintf_unfiltered (
+	      gdb_stdlog,
+	      "Prologue Analyze: Return addr saved in (CFA - 0x%lx) = %s\n",
+	      -r_cfa_offset[1], paddress (gdbarch, ret_saved));
+	}
+      else if (r_cfa_offset_p[1] /* && cfa_unknown */)
+	{
+	  if (loongarch_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Prologue Analyze: Return addr saved in (CFA "
+				"- 0x%lx), but CFA is unknown\n",
+				-r_cfa_offset[1]);
+	}
+      else
+	{
+	  trad_frame_set_reg_realreg (this_cache, gdbarch_pc_regnum (gdbarch),
+				      regs->r + 1);
+	  if (loongarch_debug)
+	    fprintf_unfiltered (gdb_stdlog,
+				"Prologue Analyze: No found $r1 pushed in "
+				"stack. Return addr saved in $r1\n");
+	}
+
+      if (cfa_unknown)
+	{
+	  trad_frame_set_this_base (this_cache, -1);
+	  break;
+	}
+
+      trad_frame_set_reg_value (this_cache, gdbarch_sp_regnum (gdbarch),
+				(LONGEST) cfa);
+      trad_frame_set_this_base (this_cache, cfa);
+
+      if (loongarch_debug)
+	fprintf_unfiltered (
+	  gdb_stdlog,
+	  "Prologue Analyze: Where caller's registers saved as follow:\n");
+
+      for (i = 0; i < 32; i++)
+	if (r_cfa_offset_p[i] && i != 1)
+	  {
+	    trad_frame_set_reg_addr (this_cache, regs->r + i,
+				     cfa + r_cfa_offset[i]);
+	    if (loongarch_debug)
+	      fprintf_unfiltered (
+		gdb_stdlog,
+		"Prologue Analyze: $%s: saved in (CFA - 0x%lx) = %s\n",
+		gdbarch_register_name (gdbarch, regs->r + i), -r_cfa_offset[i],
+		paddress (gdbarch, cfa + r_cfa_offset[i]));
+	  }
+
+      if (regs->f <= 0)
+	for (i = 0; i < 32; i++)
+	  {
+	    if (f_cfa_offset_p[i])
+	      trad_frame_set_reg_addr (this_cache, regs->f + i,
+				       cfa + f_cfa_offset[i]);
+	    if (loongarch_debug)
+	      fprintf_unfiltered (
+		gdb_stdlog,
+		"Prologue Analyze: $%s: saved in (CFA - 0x%lx) = %s\n",
+		gdbarch_register_name (gdbarch, regs->f + i), -f_cfa_offset[i],
+		paddress (gdbarch, cfa + f_cfa_offset[i]));
+	  }
+    }
+  while (0);
+
+  if (loongarch_debug)
+    fprintf_unfiltered (gdb_stdlog, "Prologue Analyze: -- End -- %s\n",
+			paddress (gdbarch, cur_pc));
+
+  return prologue_end ? prologue_end : cur_pc;
+}
+
+/* Implement the loongarch_skip_prologue gdbarch method.  */
+
+/* To skip prologues, I use this predicate.  Returns either PC itself
+   if the code at PC does not look like a function prologue; otherwise
+   returns an address that (if we're lucky) follows the prologue.  If
+   LENIENT, then we must skip everything which is involved in setting
+   up the frame (it's OK to skip more, just so long as we don't skip
+   anything which might clobber the registers which are being saved.
+   We must skip more in the case where part of the prologue is in the
+   delay slot of a non-prologue instruction).  */
+
+static CORE_ADDR
+loongarch_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc)
+{
+  CORE_ADDR limit_pc;
+  CORE_ADDR func_addr;
+
+  /* See if we can determine the end of the prologue via the symbol table.
+     If so, then return either PC, or the PC after the prologue, whichever
+     is greater.  */
+  if (find_pc_partial_function (pc, NULL, &func_addr, NULL))
+    {
+      CORE_ADDR post_prologue_pc
+	= skip_prologue_using_sal (gdbarch, func_addr);
+      if (post_prologue_pc != 0)
+	return std::max (pc, post_prologue_pc);
+    }
+
+  /* Can't determine prologue from the symbol table, need to examine
+     instructions.  */
+
+  /* Find an upper limit on the function prologue using the debug
+     information.  If the debug information could not be used to provide
+     that bound, then use an arbitrary large number as the upper bound.  */
+  limit_pc = skip_prologue_using_sal (gdbarch, pc);
+  if (limit_pc == 0)
+    limit_pc = pc + 100; /* Magic.  */
+
+  return loongarch_scan_prologue (gdbarch, pc, limit_pc, NULL, NULL);
+}
+
+/* Adjust the address downward (direction of stack growth) so that it
+   is correctly aligned for a new stack frame.  */
+static CORE_ADDR
+loongarch_frame_align (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  return align_down (addr, 16);
+}
+
+/* Implement the unwind_pc gdbarch method.  */
+
+static CORE_ADDR
+loongarch_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_signed (next_frame,
+				       gdbarch_pc_regnum (gdbarch));
+}
+
+/* Implement the unwind_sp gdbarch method.  */
+
+static CORE_ADDR
+loongarch_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
+{
+  return frame_unwind_register_signed (next_frame,
+				       gdbarch_sp_regnum (gdbarch));
+}
+
+/* Implement the dummy_id gdbarch method.  */
+
+static struct frame_id
+loongarch_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
+{
+  return frame_id_build (
+    get_frame_register_signed (this_frame, gdbarch_sp_regnum (gdbarch)),
+    get_frame_pc (this_frame));
+}
+
+/* Generate, or return the cached frame cache for the loongarch frame
+   unwinder.  */
+
+static struct trad_frame_cache *
+loongarch_frame_cache (struct frame_info *this_frame, void **this_cache)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  struct trad_frame_cache *cache;
+  CORE_ADDR pc, start_addr, stack_addr;
+
+  if (*this_cache != NULL)
+    return (struct trad_frame_cache *) *this_cache;
+  cache = trad_frame_cache_zalloc (this_frame);
+  *this_cache = cache;
+
+  pc = get_frame_address_in_block (this_frame);
+  if (find_pc_partial_function (pc, NULL, &start_addr, NULL))
+    {
+      loongarch_scan_prologue (gdbarch, start_addr, pc, this_frame, cache);
+      stack_addr = trad_frame_get_this_base (cache);
+      trad_frame_set_id (cache,
+			 stack_addr == -1
+			   ? frame_id_build_unavailable_stack (start_addr)
+			   : frame_id_build (stack_addr, start_addr));
+    }
+  else
+    {
+      auto regs = &gdbarch_tdep (gdbarch)->regs;
+      trad_frame_set_reg_realreg (cache, regs->ra, -2 /* TF_REG_UNKNOWN */);
+      trad_frame_set_reg_realreg (cache, gdbarch_pc_regnum (gdbarch),
+				  regs->ra);
+
+      trad_frame_set_id (cache, frame_id_build_unavailable_stack (pc));
+    }
+  return cache;
+}
+
+/* Implement the this_id callback for loongarch frame unwinder.  */
+
+static void
+loongarch_frame_this_id (struct frame_info *this_frame, void **prologue_cache,
+			 struct frame_id *this_id)
+{
+  struct trad_frame_cache *info;
+
+  info = loongarch_frame_cache (this_frame, prologue_cache);
+  trad_frame_get_id (info, this_id);
+}
+
+/* Implement the prev_register callback for loongarch frame unwinder.  */
+
+static struct value *
+loongarch_frame_prev_register (struct frame_info *this_frame,
+			       void **prologue_cache, int regnum)
+{
+  struct trad_frame_cache *info;
+
+  info = loongarch_frame_cache (this_frame, prologue_cache);
+  return trad_frame_get_register (info, this_frame, regnum);
+}
+
+static const struct frame_unwind loongarch_frame_unwind = {
+  "loongarch prologue",
+  /*.type	  =*/NORMAL_FRAME,
+  /*.stop_reason   =*/default_frame_unwind_stop_reason,
+  /*.this_id       =*/loongarch_frame_this_id,
+  /*.prev_register =*/loongarch_frame_prev_register,
+  /*.unwind_data   =*/NULL,
+  /*.sniffer       =*/default_frame_sniffer,
+  /*.dealloc_cache =*/NULL,
+  /*.prev_arch     =*/NULL,
+};
+
+typedef struct stack_data_t
+{
+  const gdb_byte *addr = NULL;
+  int len = 0;
+  bool ref = false;
+} stack_data_t;
+
+static void
+pass_on_stack (std::vector<stack_data_t> &stack, const gdb_byte *val, int len,
+	       int align, bool ref = false)
+{
+  stack_data_t buf;
+  buf.addr = val;
+  buf.len = align_up (len, align);
+  buf.ref = ref;
+
+  stack.push_back (buf);
+}
+
+static void
+pass_on_reg (struct regcache *regcache, int regno, const gdb_byte *val,
+	     int len)
+{
+  gdb_byte reg[32];
+  memset (reg, 0, sizeof (reg));
+  memcpy (reg, val, len);
+  regcache->cooked_write (regno, reg);
+}
+
+static void
+pass_small_struct_on_reg (struct gdbarch *gdbarch, struct type *tp,
+			  const gdb_byte *data, std::vector<stack_data_t> &gp,
+			  std::vector<stack_data_t> &fp, int &bitpos)
+{
+  const int rlen = loongarch_rlen (gdbarch) / 8;
+  int len = TYPE_LENGTH (tp);
+
+  gdb_assert (len <= 2 * rlen);
+  // gdb_assert (tp->code () == TYPE_CODE_STRUCT);
+
+  for (int i = 0; i < tp->num_fields (); i++)
+    {
+      stack_data_t elm;
+      field fd = tp->field (i);
+      struct type *t = fd.type ();
+
+      if (t->code () == TYPE_CODE_STRUCT)
+	pass_small_struct_on_reg (gdbarch, t, data, gp, fp, bitpos);
+      else if (t->code () == TYPE_CODE_FLT)
+	{
+	  elm.addr = data;
+	  elm.len = TYPE_LENGTH (t);
+	  data = data + elm.len;
+	  fp.push_back (elm);
+	  bitpos = 0;
+	}
+      else
+	{
+	  len = TYPE_LENGTH (t);
+
+	  if (fd.bitsize != 0)
+	    {
+	      if (bitpos == 0
+		  || fd.bitsize > align_up (fd.m_loc.bitpos - bitpos, 8))
+		{
+		  len = fd.bitsize / TARGET_CHAR_BIT + 1;
+		  bitpos = fd.m_loc.bitpos;
+		}
+	      else
+		continue;
+	    }
+	  else
+	    {
+	      bitpos = fd.m_loc.bitpos + len * TARGET_CHAR_BIT;
+	    }
+	  /* Merge size < rlen.  */
+
+	  if (!gp.empty () && (gp.back ().addr + gp.back ().len == data)
+	      && (gp.back ().len + len <= rlen))
+	    {
+	      gp.back ().len += len;
+	      data = data + len;
+	    }
+	  else
+	    {
+	      elm.addr = data;
+	      elm.len = len;
+	      data = data + elm.len;
+	      gp.push_back (elm);
+	    }
+	}
+    }
+}
+
+static bool
+try_pass_small_struct_on_reg (struct gdbarch *gdbarch,
+			      struct regcache *regcache, struct value *arg,
+			      int &gp, int &fp, int gp_max, int fp_max)
+{
+  const int rlen = loongarch_rlen (gdbarch) / 8;
+  struct type *a_type = check_typedef (value_type (arg));
+  int len = TYPE_LENGTH (a_type);
+  const gdb_byte *val = value_contents (arg);
+  int bitpos = 0;
+
+  std::vector<stack_data_t> gpreg;
+  std::vector<stack_data_t> fpreg;
+
+  gdb_assert (len <= 2 * rlen);
+  // gdb_assert (a_type->code () == TYPE_CODE_STRUCT);
+
+  pass_small_struct_on_reg (gdbarch, a_type, val, gpreg, fpreg, bitpos);
+
+  if (gp + gpreg.size () - 1 < gp_max && fp + fpreg.size () - 1 < fp_max)
+    {
+      for (auto it : gpreg)
+	{
+	  pass_on_reg (regcache, gp, it.addr, it.len);
+	  gp++;
+	}
+      for (auto it : fpreg)
+	{
+	  pass_on_reg (regcache, fp, it.addr, it.len);
+	  fp++;
+	}
+      return true;
+    }
+  return false;
+}
+
+/* Implement the push dummy call gdbarch callback.  */
+
+static CORE_ADDR
+loongarch_lp32lp64_push_dummy_call (
+  struct gdbarch *gdbarch, struct value *function, struct regcache *regcache,
+  CORE_ADDR bp_addr, int nargs, struct value **args, CORE_ADDR sp,
+  function_call_return_method return_method, CORE_ADDR struct_addr)
+{
+  const int rlen = loongarch_rlen (gdbarch) / 8;
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  int gp = regs->r + 4;	     /* $a0 = $r4 = regs->r + 4 */
+  int fp = regs->f;	     /* $fa0 */
+  const int gp_max = gp + 8; /* gpr $a0 ~ $a7 ($r4 ~ $r11) */
+  const int fp_max = fp + 8; /* fpr $fa0 ~ $fa7 */
+  std::vector<stack_data_t> stack;
+  int vec_insn = 0;
+
+  {
+    if (return_method != return_method_normal)
+      {
+	regcache_cooked_write_unsigned (regcache, gp++, struct_addr);
+      }
+
+    if (return_method == return_method_hidden_param)
+      {
+	args++;
+	nargs--;
+      }
+  }
+  regcache_cooked_write_signed (regcache, regs->ra, bp_addr);
+
+  struct type *f_type = check_typedef (value_type (function));
+
+  for (int i = 0; i < nargs; i++)
+    {
+      struct value *arg = args[i];
+      struct type *a_type = check_typedef (value_type (arg));
+      int len = TYPE_LENGTH (a_type);
+      const gdb_byte *val = value_contents (arg);
+
+      if (f_type->has_varargs () && i >= f_type->num_fields ())
+	{
+	  if (len == 2 * rlen && (gp & 1))
+	    gp++;
+	}
+
+      switch (a_type->code ())
+	{
+	case TYPE_CODE_INT:
+	case TYPE_CODE_BOOL:
+	case TYPE_CODE_CHAR:
+	case TYPE_CODE_RANGE:
+	case TYPE_CODE_ENUM:
+	case TYPE_CODE_PTR:
+	  if (gp < gp_max)
+	    {
+	      if (a_type->is_unsigned ())
+		{
+		  ULONGEST data
+		    = extract_unsigned_integer (val, len, BFD_ENDIAN_LITTLE);
+		  regcache_cooked_write_unsigned (regcache, gp++, data);
+		}
+	      else
+		{
+		  LONGEST data
+		    = extract_signed_integer (val, len, BFD_ENDIAN_LITTLE);
+		  regcache_cooked_write_signed (regcache, gp++, data);
+		}
+	    }
+	  else
+	    {
+	      pass_on_stack (stack, val, len, rlen);
+	    }
+	  break;
+	case TYPE_CODE_FLT:
+	  if (len <= rlen)
+	    {
+	      if (fp < fp_max)
+		pass_on_reg (regcache, fp++, val, len);
+	      else if (gp < gp_max)
+		pass_on_reg (regcache, gp++, val, len);
+	      else
+		pass_on_stack (stack, val, len, rlen);
+	    }
+	  /* Long double like struct.  */
+	  else
+	    {
+	      if (gp < gp_max - 1)
+		{
+		  pass_on_reg (regcache, gp++, val, rlen);
+		  pass_on_reg (regcache, gp++, val + rlen, len - rlen);
+		}
+	      else
+		pass_on_stack (stack, val, len, rlen);
+	    }
+	  break;
+	case TYPE_CODE_ARRAY:
+	  /* lsx */
+	  if (a_type->is_vector () && len == vec_insn && vec_insn == 16
+	      && fp < fp_max)
+	    {
+	      pass_on_reg (regcache, regs->vr + (fp++ - regs->f), val, len);
+	    }
+	  /* lasx */
+	  else if (a_type->is_vector () && len == vec_insn && vec_insn == 32
+		   && fp < fp_max)
+	    {
+	      pass_on_reg (regcache, regs->xr + (fp++ - regs->f), val, len);
+	    }
+	  /* scalar */
+	  else
+	    {
+	      if (len > rlen * 2)
+		{
+		  /* Address on register, data on stack.  */
+		  sp = align_down (sp - len, rlen);
+		  write_memory (sp, val, len);
+		  if (gp < gp_max)
+		    pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen);
+		  else
+		    pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen,
+				   true);
+		}
+	      else
+		{
+		  if (len <= rlen && gp < gp_max)
+		    {
+		      pass_on_reg (regcache, gp++, val, len);
+		    }
+		  else if (gp + 1 < gp_max)
+		    {
+		      pass_on_reg (regcache, gp++, val, rlen);
+		      pass_on_reg (regcache, gp++, val + rlen, rlen);
+		    }
+		  else
+		    {
+		      pass_on_stack (stack, val, len, rlen);
+		    }
+		}
+	    }
+	  break;
+	case TYPE_CODE_STRUCT:
+	case TYPE_CODE_UNION:
+	  if (len > rlen * 2)
+	    {
+	      /* Address on register, data on stack.  */
+	      sp = align_down (sp - len, rlen);
+	      write_memory (sp, val, len);
+	      if (gp < gp_max)
+		pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen);
+	      else
+		pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen, true);
+	    }
+	  else
+	    {
+	      if (!try_pass_small_struct_on_reg (gdbarch, regcache, arg, gp,
+						 fp, gp_max, fp_max))
+		{
+		  pass_on_stack (stack, val, len, rlen);
+		}
+	    }
+	  break;
+	case TYPE_CODE_COMPLEX:
+	  {
+	    /* Two fpr or  mem.  */
+	    struct type *t_type = check_typedef (TYPE_TARGET_TYPE (a_type));
+	    int tlen = TYPE_LENGTH (t_type);
+
+	    if (tlen < rlen)
+	      {
+		if (fp + 1 < fp_max)
+		  {
+		    pass_on_reg (regcache, fp++, (const gdb_byte *) val, tlen);
+		    pass_on_reg (regcache, fp++, (const gdb_byte *) val + tlen,
+				 tlen);
+		  }
+		else if (gp < gp_max)
+		  {
+		    pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen);
+		  }
+		else
+		  {
+		    pass_on_stack (stack, val, len, rlen);
+		  }
+	      }
+	    else if (tlen == rlen)
+	      {
+		if (fp + 1 < fp_max)
+		  {
+		    pass_on_reg (regcache, fp++, (const gdb_byte *) val, tlen);
+		    pass_on_reg (regcache, fp++, (const gdb_byte *) val + tlen,
+				 tlen);
+		  }
+		else if (gp < gp_max)
+		  {
+		    pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen);
+		    pass_on_reg (regcache, gp++, (const gdb_byte *) val + rlen,
+				 rlen);
+		  }
+		else
+		  {
+		    pass_on_stack (stack, val, len, rlen);
+		  }
+	      }
+	    else
+	      {
+		sp = align_down (sp - len, rlen);
+		write_memory (sp, val, len);
+		if (gp < gp_max)
+		  pass_on_reg (regcache, gp++, (const gdb_byte *) &sp, rlen);
+		else
+		  {
+		    pass_on_stack (stack, (const gdb_byte *) sp, rlen, rlen,
+				   true);
+		  }
+	      }
+	  }
+	  break;
+	default:
+	  break;
+	}
+    }
+
+  for (auto it : stack)
+    sp = align_down (sp - it.len, rlen);
+
+  sp = align_down (sp, 16);
+  CORE_ADDR tsp = sp;
+  for (auto it : stack)
+    {
+      if (it.ref)
+	write_memory (tsp, (const gdb_byte *) &it.addr, it.len);
+      else
+	write_memory (tsp, it.addr, it.len);
+      tsp += it.len;
+      stack.pop_back ();
+    }
+  regcache_cooked_write_unsigned (regcache, regs->sp, sp);
+  return sp;
+}
+
+static void
+loongarch_xfer_reg_part (struct regcache *regcache, int reg_num, int len,
+			 gdb_byte *readbuf, size_t readbuf_off,
+			 const gdb_byte *writebuf, size_t writebuf_off)
+{
+  if (readbuf)
+    regcache->cooked_read_part (reg_num, 0, len, readbuf + readbuf_off);
+  if (writebuf)
+    regcache->cooked_write_part (reg_num, 0, len, writebuf + writebuf_off);
+}
+
+static enum return_value_convention
+loongarch_lp64_return_value (struct gdbarch *gdbarch, struct value *function,
+			     struct type *type, struct regcache *regcache,
+			     gdb_byte *readbuf, const gdb_byte *writebuf)
+{
+  const size_t rlen = loongarch_rlen (gdbarch) / 8;
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  size_t len = TYPE_LENGTH (type);
+  enum type_code typecode = type->code ();
+  int fpu_exist = 0 <= regs->f;
+  int fv = fpu_exist ? regs->f : regs->r + 4;
+
+  gdb_assert (8 <= sizeof (LONGEST));
+
+  gdb_assert (!fpu_exist || register_size (gdbarch, regs->f) == rlen);
+
+  if (2 * rlen < len)
+    return RETURN_VALUE_STRUCT_CONVENTION;
+
+  if ((typecode == TYPE_CODE_FLT
+       || (typecode == TYPE_CODE_STRUCT && type->num_fields () == 1
+	   && check_typedef (type->field (0).type ())->code ()
+		== TYPE_CODE_FLT))
+      && len <= rlen /* FIXME: May fpu32 on LoongArch32.  */)
+    /* If $fv0 could fit in.  */
+    loongarch_xfer_reg_part (regcache, fv, len, readbuf, 0, writebuf, 0);
+  else if ((typecode == TYPE_CODE_FLT
+	    || (typecode == TYPE_CODE_STRUCT && type->num_fields () == 1
+		&& check_typedef (type->field (0).type ())->code ()
+		     == TYPE_CODE_FLT))
+	   && rlen < len && len <= 2 * rlen)
+    /* For 'long double' on fpu64 or 'double' on fpu32,
+       '$fv0 | $fv1' is that.  */
+    /* Long double pass on $a0 and $a1.  */
+    if (typecode == TYPE_CODE_FLT && len == 2 * rlen)
+      {
+	loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, readbuf, 0,
+				 writebuf, 0),
+	  loongarch_xfer_reg_part (regcache, regs->r + 4 + 1, len - rlen,
+				   readbuf, rlen, writebuf, rlen);
+      }
+    else
+      {
+	loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0, writebuf, 0),
+	  loongarch_xfer_reg_part (regcache, fv + 1, len - rlen, readbuf, rlen,
+				   writebuf, rlen);
+      }
+  else if (typecode == TYPE_CODE_STRUCT && type->num_fields () == 2
+	   && check_typedef (type->field (0).type ())->code () == TYPE_CODE_FLT
+	   && check_typedef (type->field (1).type ())->code ()
+		== TYPE_CODE_FLT)
+    {
+      /* For structure with two float member,
+	 $fv0 is the 1st member and $fv1 is the 2nd member.  */
+      int off = FIELD_BITPOS (type->field (1)) / TARGET_CHAR_BIT;
+      int len1 = TYPE_LENGTH (check_typedef (type->field (0).type ()));
+      int len2 = TYPE_LENGTH (check_typedef (type->field (1).type ()));
+      loongarch_xfer_reg_part (regcache, fv, len1, readbuf, 0, writebuf, 0);
+      loongarch_xfer_reg_part (regcache, fv + 1, len2, readbuf, off, writebuf,
+			       off);
+    }
+  else if (typecode == TYPE_CODE_COMPLEX
+	   && (check_typedef (TYPE_TARGET_TYPE (type)))->code ()
+		== TYPE_CODE_FLT)
+    /* For '_Complex', $fv0 is real and $fv1 is img.  */
+    loongarch_xfer_reg_part (regcache, fv, len / 2, readbuf, 0, writebuf, 0),
+      loongarch_xfer_reg_part (regcache, fv + 1, len / 2, readbuf, len / 2,
+			       writebuf, len / 2);
+  else if (((typecode == TYPE_CODE_INT && type->is_unsigned ())
+	    || typecode == TYPE_CODE_ENUM)
+	   && len <= rlen)
+    /* For unsigned scalar type, we have zero-extended one in $v0.  */
+    if (writebuf)
+      {
+	gdb_byte buf[rlen];
+	store_signed_integer (buf, rlen, BFD_ENDIAN_LITTLE,
+			      extract_unsigned_integer (writebuf, len,
+							BFD_ENDIAN_LITTLE));
+	loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, NULL, 0,
+				 writebuf, 0);
+      }
+    else
+      loongarch_xfer_reg_part (regcache, regs->r + 4, len, readbuf, 0, NULL,
+			       0);
+  else if (((typecode == TYPE_CODE_INT && !type->is_unsigned ())
+	    || typecode == TYPE_CODE_PTR)
+	   && len <= rlen)
+    /* For signed scalar type, we have sign-extended one in $v0.  */
+    if (writebuf)
+      {
+	gdb_byte buf[rlen];
+	store_signed_integer (buf, rlen, BFD_ENDIAN_LITTLE,
+			      extract_signed_integer (writebuf, len,
+						      BFD_ENDIAN_LITTLE));
+	loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, NULL, 0,
+				 writebuf, 0);
+      }
+    else
+      loongarch_xfer_reg_part (regcache, regs->r + 4, len, readbuf, 0, NULL,
+			       0);
+  else
+    {
+      /* For small structure or int64_t on LoongArch32.  */
+      if (len <= rlen)
+	loongarch_xfer_reg_part (regcache, regs->r + 4, len, readbuf, 0,
+				 writebuf, 0);
+      else
+	loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, readbuf, 0,
+				 writebuf, 0),
+	  loongarch_xfer_reg_part (regcache, regs->r + 5, len - rlen, readbuf,
+				   rlen, writebuf, rlen);
+    }
+
+  return RETURN_VALUE_REGISTER_CONVENTION;
+}
+
+static int
+loongarch_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int num)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  if (0 <= num && num < 32)
+    return regs->r + num;
+  else if (32 <= num && num < 64 && 0 <= regs->f)
+    return regs->f + num - 32;
+  else if (64 <= num && num < 72 && 0 <= regs->fcc)
+    return regs->fcc + num - 64;
+  else
+    return -1;
+}
+
+static std::string
+loongarch_gcc_target_options (struct gdbarch *gdbarch)
+{
+  return "";
+}
+
+static int
+loongarch_register_reggroup_p (struct gdbarch *gdbarch, int regnum,
+			       struct reggroup *group)
+{
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+
+  if (gdbarch_register_name (gdbarch, regnum) == NULL
+      || *gdbarch_register_name (gdbarch, regnum) == '\0')
+    return 0;
+
+  int raw_p = regnum < gdbarch_num_regs (gdbarch);
+
+  if (group == save_reggroup || group == restore_reggroup)
+    return raw_p;
+  if (group == all_reggroup)
+    return 1;
+
+  if (group == general_reggroup
+      && (regs->pc == regnum || regs->badvaddr == regnum
+	  || (regs->r <= regnum && regnum < regs->r + 32)))
+    return 1;
+
+  /* Only $rx and $pc in general_reggroup.  */
+  if (group == general_reggroup)
+    return 0;
+
+  if (0 <= regs->f
+      && (regs->fcsr == regnum || (regs->f <= regnum && regnum < regs->f + 32)
+	  || (regs->fcc <= regnum && regnum < regs->fcc + 8)))
+    return group == float_reggroup;
+
+  /* Only $fx / $fccx / $fcsr in float_reggroup.  */
+  if (group == float_reggroup)
+    return 0;
+
+  if (0 <= regs->vr && regs->vr <= regnum && regnum < regs->vr + 32)
+    if (group == vector_reggroup)
+      return 1;
+
+  if (0 <= regs->xr && regs->xr <= regnum && regnum < regs->xr + 32)
+    if (group == vector_reggroup)
+      return 1;
+
+  int ret = tdesc_register_in_reggroup_p (gdbarch, regnum, group);
+  if (ret != -1)
+    return ret;
+
+  return default_register_reggroup_p (gdbarch, regnum, group);
+}
+
+static void
+loongarch_print_all_r_registers (struct gdbarch *gdbarch, struct ui_file *file,
+				 struct frame_info *frame)
+{
+  int i, col;
+  int rlen = loongarch_rlen (gdbarch) / 8;
+  int ncols = rlen == 4 ? 8 : 4;
+
+  for (i = 0; i < 32; i += ncols)
+    {
+      fprintf_filtered (file, "     ");
+      for (col = 0; col < ncols; col++)
+	fprintf_filtered (file, rlen == 8 ? "%17s" : "%9s",
+			  gdbarch_register_name (gdbarch, i + col));
+
+      fprintf_filtered (file, "\nR%-4d", i);
+
+      for (col = 0; col < ncols; col++)
+	{
+	  struct value *value = get_frame_register_value (frame, i + col);
+	  if (value_optimized_out (value) || !value_entirely_available (value))
+	    fprintf_filtered (file, "%*s", 2 * rlen,
+			      rlen == 4 ? "<unavl>" : "<unavailable>");
+	  else
+	    {
+	      int byte;
+	      const gdb_byte *raw_buffer = value_contents_all (value);
+	      for (byte = rlen - 1; 0 <= byte; byte--)
+		fprintf_filtered (file, "%02x", raw_buffer[byte]);
+	    }
+	  fprintf_filtered (file, " ");
+	}
+      fprintf_filtered (file, "\n");
+    }
+}
+
+static void
+loongarch_print_registers_info (struct gdbarch *gdbarch, struct ui_file *file,
+				struct frame_info *frame, int regnum, int all)
+{
+  int i;
+  auto regs = &gdbarch_tdep (gdbarch)->regs;
+  const int numregs
+    = gdbarch_num_regs (gdbarch) + gdbarch_num_pseudo_regs (gdbarch);
+
+  for (i = 0; i < numregs; i++)
+    {
+      if (regnum == -1)
+	{
+	  if (regs->r == i)
+	    loongarch_print_all_r_registers (gdbarch, file, frame), i += 32;
+
+	  if (all)
+	    {
+	      if (!gdbarch_register_reggroup_p (gdbarch, i, all_reggroup))
+		continue;
+	    }
+	  else
+	    {
+	      if (!gdbarch_register_reggroup_p (gdbarch, i, general_reggroup))
+		continue;
+	    }
+	}
+      else if (i != regnum)
+	continue;
+
+      if (gdbarch_register_name (gdbarch, i) == NULL
+	  || *(gdbarch_register_name (gdbarch, i)) == '\0')
+	continue;
+
+      default_print_registers_info (gdbarch, file, frame, i, 0);
+    }
+}
+
+constexpr gdb_byte loongarch_default_breakpoint[] = {0x05, 0x00, 0x2a, 0x00};
+typedef BP_MANIPULATION (loongarch_default_breakpoint) loongarch_breakpoint;
+
+/* Initialize the current architecture based on INFO.  If possible,
+   re-use an architecture from ARCHES, which is a list of
+   architectures already created during this debugging session.
+
+   Called e.g. at program startup, when reading a core file, and when
+   reading a binary file.  */
+
+/* This predicate tests whether we need to read lsx/lasx registers
+   (instead of fp registers with the same DWARF2 code
+   (thus the same internal code, though lasx/lsx/fp reg internal
+   codes are different)) according to the byte-size of requested type.  */
+
+static int
+loongarch_fp_regnum_refers_to_lsx_lasx_p (struct gdbarch *gdbarch, int regnum,
+					  struct type *type)
+{
+  /* Conditions:
+       1) regnum is in "disputed" zone (fp/lsx/lasx, translated
+	  from dwarf regnum).
+       2) type is larger than 8 bytes.
+
+      (if specified type is larger than 8 bytes,
+       then regnum refers to lsx / lasx register instead of fp register).
+    */
+  return regnum >= gdbarch_tdep (gdbarch)->regs.f
+	 && regnum < gdbarch_tdep (gdbarch)->regs.f + 32
+	 && TYPE_LENGTH (type) > 8;
+}
+
+static int
+loongarch_convert_register_p (struct gdbarch *gdbarch, int regnum,
+			      struct type *type)
+{
+  return loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type);
+}
+
+static int
+loongarch_register_to_value (struct frame_info *frame, int regnum,
+			     struct type *type, gdb_byte *to, int *optimizedp,
+			     int *unavailablep)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+
+  if (loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type))
+    {
+      /* Add a displacement to regnum.  */
+      switch (TYPE_LENGTH (type))
+	{
+	case 16: /* 16-byte types, access vr.  */
+	  if (!get_frame_register_bytes (frame,
+					 regnum
+					   + gdbarch_tdep (gdbarch)->regs.vr
+					   - gdbarch_tdep (gdbarch)->regs.f,
+					 0, {to + 0, 16}, optimizedp,
+					 unavailablep))
+	    return 0;
+	  break;
+
+	case 32: /* 32-byte types, access xr.  */
+	  if (!get_frame_register_bytes (frame,
+					 regnum
+					   + gdbarch_tdep (gdbarch)->regs.xr
+					   - gdbarch_tdep (gdbarch)->regs.f,
+					 0, {to + 0, 32}, optimizedp,
+					 unavailablep))
+	    return 0;
+	  break;
+
+	default:
+	  goto fail;
+	}
+
+      *optimizedp = *unavailablep = 0;
+      return 1; /* 1 for success, 0 for fail.  */
+    }
+
+fail:
+  internal_error (__FILE__, __LINE__,
+		  _ ("loongarch_register_to_value: unrecognized case"));
+}
+
+static void
+loongarch_value_to_register (struct frame_info *frame, int regnum,
+			     struct type *type, const gdb_byte *from)
+{
+  struct gdbarch *gdbarch = get_frame_arch (frame);
+  if (loongarch_fp_regnum_refers_to_lsx_lasx_p (gdbarch, regnum, type))
+    {
+      switch (TYPE_LENGTH (type))
+	{
+	case 16: /* 16-byte types, access vr.  */
+	  put_frame_register (frame,
+			      regnum + gdbarch_tdep (gdbarch)->regs.vr
+				- gdbarch_tdep (gdbarch)->regs.f,
+			      from);
+	  return;
+
+	case 32: /* 32-byte types, access xr.  */
+	  put_frame_register (frame,
+			      regnum + gdbarch_tdep (gdbarch)->regs.xr
+				- gdbarch_tdep (gdbarch)->regs.f,
+			      from);
+	  return;
+	}
+    }
+
+  internal_error (__FILE__, __LINE__,
+		  _ ("loongarch_value_to_register: unrecognized case"));
+}
+
+static struct gdbarch *
+loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
+{
+  struct gdbarch *gdbarch;
+  struct gdbarch_tdep tdep_instant, *tdep;
+  tdesc_arch_data_up tdesc_data;
+  const struct target_desc *tdesc = info.target_desc;
+  int i;
+  size_t regnum;
+
+  tdep = &tdep_instant;
+  memset (tdep, 0, sizeof (tdep));
+  memset (&tdep->regs, -1, sizeof (tdep->regs));
+
+  if (info.abfd != NULL
+      && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
+    {
+      tdep->ef_abi = elf_elfheader (info.abfd)->e_flags & EF_LOONGARCH_ABI;
+      gdb_assert (0 != (tdep->ef_abi & EF_LOONGARCH_ABI_MASK));
+
+    }
+
+  /* Check any target description for validity.  */
+  if (!tdesc_has_registers (tdesc))
+    tdesc = loongarch_get_base_target_description (
+      EF_LOONGARCH_IS_ILP32 (tdep->ef_abi) ? 32 : 64);
+
+  int valid_p = 1;
+  const struct tdesc_feature *feature;
+
+  feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base");
+  if (feature == NULL)
+    return NULL;
+  regnum = 0;
+  tdesc_data = tdesc_data_alloc ();
+
+  tdep->regs.r = regnum;
+  for (i = 0; i < 32; i++)
+    valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					loongarch_r_normal_name[i] + 1);
+  valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+				      tdep->regs.pc = regnum++, "pc");
+  valid_p
+    &= tdesc_numbered_register (feature, tdesc_data.get (),
+				tdep->regs.badvaddr = regnum++, "badvaddr");
+
+  if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.fpu")))
+    {
+      tdep->regs.f = regnum;
+      for (i = 0; i < 32; i++)
+	valid_p
+	  &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+				      loongarch_f_normal_name[i] + 1);
+      tdep->regs.fcc = regnum;
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc0");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc1");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc2");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc3");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc4");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc5");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc6");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+					  "fcc7");
+      valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+					  tdep->regs.fcsr = regnum++, "fcsr");
+    }
+
+  if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lbt")))
+    {
+      tdep->regs.scr = regnum;
+      for (i = 0; i < 4; i++)
+	valid_p
+	  &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+				      loongarch_cr_normal_name[i] + 1);
+      valid_p
+	&= tdesc_numbered_register (feature, tdesc_data.get (),
+				    tdep->regs.EFLAG = regnum++, "EFLAG");
+      valid_p
+	&= tdesc_numbered_register (feature, tdesc_data.get (),
+				    tdep->regs.x86_top = regnum++, "x86_top");
+    }
+
+  if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lsx")))
+    {
+      tdep->regs.vr = regnum;
+      for (i = 0; i < 32; i++)
+	valid_p
+	  &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+				      loongarch_v_normal_name[i] + 1);
+    }
+
+  if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lasx")))
+    {
+      tdep->regs.xr = regnum;
+      for (i = 0; i < 32; i++)
+	valid_p
+	  &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+				      loongarch_x_normal_name[i] + 1);
+    }
+
+  if (!valid_p)
+    {
+      return NULL;
+    }
+
+  info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
+  /* Find a candidate among the list of pre-declared architectures.  */
+  for (arches = gdbarch_list_lookup_by_info (arches, &info); arches != NULL;
+       arches = gdbarch_list_lookup_by_info (arches->next, &info))
+    {
+      if (gdbarch_tdep (arches->gdbarch)->ef_abi != tdep->ef_abi)
+	continue;
+
+      return arches->gdbarch;
+    }
+
+  /* None found, so create a new architecture from the information provided. */
+  tdep = (struct gdbarch_tdep *) xmalloc (sizeof (tdep_instant));
+  memcpy (tdep, &tdep_instant, sizeof (tdep_instant));
+  gdbarch = gdbarch_alloc (&info, tdep);
+
+  /* Target data types.  */
+  if (info.abfd)
+    {
+      if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi))
+        {
+          set_gdbarch_short_bit (gdbarch, 16);
+          set_gdbarch_int_bit (gdbarch, 32);
+          set_gdbarch_long_bit (gdbarch, 32);
+          set_gdbarch_long_long_bit (gdbarch, 32);
+          set_gdbarch_float_bit (gdbarch, 32);
+          set_gdbarch_double_bit (gdbarch, 64);
+          set_gdbarch_long_double_bit (gdbarch, 128);
+          set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
+          set_gdbarch_ptr_bit (gdbarch, 32);
+          set_gdbarch_char_signed (gdbarch, 0);
+        }
+      else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+        {
+          set_gdbarch_short_bit (gdbarch, 16);
+          set_gdbarch_int_bit (gdbarch, 32);
+          set_gdbarch_long_bit (gdbarch, 64);
+          set_gdbarch_long_long_bit (gdbarch, 64);
+          set_gdbarch_float_bit (gdbarch, 32);
+          set_gdbarch_double_bit (gdbarch, 64);
+          set_gdbarch_long_double_bit (gdbarch, 128);
+          set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
+          set_gdbarch_ptr_bit (gdbarch, 64);
+          set_gdbarch_char_signed (gdbarch, 0);
+
+          tdep->regs.ra = tdep->regs.r + 1;
+          tdep->regs.sp = tdep->regs.r + 3;
+
+          for (i = 0; i < ARRAY_SIZE (loongarch_r_normal_name); ++i)
+	    if (loongarch_r_normal_name[i][0] != '\0')
+	      user_reg_add (gdbarch, loongarch_r_normal_name[i] + 1,
+			    value_of_loongarch_user_reg,
+			    (void *) (size_t) (tdep->regs.r + i));
+
+	  for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name); ++i)
+	    if (loongarch_r_lp64_name[i][0] != '\0')
+	      user_reg_add (gdbarch, loongarch_r_lp64_name[i] + 1,
+			    value_of_loongarch_user_reg,
+			    (void *) (size_t) (tdep->regs.r + i));
+
+	  for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name1); ++i)
+	    if (loongarch_r_lp64_name[i][0] != '\0')
+	      user_reg_add (gdbarch, loongarch_r_lp64_name1[i] + 1,
+			    value_of_loongarch_user_reg,
+			    (void *) (size_t) (tdep->regs.r + i));
+
+	  /* Functions handling dummy frames.  */
+	  set_gdbarch_push_dummy_call (gdbarch,
+				       loongarch_lp32lp64_push_dummy_call);
+	  set_gdbarch_return_value (gdbarch, loongarch_lp64_return_value);
+
+	}
+      else
+	gdb_assert_not_reached ("unknown ABI");
+    }
+
+  /* Hook in OS ABI-specific overrides, if they have been registered.  */
+  info.target_desc = tdesc;
+  info.tdesc_data = tdesc_data.get ();
+
+  /* Register architecture.  */
+  set_gdbarch_num_regs (gdbarch, regnum);
+  set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp);
+  set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc);
+
+  tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
+
+  /* Functions to supply register information.  */
+  set_gdbarch_register_name (gdbarch, loongarch_register_name);
+
+  /* Handle overlapping dwarf2 register code for fp/lsx/lasx.  */
+  set_gdbarch_convert_register_p (gdbarch, loongarch_convert_register_p);
+  set_gdbarch_register_to_value (gdbarch, loongarch_register_to_value);
+  set_gdbarch_value_to_register (gdbarch, loongarch_value_to_register);
+
+  /* Functions to analyze frames.  */
+  set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue);
+  set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+  set_gdbarch_frame_align (gdbarch, loongarch_frame_align);
+
+  /* Functions to access frame data.  */
+  set_gdbarch_unwind_pc (gdbarch, loongarch_unwind_pc);
+  set_gdbarch_unwind_sp (gdbarch, loongarch_unwind_sp);
+
+  set_gdbarch_dummy_id (gdbarch, loongarch_dummy_id);
+
+  set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step);
+
+  set_gdbarch_breakpoint_kind_from_pc (gdbarch,
+				       loongarch_breakpoint::kind_from_pc);
+  set_gdbarch_sw_breakpoint_from_kind (gdbarch,
+				       loongarch_breakpoint::bp_from_kind);
+
+  set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
+
+  /* Virtual tables.  */
+  set_gdbarch_vbit_in_delta (gdbarch, 1);
+
+  set_gdbarch_gcc_target_options (gdbarch, loongarch_gcc_target_options);
+
+  gdbarch_init_osabi (info, gdbarch);
+  set_gdbarch_register_reggroup_p (gdbarch, loongarch_register_reggroup_p);
+  set_gdbarch_register_name (gdbarch, loongarch_register_name);
+  set_gdbarch_print_registers_info (gdbarch, loongarch_print_registers_info);
+
+  /* Frame unwinders.  Use DWARF debug info if available, otherwise use our own
+     unwinder.  */
+  set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum);
+  dwarf2_append_unwinders (gdbarch);
+  frame_unwind_append_unwinder (gdbarch, &loongarch_frame_unwind);
+
+  return gdbarch;
+}
+
+static void
+info_loongarch (const char *addr_exp, int from_tty)
+{
+  char *buf, *t;
+  int set;
+  char *item;
+  unsigned long addr;
+  unsigned long long value;
+
+  if (addr_exp)
+    {
+      addr_exp = skip_spaces (addr_exp);
+      buf = (char *) alloca (strlen (addr_exp) + 1);
+      strcpy (buf, addr_exp);
+      loongarch_eliminate_adjacent_repeat_char (buf, ' ');
+    }
+  else
+    goto Empty;
+
+  if (!(t = strtok (buf, " ")))
+    goto Empty;
+  if (strcmp (t, "set") == 0)
+    {
+      t = strtok (NULL, " ");
+      set = 1;
+    }
+  else
+    {
+      if (strcmp (t, "get") == 0)
+	t = strtok (NULL, " ");
+      set = 0;
+    }
+  if (!(item = t))
+    goto Empty;
+  if (!(t = strtok (NULL, " ")))
+    goto Empty;
+  addr = strtoul (t, NULL, 0);
+  if (set && (t = strtok (NULL, " ")) == NULL)
+    goto Empty;
+  value = strtoll (t, NULL, 0);
+
+  if (set)
+    if (strcmp (item, "cpucfg") == 0)
+      {
+	uint32_t val32 = value;
+	ULONGEST xfered_len;
+	target_xfer_partial (current_inferior ()->top_target (),
+			     TARGET_OBJECT_LARCH, "cpucfg", NULL,
+			     (const gdb_byte *) &val32, addr * 4,
+			     sizeof (val32), &xfered_len);
+	if (0 < xfered_len)
+	  fprintf_unfiltered (gdb_stdout, "ok\n");
+	else
+	  error ("Set failed");
+      }
+    else
+      {
+	uint64_t val64 = value;
+	ULONGEST xfered_len;
+	target_xfer_partial (current_inferior ()->top_target (),
+			     TARGET_OBJECT_LARCH, item, NULL,
+			     (const gdb_byte *) &val64, addr * 8,
+			     sizeof (val64), &xfered_len);
+	if (0 < xfered_len)
+	  fprintf_unfiltered (gdb_stdout, "ok\n");
+	else
+	  error ("Set failed");
+      }
+  else if (strcmp (item, "cpucfg") == 0)
+    {
+      uint32_t val32;
+      ULONGEST xfered_len;
+      target_xfer_partial (current_inferior ()->top_target (),
+			   TARGET_OBJECT_LARCH, "cpucfg", (gdb_byte *) &val32,
+			   NULL, addr * 4, sizeof (val32), &xfered_len);
+      if (0 < xfered_len)
+	fprintf_unfiltered (gdb_stdout, "return is %x\n", val32);
+      else
+	error ("Get failed");
+    }
+  else
+    {
+      uint64_t val64;
+      ULONGEST xfered_len;
+      target_xfer_partial (current_inferior ()->top_target (),
+			   TARGET_OBJECT_LARCH, item, (gdb_byte *) &val64,
+			   NULL, addr * 8, sizeof (val64), &xfered_len);
+      if (0 < xfered_len)
+	fprintf_unfiltered (gdb_stdout, "return is %llx\n", (long long) val64);
+      else
+	error ("Get failed");
+    }
+
+  return;
+Empty:
+  error ("Empty. Should be 'info loongarch ([get]|set) item addr [value]'");
+}
+
+void _initialize_loongarch_tdep ();
+void
+_initialize_loongarch_tdep ()
+{
+  gdbarch_register (bfd_arch_loongarch, loongarch_gdbarch_init, NULL);
+
+  add_info ("loongarch", info_loongarch, _ ("Loongarch extra"));
+
+  /* Debug this files internals.  */
+  add_setshow_zuinteger_cmd ("loongarch", class_maintenance, &loongarch_debug,
+			     _ ("\
+Set loongarch debugging."),
+			     _ ("\
+Show loongarch debugging."),
+			     _ ("\
+When non-zero, loongarch specific debugging is enabled."),
+			     NULL, NULL, &setdebuglist, &showdebuglist);
+}
diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h
new file mode 100644
index 0000000000..bd48904825
--- /dev/null
+++ b/gdb/loongarch-tdep.h
@@ -0,0 +1,55 @@
+/* Target-dependent code for GNU/Linux LoongArch.
+
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_TDEP_H
+#define LOONGARCH_TDEP_H
+
+#include "arch/loongarch.h"
+
+struct gdbarch_tdep
+{
+  int ef_abi; /* EF_LOONGARCH_ABI  */
+
+  struct
+  {
+    int r;
+    int ra;
+    int sp;
+    int pc;
+    int badvaddr;
+
+    int f;
+    int fcc;
+    int fcsr;
+    int vr;
+    int xr;
+
+    int scr;
+    int EFLAG;
+    int x86_top;
+
+  } regs;
+
+  /* Return the expected next PC if FRAME is stopped at a syscall
+     instruction.  */
+  CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+};
+
+#endif /* LOONGARCH_TDEP_H  */
diff --git a/gdb/nat/loongarch-linux-watch.c b/gdb/nat/loongarch-linux-watch.c
new file mode 100644
index 0000000000..f7c0dbf9d0
--- /dev/null
+++ b/gdb/nat/loongarch-linux-watch.c
@@ -0,0 +1,330 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   Based on MIPS target.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "nat/gdb_ptrace.h"
+#include "loongarch-linux-watch.h"
+
+/* Assuming usable watch registers REGS, return the irwmask of
+   register N.  */
+
+uint8_t
+loongarch_linux_watch_get_irwmask (struct pt_watch_regs *regs, int n)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      return regs->la32[n].irwmask & IRW_MASK;
+    case pt_watch_style_la64:
+      return regs->la64[n].irwmask & IRW_MASK;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, return the irwstat of
+   register N.  */
+
+uint8_t
+loongarch_linux_watch_get_irwstat (struct pt_watch_regs *regs, int n)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      return regs->la32[n].irwstat & IRW_MASK;
+    case pt_watch_style_la64:
+      return regs->la64[n].irwstat & IRW_MASK;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, return the num_valid.  */
+
+uint32_t
+loongarch_linux_watch_get_num_valid (struct pt_watch_regs *regs)
+{
+  return regs->num_valid;
+}
+
+/* Assuming usable watch registers REGS, return the addr of
+   register N.  */
+
+CORE_ADDR
+loongarch_linux_watch_get_addr (struct pt_watch_regs *regs, int n)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      return regs->la32[n].addr;
+    case pt_watch_style_la64:
+      return regs->la64[n].addr;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, set addr of register N to
+   VALUE.  */
+
+void
+loongarch_linux_watch_set_addr (struct pt_watch_regs *regs, int n,
+				CORE_ADDR value)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      /*  The cast will never throw away bits as 64 bit addresses can
+	  never be used on a 32 bit kernel.  */
+      regs->la32[n].addr = (uint32_t) value;
+      break;
+    case pt_watch_style_la64:
+      regs->la64[n].addr = value;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, return the mask of
+   register N.  */
+
+CORE_ADDR
+loongarch_linux_watch_get_mask (struct pt_watch_regs *regs, int n)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      return regs->la32[n].mask;
+    case pt_watch_style_la64:
+      return regs->la64[n].mask;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, set mask of register N to
+   VALUE.  */
+
+void
+loongarch_linux_watch_set_mask (struct pt_watch_regs *regs, int n,
+				CORE_ADDR value)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      regs->la32[n].mask = value;
+      break;
+    case pt_watch_style_la64:
+      regs->la64[n].mask = value;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, return the irw of
+   register N.  */
+
+uint8_t
+loongarch_linux_watch_get_irw (struct pt_watch_regs *regs, int n)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      return regs->la32[n].irw;
+    case pt_watch_style_la64:
+      return regs->la64[n].irw;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Assuming usable watch registers REGS, set irw of register N to
+   VALUE.  */
+
+void
+loongarch_linux_watch_set_irw (struct pt_watch_regs *regs, int n,
+			       uint8_t value)
+{
+  switch (regs->style)
+    {
+    case pt_watch_style_la32:
+      regs->la32[n].irw = value;
+      break;
+    case pt_watch_style_la64:
+      regs->la64[n].irw = value;
+      break;
+    default:
+      internal_error (__FILE__, __LINE__,
+		      _ ("Unrecognized watch register style"));
+    }
+}
+
+/* Read the watch registers of process LWPID and store it in
+   WATCH_READBACK.  Save true to *WATCH_READBACK_VALID if watch
+   registers are valid.  Return 1 if watch registers are usable.
+   Cached information is used unless FORCE is true.  */
+
+int
+loongarch_linux_read_watch_registers (long lwpid,
+				      struct pt_watch_regs *watch_readback,
+				      int *watch_readback_valid, int force)
+{
+  if (force || *watch_readback_valid == 0)
+    {
+      if (ptrace (PTRACE_GET_WATCH_REGS, lwpid, watch_readback, NULL) == -1)
+	{
+	  *watch_readback_valid = -1;
+	  return 0;
+	}
+      if (watch_readback->num_valid == 0)
+	{
+	  *watch_readback_valid = -1;
+	  return 0;
+	}
+      /* Watch registers appear to be usable.  */
+      *watch_readback_valid = 1;
+    }
+  return (*watch_readback_valid == 1) ? 1 : 0;
+}
+
+/* Convert GDB's TYPE to an IRW mask.  */
+
+uint32_t
+loongarch_linux_watch_type_to_irw (enum target_hw_bp_type type)
+{
+  switch (type)
+    {
+    case hw_write:
+      return W_MASK;
+    case hw_read:
+      return R_MASK;
+    case hw_access:
+      return (W_MASK | R_MASK);
+    case hw_execute:
+      return I_MASK;
+    default:
+      return 0;
+    }
+}
+
+/* Set any low order bits in MASK that are not set.  */
+
+static CORE_ADDR
+fill_mask (CORE_ADDR mask)
+{
+  CORE_ADDR f = 1;
+
+  while (f && f < mask)
+    {
+      mask |= f;
+      f <<= 1;
+    }
+  return mask;
+}
+
+/* Try to add a single watch to the specified registers REGS.  The
+   address of added watch is ADDR, the length is LEN, and the mask
+   is IRW.  Return 1 on success, 0 on failure.  */
+
+int
+loongarch_linux_watch_try_one_watch (struct pt_watch_regs *regs,
+				     CORE_ADDR addr, int len, uint32_t irw)
+{
+  CORE_ADDR base_addr, last_byte;
+  CORE_ADDR mask_bits, t_addr, t_mask;
+  uint8_t t_irw;
+  int i;
+
+  if (len <= 0)
+    return 0;
+
+  last_byte = addr + len - 1;
+  mask_bits = fill_mask (addr ^ last_byte);
+  base_addr = addr & ~mask_bits;
+
+  /* Check to see if it is covered by current registers.  */
+  for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++)
+    {
+      t_addr = loongarch_linux_watch_get_addr (regs, i);
+      t_irw = loongarch_linux_watch_get_irw (regs, i);
+      if (t_addr != 0 && irw == ((uint32_t) t_irw & irw))
+	{
+	  t_mask = loongarch_linux_watch_get_mask (regs, i);
+	  if (addr >= t_addr && last_byte <= (t_addr + t_mask))
+	    return 1;
+	}
+    }
+  /* Try to find an empty register.  */
+  for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++)
+    {
+      t_addr = loongarch_linux_watch_get_addr (regs, i);
+      if (t_addr == 0
+	  && irw == (loongarch_linux_watch_get_irwmask (regs, i) & irw))
+	{
+	  /* It fits, we'll take it.  */
+	  loongarch_linux_watch_set_addr (regs, i, base_addr);
+	  loongarch_linux_watch_set_mask (regs, i, mask_bits);
+	  loongarch_linux_watch_set_irw (regs, i, irw);
+	  return 1;
+	}
+    }
+  /* It didn't fit anywhere, we failed.  */
+  return 0;
+}
+
+/* Fill in the watch registers REGS with the currently cached
+   watches CURRENT_WATCHES.  */
+
+void
+loongarch_linux_watch_populate_regs (
+  struct loongarch_watchpoint *current_watches, struct pt_watch_regs *regs)
+{
+  struct loongarch_watchpoint *w;
+  int i;
+
+  /* Clear them out.  */
+  for (i = 0; i < loongarch_linux_watch_get_num_valid (regs); i++)
+    {
+      loongarch_linux_watch_set_addr (regs, i, 0);
+      loongarch_linux_watch_set_mask (regs, i, 0);
+      loongarch_linux_watch_set_irw (regs, i, 0);
+    }
+
+  w = current_watches;
+  while (w)
+    {
+      uint32_t irw = loongarch_linux_watch_type_to_irw (w->type);
+
+      i = loongarch_linux_watch_try_one_watch (regs, w->addr, w->len, irw);
+      /* They must all fit, because we previously calculated that they
+	 would.  */
+      gdb_assert (i);
+      w = w->next;
+    }
+}
diff --git a/gdb/nat/loongarch-linux-watch.h b/gdb/nat/loongarch-linux-watch.h
new file mode 100644
index 0000000000..ab80b44b74
--- /dev/null
+++ b/gdb/nat/loongarch-linux-watch.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   Based on MIPS target.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef LOONGARCH_LINUX_WATCH_H
+#define LOONGARCH_LINUX_WATCH_H 1
+
+#include <asm/ptrace.h>
+#include "gdbsupport/break-common.h"
+
+#define MAX_DEBUG_REGISTER 16
+
+/* If macro PTRACE_GET_WATCH_REGS is not defined, kernel header doesn't
+   have hardware watchpoint-related structures.  Define them below.  */
+
+#ifndef PTRACE_GET_WATCH_REGS
+#define PTRACE_GET_WATCH_REGS 0xd0
+#define PTRACE_SET_WATCH_REGS 0xd1
+
+enum pt_watch_style
+{
+  pt_watch_style_la32,
+  pt_watch_style_la64
+};
+
+/* A value of zero in a watchlo indicates that it is available.  */
+
+struct la32_watch_regs
+{
+  uint32_t addr;
+  /* Lower 16 bits of watchhi.  */
+  uint32_t mask;
+  /* Valid mask and I R W bits.
+   * bit 0 -- 1 if W bit is usable.
+   * bit 1 -- 1 if R bit is usable.
+   * bit 2 -- 1 if I bit is usable.
+   * bits 3 - 11 -- Valid watchhi mask bits.
+   */
+  uint8_t irw;
+  uint8_t irwstat;
+  uint8_t irwmask;
+  /* There is confusion across gcc versions about structure alignment,
+     so we force 8 byte alignment for these structures so they match
+     the kernel even if it was build with a different gcc version.  */
+} __attribute__ ((aligned (8)));
+
+struct la64_watch_regs
+{
+  uint64_t addr;
+  uint64_t mask;
+  uint8_t irw;
+  uint8_t irwstat;
+  uint8_t irwmask;
+} __attribute__ ((aligned (8)));
+
+struct pt_watch_regs
+{
+  uint16_t max_valid;
+  uint16_t num_valid;
+  enum pt_watch_style style;
+  union
+  {
+    struct la32_watch_regs la32[MAX_DEBUG_REGISTER];
+    struct la64_watch_regs la64[MAX_DEBUG_REGISTER];
+  };
+};
+
+#endif /* !PTRACE_GET_WATCH_REGS  */
+
+#define W_BIT 0
+#define R_BIT 1
+#define I_BIT 2
+
+#define W_MASK (1 << W_BIT)
+#define R_MASK (1 << R_BIT)
+#define I_MASK (1 << I_BIT)
+
+#define IRW_MASK (I_MASK | R_MASK | W_MASK)
+
+/* We keep list of all watchpoints we should install and calculate the
+   watch register values each time the list changes.  This allows for
+   easy sharing of watch registers for more than one watchpoint.  */
+
+struct loongarch_watchpoint
+{
+  CORE_ADDR addr;
+  int len;
+  enum target_hw_bp_type type;
+  struct loongarch_watchpoint *next;
+};
+
+uint32_t loongarch_linux_watch_get_num_valid (struct pt_watch_regs *regs);
+uint8_t loongarch_linux_watch_get_irwmask (struct pt_watch_regs *regs, int n);
+uint8_t loongarch_linux_watch_get_irwstat (struct pt_watch_regs *regs, int n);
+CORE_ADDR loongarch_linux_watch_get_addr (struct pt_watch_regs *regs, int n);
+void loongarch_linux_watch_set_addr (struct pt_watch_regs *regs, int n,
+				     CORE_ADDR value);
+CORE_ADDR loongarch_linux_watch_get_mask (struct pt_watch_regs *regs, int n);
+void loongarch_linux_watch_set_mask (struct pt_watch_regs *regs, int n,
+				     CORE_ADDR value);
+uint8_t loongarch_linux_watch_get_irw (struct pt_watch_regs *regs, int n);
+void loongarch_linux_watch_set_irw (struct pt_watch_regs *regs, int n,
+				    uint8_t value);
+int loongarch_linux_watch_try_one_watch (struct pt_watch_regs *regs,
+					 CORE_ADDR addr, int len,
+					 uint32_t irw);
+void loongarch_linux_watch_populate_regs (
+  struct loongarch_watchpoint *current_watches, struct pt_watch_regs *regs);
+uint32_t loongarch_linux_watch_type_to_irw (enum target_hw_bp_type type);
+
+int loongarch_linux_read_watch_registers (long lwpid,
+					  struct pt_watch_regs *watch_readback,
+					  int *watch_readback_valid,
+					  int force);
+
+#endif /* #define LOONGARCH_LINUX_WATCH_H  */
diff --git a/gdb/remote.c b/gdb/remote.c
index d5eb40ce57..65add80583 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2071,6 +2071,8 @@ enum {
   PACKET_qXfer_statictrace_read,
   PACKET_qXfer_traceframe_info,
   PACKET_qXfer_uib,
+  PACKET_qXfer_loongarch_read,
+  PACKET_qXfer_loongarch_write,
   PACKET_qGetTIBAddr,
   PACKET_qGetTLSAddr,
   PACKET_qSupported,
@@ -5271,6 +5273,10 @@ static const struct protocol_feature remote_protocol_features[] = {
     PACKET_qXfer_threads },
   { "qXfer:traceframe-info:read", PACKET_DISABLE, remote_supported_packet,
     PACKET_qXfer_traceframe_info },
+  { "qXfer:loongarch:read", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_loongarch_read },
+  { "qXfer:loongarch:write", PACKET_DISABLE, remote_supported_packet,
+    PACKET_qXfer_loongarch_write },
   { "QPassSignals", PACKET_DISABLE, remote_supported_packet,
     PACKET_QPassSignals },
   { "QCatchSyscalls", PACKET_DISABLE, remote_supported_packet,
@@ -11285,6 +11291,18 @@ remote_target::xfer_partial (enum target_object object,
 	return TARGET_XFER_E_IO;
     }
 
+  if (object == TARGET_OBJECT_LARCH)
+    {
+      if (readbuf)
+	return remote_read_qxfer ("loongarch", annex, readbuf, offset, len,
+				  xfered_len, &remote_protocol_packets
+				  [PACKET_qXfer_loongarch_read]);
+      else
+	return remote_write_qxfer ("loongarch", annex, writebuf, offset, len,
+				   xfered_len, &remote_protocol_packets
+				   [PACKET_qXfer_loongarch_write]);
+    }
+
   /* Only handle flash writes.  */
   if (writebuf != NULL)
     {
@@ -15120,6 +15138,13 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_uib],
 			 "qXfer:uib:read", "unwind-info-block", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_loongarch_read],
+			 "qXfer:loongarch:read", "read-loongarch-object", 0);
+
+  add_packet_config_cmd
+    (&remote_protocol_packets[PACKET_qXfer_loongarch_write],
+     "qXfer:loongarch:write", "write-loongarch-object", 0);
+
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qGetTLSAddr],
 			 "qGetTLSAddr", "get-thread-local-storage-address",
 			 0);
diff --git a/gdb/target.h b/gdb/target.h
index 4dc17fd480..613adc5672 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -135,6 +135,9 @@ enum inferior_event_type
 
 enum target_object
 {
+  /* LARCH target specific transfer. See "loongarch-nat.c" "corelow.c"
+     and "remote.c".  */
+  TARGET_OBJECT_LARCH,
   /* AVR target specific transfer.  See "avr-tdep.c" and "remote.c".  */
   TARGET_OBJECT_AVR,
   /* Transfer up-to LEN bytes of memory starting at OFFSET.  */
diff --git a/gdb/testsuite/gdb.base/dump.exp b/gdb/testsuite/gdb.base/dump.exp
index 52c698333d..53e68736b9 100644
--- a/gdb/testsuite/gdb.base/dump.exp
+++ b/gdb/testsuite/gdb.base/dump.exp
@@ -145,11 +145,13 @@ make_dump_file "dump srec val [set intarr1.srec] intarray" \
 make_dump_file "dump srec val [set intstr1.srec] intstruct" \
 	"dump struct as value, srec"
 
+if { ![istarget loongarch*-*-*] } {
 make_dump_file "dump ihex val [set intarr1.ihex] intarray" \
 	"dump array as value, intel hex"
 
 make_dump_file "dump ihex val [set intstr1.ihex] intstruct" \
 	"dump struct as value, intel hex"
+}
 
 make_dump_file "dump tekhex val [set intarr1.tekhex] intarray" \
 	"dump array as value, tekhex"
@@ -246,11 +248,13 @@ make_dump_file "dump srec mem [set intarr2.srec] $array_start $array_end" \
 make_dump_file "dump srec mem [set intstr2.srec] $struct_start $struct_end" \
 	"dump struct as memory, srec"
 
+if { ![istarget loongarch*-*-*] } {
 make_dump_file "dump ihex mem [set intarr2.ihex] $array_start $array_end" \
 	"dump array as memory, ihex"
 
 make_dump_file "dump ihex mem [set intstr2.ihex] $struct_start $struct_end" \
 	"dump struct as memory, ihex"
+}
 
 make_dump_file "dump tekhex mem [set intarr2.tekhex] $array_start $array_end" \
 	"dump array as memory, tekhex"
diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp
index dc5e2fa6fc..d179a8f18b 100644
--- a/gdb/testsuite/gdb.base/float.exp
+++ b/gdb/testsuite/gdb.base/float.exp
@@ -120,6 +120,8 @@ if { [is_aarch64_target] } then {
 	      pass "info float (without FPU)"
 	}
     }
+} elseif [istarget "loongarch*-*-*"] then {
+    gdb_test "info float" "f.*fcc0.*fcsr.*" "info float"
 } else {
     gdb_test "info float" "No floating.point info available for this processor." "info float (unknown target)"
 }
diff --git a/gdb/testsuite/gdb.trace/entry-values.exp b/gdb/testsuite/gdb.trace/entry-values.exp
index bd8a8c5559..a17247c89a 100644
--- a/gdb/testsuite/gdb.trace/entry-values.exp
+++ b/gdb/testsuite/gdb.trace/entry-values.exp
@@ -62,6 +62,8 @@ if { [istarget "arm*-*-*"] || [istarget "aarch64*-*-*"] } {
     # returns.  The only exception is JALRC, in which case execution
     # resumes from `insn1' instead.
     set call_insn {jalrc|[jb]al[sxr]*[ \t][^\r\n]+\r\n}
+} elseif { [istarget "loongarch*-*-*"] } {
+    set call_insn "bl"
 } else {
     set call_insn "call"
 }
diff --git a/gdb/testsuite/gdb.xml/tdesc-regs.exp b/gdb/testsuite/gdb.xml/tdesc-regs.exp
index 7402ba8d95..4b94747b77 100644
--- a/gdb/testsuite/gdb.xml/tdesc-regs.exp
+++ b/gdb/testsuite/gdb.xml/tdesc-regs.exp
@@ -83,6 +83,11 @@ switch -glob -- [istarget] {
 	set regdir "i386/"
         set core-regs {64bit-core.xml 64bit-sse.xml}
     }
+    "loongarch64-*-*" {
+	set architecture "loongarch64"
+	set regdir "loongarch/"
+	set core-regs {base64.xml fpu64.xml lbt64.xml lsx.xml lasx.xml}
+    }
 }
 
 # If no core registers were specified, assume this target does not
-- 
2.27.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* [PATCH v2 2/2] gdbserver: LoongArch GDBServer Port.
  2021-11-12  3:18 [PATCH v2 1/2] gdb: LoongArch GDB Port liuzhensong
@ 2021-11-12  3:18 ` liuzhensong
  0 siblings, 0 replies; 2+ messages in thread
From: liuzhensong @ 2021-11-12  3:18 UTC (permalink / raw)
  To: gdb-patches; +Cc: xuchenghua, liuzhensong

gdbserver/Makefile.in
gdbserver/configure.srv
gdbserver/linux-loongarch-low.cc
---
 gdbserver/Makefile.in            |   2 +
 gdbserver/configure.srv          |  15 ++
 gdbserver/linux-loongarch-low.cc | 292 +++++++++++++++++++++++++++++++
 3 files changed, 309 insertions(+)
 create mode 100644 gdbserver/linux-loongarch-low.cc

diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 12e9b2777a..c2a49fbd02 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -193,6 +193,7 @@ SFILES = \
 	$(srcdir)/linux-arm-low.cc \
 	$(srcdir)/linux-ia64-low.cc \
 	$(srcdir)/linux-low.cc \
+	$(srcdir)/linux-loongarch-low.cc \
 	$(srcdir)/linux-m68k-low.cc \
 	$(srcdir)/linux-mips-low.cc \
 	$(srcdir)/linux-nios2-low.cc \
@@ -226,6 +227,7 @@ SFILES = \
 	$(srcdir)/../gdb/arch/arm.c \
 	$(srcdir)/../gdb/arch/arm-get-next-pcs.c \
 	$(srcdir)/../gdb/arch/arm-linux.c \
+	$(srcdir)/../gdb/arch/loongarch.c \
 	$(srcdir)/../gdb/arch/ppc-linux-common.c \
 	$(srcdir)/../gdb/arch/riscv.c \
 	$(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 971f537bff..2881d85e4d 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -381,6 +381,21 @@ case "${gdbserver_host}" in
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			;;
+  loongarch*-linux*)	srv_tgtobj="$srv_linux_obj linux-loongarch-low.o"
+			srv_tgtobj="${srv_tgtobj} arch/loongarch.o"
+			srv_tgtobj="${srv_tgtobj} arch/loongarch-linux-nat.o"
+			srv_xmlfiles="loongarch/base32.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/base64.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/fpu32.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/fpu64.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/lbt32.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/lbt64.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/lsx.xml"
+			srv_xmlfiles="${srv_xmlfiles} loongarch/lasx.xml"
+			srv_linux_regsets=yes
+			srv_linux_usrregs=yes
+			srv_linux_thread_db=yes
+			;;
   *)
 			# Who are you?
 			UNSUPPORTED=1
diff --git a/gdbserver/linux-loongarch-low.cc b/gdbserver/linux-loongarch-low.cc
new file mode 100644
index 0000000000..67f1de1907
--- /dev/null
+++ b/gdbserver/linux-loongarch-low.cc
@@ -0,0 +1,292 @@
+/* Copyright (C) 2021 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "server.h"
+#include "linux-low.h"
+
+#include "nat/gdb_ptrace.h"
+
+#include "gdb_proc_service.h"
+#include "arch/loongarch.h"
+#include "arch/loongarch-linux-nat.h"
+#include "elf/common.h"
+#include "tdesc.h"
+
+class loongarch_target : public linux_process_target
+{
+public:
+  const regs_info *get_regs_info () override;
+
+  const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
+
+protected:
+  void low_arch_setup () override;
+
+  bool low_cannot_fetch_register (int regno) override;
+
+  bool low_cannot_store_register (int regno) override;
+
+  bool low_fetch_register (regcache *regcache, int regno) override;
+
+  bool low_supports_breakpoints () override;
+
+  CORE_ADDR low_get_pc (regcache *regcache) override;
+
+  void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
+
+  bool low_breakpoint_at (CORE_ADDR pc) override;
+};
+
+static loongarch_target the_loongarch_target;
+
+static const unsigned int loongarch_breakpoint = 0x002a0005;
+#define loongarch_breakpoint_len 4
+
+void
+loongarch_target::low_arch_setup ()
+{
+  int pid = lwpid_of (current_thread);
+  struct target_desc *tdesc = loongarch_linux_read_description_runtime (pid);
+  init_target_desc (tdesc, loongarch_expedite_regs);
+  current_process ()->tdesc = tdesc;
+}
+
+bool
+loongarch_target::low_cannot_fetch_register (int regno)
+{
+  return false;
+}
+
+bool
+loongarch_target::low_cannot_store_register (int regno)
+{
+  return false;
+}
+
+bool
+loongarch_target::low_fetch_register (regcache *regcache, int regno)
+{
+  const struct target_desc *tdesc = current_process ()->tdesc;
+
+  if (find_regno (tdesc, "r0") == regno)
+    {
+      supply_register_zeroed (regcache, regno);
+      return true;
+    }
+
+  return false;
+}
+
+bool
+loongarch_target::low_supports_breakpoints ()
+{
+  return true;
+}
+
+CORE_ADDR
+loongarch_target::low_get_pc (regcache *regcache)
+{
+  int regno = find_regno (regcache->tdesc, "pc");
+  if (register_size (regcache->tdesc, regno) == 4)
+    {
+      int32_t pc;
+      collect_register (regcache, regno, &pc);
+      return pc;
+    }
+  else
+    {
+      int64_t pc;
+      collect_register (regcache, regno, &pc);
+      return pc;
+    }
+}
+
+void
+loongarch_target::low_set_pc (regcache *regcache, CORE_ADDR pc)
+{
+  supply_register_by_name (regcache, "pc", &pc);
+}
+
+const gdb_byte *
+loongarch_target::sw_breakpoint_from_kind (int kind, int *size)
+{
+  *size = loongarch_breakpoint_len;
+  return (const gdb_byte *) &loongarch_breakpoint;
+}
+
+bool
+loongarch_target::low_breakpoint_at (CORE_ADDR where)
+{
+  uint32_t insn;
+
+  read_memory (where, (unsigned char *) &insn, sizeof (insn));
+  if (insn == loongarch_breakpoint)
+    return true;
+
+  return false;
+}
+
+static void
+loongarch_fill_gregset (struct regcache *regcache, void *buf)
+{
+  int i, r = find_regno (regcache->tdesc, "r0");
+
+  for (i = 0; i < 32; i++)
+    collect_register (regcache, r + i, (uint64_t *) buf + i);
+  collect_register_by_name (regcache, "pc", (uint64_t *) buf + 32);
+}
+
+static void
+loongarch_store_gregset (struct regcache *regcache, const void *buf)
+{
+  int i, r = find_regno (regcache->tdesc, "r0");
+
+  supply_register_zeroed (regcache, r);
+  for (i = 1; i < 32; i++)
+    supply_register (regcache, r + i, (const uint64_t *) buf + i);
+  supply_register_by_name (regcache, "pc", (const uint64_t *) buf + 32);
+}
+
+static void
+loongarch_fill_fpregset (struct regcache *regcache, void *buf)
+{
+  int i, f, fcc;
+  f = find_regno (regcache->tdesc, "f0");
+  fcc = find_regno (regcache->tdesc, "fcc0");
+  uint8_t *fccs = (uint8_t *) ((uint64_t *) buf + 32);
+
+  for (i = 0; i < 32; i++)
+    collect_register (regcache, f + i, (uint64_t *) buf + i);
+  for (i = 0; i < 8; i++)
+    collect_register (regcache, fcc + i, fccs + i);
+  collect_register_by_name (regcache, "fcsr", (uint64_t *) buf + 33);
+}
+
+static void
+loongarch_store_fpregset (struct regcache *regcache, const void *buf)
+{
+  int i, f, fcc;
+  f = find_regno (regcache->tdesc, "f0");
+  fcc = find_regno (regcache->tdesc, "fcc0");
+  const uint8_t *fccs = (const uint8_t *) ((const uint64_t *) buf + 32);
+
+  for (i = 0; i < 32; i++)
+    supply_register (regcache, f + i, (const uint64_t *) buf + i);
+  for (i = 0; i < 8; i++)
+    supply_register (regcache, fcc + i, fccs + i);
+  supply_register_by_name (regcache, "fcsr", (const uint64_t *) buf + 33);
+}
+
+static void
+loongarch_fill_lbtregset (struct regcache *regcache, void *buf)
+{
+  int i, scr = find_regno (regcache->tdesc, "scr0");
+
+  for (i = 0; i < 4; i++)
+    collect_register (regcache, scr + i, (uint64_t *) buf + i);
+  collect_register_by_name (regcache, "EFLAG", (uint64_t *) buf + 4);
+  collect_register_by_name (regcache, "x86_top", (uint32_t *) buf + 9);
+}
+
+static void
+loongarch_store_lbtregset (struct regcache *regcache, const void *buf)
+{
+  int i, scr = find_regno (regcache->tdesc, "scr0");
+
+  for (i = 0; i < 4; i++)
+    supply_register (regcache, scr + i, (const uint64_t *) buf + i);
+  supply_register_by_name (regcache, "EFLAG", (const uint64_t *) buf + 4);
+  supply_register_by_name (regcache, "x86_top", (const uint32_t *) buf + 9);
+}
+
+static void
+loongarch_fill_lsxregset (struct regcache *regcache, void *buf)
+{
+  int i, vr = find_regno (regcache->tdesc, "vr0");
+
+  for (i = 0; i < 32; i++)
+    collect_register (regcache, vr + i, (uint64_t *) buf + 2 * i);
+}
+
+static void
+loongarch_store_lsxregset (struct regcache *regcache, const void *buf)
+{
+  int i, vr = find_regno (regcache->tdesc, "vr0");
+
+  for (i = 1; i < 32; i++)
+    supply_register (regcache, vr + i, (const uint64_t *) buf + 2 * i);
+}
+
+static void
+loongarch_fill_lasxregset (struct regcache *regcache, void *buf)
+{
+  int i, xr = find_regno (regcache->tdesc, "xr0");
+
+  for (i = 0; i < 32; i++)
+    collect_register (regcache, xr + i, (uint64_t *) buf + 4 * i);
+}
+
+static void
+loongarch_store_lasxregset (struct regcache *regcache, const void *buf)
+{
+  int i, xr = find_regno (regcache->tdesc, "xr0");
+
+  for (i = 1; i < 32; i++)
+    supply_register (regcache, xr + i, (const uint64_t *) buf + 4 * i);
+}
+
+static struct regset_info loongarch_regsets[] =
+{
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, 33 * 8, GENERAL_REGS,
+    loongarch_fill_gregset, loongarch_store_gregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, 34 * 8, FP_REGS,
+    loongarch_fill_fpregset, loongarch_store_fpregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_LARCH_LBT, 5 * 8, EXTENDED_REGS,
+    loongarch_fill_lbtregset, loongarch_store_lbtregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_LARCH_LSX, 32 * 16, EXTENDED_REGS,
+    loongarch_fill_lsxregset, loongarch_store_lsxregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_LARCH_LASX, 32 * 32, EXTENDED_REGS,
+    loongarch_fill_lasxregset, loongarch_store_lasxregset },
+  NULL_REGSET
+};
+
+static struct regsets_info loongarch_regsets_info =
+{
+  loongarch_regsets, 	/* regsets.  */
+  0,		 	/* num_regsets.  */
+  NULL,	      		/* disabled_regsets.  */
+};
+
+static regs_info loongarch_regs_info = { NULL, /* regset_bitmap.  */
+					 NULL, &loongarch_regsets_info };
+
+const regs_info *
+loongarch_target::get_regs_info ()
+{
+  return &loongarch_regs_info;
+}
+
+/* The linux target ops object.  */
+
+linux_process_target *the_linux_target = &the_loongarch_target;
+
+void initialize_low_arch ();
+void
+initialize_low_arch ()
+{
+  initialize_regsets_info (&loongarch_regsets_info);
+}
-- 
2.27.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-11-12  3:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-12  3:18 [PATCH v2 1/2] gdb: LoongArch GDB Port liuzhensong
2021-11-12  3:18 ` [PATCH v2 2/2] gdbserver: LoongArch GDBServer Port liuzhensong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).