[PATCH] riscv: add gdbserver support

jiangshuai_li
2020-01-15T03:18:07Z

This patch is a base support for Riscv32 and Riscv64 arch. It implemented
how to
r/w gprs and fprs, identify the software bkpt insns and the *.dat files for
regs_info.
I have tested it on kernel 5.1.15, and it works normally.

Some requirements may be implemented later:
1. if fpu has 64bits in riscv32
2. hardware bkpt/watchpoint support
3. vector regitsers r/w

Add riscv base linux32 add linux64 gdbserver support.

gdb/gdbserver/Changlog:

2020-0113 Jiangshuai Li <jiangshuai_li@c-sky.com>

* Makefile.in (SFILES): Add '$(srcdir)/linux-riscv-low.c'.
* configure.srv: Target support riscv*-*linux*
* linux-riscv-low.c: New file: Riscv low target supporting code
(init_registers_riscv32_linux): Defined in auto-generated file
riscv32-linux-generated.c for RV32 linux.
(init_registers_riscv64_linux): Defined in auto-generated file
riscv64-linux-generated.c for RV64 linux.
(riscv_cannot_fetch_register): Implement
linux_target_ops.cannot_fetch_register.
(riscv_cannot_store_register,): Implement
linux_target_ops.cannot_store_register.
(riscv_fill_gregset): Fill *BUF according to elf_greg_t.
(riscv_store_gregset): Supply *BUF according to elf_greg_t.
(riscv_fill_fpregset): Fill *BUF according to prfpregset_t.
(riscv_store_fpregset): Supply *BUF according to prfpregset_t.
(riscv_arch_setup): Init current_process ()->tdesc with RV32 or
RV64.
(regs_info): Implement linux_target_ops.riscv_regs_info.
(riscv_sw_breakpoint_from_kind): Implement
linux_target_ops.sw_breakpoint_from_kind, 32bits ebreak or 16bits c.
ebreak.
(riscv_breakpoint_at): Implement
linux_target_ops.breakpoint_at, check if mem at pc is bkpt-insn.

2020-0113 Jiangshuai Li <jiangshuai_li@c-sky.com>

* regformats/riscv32-linux.dat: New file.
* regformats/riscv64-linux.dat: New file.

Change-Id: Ie94277dd709b853912afe3a146be0ac2e636d5a1
---
gdb/ChangeLog | 5 +
gdb/gdbserver/ChangeLog | 24 ++++
gdb/gdbserver/Makefile.in | 1 +
gdb/gdbserver/configure.srv | 5 +
gdb/gdbserver/linux-riscv-low.c | 229
+++++++++++++++++++++++++++++++++++++++
gdb/regformats/riscv32-linux.dat | 71 ++++++++++++
gdb/regformats/riscv64-linux.dat | 71 ++++++++++++
7 files changed, 406 insertions(+)
create mode 100644 gdb/gdbserver/linux-riscv-low.c
create mode 100644 gdb/regformats/riscv32-linux.dat
create mode 100644 gdb/regformats/riscv64-linux.dat

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 39fcac7..f5cf6c7 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,8 @@
+2020-0113 Jiangshuai Li <jiangshuai_li@c-sky.com>
+
+ * regformats/riscv32-linux.dat: New file.
+ * regformats/riscv64-linux.dat: New file.
+
2019-09-18 Christian Biesinger <cbiesinger@google.com>

