From 33b7b2b05fc32dc81e7f69bc0799aae55fde8255 Mon Sep 17 00:00:00 2001 From: Qing Zhang Date: Thu, 2 Jun 2022 17:14:39 +0800 Subject: [PATCH 4/5] gdbserver-Add LoongArch port support Signed-off-by: Qing Zhang --- gdb/arch/loongarch.c | 2 - gdb/arch/loongarch.h | 2 - gdbserver/Makefile.in | 2 + gdbserver/configure.srv | 7 + gdbserver/linux-loongarch-low.cc | 284 +++++++++++++++++++++++++++++++ 5 files changed, 293 insertions(+), 4 deletions(-) create mode 100644 gdbserver/linux-loongarch-low.cc diff --git a/gdb/arch/loongarch.c b/gdb/arch/loongarch.c index 007c638..34307f5 100644 --- a/gdb/arch/loongarch.c +++ b/gdb/arch/loongarch.c @@ -19,8 +19,6 @@ #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> diff --git a/gdb/arch/loongarch.h b/gdb/arch/loongarch.h index 9d9f65b..dfe011f 100644 --- a/gdb/arch/loongarch.h +++ b/gdb/arch/loongarch.h @@ -26,8 +26,6 @@ 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 * diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in index 12e9b27..ddd52ec 100644 --- a/gdbserver/Makefile.in +++ b/gdbserver/Makefile.in @@ -192,6 +192,7 @@ SFILES = \ $(srcdir)/linux-arc-low.cc \ $(srcdir)/linux-arm-low.cc \ $(srcdir)/linux-ia64-low.cc \ + $(srcdir)/linux-loongarch-low.cc \ $(srcdir)/linux-low.cc \ $(srcdir)/linux-m68k-low.cc \ $(srcdir)/linux-mips-low.cc \ @@ -226,6 +227,7 @@ SFILES = \ $(srcdir)/../gdb/arch/arm.c \ $(srcdir)/../gdb/arch/arm-get-next-pcs.c \ $(srcdir)/../gdb/arch/arm-linux.c \ + $(srcdir)/../gdb/arch/loongarch.c \ $(srcdir)/../gdb/arch/ppc-linux-common.c \ $(srcdir)/../gdb/arch/riscv.c \ $(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \ diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index 971f537..136be26 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -125,6 +125,13 @@ case "${gdbserver_host}" in srv_tgtobj="$srv_linux_obj linux-ia64-low.o" srv_linux_usrregs=yes ;; + loongarch*-*-linux*) srv_tgtobj="arch/loongarch.o arch/loongarch-linux-nat.o" + srv_tgtobj="${srv_tgtobj} linux-loongarch-low.o" + srv_tgtobj="${srv_tgtobj} ${srv_linux_obj}" + srv_linux_regsets=yes + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; m68*-*-linux*) if test "$gdb_cv_m68k_is_coldfire" = yes; then srv_regobj=reg-cf.o else diff --git a/gdbserver/linux-loongarch-low.cc b/gdbserver/linux-loongarch-low.cc new file mode 100644 index 0000000..cd8ad6e --- /dev/null +++ b/gdbserver/linux-loongarch-low.cc @@ -0,0 +1,284 @@ +/* GNU/Linux/LoongArch specific low level interface, for the remote server + for GDB. + Copyright (C) 2022 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 . */ + +#include "server.h" +#include "linux-low.h" +#include "tdesc.h" +#include "elf/common.h" +#include "arch/loongarch-linux-nat.h" + +/* Linux target op definitions for the LoongArch architecture. */ + +class loongarch_target : public linux_process_target +{ +public: + + const regs_info *get_regs_info () override; + + int breakpoint_kind_from_pc (CORE_ADDR *pcptr) override; + + const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override; + +protected: + + void low_arch_setup () override; + + bool low_cannot_fetch_register (int regno) override; + + bool low_cannot_store_register (int regno) override; + + bool low_fetch_register (regcache *regcache, int regno) override; + + bool low_supports_breakpoints () override; + + CORE_ADDR low_get_pc (regcache *regcache) override; + + void low_set_pc (regcache *regcache, CORE_ADDR newpc) override; + + bool low_breakpoint_at (CORE_ADDR pc) override; +}; + +/* The singleton target ops object. */ + +static loongarch_target the_loongarch_target; + +bool +loongarch_target::low_cannot_fetch_register (int regno) +{ + gdb_assert_not_reached ("linux target op low_cannot_fetch_register " + "is not implemented by the target"); +} + +bool +loongarch_target::low_cannot_store_register (int regno) +{ + gdb_assert_not_reached ("linux target op low_cannot_store_register " + "is not implemented by the target"); +} + +/* Implementation of linux target ops method "low_arch_setup". */ + +void +loongarch_target::low_arch_setup () +{ + static const char *expedite_regs[] = { "r3", "pc", NULL }; + int pid = lwpid_of (current_thread); + struct target_desc *tdesc = loongarch_linux_read_description_runtime (pid); + + if (!tdesc->expedite_regs) + init_target_desc (tdesc, expedite_regs); + current_process ()->tdesc = tdesc; +} + +/* Collect GPRs from REGCACHE into BUF. */ + +static void +loongarch_fill_gregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + elf_gregset_t *regset = (elf_gregset_t *) buf; + int regno = find_regno (tdesc, "r0"); + int i; + + for (i = 1; i < 32; i++) + collect_register (regcache, regno + i, *regset + i); + collect_register_by_name (regcache, "orig_a0", *regset + 32); + collect_register_by_name (regcache, "pc", *regset + 33); + collect_register_by_name (regcache, "badv", *regset + 34); +} + +/* Supply GPRs from BUF into REGCACHE. */ + +static void +loongarch_store_gregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + const elf_gregset_t *regset = (const elf_gregset_t *) buf; + int regno = find_regno (tdesc, "r0"); + int i; + + supply_register_zeroed (regcache, regno); + for (i = 1; i < 32; i++) + supply_register (regcache, regno + i, *regset + i); + supply_register_by_name (regcache, "orig_a0", *regset + 32); + supply_register_by_name (regcache, "pc", *regset + 33); + supply_register_by_name (regcache, "badv", *regset + 34); +} + +/* Collect FPRs from REGCACHE into BUF. */ + +static void +loongarch_fill_fpregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int f = find_regno (tdesc, "f0"); + int fcc = find_regno (tdesc, "fcc0"); + int flen = register_size (regcache->tdesc, f); + gdb_byte *regbuf = (gdb_byte *) buf; + int i; + + for (i = 0; i < ELF_NFPREG - 2; i++, regbuf += flen) + collect_register (regcache, f + i, regbuf); + for (i = 0; i < 8; i++) + collect_register (regcache, fcc + i, regbuf++); + collect_register_by_name (regcache, "fcsr", regbuf); +} + +/* Supply FPRs from BUF into REGCACHE. */ + +static void +loongarch_store_fpregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int f = find_regno (tdesc, "f0"); + int fcc = find_regno (tdesc, "fcc0"); + int flen = register_size (regcache->tdesc, f); + const gdb_byte *regbuf = (const gdb_byte *) buf; + int i; + + for (i = 0; i < ELF_NFPREG - 2; i++, regbuf += flen) + supply_register (regcache, f + i, regbuf); + for (i = 0; i < 8; i++) + supply_register (regcache, fcc + i, regbuf++); + supply_register_by_name (regcache, "fcsr", regbuf); +} + +/* LoongArch/Linux regsets. */ +static struct regset_info loongarch_regsets[] = { + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, sizeof (elf_gregset_t), + GENERAL_REGS, loongarch_fill_gregset, loongarch_store_gregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, sizeof (elf_fpregset_t), + FP_REGS, loongarch_fill_fpregset, loongarch_store_fpregset }, + NULL_REGSET +}; + +/* LoongArch/Linux regset information. */ +static struct regsets_info loongarch_regsets_info = + { + loongarch_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +/* Definition of linux_target_ops data member "regs_info". */ +static struct regs_info loongarch_regs = + { + NULL, /* regset_bitmap */ + NULL, /* usrregs */ + &loongarch_regsets_info, + }; + +/* Implementation of linux target ops method "get_regs_info". */ + +const regs_info * +loongarch_target::get_regs_info () +{ + return &loongarch_regs; +} + +/* Implementation of linux target ops method "low_fetch_register". */ + +bool +loongarch_target::low_fetch_register (regcache *regcache, int regno) +{ + const struct target_desc *tdesc = regcache->tdesc; + + if (regno != find_regno (tdesc, "r0")) + return false; + supply_register_zeroed (regcache, regno); + return true; +} + +bool +loongarch_target::low_supports_breakpoints () +{ + return true; +} + +/* Implementation of linux target ops method "low_get_pc". */ + +CORE_ADDR +loongarch_target::low_get_pc (regcache *regcache) +{ + if (register_size (regcache->tdesc, 0) == 8) + return linux_get_pc_64bit (regcache); + else + return linux_get_pc_32bit (regcache); +} + +/* Implementation of linux target ops method "low_set_pc". */ + +void +loongarch_target::low_set_pc (regcache *regcache, CORE_ADDR newpc) +{ + if (register_size (regcache->tdesc, 0) == 8) + linux_set_pc_64bit (regcache, newpc); + else + linux_set_pc_32bit (regcache, newpc); +} + +#define loongarch_breakpoint_len 4 + +/* LoongArch BRK software debug mode instruction. + This instruction needs to match gdb/loongarch-tdep.c + (loongarch_default_breakpoint). */ +static const gdb_byte loongarch_breakpoint[] = {0x05, 0x00, 0x2a, 0x00}; + +/* Implementation of target ops method "breakpoint_kind_from_pc". */ + +int +loongarch_target::breakpoint_kind_from_pc (CORE_ADDR *pcptr) +{ + return loongarch_breakpoint_len; +} + +/* Implementation of target ops method "sw_breakpoint_from_kind". */ + +const gdb_byte * +loongarch_target::sw_breakpoint_from_kind (int kind, int *size) +{ + *size = loongarch_breakpoint_len; + return (const gdb_byte *) &loongarch_breakpoint; +} + +/* Implementation of linux target ops method "low_breakpoint_at". */ + +bool +loongarch_target::low_breakpoint_at (CORE_ADDR pc) +{ + gdb_byte insn[loongarch_breakpoint_len]; + + read_memory (pc, (unsigned char *) &insn, loongarch_breakpoint_len); + if (memcmp (insn, loongarch_breakpoint, loongarch_breakpoint_len) == 0) + return true; + + return false; +} + +/* The linux target ops object. */ + +linux_process_target *the_linux_target = &the_loongarch_target; + +/* Initialize the LoongArch/Linux target. */ + +void +initialize_low_arch () +{ + initialize_regsets_info (&loongarch_regsets_info); +} -- 2.36.0