xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/riscv-linux-nat.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
17f2ac410Schristos /* Native-dependent code for GNU/Linux RISC-V.
2*6881a400Schristos    Copyright (C) 2018-2023 Free Software Foundation, Inc.
37f2ac410Schristos 
47f2ac410Schristos    This file is part of GDB.
57f2ac410Schristos 
67f2ac410Schristos    This program is free software; you can redistribute it and/or modify
77f2ac410Schristos    it under the terms of the GNU General Public License as published by
87f2ac410Schristos    the Free Software Foundation; either version 3 of the License, or
97f2ac410Schristos    (at your option) any later version.
107f2ac410Schristos 
117f2ac410Schristos    This program is distributed in the hope that it will be useful,
127f2ac410Schristos    but WITHOUT ANY WARRANTY; without even the implied warranty of
137f2ac410Schristos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
147f2ac410Schristos    GNU General Public License for more details.
157f2ac410Schristos 
167f2ac410Schristos    You should have received a copy of the GNU General Public License
177f2ac410Schristos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
187f2ac410Schristos 
197f2ac410Schristos #include "defs.h"
207f2ac410Schristos #include "regcache.h"
217f2ac410Schristos #include "gregset.h"
227f2ac410Schristos #include "linux-nat.h"
237f2ac410Schristos #include "riscv-tdep.h"
247f2ac410Schristos #include "inferior.h"
257f2ac410Schristos 
267f2ac410Schristos #include "elf/common.h"
277f2ac410Schristos 
287d62b00eSchristos #include "nat/riscv-linux-tdesc.h"
297d62b00eSchristos 
307f2ac410Schristos #include <sys/ptrace.h>
317f2ac410Schristos 
327d62b00eSchristos /* Work around glibc header breakage causing ELF_NFPREG not to be usable.  */
337d62b00eSchristos #ifndef NFPREG
347d62b00eSchristos # define NFPREG 33
357d62b00eSchristos #endif
367d62b00eSchristos 
377f2ac410Schristos /* RISC-V Linux native additions to the default linux support.  */
387f2ac410Schristos 
397f2ac410Schristos class riscv_linux_nat_target final : public linux_nat_target
407f2ac410Schristos {
417f2ac410Schristos public:
427f2ac410Schristos   /* Add our register access methods.  */
437f2ac410Schristos   void fetch_registers (struct regcache *regcache, int regnum) override;
447f2ac410Schristos   void store_registers (struct regcache *regcache, int regnum) override;
457f2ac410Schristos 
467f2ac410Schristos   /* Read suitable target description.  */
477f2ac410Schristos   const struct target_desc *read_description () override;
487f2ac410Schristos };
497f2ac410Schristos 
507f2ac410Schristos static riscv_linux_nat_target the_riscv_linux_nat_target;
517f2ac410Schristos 
527f2ac410Schristos /* Copy general purpose register REGNUM (or all gp regs if REGNUM == -1)
537f2ac410Schristos    from regset GREGS into REGCACHE.  */
547f2ac410Schristos 
557f2ac410Schristos static void
567f2ac410Schristos supply_gregset_regnum (struct regcache *regcache, const prgregset_t *gregs,
577f2ac410Schristos 		       int regnum)
587f2ac410Schristos {
597f2ac410Schristos   int i;
607f2ac410Schristos   const elf_greg_t *regp = *gregs;
617f2ac410Schristos 
627f2ac410Schristos   if (regnum == -1)
637f2ac410Schristos     {
647f2ac410Schristos       /* We only support the integer registers and PC here.  */
657f2ac410Schristos       for (i = RISCV_ZERO_REGNUM + 1; i < RISCV_PC_REGNUM; i++)
667f2ac410Schristos 	regcache->raw_supply (i, regp + i);
677f2ac410Schristos 
687f2ac410Schristos       /* GDB stores PC in reg 32.  Linux kernel stores it in reg 0.  */
69*6881a400Schristos       regcache->raw_supply (RISCV_PC_REGNUM, regp + 0);
707f2ac410Schristos 
717f2ac410Schristos       /* Fill the inaccessible zero register with zero.  */
72*6881a400Schristos       regcache->raw_supply_zeroed (RISCV_ZERO_REGNUM);
737f2ac410Schristos     }
747f2ac410Schristos   else if (regnum == RISCV_ZERO_REGNUM)
75*6881a400Schristos     regcache->raw_supply_zeroed (RISCV_ZERO_REGNUM);
767f2ac410Schristos   else if (regnum > RISCV_ZERO_REGNUM && regnum < RISCV_PC_REGNUM)
777f2ac410Schristos     regcache->raw_supply (regnum, regp + regnum);
787f2ac410Schristos   else if (regnum == RISCV_PC_REGNUM)
79*6881a400Schristos     regcache->raw_supply (RISCV_PC_REGNUM, regp + 0);
807f2ac410Schristos }
817f2ac410Schristos 
827f2ac410Schristos /* Copy all general purpose registers from regset GREGS into REGCACHE.  */
837f2ac410Schristos 
847f2ac410Schristos void
857f2ac410Schristos supply_gregset (struct regcache *regcache, const prgregset_t *gregs)
867f2ac410Schristos {
877f2ac410Schristos   supply_gregset_regnum (regcache, gregs, -1);
887f2ac410Schristos }
897f2ac410Schristos 
907f2ac410Schristos /* Copy floating point register REGNUM (or all fp regs if REGNUM == -1)
917f2ac410Schristos    from regset FPREGS into REGCACHE.  */
927f2ac410Schristos 
937f2ac410Schristos static void
947f2ac410Schristos supply_fpregset_regnum (struct regcache *regcache, const prfpregset_t *fpregs,
957f2ac410Schristos 			int regnum)
967f2ac410Schristos {
977d62b00eSchristos   int flen = register_size (regcache->arch (), RISCV_FIRST_FP_REGNUM);
987d62b00eSchristos   union
997d62b00eSchristos     {
1007d62b00eSchristos       const prfpregset_t *fpregs;
1017d62b00eSchristos       const gdb_byte *buf;
1027d62b00eSchristos     }
1037d62b00eSchristos   fpbuf = { .fpregs = fpregs };
1047f2ac410Schristos   int i;
1057f2ac410Schristos 
1067f2ac410Schristos   if (regnum == -1)
1077f2ac410Schristos     {
1087f2ac410Schristos       /* We only support the FP registers and FCSR here.  */
1097d62b00eSchristos       for (i = RISCV_FIRST_FP_REGNUM;
1107d62b00eSchristos 	   i <= RISCV_LAST_FP_REGNUM;
1117d62b00eSchristos 	   i++, fpbuf.buf += flen)
1127d62b00eSchristos 	regcache->raw_supply (i, fpbuf.buf);
1137f2ac410Schristos 
1147d62b00eSchristos       regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, fpbuf.buf);
1157f2ac410Schristos     }
1167f2ac410Schristos   else if (regnum >= RISCV_FIRST_FP_REGNUM && regnum <= RISCV_LAST_FP_REGNUM)
1177d62b00eSchristos     {
1187d62b00eSchristos       fpbuf.buf += flen * (regnum - RISCV_FIRST_FP_REGNUM);
1197d62b00eSchristos       regcache->raw_supply (regnum, fpbuf.buf);
1207d62b00eSchristos     }
1217f2ac410Schristos   else if (regnum == RISCV_CSR_FCSR_REGNUM)
1227d62b00eSchristos     {
1237d62b00eSchristos       fpbuf.buf += flen * (RISCV_LAST_FP_REGNUM - RISCV_FIRST_FP_REGNUM + 1);
1247d62b00eSchristos       regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, fpbuf.buf);
1257d62b00eSchristos     }
1267f2ac410Schristos }
1277f2ac410Schristos 
1287f2ac410Schristos /* Copy all floating point registers from regset FPREGS into REGCACHE.  */
1297f2ac410Schristos 
1307f2ac410Schristos void
1317f2ac410Schristos supply_fpregset (struct regcache *regcache, const prfpregset_t *fpregs)
1327f2ac410Schristos {
1337f2ac410Schristos   supply_fpregset_regnum (regcache, fpregs, -1);
1347f2ac410Schristos }
1357f2ac410Schristos 
1367f2ac410Schristos /* Copy general purpose register REGNUM (or all gp regs if REGNUM == -1)
1377f2ac410Schristos    from REGCACHE into regset GREGS.  */
1387f2ac410Schristos 
1397f2ac410Schristos void
1407f2ac410Schristos fill_gregset (const struct regcache *regcache, prgregset_t *gregs, int regnum)
1417f2ac410Schristos {
1427f2ac410Schristos   elf_greg_t *regp = *gregs;
1437f2ac410Schristos 
1447f2ac410Schristos   if (regnum == -1)
1457f2ac410Schristos     {
1467f2ac410Schristos       /* We only support the integer registers and PC here.  */
1477f2ac410Schristos       for (int i = RISCV_ZERO_REGNUM + 1; i < RISCV_PC_REGNUM; i++)
1487f2ac410Schristos 	regcache->raw_collect (i, regp + i);
1497f2ac410Schristos 
150*6881a400Schristos       regcache->raw_collect (RISCV_PC_REGNUM, regp + 0);
1517f2ac410Schristos     }
1527f2ac410Schristos   else if (regnum == RISCV_ZERO_REGNUM)
1537f2ac410Schristos     /* Nothing to do here.  */
1547f2ac410Schristos     ;
1557f2ac410Schristos   else if (regnum > RISCV_ZERO_REGNUM && regnum < RISCV_PC_REGNUM)
1567f2ac410Schristos     regcache->raw_collect (regnum, regp + regnum);
1577f2ac410Schristos   else if (regnum == RISCV_PC_REGNUM)
158*6881a400Schristos     regcache->raw_collect (RISCV_PC_REGNUM, regp + 0);
1597f2ac410Schristos }
1607f2ac410Schristos 
1617f2ac410Schristos /* Copy floating point register REGNUM (or all fp regs if REGNUM == -1)
1627f2ac410Schristos    from REGCACHE into regset FPREGS.  */
1637f2ac410Schristos 
1647f2ac410Schristos void
1657f2ac410Schristos fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs,
1667f2ac410Schristos 	       int regnum)
1677f2ac410Schristos {
1687d62b00eSchristos   int flen = register_size (regcache->arch (), RISCV_FIRST_FP_REGNUM);
1697d62b00eSchristos   union
1707d62b00eSchristos     {
1717d62b00eSchristos       prfpregset_t *fpregs;
1727d62b00eSchristos       gdb_byte *buf;
1737d62b00eSchristos     }
1747d62b00eSchristos   fpbuf = { .fpregs = fpregs };
1757d62b00eSchristos   int i;
1767d62b00eSchristos 
1777f2ac410Schristos   if (regnum == -1)
1787f2ac410Schristos     {
1797f2ac410Schristos       /* We only support the FP registers and FCSR here.  */
1807d62b00eSchristos       for (i = RISCV_FIRST_FP_REGNUM;
1817d62b00eSchristos 	   i <= RISCV_LAST_FP_REGNUM;
1827d62b00eSchristos 	   i++, fpbuf.buf += flen)
1837d62b00eSchristos 	regcache->raw_collect (i, fpbuf.buf);
1847f2ac410Schristos 
1857d62b00eSchristos       regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, fpbuf.buf);
1867f2ac410Schristos     }
1877f2ac410Schristos   else if (regnum >= RISCV_FIRST_FP_REGNUM && regnum <= RISCV_LAST_FP_REGNUM)
1887d62b00eSchristos     {
1897d62b00eSchristos       fpbuf.buf += flen * (regnum - RISCV_FIRST_FP_REGNUM);
1907d62b00eSchristos       regcache->raw_collect (regnum, fpbuf.buf);
1917d62b00eSchristos     }
1927f2ac410Schristos   else if (regnum == RISCV_CSR_FCSR_REGNUM)
1937d62b00eSchristos     {
1947d62b00eSchristos       fpbuf.buf += flen * (RISCV_LAST_FP_REGNUM - RISCV_FIRST_FP_REGNUM + 1);
1957d62b00eSchristos       regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, fpbuf.buf);
1967d62b00eSchristos     }
1977f2ac410Schristos }
1987f2ac410Schristos 
1997f2ac410Schristos /* Return a target description for the current target.  */
2007f2ac410Schristos 
2017f2ac410Schristos const struct target_desc *
2027f2ac410Schristos riscv_linux_nat_target::read_description ()
2037f2ac410Schristos {
2047d62b00eSchristos   const struct riscv_gdbarch_features features
205*6881a400Schristos     = riscv_linux_read_features (inferior_ptid.pid ());
2067d62b00eSchristos   return riscv_lookup_target_description (features);
2077f2ac410Schristos }
2087f2ac410Schristos 
2097f2ac410Schristos /* Fetch REGNUM (or all registers if REGNUM == -1) from the target
2107f2ac410Schristos    into REGCACHE using PTRACE_GETREGSET.  */
2117f2ac410Schristos 
2127f2ac410Schristos void
2137f2ac410Schristos riscv_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum)
2147f2ac410Schristos {
2157f2ac410Schristos   int tid;
2167f2ac410Schristos 
2177f2ac410Schristos   tid = get_ptrace_pid (regcache->ptid());
2187f2ac410Schristos 
2197f2ac410Schristos   if ((regnum >= RISCV_ZERO_REGNUM && regnum <= RISCV_PC_REGNUM)
2207f2ac410Schristos       || (regnum == -1))
2217f2ac410Schristos     {
2227f2ac410Schristos       struct iovec iov;
2237f2ac410Schristos       elf_gregset_t regs;
2247f2ac410Schristos 
2257f2ac410Schristos       iov.iov_base = &regs;
2267f2ac410Schristos       iov.iov_len = sizeof (regs);
2277f2ac410Schristos 
2287f2ac410Schristos       if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS,
2297f2ac410Schristos 		  (PTRACE_TYPE_ARG3) &iov) == -1)
2307f2ac410Schristos 	perror_with_name (_("Couldn't get registers"));
2317f2ac410Schristos       else
2327f2ac410Schristos 	supply_gregset_regnum (regcache, &regs, regnum);
2337f2ac410Schristos     }
2347f2ac410Schristos 
2357f2ac410Schristos   if ((regnum >= RISCV_FIRST_FP_REGNUM
2367f2ac410Schristos        && regnum <= RISCV_LAST_FP_REGNUM)
2377f2ac410Schristos       || (regnum == RISCV_CSR_FCSR_REGNUM)
2387f2ac410Schristos       || (regnum == -1))
2397f2ac410Schristos     {
2407f2ac410Schristos       struct iovec iov;
2417f2ac410Schristos       elf_fpregset_t regs;
2427f2ac410Schristos 
2437f2ac410Schristos       iov.iov_base = &regs;
2447d62b00eSchristos       iov.iov_len = ELF_NFPREG * register_size (regcache->arch (),
2457d62b00eSchristos 						RISCV_FIRST_FP_REGNUM);
2467d62b00eSchristos       gdb_assert (iov.iov_len <= sizeof (regs));
2477f2ac410Schristos 
2487f2ac410Schristos       if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET,
2497f2ac410Schristos 		  (PTRACE_TYPE_ARG3) &iov) == -1)
2507f2ac410Schristos 	perror_with_name (_("Couldn't get registers"));
2517f2ac410Schristos       else
2527f2ac410Schristos 	supply_fpregset_regnum (regcache, &regs, regnum);
2537f2ac410Schristos     }
2547f2ac410Schristos 
2557f2ac410Schristos   if ((regnum == RISCV_CSR_MISA_REGNUM)
2567f2ac410Schristos       || (regnum == -1))
2577f2ac410Schristos     {
2587f2ac410Schristos       /* TODO: Need to add a ptrace call for this.  */
2597f2ac410Schristos       regcache->raw_supply_zeroed (RISCV_CSR_MISA_REGNUM);
2607f2ac410Schristos     }
2617f2ac410Schristos 
2627f2ac410Schristos   /* Access to other CSRs has potential security issues, don't support them for
2637f2ac410Schristos      now.  */
2647f2ac410Schristos }
2657f2ac410Schristos 
2667f2ac410Schristos /* Store REGNUM (or all registers if REGNUM == -1) to the target
2677f2ac410Schristos    from REGCACHE using PTRACE_SETREGSET.  */
2687f2ac410Schristos 
2697f2ac410Schristos void
2707f2ac410Schristos riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum)
2717f2ac410Schristos {
2727f2ac410Schristos   int tid;
2737f2ac410Schristos 
2747f2ac410Schristos   tid = get_ptrace_pid (regcache->ptid ());
2757f2ac410Schristos 
2767f2ac410Schristos   if ((regnum >= RISCV_ZERO_REGNUM && regnum <= RISCV_PC_REGNUM)
2777f2ac410Schristos       || (regnum == -1))
2787f2ac410Schristos     {
2797f2ac410Schristos       struct iovec iov;
2807f2ac410Schristos       elf_gregset_t regs;
2817f2ac410Schristos 
2827f2ac410Schristos       iov.iov_base = &regs;
2837f2ac410Schristos       iov.iov_len = sizeof (regs);
2847f2ac410Schristos 
2857f2ac410Schristos       if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS,
2867f2ac410Schristos 		  (PTRACE_TYPE_ARG3) &iov) == -1)
2877f2ac410Schristos 	perror_with_name (_("Couldn't get registers"));
2887f2ac410Schristos       else
2897f2ac410Schristos 	{
2907f2ac410Schristos 	  fill_gregset (regcache, &regs, regnum);
2917f2ac410Schristos 
2927f2ac410Schristos 	  if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS,
2937f2ac410Schristos 		      (PTRACE_TYPE_ARG3) &iov) == -1)
2947f2ac410Schristos 	    perror_with_name (_("Couldn't set registers"));
2957f2ac410Schristos 	}
2967f2ac410Schristos     }
2977f2ac410Schristos 
2987f2ac410Schristos   if ((regnum >= RISCV_FIRST_FP_REGNUM
2997f2ac410Schristos        && regnum <= RISCV_LAST_FP_REGNUM)
3007f2ac410Schristos       || (regnum == RISCV_CSR_FCSR_REGNUM)
3017f2ac410Schristos       || (regnum == -1))
3027f2ac410Schristos     {
3037f2ac410Schristos       struct iovec iov;
3047f2ac410Schristos       elf_fpregset_t regs;
3057f2ac410Schristos 
3067f2ac410Schristos       iov.iov_base = &regs;
3077d62b00eSchristos       iov.iov_len = ELF_NFPREG * register_size (regcache->arch (),
3087d62b00eSchristos 						RISCV_FIRST_FP_REGNUM);
3097d62b00eSchristos       gdb_assert (iov.iov_len <= sizeof (regs));
3107f2ac410Schristos 
3117f2ac410Schristos       if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET,
3127f2ac410Schristos 		  (PTRACE_TYPE_ARG3) &iov) == -1)
3137f2ac410Schristos 	perror_with_name (_("Couldn't get registers"));
3147f2ac410Schristos       else
3157f2ac410Schristos 	{
3167f2ac410Schristos 	  fill_fpregset (regcache, &regs, regnum);
3177f2ac410Schristos 
3187f2ac410Schristos 	  if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET,
3197f2ac410Schristos 		      (PTRACE_TYPE_ARG3) &iov) == -1)
3207f2ac410Schristos 	    perror_with_name (_("Couldn't set registers"));
3217f2ac410Schristos 	}
3227f2ac410Schristos     }
3237f2ac410Schristos 
3247f2ac410Schristos   /* Access to CSRs has potential security issues, don't support them for
3257f2ac410Schristos      now.  */
3267f2ac410Schristos }
3277f2ac410Schristos 
3287f2ac410Schristos /* Initialize RISC-V Linux native support.  */
3297f2ac410Schristos 
3307d62b00eSchristos void _initialize_riscv_linux_nat ();
3317f2ac410Schristos void
3327d62b00eSchristos _initialize_riscv_linux_nat ()
3337f2ac410Schristos {
3347f2ac410Schristos   /* Register the target.  */
3357f2ac410Schristos   linux_target = &the_riscv_linux_nat_target;
3367f2ac410Schristos   add_inf_child_target (&the_riscv_linux_nat_target);
3377f2ac410Schristos }
338