Regenerate AArch64 opcodes files
[binutils-gdb.git] / gdb / loongarch-linux-nat.c
blob9bceb8a5953d709b8c0d74cb2f0555a087c51ba6
1 /* Native-dependent code for GNU/Linux on LoongArch processors.
3 Copyright (C) 2022-2024 Free Software Foundation, Inc.
4 Contributed by Loongson Ltd.
6 This file is part of GDB.
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "defs.h"
22 #include "elf/common.h"
23 #include "gregset.h"
24 #include "inferior.h"
25 #include "linux-nat-trad.h"
26 #include "loongarch-tdep.h"
27 #include "nat/gdb_ptrace.h"
28 #include "target-descriptions.h"
30 #include <asm/ptrace.h>
32 /* LoongArch Linux native additions to the default Linux support. */
34 class loongarch_linux_nat_target final : public linux_nat_trad_target
36 public:
37 /* Add our register access methods. */
38 void fetch_registers (struct regcache *, int) override;
39 void store_registers (struct regcache *, int) override;
41 protected:
42 /* Override linux_nat_trad_target methods. */
43 CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regnum,
44 int store_p) override;
47 /* Fill GDB's register array with the general-purpose, orig_a0, pc and badv
48 register values from the current thread. */
50 static void
51 fetch_gregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
53 elf_gregset_t regset;
55 if (regnum == -1 || (regnum >= 0 && regnum < 32)
56 || regnum == LOONGARCH_ORIG_A0_REGNUM
57 || regnum == LOONGARCH_PC_REGNUM
58 || regnum == LOONGARCH_BADV_REGNUM)
60 struct iovec iov;
62 iov.iov_base = &regset;
63 iov.iov_len = sizeof (regset);
65 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
66 perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
67 else
68 loongarch_gregset.supply_regset (nullptr, regcache, -1,
69 &regset, sizeof (regset));
73 /* Store to the current thread the valid general-purpose, orig_a0, pc and badv
74 register values in the GDB's register array. */
76 static void
77 store_gregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
79 elf_gregset_t regset;
81 if (regnum == -1 || (regnum >= 0 && regnum < 32)
82 || regnum == LOONGARCH_ORIG_A0_REGNUM
83 || regnum == LOONGARCH_PC_REGNUM
84 || regnum == LOONGARCH_BADV_REGNUM)
86 struct iovec iov;
88 iov.iov_base = &regset;
89 iov.iov_len = sizeof (regset);
91 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
92 perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
93 else
95 loongarch_gregset.collect_regset (nullptr, regcache, regnum,
96 &regset, sizeof (regset));
97 if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
98 perror_with_name (_("Couldn't set NT_PRSTATUS registers"));
103 /* Fill GDB's register array with the fp, fcc and fcsr
104 register values from the current thread. */
106 static void
107 fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
109 elf_fpregset_t regset;
111 if ((regnum == -1)
112 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
114 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
116 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
117 perror_with_name (_("Couldn't get NT_FPREGSET registers"));
118 else
119 loongarch_fpregset.supply_regset (nullptr, regcache, -1,
120 &regset, sizeof (regset));
124 /* Store to the current thread the valid fp, fcc and fcsr
125 register values in the GDB's register array. */
127 static void
128 store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
130 elf_fpregset_t regset;
132 if ((regnum == -1)
133 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
135 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
137 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
138 perror_with_name (_("Couldn't get NT_FPREGSET registers"));
139 else
141 loongarch_fpregset.collect_regset (nullptr, regcache, regnum,
142 &regset, sizeof (regset));
143 if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
144 perror_with_name (_("Couldn't set NT_FPREGSET registers"));
149 /* Fill GDB's register array with the Loongson SIMD Extension
150 register values from the current thread. */
152 static void
153 fetch_lsxregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
155 elf_lsxregset_t regset;
157 if ((regnum == -1)
158 || (regnum >= LOONGARCH_FIRST_LSX_REGNUM && regnum < LOONGARCH_FIRST_LASX_REGNUM))
160 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
162 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
164 /* If kernel dose not support lsx, just return. */
165 if (errno == EINVAL)
166 return;
168 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
170 else
171 loongarch_lsxregset.supply_regset (nullptr, regcache, -1,
172 &regset, sizeof (regset));
176 /* Store to the current thread the valid Loongson SIMD Extension
177 register values in the GDB's register array. */
179 static void
180 store_lsxregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
182 elf_lsxregset_t regset;
184 if ((regnum == -1)
185 || (regnum >= LOONGARCH_FIRST_LSX_REGNUM && regnum < LOONGARCH_FIRST_LASX_REGNUM))
187 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
189 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
191 /* If kernel dose not support lsx, just return. */
192 if (errno == EINVAL)
193 return;
195 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
197 else
199 loongarch_lsxregset.collect_regset (nullptr, regcache, regnum,
200 &regset, sizeof (regset));
201 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
202 perror_with_name (_("Couldn't set NT_LARCH_LSX registers"));
207 /* Fill GDB's register array with the Loongson Advanced SIMD Extension
208 register values from the current thread. */
210 static void
211 fetch_lasxregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
213 elf_lasxregset_t regset;
215 if ((regnum == -1)
216 || (regnum >= LOONGARCH_FIRST_LASX_REGNUM
217 && regnum < LOONGARCH_FIRST_LASX_REGNUM + LOONGARCH_LINUX_NUM_LASXREGSET))
219 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
221 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
223 /* If kernel dose not support lasx, just return. */
224 if (errno == EINVAL)
225 return;
227 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
229 else
230 loongarch_lasxregset.supply_regset (nullptr, regcache, -1,
231 &regset, sizeof (regset));
235 /* Store to the current thread the valid Loongson Advanced SIMD Extension
236 register values in the GDB's register array. */
238 static void
239 store_lasxregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
241 elf_lasxregset_t regset;
243 if ((regnum == -1)
244 || (regnum >= LOONGARCH_FIRST_LASX_REGNUM
245 && regnum < LOONGARCH_FIRST_LASX_REGNUM + LOONGARCH_LINUX_NUM_LASXREGSET))
247 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
249 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
251 /* If kernel dose not support lasx, just return. */
252 if (errno == EINVAL)
253 return;
255 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
257 else
259 loongarch_lasxregset.collect_regset (nullptr, regcache, regnum,
260 &regset, sizeof (regset));
261 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
262 perror_with_name (_("Couldn't set NT_LARCH_LASX registers"));
268 /* Fill GDB's register array with the lbt register values
269 from the current thread. */
271 static void
272 fetch_lbt_from_thread (struct regcache *regcache, int regnum, pid_t tid)
274 gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
276 if (regnum == -1
277 || (regnum >= LOONGARCH_FIRST_SCR_REGNUM
278 && regnum <= LOONGARCH_FTOP_REGNUM))
280 struct iovec iov;
282 iov.iov_base = regset;
283 iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
285 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
287 /* If kernel dose not support lbt, just return. */
288 if (errno == EINVAL)
289 return;
290 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
292 else
293 loongarch_lbtregset.supply_regset (nullptr, regcache, -1,
294 regset, LOONGARCH_LBT_REGS_SIZE);
298 /* Store to the current thread the valid lbt register values
299 in the GDB's register array. */
301 static void
302 store_lbt_to_thread (struct regcache *regcache, int regnum, pid_t tid)
304 gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
306 if (regnum == -1
307 || (regnum >= LOONGARCH_FIRST_SCR_REGNUM
308 && regnum <= LOONGARCH_FTOP_REGNUM))
310 struct iovec iov;
312 iov.iov_base = regset;
313 iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
315 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
317 /* If kernel dose not support lbt, just return. */
318 if (errno == EINVAL)
319 return;
320 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
322 else
324 loongarch_lbtregset.collect_regset (nullptr, regcache, regnum,
325 regset, LOONGARCH_LBT_REGS_SIZE);
326 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
327 perror_with_name (_("Couldn't set NT_LARCH_LBT registers"));
332 /* Implement the "fetch_registers" target_ops method. */
334 void
335 loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
336 int regnum)
338 pid_t tid = get_ptrace_pid (regcache->ptid ());
340 fetch_gregs_from_thread(regcache, regnum, tid);
341 fetch_fpregs_from_thread(regcache, regnum, tid);
342 fetch_lsxregs_from_thread(regcache, regnum, tid);
343 fetch_lasxregs_from_thread(regcache, regnum, tid);
344 fetch_lbt_from_thread (regcache, regnum, tid);
347 /* Implement the "store_registers" target_ops method. */
349 void
350 loongarch_linux_nat_target::store_registers (struct regcache *regcache,
351 int regnum)
353 pid_t tid = get_ptrace_pid (regcache->ptid ());
355 store_gregs_to_thread (regcache, regnum, tid);
356 store_fpregs_to_thread(regcache, regnum, tid);
357 store_lsxregs_to_thread(regcache, regnum, tid);
358 store_lasxregs_to_thread(regcache, regnum, tid);
359 store_lbt_to_thread (regcache, regnum, tid);
362 /* Return the address in the core dump or inferior of register REGNO. */
364 CORE_ADDR
365 loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch,
366 int regnum, int store_p)
368 if (regnum >= 0 && regnum < 32)
369 return regnum;
370 else if (regnum == LOONGARCH_PC_REGNUM)
371 return LOONGARCH_PC_REGNUM;
372 else
373 return -1;
376 static loongarch_linux_nat_target the_loongarch_linux_nat_target;
378 /* Wrapper functions. These are only used by libthread_db. */
380 void
381 supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregset)
383 loongarch_gregset.supply_regset (nullptr, regcache, -1, gregset,
384 sizeof (gdb_gregset_t));
387 void
388 fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregset,
389 int regnum)
391 loongarch_gregset.collect_regset (nullptr, regcache, regnum, gregset,
392 sizeof (gdb_gregset_t));
395 void
396 supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset)
398 loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset,
399 sizeof (gdb_fpregset_t));
402 void
403 fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset,
404 int regnum)
406 loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset,
407 sizeof (gdb_fpregset_t));
410 /* Initialize LoongArch Linux native support. */
412 void _initialize_loongarch_linux_nat ();
413 void
414 _initialize_loongarch_linux_nat ()
416 linux_target = &the_loongarch_linux_nat_target;
417 add_inf_child_target (&the_loongarch_linux_nat_target);