* dwarf2loc.c: Change extern declaration of dwarf_always_disassemble
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index faafdfd..be2b8f0 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,27 @@
+2020-0113 Jiangshuai Li <jiangshuai_li@c-sky.com>
+
+ * Makefile.in (SFILES): Add '$(srcdir)/linux-riscv-low.c'.
+ * configure.srv: Target support riscv*-*linux*
+ * linux-riscv-low.c: New file: Riscv low target supporting code
+ (init_registers_riscv32_linux): Defined in auto-generated file
+ riscv32-linux-generated.c for RV32 linux.
+ (init_registers_riscv64_linux): Defined in auto-generated file
+ riscv64-linux-generated.c for RV64 linux.
+ (riscv_cannot_fetch_register): Implement
+ linux_target_ops.cannot_fetch_register.
+ (riscv_cannot_store_register,): Implement
+ linux_target_ops.cannot_store_register.
+ (riscv_fill_gregset): Fill *BUF according to elf_greg_t.
+ (riscv_store_gregset): Supply *BUF according to elf_greg_t.
+ (riscv_fill_fpregset): Fill *BUF according to prfpregset_t.
+ (riscv_store_fpregset): Supply *BUF according to prfpregset_t.
+ (riscv_arch_setup): Init current_process ()->tdesc with RV32 or
RV64.
+ (regs_info): Implement linux_target_ops.riscv_regs_info.
+ (riscv_sw_breakpoint_from_kind): Implement
+ linux_target_ops.sw_breakpoint_from_kind, 32bits ebreak or 16bits c.
ebreak.
+ (riscv_breakpoint_at): Implement
+ linux_target_ops.breakpoint_at, check if mem at pc is bkpt-insn.
+
2019-08-23 Sergio Durigan Junior <sergiodj@redhat.com>

