gdb/0003-gdb-Add-LoongArch-gdb-support.patch
yangchenguang 1c176507d2 Sync 2203 loongarch64 support patch file
Signed-off-by: yangchenguang <yangchenguang@kylinsec.com.cn>
(cherry picked from commit 6d10caf6da2fd566b2d4ed52ebc1b89aa22628f0)
2023-04-28 15:16:05 +08:00

5651 lines
192 KiB
Diff

From 39adfc7cd9f340503f8ee64777e716b9b7a65ec7 Mon Sep 17 00:00:00 2001
From: Qing Zhang <zhangqing@loongson.cn>
Date: Tue, 29 Mar 2022 16:11:47 +0800
Subject: [PATCH 3/5] gdb-Add LoongArch gdb support
Signed-off-by: Qing Zhang <zhangqing@loongson.cn>
---
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/doc/gdb.texinfo | 10 +
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 | 876 ++++++++++
gdb/loongarch-linux-tdep.c | 699 ++++++++
gdb/loongarch-linux-tdep.h | 48 +
gdb/loongarch-tdep.c | 1925 ++++++++++++++++++++++
gdb/loongarch-tdep.h | 60 +
gdb/nat/loongarch-linux-watch.c | 330 ++++
gdb/nat/loongarch-linux-watch.h | 132 ++
gdb/remote.c | 25 +
gdb/target.h | 3 +
gdb/testsuite/gdb.base/dump.exp | 4 +
gdb/testsuite/gdb.base/float.exp | 2 +
gdb/testsuite/gdb.trace/entry-values.exp | 2 +
gdb/testsuite/gdb.xml/tdesc-regs.exp | 5 +
39 files changed, 5170 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 b8729ed..c9cbc5d 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -726,6 +726,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 \
@@ -774,6 +776,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 \
@@ -1348,6 +1353,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 \
@@ -1482,6 +1489,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 \
@@ -1527,6 +1536,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 \
@@ -2215,6 +2225,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 0000000..70bf742
--- /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 0000000..a9cd453
--- /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 0000000..007c638
--- /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 0000000..9d9f65b
--- /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 e94a19b..21a4310 100644
--- a/gdb/configure.host
+++ b/gdb/configure.host
@@ -64,6 +64,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 ;;
@@ -123,6 +124,8 @@ i[34567]86-*-cygwin*) gdb_host=cygwin ;;
ia64-*-linux*) gdb_host=linux ;;
+loongarch*-linux*) gdb_host=linux ;;
+
m68*-*-linux*) gdb_host=linux ;;
m68*-*-netbsdelf* | m68*-*-knetbsd*-gnu)
gdb_host=nbsdelf ;;
diff --git a/gdb/configure.nat b/gdb/configure.nat
index e34cccf..ce5cbc7 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 97a5a57..0b24ee2 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -97,6 +97,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.
@@ -345,6 +348,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 8608c86..d563d59 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -46047,6 +46047,7 @@ registers using the capitalization used in the description.
* ARC Features::
* ARM Features::
* i386 Features::
+* LoongArch Features::
* MicroBlaze Features::
* MIPS Features::
* M68K Features::
@@ -46264,6 +46265,15 @@ The @samp{org.gnu.gdb.i386.pkeys} feature is optional. It should
describe a single register, @samp{pkru}. It is a 32-bit register
valid for i386 and amd64.
+@node LoongArch Features
+@subsection LoongArch Features
+@cindex target descriptions, LoongArch Features
+
+The @samp{org.gnu.gdb.loongarch.base} feature is required for LoongArch
+targets. It should contain the registers @samp{r0} through @samp{r31},
+@samp{pc}, and @samp{badvaddr}. Either the architectural names (@samp{r0},
+@samp{r1}, etc) can be used, or the ABI names (@samp{zero}, @samp{ra}, etc).
+
@node MicroBlaze Features
@subsection MicroBlaze Features
@cindex target descriptions, MicroBlaze features
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index ded8c3b..504e689 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -74,6 +74,7 @@ arm-expedite = r11,sp,pc
i386-expedite = ebp,esp,eip
amd64-expedite = rbp,rsp,rip
x32-expedite = rbp,rsp,rip
+loongarch-expedite = r3,pc
mips-expedite = r29,pc
mips-dsp-expedite = r29,pc
mips64-expedite = r29,pc
@@ -180,6 +181,7 @@ GDB = false
aarch64-feature = 1
arm-feature = 1
i386-feature = 1
+loongarch-feature = 1
riscv-feature = 1
tic6x-feature = 1
@@ -231,6 +233,14 @@ FEATURE_XMLFILES = aarch64-core.xml \
i386/64bit-pkeys.xml \
i386/64bit-sse.xml \
i386/x32-core.xml \
+ loongarch/base32.xml \
+ loongarch/base64.xml \
+ loongarch/fpu32.xml \
+ loongarch/fpu64.xml \
+ loongarch/lbt32.xml \
+ loongarch/lbt64.xml \
+ loongarch/lsx.xml \
+ loongarch/lasx.xml \
riscv/rv32e-xregs.xml \
riscv/32bit-cpu.xml \
riscv/32bit-fpu.xml \
diff --git a/gdb/features/loongarch/base32.c b/gdb/features/loongarch/base32.c
new file mode 100644
index 0000000..b6f2d06
--- /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, "code_ptr");
+ tdesc_create_reg (feature, "r2", regnum++, 1, "general", 32, "data_ptr");
+ tdesc_create_reg (feature, "r3", regnum++, 1, "general", 32, "data_ptr");
+ 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, "data_ptr");
+ 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 0000000..0afe81b
--- /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="code_ptr" group="general"/>
+ <reg name="r2" bitsize="32" type="data_ptr" group="general"/>
+ <reg name="r3" bitsize="32" type="data_ptr" 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="data_ptr" 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 0000000..3ee2d9a
--- /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, "code_ptr");
+ tdesc_create_reg (feature, "r2", regnum++, 1, "general", 64, "data_ptr");
+ tdesc_create_reg (feature, "r3", regnum++, 1, "general", 64, "data_ptr");
+ 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, "data_ptr");
+ 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 0000000..b53479f
--- /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="code_ptr" group="general"/>
+ <reg name="r2" bitsize="64" type="data_ptr" group="general"/>
+ <reg name="r3" bitsize="64" type="data_ptr" 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="data_ptr" 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 0000000..bf8964a
--- /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 0000000..8421730
--- /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 0000000..f9e24c3
--- /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 0000000..420be8b
--- /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 0000000..96e3ea9
--- /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 0000000..6f73df0
--- /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 0000000..d245c75
--- /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 0000000..1c0133e
--- /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 0000000..ecef330
--- /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 0000000..1df26f5
--- /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 0000000..dd253f3
--- /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 0000000..a0a6ba4
--- /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 0000000..f31eff4
--- /dev/null
+++ b/gdb/loongarch-linux-nat.c
@@ -0,0 +1,876 @@
+/* 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;
+};
+
+const struct target_desc *
+loongarch_linux_nat_target::read_description ()
+{
+ return loongarch_linux_read_description_runtime (inferior_ptid.pid ());
+}
+
+/* Fill GDB's register array with the general-purpose, pc and badvaddr
+ register values from the current thread. */
+
+static void
+fetch_gregs_from_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_gregset_t regset;
+
+ if ((regno == -1) || (regs->r <= regno && regno < regs->r + 32) ||
+ (regs->pc == regno) || (regs->badvaddr == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
+ else
+ loongarch_elf_gregset.supply_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid general-purpose, pc and badvaddr
+ register values in the GDB's register array. */
+
+static void
+store_gregs_to_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_gregset_t regset;
+
+ if ((regno == -1) || (regs->r <= regno && regno < regs->r + 32) ||
+ (regs->pc == regno) || (regs->badvaddr == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
+ else
+ {
+ loongarch_elf_gregset.collect_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set NT_PRSTATUS registers"));
+ }
+ }
+}
+
+/* Fill GDB's register array with the fp, fcc and fcsr
+ register values from the current thread. */
+
+static void
+fetch_fpregs_from_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_fpregset_t regset;
+
+ if ((regno == -1) || (regs->f <= regno && regno < regs->f + 32) ||
+ (regs->fcc <= regno && regno < regs->fcc + 8) || (regs->fcsr == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_FPREGSET registers"));
+ else
+ loongarch_elf_fpregset.supply_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid fp, fcc and fcsr
+ register values in the GDB's register array. */
+
+static void
+store_fpregs_to_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_fpregset_t regset;
+
+ if ((regno == -1) || (regs->f <= regno && regno < regs->f + 32) ||
+ (regs->fcc <= regno && regno < regs->fcc + 8) || (regs->fcsr == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_FPREGSET registers"));
+ else
+ {
+ loongarch_elf_fpregset.collect_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set NT_FPREGSET registers"));
+ }
+ }
+}
+
+/* Fill GDB's register array with the Binary Translation
+ register values from the current thread. */
+
+static void
+fetch_lbtregs_from_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lbtregset_t regset;
+
+ if ((regno == -1) || (regs->scr <= regno && regno < regs->scr + 4) ||
+ (regs->EFLAG == regno) || (regs->x86_top == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LBT registers"));
+ else
+ loongarch_elf_lbtregset.supply_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid Binary Translation
+ register values in the GDB's register array. */
+
+static void
+store_lbtregs_to_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lbtregset_t regset;
+
+ if ((regno == -1) || (regs->scr <= regno && regno < regs->scr + 4) ||
+ (regs->EFLAG == regno) || (regs->x86_top == regno))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LBT registers"));
+ else
+ {
+ loongarch_elf_lbtregset.collect_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set LBT registers"));
+ }
+ }
+}
+
+/* Fill GDB's register array with the SIMD eXtension
+ register values from the current thread. */
+
+static void
+fetch_lsxregs_from_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lsxregset_t regset;
+
+ if ((regno == -1) || (regs->vr <= regno && regno < regs->vr + 32))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LSX registers"));
+ else
+ loongarch_elf_lsxregset.supply_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid SIMD eXtension
+ register values in the GDB's register array. */
+
+static void
+store_lsxregs_to_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lsxregset_t regset;
+
+ if ((regno == -1) || (regs->vr <= regno && regno < regs->vr + 32))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LSX registers"));
+ else
+ {
+ loongarch_elf_lsxregset.collect_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set LSX registers"));
+ }
+ }
+}
+
+/* Fill GDB's register array with the Advanced SIMD eXtension
+ register values from the current thread. */
+
+static void
+fetch_lasxregs_from_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lasxregset_t regset;
+
+ if ((regno == -1) || (regs->xr <= regno && regno < regs->xr + 32))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LASX registers"));
+ else
+ loongarch_elf_lasxregset.supply_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid Advanced SIMD eXtension
+ register values in the GDB's register array. */
+
+static void
+store_lasxregs_to_thread (struct regcache *regcache, int regno, pid_t tid)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ loongarch_elf_lasxregset_t regset;
+
+ if ((regno == -1) || (regs->xr <= regno && regno < regs->xr + 32))
+ {
+ struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get LSX registers"));
+ else
+ {
+ loongarch_elf_lsxregset.collect_regset (NULL, regcache, regno, &regset,
+ sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set LSX registers"));
+ }
+ }
+}
+
+/* Implement the "fetch_registers" target_ops method. */
+
+void
+loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
+ int regno)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ pid_t tid = get_ptrace_pid (regcache->ptid ());
+
+ fetch_gregs_from_thread(regcache, regno, tid);
+
+ if (regs->f > 0)
+ fetch_fpregs_from_thread(regcache, regno, tid);
+
+ if (regs->scr > 0)
+ fetch_lbtregs_from_thread(regcache, regno, tid);
+
+ if (regs->vr > 0)
+ fetch_lsxregs_from_thread(regcache, regno, tid);
+
+ if (regs->xr > 0)
+ fetch_lasxregs_from_thread(regcache, regno, tid);
+}
+
+/* Implement the "store_registers" target_ops method. */
+
+void
+loongarch_linux_nat_target::store_registers (struct regcache *regcache,
+ int regno)
+{
+ auto regs = &gdbarch_tdep (regcache->arch ())->regs;
+ pid_t tid = get_ptrace_pid (regcache->ptid ());
+
+ store_gregs_to_thread(regcache, regno, tid);
+
+ if (regs->f > 0)
+ store_fpregs_to_thread(regcache, regno, tid);
+
+ if (regs->scr > 0)
+ store_lbtregs_to_thread(regcache, regno, tid);
+
+ if (regs->vr > 0)
+ store_lsxregs_to_thread(regcache, regno, tid);
+
+ if (regs->xr > 0)
+ store_lasxregs_to_thread(regcache, regno, tid);
+}
+
+/* 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 0000000..1145d9e
--- /dev/null
+++ b/gdb/loongarch-linux-tdep.c
@@ -0,0 +1,699 @@
+/* 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);
+}
+
+/* The RT signal frames look like this:
+ struct rt_sigframe {
+ u32 rs_ass[4];
+ u32 rs_pad[2];
+ struct siginfo rs_info;
+ struct ucontext rs_uc;
+ };
+
+ struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ struct sigcontext uc_mcontext;
+ sigset_t uc_sigmask;
+ unsigned long long uc_extcontext[0];
+ }; */
+
+#define RTSIGFRAME_SIGINFO_SIZE 136
+#define RTSIGFRAME_SIGINFO_OFFSET (6 * 4)
+#define UCONTEXT_SIGCONTEXT_OFFSET 64
+#define RTSIGFRAME_SIGCONTEXT_OFFSET (RTSIGFRAME_SIGINFO_OFFSET \
+ + RTSIGFRAME_SIGINFO_SIZE \
+ + UCONTEXT_SIGCONTEXT_OFFSET)
+
+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 + RTSIGFRAME_SIGCONTEXT_OFFSET;
+ int i;
+
+ trad_frame_set_reg_addr (this_cache, regs->pc, sigcontext_base);
+ for (i = 0; i < 32; i++)
+ trad_frame_set_reg_addr (this_cache, regs->r + i,
+ sigcontext_base + 8 + i * 8);
+
+ trad_frame_set_id (this_cache, frame_id_build (frame_sp, func));
+}
+
+static const struct tramp_frame loongarch_linux_lp64_rt_sigframe =
+{
+ SIGTRAMP_FRAME,
+ 4,
+ { /* From $kernel/arch/loongarch/vdso/sigreturn.S. */
+ /* ori $r11, $r0, 0x8b(__NR_rt_sigreturn) */
+ { 0x03822c0b, ULONGEST_MAX },
+ { 0x002b0000, ULONGEST_MAX }, /* syscall 0 */
+ { TRAMP_SENTINEL_INSN, ULONGEST_MAX } },
+ loongarch_linux_lp64_sigframe_init,
+ NULL
+};
+
+/* Return the current system call's number present in the
+ a7 register. When the function fails, it returns -1. */
+
+static LONGEST
+loongarch_linux_get_syscall_number (struct gdbarch *gdbarch,
+ thread_info *thread)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ auto regs = &tdep->regs;
+ struct regcache *regcache = get_thread_regcache (thread);
+ LONGEST ret;
+
+ if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+ && (REG_VALID == regcache_cooked_read_signed (regcache, regs->r + 11, &ret)))
+ return ret;
+
+ return -1;
+}
+
+static CORE_ADDR
+loongarch_linux_syscall_next_pc (struct frame_info *frame)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ auto regs = &tdep->regs;
+ ULONGEST a7 = get_frame_register_unsigned (frame, regs->r + 11);
+
+ /* If we are about to make a sigreturn syscall, use the unwinder to
+ decode the signal frame. */
+ if ((EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+ && (a7 == 0x8b /* LP64: __NR_rt_sigreturn. */))
+ return frame_unwind_caller_pc (get_current_frame ());
+
+ return -1;
+}
+
+static void
+loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ linux_init_abi (info, gdbarch, 0); /* FIXME displaced step support. */
+
+ if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi))
+ set_solib_svr4_fetch_link_map_offsets (
+ gdbarch, svr4_ilp32_fetch_link_map_offsets);
+
+ else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+ {
+ set_solib_svr4_fetch_link_map_offsets (gdbarch,
+ svr4_lp64_fetch_link_map_offsets);
+ tramp_frame_prepend_unwinder (gdbarch,
+ &loongarch_linux_lp64_rt_sigframe);
+ tdep->syscall_next_pc = loongarch_linux_syscall_next_pc;
+
+ set_gdbarch_get_syscall_number (gdbarch,
+ loongarch_linux_get_syscall_number);
+ }
+
+ /* GNU/Linux uses the dynamic linker included in the GNU C Library. */
+ set_gdbarch_skip_solib_resolver (gdbarch, glibc_skip_solib_resolver);
+
+ /* Enable TLS support. */
+ set_gdbarch_fetch_tls_load_module_address (gdbarch,
+ svr4_fetch_objfile_link_map);
+
+ set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT);
+
+ /* Core file support. */
+ set_gdbarch_iterate_over_regset_sections (
+ gdbarch, loongarch_linux_iterate_over_regset_sections);
+ set_gdbarch_core_read_description (gdbarch,
+ loongarch_linux_core_read_description);
+}
+
+void _initialize_loongarch_linux_tdep ();
+void
+_initialize_loongarch_linux_tdep ()
+{
+ gdbarch_register_osabi (bfd_arch_loongarch, bfd_mach_loongarch32,
+ GDB_OSABI_LINUX, loongarch_linux_init_abi);
+ gdbarch_register_osabi (bfd_arch_loongarch, bfd_mach_loongarch64,
+ 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 0000000..e3456a8
--- /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 0000000..87331bd
--- /dev/null
+++ b/gdb/loongarch-tdep.c
@@ -0,0 +1,1925 @@
+/* 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>
+
+/* Figure out where the longjmp will land.
+ We expect the first arg to be a pointer to the jmp_buf structure
+ from which we extract the pc (LOONGARCH_JB_PC) that we will land
+ at. The pc is copied into PC. This routine returns 1 on
+ success. */
+#define LOONGARCH_JB_PC 0
+
+static int
+loongarch_rlen (struct gdbarch *gdbarch)
+{
+ if (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi))
+ return 64;
+ else if (EF_LOONGARCH_IS_ILP32 (gdbarch_tdep (gdbarch)->ef_abi))
+ return 32;
+ else
+ gdb_assert_not_reached ("unknown ABI");
+ return 0;
+}
+
+static insn_t
+loongarch_fetch_instruction (CORE_ADDR addr, int *errp)
+{
+ size_t insnlen = loongarch_insn_length (0);
+ gdb_byte buf[insnlen];
+ int err;
+ ULONGEST ret;
+
+ err = target_read_memory (addr, buf, insnlen);
+ if (errp != NULL)
+ *errp = err;
+ if (err != 0)
+ {
+ if (errp == NULL)
+ memory_error (TARGET_XFER_E_IO, addr);
+ return 0;
+ }
+ ret = extract_unsigned_integer (buf, insnlen, BFD_ENDIAN_LITTLE);
+ return ret;
+}
+
+static int
+loongarch_insn_is_branch_and_must_branch (insn_t insn)
+{
+ if ((insn & 0xfc000000) == 0x4c000000 /* jirl r0:5,r5:5,s10:16<<2 */
+ || (insn & 0xfc000000) == 0x50000000 /* b sb0:10|10:16<<2 */
+ || (insn & 0xfc000000) == 0x54000000) /* bl sb0:10|10:16<<2 */
+ return 1;
+ return 0;
+}
+
+static int
+loongarch_insn_is_branch (insn_t insn)
+{
+ if (loongarch_insn_is_branch_and_must_branch (insn)
+ || (insn & 0xfc000000) == 0x40000000 /* beqz r5:5,sb0:5|10:16<<2 */
+ || (insn & 0xfc000000) == 0x44000000 /* bnez r5:5,sb0:5|10:16<<2 */
+ || (insn & 0xfc000300) == 0x48000000 /* bceqz c5:3,sb0:5|10:16<<2 */
+ || (insn & 0xfc000300) == 0x48000100 /* bcnez c5:3,sb0:5|10:16<<2 */
+ || (insn & 0xfc000000) == 0x58000000 /* beq r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x5c000000 /* bne r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x60000000 /* blt r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x64000000 /* bge r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x68000000 /* bltu r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x6c000000) /* bgeu r5:5,r0:5,sb10:16<<2 */
+ return 1;
+ return 0;
+}
+
+static CORE_ADDR
+loongarch_next_pc_if_branch (struct regcache *regcache, CORE_ADDR cur_pc,
+ insn_t insn)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
+ auto regs = &gdbarch_tdep (gdbarch)->regs;
+ CORE_ADDR next_pc;
+
+ if ((insn & 0xfc000000) == 0x40000000 /* beqz r5:5,sb0:5|10:16<<2 */
+ || (insn & 0xfc000000) == 0x44000000 /* bnez r5:5,sb0:5|10:16<<2 */
+ || (insn & 0xfc000300) == 0x48000000 /* bceqz c5:3,sb0:5|10:16<<2 */
+ || (insn & 0xfc000300) == 0x48000100) /* bcnez c5:3,sb0:5|10:16<<2 */
+ next_pc = cur_pc + loongarch_decode_imm ("0:5|10:16<<2", insn, 1);
+ else if ((insn & 0xfc000000) == 0x4c000000) /* jirl r0:5,r5:5,s10:16<<2 */
+ next_pc = regcache_raw_get_signed (
+ regcache, regs->r + loongarch_decode_imm ("5:5", insn, 0))
+ + loongarch_decode_imm ("10:16<<2", insn, 1);
+ else if ((insn & 0xfc000000) == 0x50000000 /* b sb0:10|10:16<<2 */
+ || (insn & 0xfc000000) == 0x54000000) /* bl sb0:10|10:16<<2 */
+ next_pc = cur_pc + loongarch_decode_imm ("0:10|10:16<<2", insn, 1);
+ else if ((insn & 0xfc000000) == 0x58000000 /* beq r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x5c000000 /* bne r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x60000000 /* blt r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x64000000 /* bge r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000) == 0x68000000 /* bltu r5:5,r0:5,sb10:16<<2 */
+ || (insn & 0xfc000000)
+ == 0x6c000000) /* bgeu r5:5,r0:5,sb10:16<<2 */
+ next_pc = cur_pc + loongarch_decode_imm ("10:16<<2", insn, 1);
+ else
+ gdb_assert_not_reached ("I don't know what branch is this");
+
+ return next_pc;
+}
+
+/* Checks for an atomic sequence of instructions beginning with a LL/LLD
+ instruction and ending with a SC/SCD instruction. If such a sequence
+ is found, attempt to step through it. A breakpoint is placed at the end of
+ the sequence. */
+
+static std::vector<CORE_ADDR>
+loongarch_deal_with_atomic_sequence (struct regcache *regcache, CORE_ADDR pc)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
+ CORE_ADDR next_pc;
+ std::vector<CORE_ADDR> next_pcs;
+ insn_t insn = loongarch_fetch_instruction (pc, NULL);
+ size_t insnlen = loongarch_insn_length (insn);
+ int i, atomic_sequence_length, found_atomic_sequence_endpoint;
+
+ if ((insn & 0xff000000) != 0x20000000 /* ll.w */
+ && (insn & 0xff000000) != 0x22000000) /* ll.d */
+ return {};
+
+ if (loongarch_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Single step: PC: %s OK, I found ll\\.[wd] here. It's "
+ "atomic sequence?\n",
+ paddress (gdbarch, pc));
+
+ atomic_sequence_length = 30; /* Magic. */
+ found_atomic_sequence_endpoint = 0;
+ for (pc += insnlen, i = 0; i < atomic_sequence_length; pc += insnlen, i++)
+ {
+ insn = loongarch_fetch_instruction (pc, NULL);
+ insnlen = loongarch_insn_length (insn);
+
+ if (loongarch_insn_is_branch_and_must_branch (insn))
+ {
+ if (loongarch_debug)
+ fprintf_unfiltered (
+ gdb_stdlog,
+ "Single step: PC: %s Must branch here. Treat it normally.\n",
+ paddress (gdbarch, pc));
+ break;
+ }
+ else if (loongarch_insn_is_branch (insn))
+ {
+ next_pc = loongarch_next_pc_if_branch (regcache, pc, insn);
+
+ if (loongarch_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Single step: PC: %s May branch inside and "
+ "target is %s. Breakpoint there.\n",
+ paddress (gdbarch, pc),
+ paddress (gdbarch, next_pc));
+
+ next_pcs.push_back (next_pc);
+ }
+ else if ((insn & 0xff000000) == 0x21000000 /* sc.w */
+ || (insn & 0xff000000) == 0x23000000) /* sc.d */
+ {
+ found_atomic_sequence_endpoint = 1;
+ next_pc = pc + insnlen;
+
+ if (loongarch_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "Single step: PC: %s I found sc\\.[wd] and "
+ "atomic sequence ends at here.\n"
+ "Breakpoint next pc: %s.\n",
+ paddress (gdbarch, pc),
+ paddress (gdbarch, next_pc));
+
+ next_pcs.push_back (next_pc);
+ break;
+ }
+ }
+
+ if (!found_atomic_sequence_endpoint)
+ {
+ if (loongarch_debug)
+ fprintf_unfiltered (
+ gdb_stdlog,
+ "Single step: PC: %s Not ends with sc\\.[wd] in %d insns?\n"
+ "Treat it as not atomic sequence.\n",
+ paddress (gdbarch, pc), atomic_sequence_length);
+
+ return {};
+ }
+
+ return next_pcs;
+}
+
+/* Implement LoongArch software single step. */
+
+std::vector<CORE_ADDR>
+loongarch_software_single_step (struct regcache *regcache);
+std::vector<CORE_ADDR>
+loongarch_software_single_step (struct regcache *regcache)
+{
+ struct gdbarch *gdbarch = regcache->arch ();
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ CORE_ADDR pc = regcache_read_pc (regcache);
+ std::vector<CORE_ADDR> next_pcs
+ = loongarch_deal_with_atomic_sequence (regcache, pc);
+
+ if (!next_pcs.empty ())
+ return next_pcs;
+
+ insn_t insn = loongarch_fetch_instruction (pc, NULL);
+ size_t insnlen = loongarch_insn_length (insn);
+ CORE_ADDR next = pc + insnlen;
+
+ if ((insn & 0xffff8000) == 0x002b0000 && tdep->syscall_next_pc)
+ {
+ CORE_ADDR syscall_next = tdep->syscall_next_pc (get_current_frame ());
+ if (syscall_next != -1)
+ {
+ if (loongarch_debug)
+ fprintf_unfiltered (gdb_stdlog,
+ "PC: %s Syscall found. Next pc is %s.\n",
+ paddress (gdbarch, pc),
+ paddress (gdbarch, syscall_next));
+ return {syscall_next};
+ }
+ }
+
+ if (loongarch_insn_is_branch (insn))
+ {
+ CORE_ADDR branch_tgt = loongarch_next_pc_if_branch (regcache, pc, insn);
+ if (loongarch_debug)
+ fprintf_unfiltered (
+ gdb_stdlog, "PC: %s Next pc is %s if branch, %s for non-branch.\n",
+ paddress (gdbarch, pc), paddress (gdbarch, branch_tgt),
+ paddress (gdbarch, next));
+ return {next, branch_tgt};
+ }
+ else
+ {
+ if (loongarch_debug)
+ fprintf_unfiltered (gdb_stdlog, "PC: %s Next pc is %s.\n",
+ paddress (gdbarch, pc), paddress (gdbarch, next));
+ return {next};
+ }
+}
+
+/* Callback function for user_reg_add. */
+
+static struct value *
+value_of_loongarch_user_reg (struct frame_info *frame, const void *baton)
+{
+ return value_of_register ((long long) baton, frame);
+}
+
+/* Implement the register_name gdbarch method. */
+
+static const char *
+loongarch_register_name (struct gdbarch *gdbarch, int regnum)
+{
+ auto regs = &gdbarch_tdep (gdbarch)->regs;
+
+ if ((0 <= regs->r && regs->r <= regnum && regnum < regs->r + 32)
+ && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi)))
+ return loongarch_r_lp64_name[regnum - regs->r] + 1;
+
+ else if ((0 <= regs->f && regs->f <= regnum && regnum < regs->f + 32)
+ && (EF_LOONGARCH_IS_LP64 (gdbarch_tdep (gdbarch)->ef_abi)))
+ return loongarch_f_lp64_name[regnum - regs->f] + 1;
+
+ return tdesc_register_name (gdbarch, regnum);
+}
+
+/* Analyze the function prologue from START_PC to LIMIT_PC. Builds
+ the associated FRAME_CACHE if not null.
+ Return the address of the first instruction past the prologue. */
+
+static CORE_ADDR
+loongarch_scan_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc,
+ CORE_ADDR limit_pc, struct frame_info *this_frame,
+ struct trad_frame_cache *this_cache)
+{
+ auto regs = &gdbarch_tdep (gdbarch)->regs;
+ int rlen_is_64b = (loongarch_rlen (gdbarch) == 64);
+
+ CORE_ADDR cur_pc, prologue_end = 0;
+ insn_t insn;
+ size_t insnlen;
+
+ int sp = regs->sp - regs->r;
+
+ 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 sp,sp,si12 */
+ && !rlen_is_64b)
+ || ((insn & 0xffc00000) == 0x02c00000 /* addi.d sp,sp,si12 */
+ && rlen_is_64b))
+ && rd == sp && rj == sp)
+ {
+ 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,sp,si12 */
+ && !rlen_is_64b)
+ || ((insn & 0xffc00000) == 0x29c00000 /* st.d rd,sp,si12 */
+ && rlen_is_64b))
+ && rj == sp)
+ {
+ 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,sp,si14 */
+ && !rlen_is_64b)
+ || ((insn & 0xff000000) == 0x27000000 /* stptr.d rd,sp,si14 */
+ && rlen_is_64b))
+ && rj == sp)
+ {
+ 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,sp,si12 */
+ || (insn & 0xffc00000) == 0x2bc00000) /* fst.d fd,sp,si12 */
+ && rj == sp)
+ {
+ 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 sp,sp,rk */
+ && !rlen_is_64b)
+ || ((insn & 0xffff8000) == 0x00118000 /* sub.d sp,sp,rk */
+ && rlen_is_64b))
+ && rd == sp && rj == sp)
+ {
+ if (r_value_known[rk])
+ {
+ frame_offset += r_value[rk];
+ prologue_end = cur_pc + insnlen;
+ }
+ else
+ cfa_unknown = 1;
+ }
+ else if ((((insn & 0xffff8000) == 0x00100000 /* add.w sp,sp,rk */
+ && !rlen_is_64b)
+ || ((insn & 0xffff8000) == 0x00108000 /* add.d sp,sp,rk */
+ && rlen_is_64b))
+ && rd == sp && rj == sp)
+ {
+ if (r_value_known[rk] && r_value[rk] < 0)
+ {
+ frame_offset -= r_value[rk];
+ prologue_end = cur_pc + insnlen;
+ }
+ else
+ cfa_unknown = 1;
+ }
+ else if ((insn & 0xffff8000) == 0x00150000 /* or rd,sp,$r0 */
+ && rj == sp && rk == 0)
+ {
+ sp = 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 + sp)
+ + 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 + sp),
+ (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
+compute_type_num(struct type *tp, int &complex_num, int &float_num,
+ int &other_num, int &counter, int &float_seq,
+ int &other_seq)
+{
+ if (tp->code () == TYPE_CODE_COMPLEX)
+ complex_num++;
+ else if (tp->code () == TYPE_CODE_FLT)
+ float_num++;
+ else if (tp->code () != TYPE_CODE_STRUCT)
+ other_num++;
+
+ /* When the function parameter or return value type is a structure,
+ traverse each member in the structure and make relevant marks. */
+ for (int i = 0; i < tp->num_fields (); i++)
+ {
+ field fd = tp->field (i);
+ struct type *t = fd.type ();
+
+ /* Call check_typedef(TYPE_TARGET_TYPE (TYPE)) on our type to make
+ sure that, if TYPE is a TYPE_CODE_TYPEDEF, its TYPE is set to
+ the target type instead of TYPE_CODE_TYPEDEF. */
+ if (t->code () == TYPE_CODE_TYPEDEF)
+ t = check_typedef (TYPE_TARGET_TYPE (t));
+
+ switch (t->code ())
+ {
+ case TYPE_CODE_STRUCT:
+ compute_type_num(t, complex_num, float_num, other_num,
+ counter, float_seq, other_seq);
+ break;
+ case TYPE_CODE_COMPLEX:
+ complex_num++;
+ break;
+ case TYPE_CODE_FLT:
+ float_num++;
+ float_seq = ++counter;
+ break;
+ default:
+ other_num++;
+ other_seq = ++counter;
+ break;
+ }
+ }
+}
+
+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)
+{
+ const int rlen = loongarch_rlen (gdbarch) / 8;
+ int len = TYPE_LENGTH (tp);
+ int complex_num = 0, float_num = 0, other_num = 0;
+ int counter = 0, float_seq = 0, other_seq = 0;
+ stack_data_t elm;
+
+ gdb_assert (len <= 2 * rlen);
+
+ /* Compute the relevant members and types in the function parameters
+ and mark them. */
+ compute_type_num(tp, complex_num, float_num, other_num,
+ counter, float_seq, other_seq);
+
+ if (other_num > 0 && float_num == 0 && len <= rlen)
+ {
+ /* For the small structure has only other types (like char/short/int/long
+ etc.), and the size does not exceed rlen, pass on one gp or stack. */
+ elm.addr = data;
+ elm.len = rlen;
+ gp.push_back (elm);
+ }
+ else if (float_num == 1 && complex_num == 0 && other_num == 0 && len <= rlen)
+ {
+ /* For the small structure has only floating-point (like float/double),
+ and the size does not exceed rlen, pass on one fp or stack. */
+ elm.addr = data;
+ elm.len = rlen;
+ fp.push_back (elm);
+ }
+ else if (float_num == 1 && other_num == 1)
+ {
+ /* For the small structure has only one floating-point type and
+ one other type(like float and int, char and double etc.), the
+ floating-point type passes through one fp or stack, and the
+ other types pass on one gp or stack. */
+ if (float_seq < other_seq)
+ {
+ /* Floating point first, first pass on fp, then gp. */
+ elm.addr = data;
+ if (len == rlen)
+ elm.len = rlen / 2;
+ else
+ elm.len = rlen;
+ fp.push_back (elm);
+ elm.addr += elm.len;
+ gp.push_back (elm);
+ }
+ else
+ {
+ /* Floating point after, first pass on gp, then fp. */
+ elm.addr = data;
+ if (len == rlen)
+ elm.len = rlen / 2;
+ else
+ elm.len = rlen;
+ gp.push_back (elm);
+ elm.addr += elm.len;
+ fp.push_back (elm);
+ }
+ }
+ else if ((complex_num == 1 && float_num == 0 && other_num == 0) ||
+ (float_num ==2 && other_num == 0))
+ {
+ /* For the small structure has only two floating-point types or
+ * one complex number type, pass on two fp or stack. */
+ elm.addr = data;
+ /* 2 float or 1 'float _Complex'. */
+ if (len == rlen)
+ elm.len = rlen / 2;
+ /* 2 double or 1 'double _Complex'. */
+ else
+ elm.len = rlen;
+ fp.push_back (elm);
+ elm.addr += elm.len;
+ fp.push_back (elm);
+ }
+ else
+ {
+ /* For other cases, pass on two gp or stack. */
+ /* For example, the small structure is of the following type,
+ 1. with more than 2 other types and the size is greater than rlen
+ (like struct{int; int; int;}; struct{long; int; short; char;}; etc.).
+ 2. with 'long double' on fpu64 or 'double' on fpu32
+ (like struct{long double;}; or struct{double;}; etc.).
+ 3. with more than 2 floating-point types
+ (like struct{float; float; float;}; struct{float; float; double;};
+ struct{float; float; float; float;}; etc.)
+ 4. with 2 'float _Complex'
+ (like struct{float _Complex; float _Complex;} etc.). */
+ elm.addr = data;
+ elm.len = rlen;
+ gp.push_back (elm);
+ elm.addr += 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);
+
+ 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);
+
+ 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);
+
+ 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 (!f_type->has_varargs () && (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 (!f_type->has_varargs () && 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 (!f_type->has_varargs () && 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 + 1 < gp_max)
+ {
+ pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen);
+ pass_on_reg (regcache, gp++, (const gdb_byte *) val + rlen,
+ rlen);
+ }
+ else if (gp + 1 == gp_max)
+ {
+ pass_on_reg (regcache, gp++, (const gdb_byte *) val, rlen);
+ pass_on_stack (stack, val, tlen, 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_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
+ {
+ int complex_num = 0, float_num = 0, other_num = 0;
+ int counter = 0, float_seq = 0, other_seq = 0, tlen;
+ /* Calculate the relevant members and types in the return value
+ and mark them. */
+ compute_type_num(type, complex_num, float_num, other_num,
+ counter, float_seq, other_seq);
+
+ if (len == rlen)
+ tlen = rlen / 2;
+ else
+ tlen = rlen;
+
+ /* For the small structure has only other types members (like char/short/int/long
+ etc.), and the size does not exceed rlen, pass on $v0. */
+ /* For 'char/short/int/long' etc. pass on $v0. */
+ if (other_num > 0 && float_num == 0 && len <= rlen)
+ loongarch_xfer_reg_part (regcache, regs->r + 4, len, readbuf, 0,
+ writebuf, 0);
+ /* For small structure with only one floating-point member, (like float/double) pass on $fv0. */
+ /* For float/double pass on $fv0. */
+ else if (float_num == 1 && complex_num == 0 && other_num == 0 && len <= rlen)
+ loongarch_xfer_reg_part (regcache, fv, len, readbuf, 0, writebuf, 0);
+ /* For small structure with one float/double member and one other member
+ (char/short/int/long etc.). If the float/dobule member is in the front
+ position, the float/dobule member pass on $fv0, the other member pass
+ on $v0, otherwise the opposite . */
+ else if (float_num == 1 && other_num == 1)
+ if (float_seq < other_seq)
+ loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0,
+ writebuf, 0),
+ loongarch_xfer_reg_part (regcache, regs->r + 4, tlen, readbuf,
+ tlen, writebuf, rlen);
+ else
+ loongarch_xfer_reg_part (regcache, regs->r + 4, rlen, readbuf, 0,
+ writebuf, 0),
+ loongarch_xfer_reg_part (regcache, fv, tlen, readbuf,
+ tlen, writebuf, rlen);
+ /* For small structure with one 'float/double _Complex' member,
+ $fv0 is real and $fv1 is img. */
+ /* For small structure with only one float and double member or
+ or two float member , or two dobule member, $fv0 is the 1st
+ member and $fv1 is the 2nd member. */
+ /* For 'float/double _Complex', $fv0 is real and $fv1 is img. */
+ else if ((complex_num == 1 && float_num == 0 && other_num == 0) ||
+ (float_num ==2 && other_num == 0))
+ loongarch_xfer_reg_part (regcache, fv, rlen, readbuf, 0,
+ writebuf, 0),
+ loongarch_xfer_reg_part (regcache, fv + 1, tlen, readbuf,
+ tlen, writebuf, rlen);
+ /* For small structure with 'long double' member,
+ or when the small structure has more than two vaild members
+ and the size is greater than rlen, pass on $v0 and $v1. */
+ /* For small structure with two 'float _Complex' member,
+ $v0 is the 1st member and $v1 is the 2nd member. */
+ /* For 'long double' on fpu64 or 'double' on fpu32 pass on $v0 and $v1. */
+ 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);
+}
+
+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 int
+loongarch_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+{
+ CORE_ADDR jb_addr;
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ uint32_t ptr_size = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+ gdb_byte buf[ptr_size];
+
+ jb_addr = get_frame_register_unsigned (frame, LOONGARCH_A0_REGNUM);
+
+ if (target_read_memory ((jb_addr + LOONGARCH_JB_PC * ptr_size),
+ buf, ptr_size))
+ return 0;
+
+ *pc = extract_unsigned_integer (buf, ptr_size, BFD_ENDIAN_LITTLE);
+
+ return 1;
+}
+
+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 abfd is nullptr then a EF_LOONGARCH_ABI_LP64 is returned in
+ its default state. */
+ if (info.abfd != NULL
+ && bfd_get_flavour (info.abfd) == bfd_target_elf_flavour)
+ {
+ int eflags = elf_elfheader (info.abfd)->e_flags;
+ unsigned char eclass = elf_elfheader (info.abfd)->e_ident[EI_CLASS];
+
+ if (eflags) /* Executable file */
+ {
+ tdep->ef_abi = (EF_LOONGARCH_ABI(eflags) & EF_LOONGARCH_ABI_MASK);
+ }
+ else /* Core file */
+ {
+ if (eclass == ELFCLASS64)
+ tdep->ef_abi = EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT;
+ else
+ tdep->ef_abi = EF_LOONGARCH_ABI_ILP32_DOUBLE_FLOAT;
+ }
+ }
+ else
+ tdep->ef_abi = EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT;
+
+ /* Check any target description for validity. */
+ if (!tdesc_has_registers (tdesc))
+ tdesc = loongarch_get_base_target_description (
+ EF_LOONGARCH_IS_ILP32 (tdep->ef_abi) ? 32 : 64);
+
+ int valid_p = 1;
+ const struct tdesc_feature *feature;
+
+ feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.base");
+ if (feature == NULL)
+ return NULL;
+ regnum = 0;
+ tdesc_data = tdesc_data_alloc ();
+
+ tdep->regs.r = regnum;
+ for (i = 0; i < 32; i++)
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ loongarch_r_normal_name[i] + 1);
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+ tdep->regs.pc = regnum++, "pc");
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (),
+ tdep->regs.badvaddr = regnum++, "badvaddr");
+
+ if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.fpu")))
+ {
+ tdep->regs.f = regnum;
+ for (i = 0; i < 32; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ loongarch_f_normal_name[i] + 1);
+ tdep->regs.fcc = regnum;
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc0");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc1");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc2");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc3");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc4");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc5");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc6");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ "fcc7");
+ valid_p &= tdesc_numbered_register (feature, tdesc_data.get (),
+ tdep->regs.fcsr = regnum++, "fcsr");
+ }
+
+ if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lbt")))
+ {
+ tdep->regs.scr = regnum;
+ for (i = 0; i < 4; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ loongarch_cr_normal_name[i] + 1);
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (),
+ tdep->regs.EFLAG = regnum++, "EFLAG");
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (),
+ tdep->regs.x86_top = regnum++, "x86_top");
+ }
+
+ if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lsx")))
+ {
+ tdep->regs.vr = regnum;
+ for (i = 0; i < 32; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ loongarch_v_normal_name[i] + 1);
+ }
+
+ if ((feature = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lasx")))
+ {
+ tdep->regs.xr = regnum;
+ for (i = 0; i < 32; i++)
+ valid_p
+ &= tdesc_numbered_register (feature, tdesc_data.get (), regnum++,
+ loongarch_x_normal_name[i] + 1);
+ }
+
+ if (!valid_p)
+ {
+ return NULL;
+ }
+
+ info.byte_order_for_code = BFD_ENDIAN_LITTLE;
+
+ /* Find a candidate among the list of pre-declared architectures. */
+ for (arches = gdbarch_list_lookup_by_info (arches, &info); arches != NULL;
+ arches = gdbarch_list_lookup_by_info (arches->next, &info))
+ {
+ if (gdbarch_tdep (arches->gdbarch)->ef_abi != tdep->ef_abi)
+ continue;
+
+ return arches->gdbarch;
+ }
+
+ /* None found, so create a new architecture from the information provided. */
+ tdep = (struct gdbarch_tdep *) xmalloc (sizeof (tdep_instant));
+ memcpy (tdep, &tdep_instant, sizeof (tdep_instant));
+ gdbarch = gdbarch_alloc (&info, tdep);
+
+ /* Target data types. */
+ if (EF_LOONGARCH_IS_ILP32 (tdep->ef_abi))
+ {
+ set_gdbarch_short_bit (gdbarch, 16);
+ set_gdbarch_int_bit (gdbarch, 32);
+ set_gdbarch_long_bit (gdbarch, 32);
+ set_gdbarch_long_long_bit (gdbarch, 32);
+ set_gdbarch_float_bit (gdbarch, 32);
+ set_gdbarch_double_bit (gdbarch, 64);
+ set_gdbarch_long_double_bit (gdbarch, 128);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
+ set_gdbarch_ptr_bit (gdbarch, 32);
+ set_gdbarch_char_signed (gdbarch, 0);
+ }
+ else if (EF_LOONGARCH_IS_LP64 (tdep->ef_abi))
+ {
+ set_gdbarch_short_bit (gdbarch, 16);
+ set_gdbarch_int_bit (gdbarch, 32);
+ set_gdbarch_long_bit (gdbarch, 64);
+ set_gdbarch_long_long_bit (gdbarch, 64);
+ set_gdbarch_float_bit (gdbarch, 32);
+ set_gdbarch_double_bit (gdbarch, 64);
+ set_gdbarch_long_double_bit (gdbarch, 128);
+ set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
+ set_gdbarch_ptr_bit (gdbarch, 64);
+ set_gdbarch_char_signed (gdbarch, 0);
+
+ tdep->regs.ra = tdep->regs.r + 1;
+ tdep->regs.sp = tdep->regs.r + 3;
+
+ for (i = 0; i < ARRAY_SIZE (loongarch_r_normal_name); ++i)
+ if (loongarch_r_normal_name[i][0] != '\0')
+ user_reg_add (gdbarch, loongarch_r_normal_name[i] + 1,
+ value_of_loongarch_user_reg,
+ (void *) (size_t) (tdep->regs.r + i));
+
+ for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name); ++i)
+ if (loongarch_r_lp64_name[i][0] != '\0')
+ user_reg_add (gdbarch, loongarch_r_lp64_name[i] + 1,
+ value_of_loongarch_user_reg,
+ (void *) (size_t) (tdep->regs.r + i));
+
+ for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name1); ++i)
+ if (loongarch_r_lp64_name[i][0] != '\0')
+ user_reg_add (gdbarch, loongarch_r_lp64_name1[i] + 1,
+ value_of_loongarch_user_reg,
+ (void *) (size_t) (tdep->regs.r + i));
+
+ /* Functions handling dummy frames. */
+ set_gdbarch_push_dummy_call (gdbarch,
+ loongarch_lp32lp64_push_dummy_call);
+ set_gdbarch_return_value (gdbarch, loongarch_lp64_return_value);
+
+ }
+ else
+ gdb_assert_not_reached ("unknown ABI");
+
+ /* Hook in OS ABI-specific overrides, if they have been registered. */
+ info.target_desc = tdesc;
+ info.tdesc_data = tdesc_data.get ();
+
+ /* Register architecture. */
+ set_gdbarch_num_regs (gdbarch, regnum);
+ set_gdbarch_sp_regnum (gdbarch, tdep->regs.sp);
+ set_gdbarch_pc_regnum (gdbarch, tdep->regs.pc);
+
+ tdesc_use_registers (gdbarch, tdesc, std::move (tdesc_data));
+
+ /* Functions to supply register information. */
+ set_gdbarch_register_name (gdbarch, loongarch_register_name);
+
+ /* Handle overlapping dwarf2 register code for fp/lsx/lasx. */
+ set_gdbarch_convert_register_p (gdbarch, loongarch_convert_register_p);
+ set_gdbarch_register_to_value (gdbarch, loongarch_register_to_value);
+ set_gdbarch_value_to_register (gdbarch, loongarch_value_to_register);
+
+ /* Functions to analyze frames. */
+ set_gdbarch_skip_prologue (gdbarch, loongarch_skip_prologue);
+ set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
+ set_gdbarch_frame_align (gdbarch, loongarch_frame_align);
+
+ /* Functions to access frame data. */
+ set_gdbarch_unwind_pc (gdbarch, loongarch_unwind_pc);
+ set_gdbarch_unwind_sp (gdbarch, loongarch_unwind_sp);
+
+ set_gdbarch_dummy_id (gdbarch, loongarch_dummy_id);
+
+ set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step);
+
+ set_gdbarch_breakpoint_kind_from_pc (gdbarch,
+ loongarch_breakpoint::kind_from_pc);
+ set_gdbarch_sw_breakpoint_from_kind (gdbarch,
+ loongarch_breakpoint::bp_from_kind);
+
+ set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
+
+ /* Virtual tables. */
+ set_gdbarch_vbit_in_delta (gdbarch, 1);
+
+ set_gdbarch_gcc_target_options (gdbarch, loongarch_gcc_target_options);
+
+ gdbarch_init_osabi (info, gdbarch);
+ set_gdbarch_register_reggroup_p (gdbarch, loongarch_register_reggroup_p);
+ set_gdbarch_register_name (gdbarch, loongarch_register_name);
+
+ set_gdbarch_get_longjmp_target (gdbarch, loongarch_get_longjmp_target);
+
+ /* 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 0000000..c673c9e
--- /dev/null
+++ b/gdb/loongarch-tdep.h
@@ -0,0 +1,60 @@
+/* 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"
+
+/* Register numbers of important registers. Note that most of
+ these values are "real" register numbers, and correspond to the
+ general registers of the machine. */
+#define LOONGARCH_A0_REGNUM 4 /* Loc of first arg */
+
+struct gdbarch_tdep
+{
+ int ef_abi; /* EF_LOONGARCH_ABI */
+
+ struct
+ {
+ int r;
+ int ra;
+ int sp;
+ int pc;
+ int badvaddr;
+
+ int f;
+ int fcc;
+ int fcsr;
+ int vr;
+ int xr;
+
+ int scr;
+ int EFLAG;
+ int x86_top;
+
+ } regs;
+
+ /* Return the expected next PC if FRAME is stopped at a syscall
+ instruction. */
+ CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
+};
+
+#endif /* LOONGARCH_TDEP_H */
diff --git a/gdb/nat/loongarch-linux-watch.c b/gdb/nat/loongarch-linux-watch.c
new file mode 100644
index 0000000..f7c0dbf
--- /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 0000000..ab80b44
--- /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 f2271ad..94abdca 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2070,6 +2070,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,
@@ -5250,6 +5252,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,
@@ -11264,6 +11270,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)
{
@@ -15099,6 +15117,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 ddd4f38..f3f6045 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 52c6983..53e6873 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 dc5e2fa..d179a8f 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 3695a1e..f19f5b9 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 7402ba8..4b94747 100644
--- a/gdb/testsuite/gdb.xml/tdesc-regs.exp
+++ b/gdb/testsuite/gdb.xml/tdesc-regs.exp
@@ -83,6 +83,11 @@ switch -glob -- [istarget] {
set regdir "i386/"
set core-regs {64bit-core.xml 64bit-sse.xml}
}
+ "loongarch64-*-*" {
+ set architecture "loongarch64"
+ set regdir "loongarch/"
+ set core-regs {base64.xml fpu64.xml lbt64.xml lsx.xml lasx.xml}
+ }
}
# If no core registers were specified, assume this target does not
--
2.36.0