section-select: Fix exclude-file-3
[binutils-gdb.git] / gdbserver / linux-loongarch-low.cc
1 /* GNU/Linux/LoongArch specific low level interface, for the remote server
2 for GDB.
3 Copyright (C) 2022 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 #include "linux-low.h"
22 #include "tdesc.h"
23 #include "elf/common.h"
24 #include "arch/loongarch.h"
25
26 /* Linux target ops definitions for the LoongArch architecture. */
27
28 class loongarch_target : public linux_process_target
29 {
30 public:
31
32 const regs_info *get_regs_info () override;
33
34 int breakpoint_kind_from_pc (CORE_ADDR *pcptr) override;
35
36 const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
37
38 protected:
39
40 void low_arch_setup () override;
41
42 bool low_cannot_fetch_register (int regno) override;
43
44 bool low_cannot_store_register (int regno) override;
45
46 bool low_fetch_register (regcache *regcache, int regno) override;
47
48 bool low_supports_breakpoints () override;
49
50 CORE_ADDR low_get_pc (regcache *regcache) override;
51
52 void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
53
54 bool low_breakpoint_at (CORE_ADDR pc) override;
55 };
56
57 /* The singleton target ops object. */
58
59 static loongarch_target the_loongarch_target;
60
61 bool
62 loongarch_target::low_cannot_fetch_register (int regno)
63 {
64 gdb_assert_not_reached ("linux target op low_cannot_fetch_register "
65 "is not implemented by the target");
66 }
67
68 bool
69 loongarch_target::low_cannot_store_register (int regno)
70 {
71 gdb_assert_not_reached ("linux target op low_cannot_store_register "
72 "is not implemented by the target");
73 }
74
75 /* Implementation of linux target ops method "low_arch_setup". */
76
77 void
78 loongarch_target::low_arch_setup ()
79 {
80 static const char *expedite_regs[] = { "r3", "pc", NULL };
81 loongarch_gdbarch_features features;
82 target_desc_up tdesc;
83
84 features.xlen = sizeof (elf_greg_t);
85 tdesc = loongarch_create_target_description (features);
86
87 if (!tdesc->expedite_regs)
88 init_target_desc (tdesc.get (), expedite_regs);
89 current_process ()->tdesc = tdesc.release ();
90 }
91
92 /* Collect GPRs from REGCACHE into BUF. */
93
94 static void
95 loongarch_fill_gregset (struct regcache *regcache, void *buf)
96 {
97 elf_gregset_t *regset = (elf_gregset_t *) buf;
98 int i;
99
100 for (i = 1; i < 32; i++)
101 collect_register (regcache, i, *regset + i);
102 collect_register (regcache, LOONGARCH_ORIG_A0_REGNUM, *regset + LOONGARCH_ORIG_A0_REGNUM);
103 collect_register (regcache, LOONGARCH_PC_REGNUM, *regset + LOONGARCH_PC_REGNUM);
104 collect_register (regcache, LOONGARCH_BADV_REGNUM, *regset + LOONGARCH_BADV_REGNUM);
105 }
106
107 /* Supply GPRs from BUF into REGCACHE. */
108
109 static void
110 loongarch_store_gregset (struct regcache *regcache, const void *buf)
111 {
112 const elf_gregset_t *regset = (const elf_gregset_t *) buf;
113 int i;
114
115 supply_register_zeroed (regcache, 0);
116 for (i = 1; i < 32; i++)
117 supply_register (regcache, i, *regset + i);
118 supply_register (regcache, LOONGARCH_ORIG_A0_REGNUM, *regset + LOONGARCH_ORIG_A0_REGNUM);
119 supply_register (regcache, LOONGARCH_PC_REGNUM, *regset + LOONGARCH_PC_REGNUM);
120 supply_register (regcache, LOONGARCH_BADV_REGNUM, *regset + LOONGARCH_BADV_REGNUM);
121 }
122
123 /* Collect FPRs from REGCACHE into BUF. */
124
125 static void
126 loongarch_fill_fpregset (struct regcache *regcache, void *buf)
127 {
128 gdb_byte *regbuf = nullptr;
129 int fprsize = register_size (regcache->tdesc, LOONGARCH_FIRST_FP_REGNUM);
130 int fccsize = register_size (regcache->tdesc, LOONGARCH_FIRST_FCC_REGNUM);
131
132 for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++)
133 {
134 regbuf = (gdb_byte *)buf + fprsize * i;
135 collect_register (regcache, LOONGARCH_FIRST_FP_REGNUM + i, regbuf);
136 }
137
138 for (int i = 0; i < LOONGARCH_LINUX_NUM_FCC; i++)
139 {
140 regbuf = (gdb_byte *)buf + fprsize * LOONGARCH_LINUX_NUM_FPREGSET +
141 fccsize * i;
142 collect_register (regcache, LOONGARCH_FIRST_FCC_REGNUM + i, regbuf);
143 }
144
145 regbuf = (gdb_byte *)buf + fprsize * LOONGARCH_LINUX_NUM_FPREGSET +
146 fccsize * LOONGARCH_LINUX_NUM_FCC;
147 collect_register (regcache, LOONGARCH_FCSR_REGNUM, regbuf);
148 }
149
150 /* Supply FPRs from BUF into REGCACHE. */
151
152 static void
153 loongarch_store_fpregset (struct regcache *regcache, const void *buf)
154 {
155 const gdb_byte *regbuf = nullptr;
156 int fprsize = register_size (regcache->tdesc, LOONGARCH_FIRST_FP_REGNUM);
157 int fccsize = register_size (regcache->tdesc, LOONGARCH_FIRST_FCC_REGNUM);
158
159 for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++)
160 {
161 regbuf = (const gdb_byte *)buf + fprsize * i;
162 supply_register (regcache, LOONGARCH_FIRST_FP_REGNUM + i, regbuf);
163 }
164
165 for (int i = 0; i < LOONGARCH_LINUX_NUM_FCC; i++)
166 {
167 regbuf = (const gdb_byte *)buf + fprsize * LOONGARCH_LINUX_NUM_FPREGSET +
168 fccsize * i;
169 supply_register (regcache, LOONGARCH_FIRST_FCC_REGNUM + i, regbuf);
170 }
171
172 regbuf = (const gdb_byte *)buf + fprsize * LOONGARCH_LINUX_NUM_FPREGSET +
173 fccsize * LOONGARCH_LINUX_NUM_FCC;
174 supply_register (regcache, LOONGARCH_FCSR_REGNUM, regbuf);
175 }
176
177 /* LoongArch/Linux regsets. */
178 static struct regset_info loongarch_regsets[] = {
179 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, sizeof (elf_gregset_t),
180 GENERAL_REGS, loongarch_fill_gregset, loongarch_store_gregset },
181 { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_FPREGSET, sizeof (elf_fpregset_t),
182 FP_REGS, loongarch_fill_fpregset, loongarch_store_fpregset },
183 NULL_REGSET
184 };
185
186 /* LoongArch/Linux regset information. */
187 static struct regsets_info loongarch_regsets_info =
188 {
189 loongarch_regsets, /* regsets */
190 0, /* num_regsets */
191 NULL, /* disabled_regsets */
192 };
193
194 /* Definition of linux_target_ops data member "regs_info". */
195 static struct regs_info loongarch_regs =
196 {
197 NULL, /* regset_bitmap */
198 NULL, /* usrregs */
199 &loongarch_regsets_info,
200 };
201
202 /* Implementation of linux target ops method "get_regs_info". */
203
204 const regs_info *
205 loongarch_target::get_regs_info ()
206 {
207 return &loongarch_regs;
208 }
209
210 /* Implementation of linux target ops method "low_fetch_register". */
211
212 bool
213 loongarch_target::low_fetch_register (regcache *regcache, int regno)
214 {
215 if (regno != 0)
216 return false;
217 supply_register_zeroed (regcache, 0);
218 return true;
219 }
220
221 bool
222 loongarch_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 loongarch_target::low_get_pc (regcache *regcache)
231 {
232 if (register_size (regcache->tdesc, 0) == 8)
233 return linux_get_pc_64bit (regcache);
234 else
235 return linux_get_pc_32bit (regcache);
236 }
237
238 /* Implementation of linux target ops method "low_set_pc". */
239
240 void
241 loongarch_target::low_set_pc (regcache *regcache, CORE_ADDR newpc)
242 {
243 if (register_size (regcache->tdesc, 0) == 8)
244 linux_set_pc_64bit (regcache, newpc);
245 else
246 linux_set_pc_32bit (regcache, newpc);
247 }
248
249 #define loongarch_breakpoint_len 4
250
251 /* LoongArch BRK software debug mode instruction.
252 This instruction needs to match gdb/loongarch-tdep.c
253 (loongarch_default_breakpoint). */
254 static const gdb_byte loongarch_breakpoint[] = {0x05, 0x00, 0x2a, 0x00};
255
256 /* Implementation of target ops method "breakpoint_kind_from_pc". */
257
258 int
259 loongarch_target::breakpoint_kind_from_pc (CORE_ADDR *pcptr)
260 {
261 return loongarch_breakpoint_len;
262 }
263
264 /* Implementation of target ops method "sw_breakpoint_from_kind". */
265
266 const gdb_byte *
267 loongarch_target::sw_breakpoint_from_kind (int kind, int *size)
268 {
269 *size = loongarch_breakpoint_len;
270 return (const gdb_byte *) &loongarch_breakpoint;
271 }
272
273 /* Implementation of linux target ops method "low_breakpoint_at". */
274
275 bool
276 loongarch_target::low_breakpoint_at (CORE_ADDR pc)
277 {
278 gdb_byte insn[loongarch_breakpoint_len];
279
280 read_memory (pc, (unsigned char *) &insn, loongarch_breakpoint_len);
281 if (memcmp (insn, loongarch_breakpoint, loongarch_breakpoint_len) == 0)
282 return true;
283
284 return false;
285 }
286
287 /* The linux target ops object. */
288
289 linux_process_target *the_linux_target = &the_loongarch_target;
290
291 /* Initialize the LoongArch/Linux target. */
292
293 void
294 initialize_low_arch ()
295 {
296 initialize_regsets_info (&loongarch_regsets_info);
297 }