From patchwork Sat Feb 1 20:21:18 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: 37644 Received: (qmail 122801 invoked by alias); 1 Feb 2020 20:21:27 -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 122790 invoked by uid 89); 1 Feb 2020 20:21:27 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-8.3 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_2, GIT_PATCH_3, KAM_ASCII_DIVIDERS, KAM_SHORT autolearn=ham version=3.3.1 spammy= X-HELO: esa5.hgst.iphmx.com Received: from esa5.hgst.iphmx.com (HELO esa5.hgst.iphmx.com) (216.71.153.144) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 01 Feb 2020 20:21:25 +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=1580588485; x=1612124485; h=date:from:to:cc:subject:in-reply-to:message-id: references:mime-version; bh=WGPgbuIme1HXe1KdzsEzhsiUMuHGTkpgGdj+snxZHus=; b=l/a4VjKaurCNmJlv9ImPkcWmgghbpANPXQL4AGI6IbkFEC3z8NaRHR+B ScrxJFsk3VRNfX/syO/anRgBrOOQCzOx7KfXt+Hxk8DUd77VyI8niGPcO /h3nkqph8YXzckYUabaQsZk9AS6WM0etVb+NaY7wLIBssXaOyiJAyMGEX AjZBIB8F9qzwGy6eKXommJXP2mw4kkbV3pGHPuAteDi1CdRi/rBToaBYz aS2ZDoxzbff8chOzQN7wNgc3vB4IUqkRD0BRLx5HVxNxk7W7Xcd3vZYh6 gtBH2nTbQjvS4dkg/oa0JHur6fGkE4hom1yaxok82v3GX/GkR1QRCwQ9L Q==; IronPort-SDR: vdh4I0JkBxCRWp1YTADb+8q5GgOmR6PINdWWI8vO4Ep33HBKR7G9S7gpPSyGdGDn1wWp8UlJoD BuIcQM15PUB6PiWDvCT6U/4kDh3GTTuOFQWd1LDA/aE2+kXkNFsmTCPDSLUeKgmP7/lwzvzYT0 SyM7xTw5/c1QZeURbDYm14u2BBfaBcNH4MjyNFn/KArdILf3GDbWSLEBg1QOzkj0vMfGXEhtZF XABC8IgMnBaLRi1RuO9RSn0Js9m0v7DcQQablmoNtE//4tNa4Y7WhoO4LPkB2KYl1kcGNEjgZC uGA= Received: from uls-op-cesaip01.wdc.com (HELO uls-op-cesaep01.wdc.com) ([199.255.45.14]) by ob1.hgst.iphmx.com with ESMTP; 02 Feb 2020 04:21:23 +0800 IronPort-SDR: idDUI25nyKmAOui16wwL7/lZlwTidOnCoy8oqSu2Y+ihBDXY7ra9jnZ/iRNRoXmsSPUaHazNHX P0AsXf9HhhKxmb+O8T+oudUX49RwljZSWCKb89N0eQ2vyScNnVTb/38YveUtzQOpNuSM7aX+2Z Zb70zJLBRQmbGy6aErcvo9lKIE8cyqe0PVd/0OKdnQMsvb4nzRXticL9dZ+yp7T9Wh/F9z0Tyb xq3edp14KyZTd79LYa/dJm4r/QVnKnGa+w8Na54BF9CFEzyjtRcWZQoK5fHPH2prn+B04ELznU iJhQpDNhI9ygZiW114RmoMr/ 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; 01 Feb 2020 12:14:29 -0800 IronPort-SDR: LcyemhglHzozKrItCjF6TwrKhorFJ0cf4MC1E/dmiZ+NdtvHjcVofVyJyoxR4klZKYjhBRSjCq OTqS6Mv/KiHfli1Yub6UGbvEW6bpMRTP+OzY0i+IQnGmTFKTwLoN6gs4aLCkD14AdxLtaDiZAS le5X1f9/g5QUKO+azdUx6V3c9bDImXJbfePSESbcA0EJevKr0ROHaP96aHRplwjsJvw8cL33sa C6dZbpOiTUh9A7xDL2OOgZcVXQuPHFEWNxisr7P9jY5MhxTW91CaUAu1Qaoq93NOZpfIkg8DA0 Itw= 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; 01 Feb 2020 12:21:20 -0800 Date: Sat, 1 Feb 2020 20:21:18 +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: [PATCH v5 3/3] 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. gdb/ * arch/riscv.h (riscv_create_target_description): Remove `const' qualifier from the return type. * arch/riscv.c (riscv_create_target_description): Likewise. * nat/riscv-linux-tdesc.h (riscv_linux_read_description): Likewise. * nat/riscv-linux-tdesc.c (riscv_linux_read_description): Likewise. * configure.tgt : Set build_gdbserver=yes. gdb/gdbserver/ * linux-riscv-low.c: New file. * Makefile.in (SFILES): Add linux-riscv-low.c, arch/riscv.c, and nat/riscv-linux-tdesc.c. * configure.srv (srv_tgtobj) (srv_linux_regsets, srv_linux_usrregs, srv_linux_thread_db): Define. --- 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/arch/riscv.c | 2 gdb/arch/riscv.h | 4 gdb/configure.tgt | 1 gdb/gdbserver/Makefile.in | 3 gdb/gdbserver/configure.srv | 7 + gdb/gdbserver/linux-riscv-low.c | 277 ++++++++++++++++++++++++++++++++++++++++ gdb/nat/riscv-linux-tdesc.c | 2 gdb/nat/riscv-linux-tdesc.h | 2 8 files changed, 293 insertions(+), 5 deletions(-) gdb-riscv-gdbserver-linux.diff Index: binutils-gdb/gdb/arch/riscv.c =================================================================== --- binutils-gdb.orig/gdb/arch/riscv.c +++ binutils-gdb/gdb/arch/riscv.c @@ -43,7 +43,7 @@ static std::unordered_map. */ + +#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 }; + target_desc *tdesc; + + tdesc = riscv_linux_read_description (lwpid_of (current_thread)); + 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); +} Index: binutils-gdb/gdb/nat/riscv-linux-tdesc.c =================================================================== --- binutils-gdb.orig/gdb/nat/riscv-linux-tdesc.c +++ binutils-gdb/gdb/nat/riscv-linux-tdesc.c @@ -33,7 +33,7 @@ /* Determine XLEN and FLEN and return a corresponding target description. */ -const struct target_desc * +struct target_desc * riscv_linux_read_description (int tid) { struct riscv_gdbarch_features features; Index: binutils-gdb/gdb/nat/riscv-linux-tdesc.h =================================================================== --- binutils-gdb.orig/gdb/nat/riscv-linux-tdesc.h +++ binutils-gdb/gdb/nat/riscv-linux-tdesc.h @@ -22,6 +22,6 @@ struct target_desc; /* Return a target description for the LWP identified by TID. */ -const struct target_desc *riscv_linux_read_description (int tid); +struct target_desc *riscv_linux_read_description (int tid); #endif /* NAT_RISCV_LINUX_TDESC_H */