* Makefile.in (SFILES): Add 'gdbsupport/gdb-dlfcn.c'.
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index ca0a4cb..ada3494 100644
--- a/gdb/gdbserver/Makefile.in
+++ b/gdb/gdbserver/Makefile.in
@@ -172,6 +172,7 @@ SFILES = \
$(srcdir)/linux-mips-low.c \
$(srcdir)/linux-nios2-low.c \
$(srcdir)/linux-ppc-low.c \
+ $(srcdir)/linux-riscv-low.c \
$(srcdir)/linux-s390-low.c \
$(srcdir)/linux-sh-low.c \
$(srcdir)/linux-sparc-low.c \
diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv
index 66d3d42..b1dc77d 100644
--- a/gdb/gdbserver/configure.srv
+++ b/gdb/gdbserver/configure.srv
@@ -391,6 +391,11 @@ case "${target}" in
srv_linux_regsets=yes
srv_linux_thread_db=yes
;;
+ riscv*-*linux*) srv_regobj="riscv32-linux.o riscv64-linux.o"
+ srv_tgtobj="$srv_linux_obj linux-riscv-low.o"
+ srv_linux_regsets=yes
+ srv_linux_thread_db=yes
+ ;;
*) echo "Error: target not supported by gdbserver."
exit 1
;;
diff --git a/gdb/gdbserver/linux-riscv-low.c
b/gdb/gdbserver/linux-riscv-low.c
new file mode 100644
index 0000000..efd156e
--- /dev/null
+++ b/gdb/gdbserver/linux-riscv-low.c
@@ -0,0 +1,229 @@
+/* GNU/Linux/RISCV specific low level interface, for the remote server for
GDB.
+
+ Copyright (C) 2020 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+
+#include "server.h"
+#include "linux-low.h"
+#include <asm/ptrace.h>
+#include "elf.h"
+#include "riscv-tdep.h"
+
+
+/* Defined in auto-generated file riscv32-linux-generated.c. */
+void init_registers_riscv32_linux (void);
+extern const struct target_desc *tdesc_riscv32_linux;
+
+/* Defined in auto-generated file riscv64-linux-generated.c. */
+void init_registers_riscv64_linux (void);
+extern const struct target_desc *tdesc_riscv64_linux;
+
+static int
+riscv_cannot_fetch_register (int regno)
+{
+ if (regno >=0 && regno <= 63)
+ return 0;
+ else
+ return 1;
+}
+
+static int
+riscv_cannot_store_register (int regno)
+{
+ if (regno >=0 && regno <= 63)
+ return 0;
+ else
+ return 1;
+}
+
+static void
+riscv_fill_gregset (struct regcache *regcache, void *buf)
+{
+ elf_greg_t *regp = (elf_greg_t *)buf;
+
+ /* We only support the integer registers and PC here. */
+ for (int i = RISCV_ZERO_REGNUM + 1; i < RISCV_PC_REGNUM; i++)
+ collect_register (regcache, i, regp + i);
+
+ collect_register (regcache, 32, regp + 0);
+}
+
+static void
+riscv_store_gregset (struct regcache *regcache, const void *buf)
+{
+ const elf_greg_t *regp = (const elf_greg_t *)buf;
+
+ /* We only support the integer registers and PC here. */
+ for (int i = RISCV_ZERO_REGNUM + 1; i < RISCV_PC_REGNUM; i++)
+ supply_register (regcache, i, regp + i);
+
+ /* GDB stores PC in reg 32. Linux kernel stores it in reg 0. */
+ supply_register (regcache, 32, regp + 0);
+
+ /* Fill the inaccessible zero register with zero. */
+ supply_register_zeroed (regcache, 0);
+}
+
+static void
+riscv_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ prfpregset_t *fpregs = (prfpregset_t *)buf;
+
+ /* We only support the FP registers and FCSR here. */
+ for (int i = RISCV_FIRST_FP_REGNUM; i <= RISCV_LAST_FP_REGNUM; i++)
+ collect_register (regcache, i, &fpregs->__d.__f[i -
RISCV_FIRST_FP_REGNUM]);
+
+ collect_register (regcache, RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr);
+}
+
+static void
+riscv_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ const prfpregset_t *fpregs = (const prfpregset_t *)buf;
+
+ /* We only support the FP registers and FCSR here. */
+ for (int i = RISCV_FIRST_FP_REGNUM; i <= RISCV_LAST_FP_REGNUM; i++)
+ supply_register (regcache, i, &fpregs->__d.__f[i -
RISCV_FIRST_FP_REGNUM]);
+
+ supply_register (regcache, RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr);
+}
+
+static struct regset_info riscv_regsets[] = {
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, sizeof
(elf_gregset_t),
+ GENERAL_REGS,
+ riscv_fill_gregset, riscv_store_gregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, sizeof
(elf_fpregset_t),
+ FP_REGS,
+ riscv_fill_fpregset, riscv_store_fpregset },
+ NULL_REGSET
+};
+
+
+static void
+riscv_arch_setup (void)
+{
+ int size = sizeof (elf_greg_t);
+
+ if (size == 4)
+ current_process ()->tdesc = tdesc_riscv32_linux;
+ else
+ current_process ()->tdesc = tdesc_riscv64_linux;
+}
+
+static struct regsets_info riscv_regsets_info =
+ {
+ riscv_regsets, /* regsets */
+ 0, /* num_regsets */
+ NULL, /* disabled_regsets */
+ };
+
+static struct regs_info regs_info =
+ {
+ NULL, /* regset_bitmap */
+ NULL, /* usrregs */
+ &riscv_regsets_info
+ };
+
+static const struct regs_info *
+riscv_regs_info (void)
+{
+ return &regs_info;
+}
+
+/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */
+static const gdb_byte ebreak[] = {0x73, 0x00, 0x10, 0x00};
+static const gdb_byte c_ebreak[] = {0x02, 0x90};
+
+static const gdb_byte *
+riscv_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = kind;
+
+ switch (kind)
+ {
+ case 2:
+ return c_ebreak;
+ case 4:
+ return ebreak;
+ default:
+ gdb_assert (0);
+ }
+}
+
+static int
+riscv_breakpoint_at (CORE_ADDR where)
+{
+ unsigned char insn[4];
+
+ (*the_target->read_memory) (where, (unsigned char *) insn, 4);
+
+ if (insn[0] == ebreak[0] && insn[1] == ebreak[1]
+ && insn[2] == ebreak[2] && insn[3] == ebreak[3])
+ return 1;
+ else if (insn[0] == ebreak[0] && insn[1] == ebreak[1])
+ return 1;
+ else
+ return 0;
+}
+
+
+struct linux_target_ops the_low_target = {
+ riscv_arch_setup, /* arch_setup */
+ riscv_regs_info, /* regs_info */
+ riscv_cannot_fetch_register, /* cannot_fetch_register */
+ riscv_cannot_store_register, /* cannot_store_register */
+ NULL, /* fetch_register */
+ linux_get_pc_64bit, /* get_pc */
+ linux_set_pc_64bit, /* set_pc */
+ NULL, /* breakpoint_kind_from_pc */
+ riscv_sw_breakpoint_from_kind, /* sw_breakpoint_from_kind */
+ NULL, /* get_next_pcs */
+ 0, /* decr_pc_after_break */
+ riscv_breakpoint_at, /* breakpoint_at */
+ NULL, /* supports_z_point_type */
+ NULL, /* insert_point */
+ NULL, /* remove_point */
+ NULL, /* stopped_by_watchpoint */
+ NULL, /* stopped_data_address */
+ NULL, /* collect_ptrace_register */
+ NULL, /* supply_ptrace_register */
+ NULL, /* siginfo_fixup */
+ NULL, /* new_process */
+ NULL, /* delete_process */
+ NULL, /* new_thread */
+ NULL, /* delete_thread */
+ NULL, /* new_fork */
+ NULL, /* prepare_to_resume */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* get_thread_area */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* supports_range_stepping */
+ NULL, /* breakpoint_kind_from_current_state */
+ NULL, /* supports_hardware_single_step */
+};
+
+
+void
+initialize_low_arch (void)
+{
+ init_registers_riscv32_linux ();
+ init_registers_riscv64_linux ();
+ initialize_regsets_info (&riscv_regsets_info);
+}
diff --git a/gdb/regformats/riscv32-linux.dat
b/gdb/regformats/riscv32-linux.dat
new file mode 100644
index 0000000..d7dae52
--- /dev/null
+++ b/gdb/regformats/riscv32-linux.dat
@@ -0,0 +1,71 @@
+name:riscv32_linux
+xmltarget:riscv32-linux.xml
+expedite:sp,fp,pc
+32:zero
+32:ra
+32:sp
+32:gp
+32:tp
+32:t0
+32:t1
+32:t2
+32:fp
+32:s1
+32:a0
+32:a1
+32:a2
+32:a3
+32:a4
+32:a5
+32:a6
+32:a7
+32:s2
+32:s3
+32:s4
+32:s5
+32:s6
+32:s7
+32:s8
+32:s9
+32:s10
+32:s11
+32:t3
+32:t4
+32:t5
+32:t6
+32:pc
+32:ft0
+32:ft1
+32:ft2
+32:ft3
+32:ft4
+32:ft5
+32:ft6
+32:ft7
+32:fs0
+32:fs1
+32:fa0
+32:fa1
+32:fa2
+32:fa3
+32:fa4
+32:fa5
+32:fa6
+32:fa7
+32:fs2
+32:fs3
+32:fs4
+32:fs5
+32:fs6
+32:fs7
+32:fs8
+32:fs9
+32:fs10
+32:fs11
+32:ft8
+32:ft9
+32:ft10
+32:ft11
+32:fflags
+32:frm
+32:fcsr
diff --git a/gdb/regformats/riscv64-linux.dat
b/gdb/regformats/riscv64-linux.dat
new file mode 100644
index 0000000..1b88f6c
--- /dev/null
+++ b/gdb/regformats/riscv64-linux.dat
@@ -0,0 +1,71 @@
+name:riscv64_linux
+xmltarget:riscv64-linux.xml
+expedite:sp,fp,pc
+64:zero
+64:ra
+64:sp
+64:gp
+64:tp
+64:t0
+64:t1
+64:t2
+64:fp
+64:s1
+64:a0
+64:a1
+64:a2
+64:a3
+64:a4
+64:a5
+64:a6
+64:a7
+64:s2
+64:s3
+64:s4
+64:s5
+64:s6
+64:s7
+64:s8
+64:s9
+64:s10
+64:s11
+64:t3
+64:t4
+64:t5
+64:t6
+64:pc
+64:ft0
+64:ft1
+64:ft2
+64:ft3
+64:ft4
+64:ft5
+64:ft6
+64:ft7
+64:fs0
+64:fs1
+64:fa0
+64:fa1
+64:fa2
+64:fa3
+64:fa4
+64:fa5
+64:fa6
+64:fa7
+64:fs2
+64:fs3
+64:fs4
+64:fs5
+64:fs6
+64:fs7
+64:fs8
+64:fs9
+64:fs10
+64:fs11
+64:ft8
+64:ft9
+64:ft10
+64:ft11
+32:fflags
+32:frm
+32:fcsr
--
2.7.4