[PATCH1/7,LoongArch] GDB support

Message ID CAKjxQHn99VBCrFB+jG8YidvXS64dfZ_s2MArGQ60mi2bE2KtKg@mail.gmail.com
State New
Headers show
Series
  • [PATCH1/7,LoongArch] GDB support
Related show

Commit Message

Alan Modra via Binutils Aug. 14, 2021, 8:09 a.m.

Comments

Alan Modra via Binutils Sept. 1, 2021, 1:05 p.m. | #1
Hi Paul,

   The changes to include/elf/common.h are approved.

   You will need approval from a GDB maintainer for the rest.

Cheers
   Nick

Patch

From 142c663124c17aa8886bc641eeee44d16837145b Mon Sep 17 00:00:00 2001
From: liuzhensong <liuzhensong@loongson.cn>
Date: Fri, 13 Aug 2021 09:50:24 +0800
Subject: [PATCH 1/7] gdb: LoongArch GDB Port.

  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
  include/elf/common.h
---
 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                |  882 ++++++++++
 gdb/loongarch-linux-tdep.c               |  686 ++++++++
 gdb/loongarch-linux-tdep.h               |   48 +
 gdb/loongarch-tdep.c                     | 1880 ++++++++++++++++++++++
 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 +
 include/elf/common.h                     |   10 +
 40 files changed, 5152 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 73a1bf83c85..8d282fefeb1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -727,6 +727,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 \
@@ -775,6 +777,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 \
@@ -1349,6 +1354,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 \
@@ -1483,6 +1490,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 \
@@ -1528,6 +1537,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 \
@@ -2216,6 +2226,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 00000000000..70bf74260a4
--- /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 00000000000..a9cd453734b
--- /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 00000000000..007c638cdbb
--- /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 00000000000..9d9f65b3e7a
--- /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 1fc83601e0e..20fa4ccd814 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 655c75dd1ab..150e02751d6 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 cac76f4106c..3dcc46cf55c 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -98,6 +98,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.
@@ -346,6 +349,11 @@  lm32-*-*)
 	gdb_sim=../sim/lm32/libsim.a
 	;;
 
+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 eb785a08633..958b5b2816a 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -965,6 +965,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 aa38d176539..837cc9273ba 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
@@ -174,6 +175,7 @@  arm-feature = 1
 i386-feature = 1
 riscv-feature = 1
 tic6x-feature = 1
+loongarch-feature = 1
 
 all: $(OUTPUTS)
 
@@ -229,6 +231,14 @@  FEATURE_XMLFILES = aarch64-core.xml \
 	riscv/32bit-fpu.xml \
 	riscv/64bit-cpu.xml \
 	riscv/64bit-fpu.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 \
 	tic6x-c6xp.xml \
 	tic6x-core.xml \
 	tic6x-gp.xml
