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