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