From patchwork Thu Jan 23 19:42:38 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: 37519 Received: (qmail 23214 invoked by alias); 23 Jan 2020 19:42:47 -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 23205 invoked by uid 89); 23 Jan 2020 19:42:47 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-8.2 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=1, 300 X-HELO: esa6.hgst.iphmx.com Received: from esa6.hgst.iphmx.com (HELO esa6.hgst.iphmx.com) (216.71.154.45) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Thu, 23 Jan 2020 19:42:45 +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=1579808564; x=1611344564; h=date:from:to:cc:subject:in-reply-to:message-id: references:mime-version; bh=OGFyiBuk39iw5rx2PtExk9blK1BYbW6vLMKbcs3LRls=; b=rF5sW/tAznevwhje5GFfmTdllFmlJ3dQyBt32M8iXeJ5tx1uYDkVVoeL vQABuSzub5XWwVOKFOxmuA3mfWvCfIBjY7SqJ4Dfgrykypzhd9qHxytw6 9+uBbk5r0d2cD0rNeudwMadflTgLWgI6t+taqJQqjbkWYlpb74RnYPBdN AE4ZL1v7X+ad3ED67H12136rBNeCDlXqAAp116JN06mrRCRshbz7U/hTg T1a3lzdhzkiZ6rUBBeF5FlFrQpjxRC5Etay2lfupCZypDpz1bfH8HJ3bz rscZ8c6eU5vHwV47URojyhKoeOgH6vSSCDvpX/JrJUAdgdoD5UROtGR+L A==; IronPort-SDR: mNeIdwQzM6Sv/3YcIz8pPAL6PlQ3u4NP8RHQ/ABfpPElF8wGiAVOwu2bj9/RXLNppHFsb57hMb g9+Uo6GfTvBT4b2SGh/hzRFH8q/O+9mg8EJBG19k30ggvtlnGfgDOUAAu35dwBSXu8MTxlJ6zc uRbzfBIq4Dg4Rm2tnR7739Z2Z0CWsmyE+DDWFbiwrdEmG3twz6/eGmMJVaoVgL2HFrFpapu50b jFiFP5R9ae1KSzvIVQTmccuS5rZkZcjPZKUYDD8zX03pOka/2ayl8OgOfAn2bxpws/T1i9RFZH CfI= Received: from uls-op-cesaip01.wdc.com (HELO uls-op-cesaep01.wdc.com) ([199.255.45.14]) by ob1.hgst.iphmx.com with ESMTP; 24 Jan 2020 03:42:43 +0800 IronPort-SDR: LHQMvToRM0NzdD/U6cSifpM1B2UgYzcbwxrxL4zagwduRqR8UqkbJ3sevA5I5NfVUmsJrUvQmE /XMvHgyO6hE7OkIPP5x2irL23RN8UxMI7PCrSBrwIwEAeuOL4mFdak5ylcj/VuzTNGMy6bIh0h aIBOpPyhwOJHISbS5txysYn+mqZh7NZOMaHq0eGux8vE2Ha7r+i/XqvkC+MZx64dx27Wc0gurP cZrM3Eh6s4nlZ4m+B/UaJTuz7vW1iVFXtXL1og9ZYeJgDSFGzKtE3NSPIPLDDt+xsn5wI7W/SO gAvF3Pn/3mSkuWtG658saEGa 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; 23 Jan 2020 11:36:05 -0800 IronPort-SDR: +nveRu1zw6HB0TSYCdwUDZqOePx1wCfZaU/Lq6dEEIub140roYg4NUY/Eq1eFdItSdE1zsMqw3 fMF2WlSPgEDha+Q54Bpzl/K0awdHj3eM6EGZRlcJltziDVJ2aRL/KISWBSTrX7aMkm9aJcMCx0 n03qHAJzt3ObjNz7z7fMsupKklbHXAzK7PjS+g37RQVfVkFKnm8ILCyGcwVz7O0PWd5z888Djq HUa12Y8Kf5gEQwDaZpn35IZWrY1O12OFYz8GhQ+TRO7Jx221BQCAX4JHR/fGFSQJ2AyzPTe+pJ l3I= 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; 23 Jan 2020 11:42:41 -0800 Date: Thu, 23 Jan 2020 19:42:38 +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: [RFC][PATCH 4/4] 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. 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. gdb/gdbserver/ * linux-riscv-low.c: New file. * Makefile.in (SFILES): Add linux-riscv-low.c and arch/riscv.c. * configure.srv (srv_tgtobj) (srv_linux_regsets, srv_linux_usrregs, srv_linux_thread_db): Define. --- Hi, Posted as an RFC as this hits a major issue with GDB rejecting the XML description supplied, e.g.: warning: while parsing target description (at line 4): Target description specified unknown architecture "riscv:rv64id" warning: Could not load XML target description; ignoring with HiFive Unleashed; clearly GDB is unhappy with the `riscv:rv64id' architecture name produced by its own shared code, and not surprisingly, as it only has `riscv', `riscv:rv64' and `riscv:rv32' available to choose from as far as RISC-V targets are concerned, as easily verified with: (gdb) set architecture As the architecture names come from BFD it will clearly have to be there that the problem is fixed, with the ISA suffixes driving the choice of instructions handled by the disassembler and possibly other functions. Also I think large parts of `riscv_linux_nat_target::read_description' in RISC-V/Linux/native support could be factored out to a piece of code shared with `riscv_arch_setup'. This might be less important a problem, but nevertheless I think it should be done sooner than later so as not to make the two pieces diverge, and a preparatory change inserted ahead of this one for this purpose might actually be the best approach. I have planned to do that once the XML target description acceptance problem has been fixed. I have run initial lp64d multilib remote testing with HiFive Unleashed as the target. Results were as follows: === gdb Summary === # of expected passes 58054 # of unexpected failures 621 # of unexpected successes 4 # of expected failures 49 # of unknown successes 5 # of known failures 72 # of unresolved testcases 110 # of untested testcases 100 # of unsupported tests 232 but no further analysis has been made and also they come from Nov 6th only, so they may not be up to date with current GDB. I thought it would be more important to get the XML issue fixed first. Also native gdbserver testing should also be done as well as should native reference testing. Questions, comments? Maciej --- gdb/arch/riscv.c | 2 gdb/arch/riscv.h | 4 gdb/gdbserver/Makefile.in | 2 gdb/gdbserver/configure.srv | 6 gdb/gdbserver/linux-riscv-low.c | 300 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 311 insertions(+), 3 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 "gdb_proc_service.h" +#include "linux-low.h" +#include "tdesc.h" +#include "arch/riscv.h" +#include "elf/common.h" +#include "nat/gdb_ptrace.h" +#include "opcode/riscv.h" + +#include + +/* 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 (void) +{ + static const char *expedite_regs[] = { "sp", "pc", NULL }; + struct riscv_gdbarch_features features; + elf_fpregset_t regs; + target_desc *tdesc; + int flen; + int tid; + + /* Figuring out xlen is easy. */ + features.xlen = sizeof (elf_greg_t); + + tid = lwpid_of (current_thread); + + /* Start with no f-registers. */ + features.flen = 0; + + /* How much worth of f-registers can we fetch if any? */ + for (flen = sizeof (regs.__f.__f[0]); ; flen *= 2) + { + size_t regset_size; + struct iovec iov; + + /* Regsets have a uniform slot size, so we count FSCR like an FGR. */ + regset_size = ELF_NFPREG * flen; + if (regset_size > sizeof (regs)) + break; + + iov.iov_base = ®s; + iov.iov_len = regset_size; + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, + (PTRACE_TYPE_ARG3) &iov) == -1) + { + switch (errno) + { + case EINVAL: + continue; + case EIO: + break; + default: + perror_with_name (_("Couldn't get registers")); + break; + } + } + else + features.flen = flen; + break; + } + + tdesc = riscv_create_target_description (features); + if (!tdesc->expedite_regs) + init_target_desc (tdesc, expedite_regs); + current_process ()->tdesc = tdesc; +} + +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); +} + +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); +} + +static void +riscv_fill_fpregset (struct regcache *regcache, void *buf) +{ + const struct target_desc *tdesc = regcache->tdesc; + elf_fpregset_t *regset = (elf_fpregset_t *) buf; + int regno = find_regno (tdesc, "ft0"); + int i; + + for (i = 0; i < ARRAY_SIZE (regset->__d.__f); i++) + collect_register (regcache, regno + i, regset->__d.__f + i); + collect_register_by_name (regcache, "fcsr", ®set->__d.__fcsr); +} + +static void +riscv_store_fpregset (struct regcache *regcache, const void *buf) +{ + const elf_fpregset_t *regset = (const elf_fpregset_t *) buf; + const struct target_desc *tdesc = regcache->tdesc; + int regno = find_regno (tdesc, "ft0"); + int i; + + for (i = 0; i < ARRAY_SIZE (regset->__d.__f); i++) + supply_register (regcache, regno + i, regset->__d.__f + i); + supply_register_by_name (regcache, "fcsr", ®set->__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 struct regsets_info riscv_regsets_info = + { + riscv_regsets, /* regsets */ + 0, /* num_regsets */ + NULL, /* disabled_regsets */ + }; + +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 (void) +{ + 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; +} + +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, +}; + +void +initialize_low_arch (void) +{ + initialize_regsets_info (&riscv_regsets_info); +}