diff --git a/gdb/features/loongarch/base32.c b/gdb/features/loongarch/base32.c
new file mode 100644
index 00000000000..4fcdb21a137
--- /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 00000000000..8eacfdd3e70
--- /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 00000000000..5212c16b606
--- /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 00000000000..afbbb631474
--- /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 00000000000..bf8964a3b31
--- /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 00000000000..8421730e96a
--- /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 00000000000..f9e24c30f31
--- /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 00000000000..420be8bdb33
--- /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 00000000000..96e3ea9000f
--- /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 00000000000..6f73df04d24
--- /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 00000000000..d245c750040
--- /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 00000000000..1c0133e415c
--- /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 00000000000..ecef3307b7f
--- /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 00000000000..1df26f51711
--- /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 00000000000..dd253f3c769
--- /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 00000000000..a0a6ba49e4d
--- /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 00000000000..2c4fad05049
--- /dev/null
+++ b/gdb/loongarch-linux-nat.c
@@ -0,0 +1,882 @@ 
+/* 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);
+};
+
+/* Assume that we have PTRACE_{GET,SET}REGSET et al. support.  If we do not,
+   we'll clear this and use PTRACE_PEEKUSR instead.  */
+extern enum tribool have_ptrace_getregset;
+
+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)
+{
+  struct lwp_info *lp;
+  int tid;
+
+  ALL_LWPS (lp)
+  {
+    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 00000000000..50de439386e
--- /dev/null
+++ b/gdb/loongarch-linux-tdep.c
@@ -0,0 +1,686 @@ 
+/* 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;
+
+  switch (tdep->ef_abi)
+    {
+    case EF_LARCH_ABI_LP64:
+      if (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);
+
+  switch (tdep->ef_abi)
+    {
+    case EF_LARCH_ABI_LP64:
+      /* If we are about to make a sigreturn syscall, use the unwinder to
+	 decode the signal frame.  */
+      if (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.  */
+
+  switch (tdep->ef_abi)
+    {
+    case EF_LARCH_ABI_LP32:
+      set_solib_svr4_fetch_link_map_offsets (
+	gdbarch, svr4_ilp32_fetch_link_map_offsets);
+      break;
+    case EF_LARCH_ABI_LP64:
+      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);
+      break;
+    }
+
+  /* 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 00000000000..e3456a8fa4b
--- /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 00000000000..23d814db3bb
--- /dev/null
+++ b/gdb/loongarch-tdep.c
@@ -0,0 +1,1880 @@ 
+/* 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)
+{
+  switch (gdbarch_tdep (gdbarch)->ef_abi)
+    {
+    case EF_LARCH_ABI_LP64:
+      return 64;
+    case EF_LARCH_ABI_LP32:
+      return 32;
+    default:
+      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)
+    switch (gdbarch_tdep (gdbarch)->ef_abi)
+      {
+      case EF_LARCH_ABI_LP64:
+	return loongarch_r_lp64_name[regnum - regs->r] + 1;
+      }
+  else if (0 <= regs->f && regs->f <= regnum && regnum < regs->f + 32)
+    switch (gdbarch_tdep (gdbarch)->ef_abi)
+      {
+      case EF_LARCH_ABI_LP64:
+	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.loc.bitpos - bitpos, 8))
+		{
+		  len = fd.bitsize / TARGET_CHAR_BIT + 1;
+		  bitpos = fd.loc.bitpos;
+		}
+	      else
+		continue;
+	    }
+	  else
+	    {
+	      bitpos = fd.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)
+    {
+      int e_flags = elf_elfheader (info.abfd)->e_flags;
+      auto e_abi = e_flags & EF_LARCH_ABI;
+
+      switch (e_abi)
+	{
+	case EF_LARCH_ABI_LP32:
+	case EF_LARCH_ABI_LP64:
+	  tdep->ef_abi = e_abi;
+	  break;
+	default:
+	  tdep->ef_abi = EF_LARCH_ABI_LP64;
+	}
+    }
+  else
+    tdep->ef_abi = EF_LARCH_ABI_LP64;
+
+  /* Check any target description for validity.  */
+  if (!tdesc_has_registers (tdesc))
+    tdesc = loongarch_get_base_target_description (
+      tdep->ef_abi == EF_LARCH_ABI_LP32 ? 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.  */
+  switch (tdep->ef_abi)
+    {
+    case EF_LARCH_ABI_LP32:
+      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);
+      break;
+    case EF_LARCH_ABI_LP64:
+      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);
+
+      break;
+    default:
+      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 00000000000..593adfa2b33
--- /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_LARCH_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 00000000000..f7c0dbf9d02
--- /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 00000000000..ab80b44b749
--- /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 b6da6b086a2..687a18930c4 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,
@@ -5262,6 +5264,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,
@@ -11275,6 +11281,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)
     {
@@ -15110,6 +15128,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 57afebef876..3b4d5cbf73c 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 52c698333d2..53e68736b94 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 dc5e2fa6fcc..d179a8f18be 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 3695a1ee114..f19f5b900b4 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 7402ba8d952..4b94747b77d 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
diff --git a/include/elf/common.h b/include/elf/common.h
index 95ade894e98..52ffce0c169 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -687,6 +687,16 @@ 
 #define NT_ARC_V2	0x600		/* ARC HS accumulator/extra registers.  */
 					/*   note name must be "LINUX".  */
 #define NT_RISCV_CSR    0x900		/* RISC-V Control and Status Registers */
+					/*   note name must be "LINUX".  */
+#define NT_LARCH_CPUCFG 0xa00		/* LoongArch CPU config registers */
+					/*   note name must be "LINUX".  */
+#define NT_LARCH_CSR    0xa01		/* LoongArch control state registers */
+					/*   note name must be "LINUX".  */
+#define NT_LARCH_LSX    0xa02		/* LoongArch Loongson SIMD Extension registers */
+					/*   note name must be "LINUX".  */
+#define NT_LARCH_LASX   0xa03		/* LoongArch Loongson Advanced SIMD Extension registers */
+					/*   note name must be "LINUX".  */
+#define NT_LARCH_LBT    0xa04		/* LoongArch Loongson Binary Translation registers */
 					/*   note name must be "CORE".  */
 #define NT_SIGINFO	0x53494749	/* Fields of siginfo_t.  */
 #define NT_FILE		0x46494c45	/* Description of mapped files.  */
-- 
2.27.0