From patchwork Wed Feb 19 01:35:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Maciej W. Rozycki" X-Patchwork-Id: 38219 Received: (qmail 27828 invoked by alias); 19 Feb 2020 01:35:16 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 27820 invoked by uid 89); 19 Feb 2020 01:35:15 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-16.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT autolearn=ham version=3.3.1 spammy=out, linux-low.h, UD:linux-low.h, linuxlowh X-HELO: esa3.hgst.iphmx.com Received: from esa3.hgst.iphmx.com (HELO esa3.hgst.iphmx.com) (216.71.153.141) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Wed, 19 Feb 2020 01:35:13 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=wdc.com; i=@wdc.com; q=dns/txt; s=dkim.wdc.com; t=1582076113; x=1613612113; h=date:from:to:cc:subject:in-reply-to:message-id: references:mime-version; bh=f2bWh0pbH1vqbD419FCgPZqERhZuIIhuewL4b7XbEfM=; b=Bb26Xe87TD77GGYQ8acIZlySw1uYXPHIMM+4TyXE3G4gtT56+ZuVHPCL B/IXgUzL2Hr31QCGc5oppTuLgkwn1AnEQzHSv1S5xzHTYcseeP2fHl6oE oAusIyee7eGOC5U2Kbvykq3+SubNKGDuvqotvkisVAlmWfwDeqYBOCo/A 0Z6zkI2v8vJi1R27otRylTce/hCnzo5/ayX5hy5rU9xTFLA4oRZWOkaxE TgdHtMkJl7+Kzes5mGazwTLbr/FcPPO/vnhQfsYu1kpqsxA22SKDcqH3+ Ln2bFH3+aV7r0kW4w04DnyfbWGUDcAFWMWiC8okXJIxOEAlc4uiOyy5KF A==; IronPort-SDR: MJqUJ1kBNpJc9M3O7DUKVJWV3OGRGqTKCXRVvzjOrhdxgqqx0gxgSYs2znuHm8blxYMo0SUjcQ U18yrVlynx3r/P6BiDMRJmp1CSVEECgJgSUlAcYL3Tpt/f30Ok+DkFjyOI3aUs0ymf9agUKumt 9prjX/Fx2lo6EDKNEwqNoq2B7QGLWfvFWxxulVpl+fL28nkOjrr+hDCcn7Lkq9qeDe7PzUmuJ7 ihbA92gqMYq00KS/Mp6MobFVwf3XZ81sUGprMXtWNPiz0HxcvUgCRAesV1wTg2RjjWw60bX0iS s4I= Received: from h199-255-45-14.hgst.com (HELO uls-op-cesaep01.wdc.com) ([199.255.45.14]) by ob1.hgst.iphmx.com with ESMTP; 19 Feb 2020 09:35:11 +0800 IronPort-SDR: APrt1e4FZ6cba5+YLSjjgplfXCmIBLBnIgqrSnaSHW8uf8waXlsvp9Bf8z7R4LlQcHfbcDlw1D rUWw4oi6AZ1O5R65yZJtnOKjbpPJftdyK/8PJVvU3HBmCCMZdgd4VzopGexvHB7N47Yk41uhy7 xBJP7oIhGw1GgtXpi56Z6o3S4AZl/J/t64Kor/rH8Y+4eMj22BvEzIQod0qml3Tb5r9hnvy4FN z0jtcVK1/dlJUJYNF1yX0m2O7bnYtx8ObANq3KdWE4TEsseWOF+WGZKr2Wfm7sd7xl4x0CK1wU 0b2tTE5M6fKrWts80XeOwNwv Received: from uls-op-cesaip02.wdc.com ([10.248.3.37]) by uls-op-cesaep01.wdc.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 Feb 2020 17:27:49 -0800 IronPort-SDR: reoVNBjpbu0fKE0ZlXaPKJ0XMdNX2s7tToEiE9dZIPUr+p7K1PuVKpQEw4yJ9KtMmeZVBdpQnP ukYGptaAIBU/lRvgYBnS4rO7FdhOC9oBZ3u3ZoppknE8eHbL8omWgt32094R1Ofz3tS3jfazLY QAcUe2SwBQQkmO1WEHDrKXKDJLoA9hle+DLW+geDfoBuj0C6cn7DugeElp4GBBXDuvhViMZkR1 YZGZZ0T9IWYwmbqf8cuVa8nQatQD0YhFU9+GFH9vrvsi7z09gddrsH9J6yrMtuLMzcNYbN3pea REA= WDCIronportException: Internal Received: from unknown (HELO redsun52) ([10.149.66.28]) by uls-op-cesaip02.wdc.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 18 Feb 2020 17:35:09 -0800 Date: Wed, 19 Feb 2020 01:35:07 +0000 (GMT) From: "Maciej W. Rozycki" To: gdb-patches@sourceware.org cc: Jim Wilson , Andrew Burgess , Palmer Dabbelt , Tom Tromey , guoren@kernel.org, lifang_xia@c-sky.com, yunhai_shang@c-sky.com, jiangshuai_li@c-sky.com Subject: [committed v8 2/2] gdbserver: Add RISC-V/Linux support In-Reply-To: Message-ID: References: User-Agent: Alpine 2.21 (LFD 202 2017-01-01) MIME-Version: 1.0 Implement RISC-V/Linux support for both RV64 and RV32 systems, including XML target description handling based on features determined, GPR and FPR regset support including dynamic sizing of the latter, and software breakpoint handling. Define two NT_FPREGSET regsets of a different size matching the FPR sizes supported for generic `gdbserver' code to pick from according to what the OS supplies. Also handle a glibc bug where ELF_NFPREG is defined in terms of NFPREG, however NFPREG is nowhere defined. 2020-02-19 Maciej W. Rozycki Andrew Burgess gdb/ * NEWS: Mention RISC-V GNU/Linux GDBserver support. gdbserver/ * linux-riscv-low.cc: New file. * Makefile.in (SFILES): Add linux-riscv-low.cc, arch/riscv.c, and nat/riscv-linux-tdesc.c. * configure.srv (srv_tgtobj) (srv_linux_regsets, srv_linux_usrregs, srv_linux_thread_db): Define. --- This is the final version I have committed. Changes from v7: - Mention RISC-V GNU/Linux GDBserver support in gdb/NEWS. Changes from v6: - Regenerate for the gdbserver/ move to the top level. - Use the .cc suffix following the rename of other files. Changes from v4: - Add 128-bit FP regset. Changes from v3: - Actually include changes from v2. Changes from v2: - Advance through the buffer accessed in `riscv_fill_fpregset' and `riscv_store_fpregset' so as to avoid doing a variable multiplication in a loop; rename the `regset' variable to `regbuf'. - Use OPTIONAL_REGS rather than FP_REGS as the type for the floating-point regsets. - Add comments throughout. Changes from v1: - Make `gdbserver' selected for automatic build in a RISC-V/Linux/native GDB configuration (thanks, Jim, for pointing this out!). - Remove most of `riscv_arch_setup' and use `riscv_linux_read_description' from 2/3 instead. - Stop using `elf_fpregset_t*' in favour to just a raw `gdb_byte *' buffer and size the regset according to the FPR size in `riscv_fill_fpregset' and `riscv_store_fpregset'. - Define 2 NT_FPREGSET regsets of a different size for generic `gdbserver' code to pick from according to what the OS supplies. --- gdb/ChangeLog | 4 + gdb/NEWS | 8 ++ gdbserver/ChangeLog | 10 ++ gdbserver/Makefile.in | 3 + gdbserver/configure.srv | 7 ++ gdbserver/linux-riscv-low.cc | 279 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 311 insertions(+) create mode 100644 gdbserver/linux-riscv-low.cc diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 144fd4f256a..0077a9824aa 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,7 @@ +2020-02-19 Maciej W. Rozycki + + * NEWS: Mention RISC-V GNU/Linux GDBserver support. + 2020-02-19 Andrew Burgess * arch/riscv.c (struct riscv_gdbarch_features_hasher): Only define diff --git a/gdb/NEWS b/gdb/NEWS index c202fe06de2..91add18d6f5 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -3,6 +3,10 @@ *** Changes since GDB 9 +* New features in the GDB remote stub, GDBserver + + ** GDBserver is now supported on RISC-V GNU/Linux. + * Debugging MS-Windows processes now sets $_exitsignal when the inferior is terminated by a signal, instead of setting $_exitcode. @@ -26,6 +30,10 @@ show exec-file-mismatch -- Show exec-file-mismatch handling (ask|warn|off). whether to load the process executable file; if 'warn', just display a warning; if 'off', don't attempt to detect a mismatch. +* New targets + +GNU/Linux/RISC-V (gdbserver) riscv*-*-linux* + *** Changes in GDB 9 * 'thread-exited' event is now available in the annotations interface. diff --git a/gdbserver/ChangeLog b/gdbserver/ChangeLog index d27eeb557da..4851002a7e5 100644 --- a/gdbserver/ChangeLog +++ b/gdbserver/ChangeLog @@ -1,3 +1,13 @@ +2020-02-19 Maciej W. Rozycki + Andrew Burgess + + * linux-riscv-low.cc: New file. + * Makefile.in (SFILES): Add linux-riscv-low.cc, arch/riscv.c, + and nat/riscv-linux-tdesc.c. + * configure.srv (srv_tgtobj) + (srv_linux_regsets, srv_linux_usrregs, srv_linux_thread_db): + Define. + 2020-02-14 Tom Tromey * acinclude.m4: Don't include acx_configure_dir.m4. diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in index d66bc81da1c..1baebba0f56 100644 --- a/gdbserver/Makefile.in +++ b/gdbserver/Makefile.in @@ -177,6 +177,7 @@ SFILES = \ $(srcdir)/linux-mips-low.cc \ $(srcdir)/linux-nios2-low.cc \ $(srcdir)/linux-ppc-low.cc \ + $(srcdir)/linux-riscv-low.cc \ $(srcdir)/linux-s390-low.cc \ $(srcdir)/linux-sh-low.cc \ $(srcdir)/linux-sparc-low.cc \ @@ -203,6 +204,7 @@ SFILES = \ $(srcdir)/../gdb/arch/arm-get-next-pcs.c \ $(srcdir)/../gdb/arch/arm-linux.c \ $(srcdir)/../gdb/arch/ppc-linux-common.c \ + $(srcdir)/../gdb/arch/riscv.c \ $(srcdir)/../gdbsupport/btrace-common.cc \ $(srcdir)/../gdbsupport/buffer.cc \ $(srcdir)/../gdbsupport/cleanups.cc \ @@ -236,6 +238,7 @@ SFILES = \ $(srcdir)/../gdb/nat/linux-personality.c \ $(srcdir)/../gdb/nat/mips-linux-watch.c \ $(srcdir)/../gdb/nat/ppc-linux.c \ + $(srcdir)/../gdb/nat/riscv-linux-tdesc.c \ $(srcdir)/../gdb/nat/fork-inferior.c \ $(srcdir)/../gdb/target/waitstatus.c diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index 375ac0aeb2a..ecdd63a310a 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -278,6 +278,13 @@ case "${gdbserver_host}" in srv_xmlfiles="${srv_xmlfiles} rs6000/power-fpu.xml" srv_lynxos=yes ;; + riscv*-*-linux*) srv_tgtobj="arch/riscv.o nat/riscv-linux-tdesc.o" + srv_tgtobj="${srv_tgtobj} linux-riscv-low.o" + srv_tgtobj="${srv_tgtobj} ${srv_linux_obj}" + srv_linux_regsets=yes + srv_linux_usrregs=yes + srv_linux_thread_db=yes + ;; s390*-*-linux*) srv_regobj="s390-linux32.o" srv_regobj="${srv_regobj} s390-linux32v1.o" srv_regobj="${srv_regobj} s390-linux32v2.o" diff --git a/gdbserver/linux-riscv-low.cc b/gdbserver/linux-riscv-low.cc new file mode 100644 index 00000000000..07ae6174ee3 --- /dev/null +++ b/gdbserver/linux-riscv-low.cc @@ -0,0 +1,279 @@ +/* GNU/Linux/RISC-V 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 . */ + +#include "server.h" + +#include "linux-low.h" +#include "tdesc.h" +#include "elf/common.h" +#include "nat/riscv-linux-tdesc.h" +#include "opcode/riscv.h" + +/* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ +#ifndef NFPREG +# define NFPREG 33 +#endif + +/* Implementation of linux_target_ops method "arch_setup". */ + +static void +riscv_arch_setup () +{ + static const char *expedite_regs[] = { "sp", "pc", NULL }; + + const riscv_gdbarch_features features + = riscv_linux_read_features (lwpid_of (current_thread)); + target_desc *tdesc = riscv_create_target_description (features); + + if (!tdesc->expedite_regs) + init_target_desc (tdesc, expedite_regs); + current_process ()->tdesc = tdesc; +} + +/* Collect GPRs from REGCACHE into BUF. */ + +static void +riscv_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, "zero"); + int i; + + collect_register_by_name (regcache, "pc", *regset); + for (i = 1; i < ARRAY_SIZE (*regset); i++) + collect_register (regcache, regno + i, *regset + i); +} + +/* Supply GPRs from BUF into REGCACHE. */ + +static void +riscv_store_gregset (struct regcache *regcache, const void *buf) +{ + const elf_gregset_t *regset = (const elf_gregset_t *) buf; + const struct target_desc *tdesc = regcache->tdesc; + int regno = find_regno (tdesc, "zero"); + int i; + + supply_register_by_name (regcache, "pc", *regset); + supply_register_zeroed (regcache, regno); + for (i = 1; i < ARRAY_SIZE (*regset); i++) + supply_register (regcache, regno + i, *regset + i); +} + +/* Collect FPRs from REGCACHE into BUF. */ + +static void +riscv_fill_fpregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int regno = find_regno (tdesc, "ft0"); + int flen = register_size (regcache->tdesc, regno); + gdb_byte *regbuf = (gdb_byte *) buf; + int i; + + for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen) + collect_register (regcache, regno + i, regbuf); + collect_register_by_name (regcache, "fcsr", regbuf); +} + +/* Supply FPRs from BUF into REGCACHE. */ + +static void +riscv_store_fpregset (struct regcache *regcache, const void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + int regno = find_regno (tdesc, "ft0"); + int flen = register_size (regcache->tdesc, regno); + const gdb_byte *regbuf = (const gdb_byte *) buf; + int i; + + for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen) + supply_register (regcache, regno + i, regbuf); + supply_register_by_name (regcache, "fcsr", regbuf); +} + +/* RISC-V/Linux regsets. FPRs are optional and come in different sizes, + so define multiple regsets for them marking them all as OPTIONAL_REGS + rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks + the right one according to size. */ +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 (struct __riscv_mc_q_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, + sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS, + riscv_fill_fpregset, riscv_store_fpregset }, + NULL_REGSET +}; + +/* RISC-V/Linux regset information. */ +static struct regsets_info riscv_regsets_info = + { + riscv_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +/* Definition of linux_target_ops data member "regs_info". */ +static struct regs_info riscv_regs = + { + NULL, /* regset_bitmap */ + NULL, /* usrregs */ + &riscv_regsets_info, + }; + +/* Implementation of linux_target_ops method "regs_info". */ + +static const struct regs_info * +riscv_regs_info () +{ + return &riscv_regs; +} + +/* Implementation of linux_target_ops method "fetch_register". */ + +static int +riscv_fetch_register (struct regcache *regcache, int regno) +{ + const struct target_desc *tdesc = regcache->tdesc; + + if (regno != find_regno (tdesc, "zero")) + return 0; + supply_register_zeroed (regcache, regno); + return 1; +} + +/* Implementation of linux_target_ops method "get_pc". */ + +static CORE_ADDR +riscv_get_pc (struct regcache *regcache) +{ + elf_gregset_t regset; + + if (sizeof (regset[0]) == 8) + return linux_get_pc_64bit (regcache); + else + return linux_get_pc_32bit (regcache); +} + +/* Implementation of linux_target_ops method "set_pc". */ + +static void +riscv_set_pc (struct regcache *regcache, CORE_ADDR newpc) +{ + elf_gregset_t regset; + + if (sizeof (regset[0]) == 8) + linux_set_pc_64bit (regcache, newpc); + else + linux_set_pc_32bit (regcache, newpc); +} + +/* Correct in either endianness. */ +static const uint16_t riscv_ibreakpoint[] = { 0x0073, 0x0010 }; +static const uint16_t riscv_cbreakpoint = 0x9002; + +/* Implementation of linux_target_ops method "breakpoint_kind_from_pc". */ + +static int +riscv_breakpoint_kind_from_pc (CORE_ADDR *pcptr) +{ + union + { + gdb_byte bytes[2]; + uint16_t insn; + } + buf; + + if (target_read_memory (*pcptr, buf.bytes, sizeof (buf.insn)) == 0 + && riscv_insn_length (buf.insn == sizeof (riscv_ibreakpoint))) + return sizeof (riscv_ibreakpoint); + else + return sizeof (riscv_cbreakpoint); +} + +/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */ + +static const gdb_byte * +riscv_sw_breakpoint_from_kind (int kind, int *size) +{ + *size = kind; + switch (kind) + { + case sizeof (riscv_ibreakpoint): + return (const gdb_byte *) &riscv_ibreakpoint; + default: + return (const gdb_byte *) &riscv_cbreakpoint; + } +} + +/* Implementation of linux_target_ops method "breakpoint_at". */ + +static int +riscv_breakpoint_at (CORE_ADDR pc) +{ + union + { + gdb_byte bytes[2]; + uint16_t insn; + } + buf; + + if (target_read_memory (pc, buf.bytes, sizeof (buf.insn)) == 0 + && (buf.insn == riscv_cbreakpoint + || (buf.insn == riscv_ibreakpoint[0] + && target_read_memory (pc + sizeof (buf.insn), buf.bytes, + sizeof (buf.insn)) == 0 + && buf.insn == riscv_ibreakpoint[1]))) + return 1; + else + return 0; +} + +/* RISC-V/Linux target operations. */ +struct linux_target_ops the_low_target = +{ + riscv_arch_setup, + riscv_regs_info, + NULL, /* cannot_fetch_register */ + NULL, /* cannot_store_register */ + riscv_fetch_register, + riscv_get_pc, + riscv_set_pc, + riscv_breakpoint_kind_from_pc, + riscv_sw_breakpoint_from_kind, + NULL, /* get_next_pcs */ + 0, /* decr_pc_after_break */ + riscv_breakpoint_at, +}; + +/* Initialize the RISC-V/Linux target. */ + +void +initialize_low_arch () +{ + initialize_regsets_info (&riscv_regsets_info); +}