xref: /netbsd-src/external/gpl3/gdb/dist/gdbserver/linux-riscv-low.cc (revision 64f917f5a88990e32dd65fcd4348042fa7f852b9)
1 /* GNU/Linux/RISC-V specific low level interface, for the remote server
2    for GDB.
3    Copyright (C) 2020-2024 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 
21 #include "linux-low.h"
22 #include "tdesc.h"
23 #include "elf/common.h"
24 #include "nat/riscv-linux-tdesc.h"
25 #include "opcode/riscv.h"
26 
27 /* Work around glibc header breakage causing ELF_NFPREG not to be usable.  */
28 #ifndef NFPREG
29 # define NFPREG 33
30 #endif
31 
32 /* Linux target op definitions for the RISC-V architecture.  */
33 
34 class riscv_target : public linux_process_target
35 {
36 public:
37 
38   const regs_info *get_regs_info () override;
39 
40   int breakpoint_kind_from_pc (CORE_ADDR *pcptr) override;
41 
42   const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
43 
44 protected:
45 
46   void low_arch_setup () override;
47 
48   bool low_cannot_fetch_register (int regno) override;
49 
50   bool low_cannot_store_register (int regno) override;
51 
52   bool low_fetch_register (regcache *regcache, int regno) override;
53 
54   bool low_supports_breakpoints () override;
55 
56   CORE_ADDR low_get_pc (regcache *regcache) override;
57 
58   void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
59 
60   bool low_breakpoint_at (CORE_ADDR pc) override;
61 };
62 
63 /* The singleton target ops object.  */
64 
65 static riscv_target the_riscv_target;
66 
67 bool
68 riscv_target::low_cannot_fetch_register (int regno)
69 {
70   gdb_assert_not_reached ("linux target op low_cannot_fetch_register "
71 			  "is not implemented by the target");
72 }
73 
74 bool
75 riscv_target::low_cannot_store_register (int regno)
76 {
77   gdb_assert_not_reached ("linux target op low_cannot_store_register "
78 			  "is not implemented by the target");
79 }
80 
81 /* Implementation of linux target ops method "low_arch_setup".  */
82 
83 void
84 riscv_target::low_arch_setup ()
85 {
86   static const char *expedite_regs[] = { "sp", "pc", NULL };
87 
88   const riscv_gdbarch_features features
89     = riscv_linux_read_features (lwpid_of (current_thread));
90   target_desc_up tdesc = riscv_create_target_description (features);
91 
92   if (tdesc->expedite_regs.empty ())
93     {
94       init_target_desc (tdesc.get (), expedite_regs);
95       gdb_assert (!tdesc->expedite_regs.empty ());
96     }
97 
98   current_process ()->tdesc = tdesc.release ();
99 }
100 
101 /* Collect GPRs from REGCACHE into BUF.  */
102 
103 static void
104 riscv_fill_gregset (struct regcache *regcache, void *buf)
105 {
106   const struct target_desc *tdesc = regcache->tdesc;
107   elf_gregset_t *regset = (elf_gregset_t *) buf;
108   int regno = find_regno (tdesc, "zero");
109   int i;
110 
111   collect_register_by_name (regcache, "pc", *regset);
112   for (i = 1; i < ARRAY_SIZE (*regset); i++)
113     collect_register (regcache, regno + i, *regset + i);
114 }
115 
116 /* Supply GPRs from BUF into REGCACHE.  */
117 
118 static void
119 riscv_store_gregset (struct regcache *regcache, const void *buf)
120 {
121   const elf_gregset_t *regset = (const elf_gregset_t *) buf;
122   const struct target_desc *tdesc = regcache->tdesc;
123   int regno = find_regno (tdesc, "zero");
124   int i;
125 
126   supply_register_by_name (regcache, "pc", *regset);
127   supply_register_zeroed (regcache, regno);
128   for (i = 1; i < ARRAY_SIZE (*regset); i++)
129     supply_register (regcache, regno + i, *regset + i);
130 }
131 
132 /* Collect FPRs from REGCACHE into BUF.  */
133 
134 static void
135 riscv_fill_fpregset (struct regcache *regcache, void *buf)
136 {
137   const struct target_desc *tdesc = regcache->tdesc;
138   int regno = find_regno (tdesc, "ft0");
139   int flen = register_size (regcache->tdesc, regno);
140   gdb_byte *regbuf = (gdb_byte *) buf;
141   int i;
142 
143   for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen)
144     collect_register (regcache, regno + i, regbuf);
145   collect_register_by_name (regcache, "fcsr", regbuf);
146 }
147 
148 /* Supply FPRs from BUF into REGCACHE.  */
149 
150 static void
151 riscv_store_fpregset (struct regcache *regcache, const void *buf)
152 {
153   const struct target_desc *tdesc = regcache->tdesc;
154   int regno = find_regno (tdesc, "ft0");
155   int flen = register_size (regcache->tdesc, regno);
156   const gdb_byte *regbuf = (const gdb_byte *) buf;
157   int i;
158 
159   for (i = 0; i < ELF_NFPREG - 1; i++, regbuf += flen)
160     supply_register (regcache, regno + i, regbuf);
161   supply_register_by_name (regcache, "fcsr", regbuf);
162 }
163 
164 /* RISC-V/Linux regsets.  FPRs are optional and come in different sizes,
165    so define multiple regsets for them marking them all as OPTIONAL_REGS
166    rather than FP_REGS, so that "regsets_fetch_inferior_registers" picks
167    the right one according to size.  */
168 static struct regset_info riscv_regsets[] = {
169   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS,
170     sizeof (elf_gregset_t), GENERAL_REGS,
171     riscv_fill_gregset, riscv_store_gregset },
172   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
173     sizeof (struct __riscv_mc_q_ext_state), OPTIONAL_REGS,
174     riscv_fill_fpregset, riscv_store_fpregset },
175   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
176     sizeof (struct __riscv_mc_d_ext_state), OPTIONAL_REGS,
177     riscv_fill_fpregset, riscv_store_fpregset },
178   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET,
179     sizeof (struct __riscv_mc_f_ext_state), OPTIONAL_REGS,
180     riscv_fill_fpregset, riscv_store_fpregset },
181   NULL_REGSET
182 };
183 
184 /* RISC-V/Linux regset information.  */
185 static struct regsets_info riscv_regsets_info =
186   {
187     riscv_regsets, /* regsets */
188     0, /* num_regsets */
189     NULL, /* disabled_regsets */
190   };
191 
192 /* Definition of linux_target_ops data member "regs_info".  */
193 static struct regs_info riscv_regs =
194   {
195     NULL, /* regset_bitmap */
196     NULL, /* usrregs */
197     &riscv_regsets_info,
198   };
199 
200 /* Implementation of linux target ops method "get_regs_info".  */
201 
202 const regs_info *
203 riscv_target::get_regs_info ()
204 {
205   return &riscv_regs;
206 }
207 
208 /* Implementation of linux target ops method "low_fetch_register".  */
209 
210 bool
211 riscv_target::low_fetch_register (regcache *regcache, int regno)
212 {
213   const struct target_desc *tdesc = regcache->tdesc;
214 
215   if (regno != find_regno (tdesc, "zero"))
216     return false;
217   supply_register_zeroed (regcache, regno);
218   return true;
219 }
220 
221 bool
222 riscv_target::low_supports_breakpoints ()
223 {
224   return true;
225 }
226 
227 /* Implementation of linux target ops method "low_get_pc".  */
228 
229 CORE_ADDR
230 riscv_target::low_get_pc (regcache *regcache)
231 {
232   elf_gregset_t regset;
233 
234   if (sizeof (regset[0]) == 8)
235     return linux_get_pc_64bit (regcache);
236   else
237     return linux_get_pc_32bit (regcache);
238 }
239 
240 /* Implementation of linux target ops method "low_set_pc".  */
241 
242 void
243 riscv_target::low_set_pc (regcache *regcache, CORE_ADDR newpc)
244 {
245   elf_gregset_t regset;
246 
247   if (sizeof (regset[0]) == 8)
248     linux_set_pc_64bit (regcache, newpc);
249   else
250     linux_set_pc_32bit (regcache, newpc);
251 }
252 
253 /* Correct in either endianness.  */
254 static const uint16_t riscv_ibreakpoint[] = { 0x0073, 0x0010 };
255 static const uint16_t riscv_cbreakpoint = 0x9002;
256 
257 /* Implementation of target ops method "breakpoint_kind_from_pc".  */
258 
259 int
260 riscv_target::breakpoint_kind_from_pc (CORE_ADDR *pcptr)
261 {
262   union
263     {
264       gdb_byte bytes[2];
265       uint16_t insn;
266     }
267   buf;
268 
269   if (target_read_memory (*pcptr, buf.bytes, sizeof (buf.insn)) == 0
270       && riscv_insn_length (buf.insn == sizeof (riscv_ibreakpoint)))
271     return sizeof (riscv_ibreakpoint);
272   else
273     return sizeof (riscv_cbreakpoint);
274 }
275 
276 /* Implementation of target ops method "sw_breakpoint_from_kind".  */
277 
278 const gdb_byte *
279 riscv_target::sw_breakpoint_from_kind (int kind, int *size)
280 {
281   *size = kind;
282   switch (kind)
283     {
284       case sizeof (riscv_ibreakpoint):
285 	return (const gdb_byte *) &riscv_ibreakpoint;
286       default:
287 	return (const gdb_byte *) &riscv_cbreakpoint;
288     }
289 }
290 
291 /* Implementation of linux target ops method "low_breakpoint_at".  */
292 
293 bool
294 riscv_target::low_breakpoint_at (CORE_ADDR pc)
295 {
296   union
297     {
298       gdb_byte bytes[2];
299       uint16_t insn;
300     }
301   buf;
302 
303   if (target_read_memory (pc, buf.bytes, sizeof (buf.insn)) == 0
304       && (buf.insn == riscv_cbreakpoint
305 	  || (buf.insn == riscv_ibreakpoint[0]
306 	      && target_read_memory (pc + sizeof (buf.insn), buf.bytes,
307 				     sizeof (buf.insn)) == 0
308 	      && buf.insn == riscv_ibreakpoint[1])))
309     return true;
310   else
311     return false;
312 }
313 
314 /* The linux target ops object.  */
315 
316 linux_process_target *the_linux_target = &the_riscv_target;
317 
318 /* Initialize the RISC-V/Linux target.  */
319 
320 void
321 initialize_low_arch ()
322 {
323   initialize_regsets_info (&riscv_regsets_info);
324 }
325