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