Update description of macro keyword argument assignment in assembler documentation.
[binutils-gdb.git] / gdb / loongarch-linux-nat.c
blob15fca6ad781cc39c08356764c623145f619a457c
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 "elf/common.h"
22 #include "gregset.h"
23 #include "inferior.h"
24 #include "linux-nat-trad.h"
25 #include "loongarch-tdep.h"
26 #include "nat/gdb_ptrace.h"
27 #include "target-descriptions.h"
29 #include <asm/ptrace.h>
31 /* LoongArch Linux native additions to the default Linux support. */
33 class loongarch_linux_nat_target final : public linux_nat_trad_target
35 public:
36 /* Add our register access methods. */
37 void fetch_registers (struct regcache *, int) override;
38 void store_registers (struct regcache *, int) override;
40 protected:
41 /* Override linux_nat_trad_target methods. */
42 CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regnum,
43 int store_p) override;
46 /* Fill GDB's register array with the general-purpose, orig_a0, pc and badv
47 register values from the current thread. */
49 static void
50 fetch_gregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
52 elf_gregset_t regset;
54 if (regnum == -1 || (regnum >= 0 && regnum < 32)
55 || regnum == LOONGARCH_ORIG_A0_REGNUM
56 || regnum == LOONGARCH_PC_REGNUM
57 || regnum == LOONGARCH_BADV_REGNUM)
59 struct iovec iov;
61 iov.iov_base = &regset;
62 iov.iov_len = sizeof (regset);
64 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
65 perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
66 else
67 loongarch_gregset.supply_regset (nullptr, regcache, -1,
68 &regset, sizeof (regset));
72 /* Store to the current thread the valid general-purpose, orig_a0, pc and badv
73 register values in the GDB's register array. */
75 static void
76 store_gregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
78 elf_gregset_t regset;
80 if (regnum == -1 || (regnum >= 0 && regnum < 32)
81 || regnum == LOONGARCH_ORIG_A0_REGNUM
82 || regnum == LOONGARCH_PC_REGNUM
83 || regnum == LOONGARCH_BADV_REGNUM)
85 struct iovec iov;
87 iov.iov_base = &regset;
88 iov.iov_len = sizeof (regset);
90 if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
91 perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
92 else
94 loongarch_gregset.collect_regset (nullptr, regcache, regnum,
95 &regset, sizeof (regset));
96 if (ptrace (PTRACE_SETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
97 perror_with_name (_("Couldn't set NT_PRSTATUS registers"));
102 /* Fill GDB's register array with the fp, fcc and fcsr
103 register values from the current thread. */
105 static void
106 fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
108 elf_fpregset_t regset;
110 if ((regnum == -1)
111 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
113 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
115 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
116 perror_with_name (_("Couldn't get NT_FPREGSET registers"));
117 else
118 loongarch_fpregset.supply_regset (nullptr, regcache, -1,
119 &regset, sizeof (regset));
123 /* Store to the current thread the valid fp, fcc and fcsr
124 register values in the GDB's register array. */
126 static void
127 store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
129 elf_fpregset_t regset;
131 if ((regnum == -1)
132 || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
134 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
136 if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
137 perror_with_name (_("Couldn't get NT_FPREGSET registers"));
138 else
140 loongarch_fpregset.collect_regset (nullptr, regcache, regnum,
141 &regset, sizeof (regset));
142 if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
143 perror_with_name (_("Couldn't set NT_FPREGSET registers"));
148 /* Fill GDB's register array with the Loongson SIMD Extension
149 register values from the current thread. */
151 static void
152 fetch_lsxregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
154 elf_lsxregset_t regset;
156 if ((regnum == -1)
157 || (regnum >= LOONGARCH_FIRST_LSX_REGNUM && regnum < LOONGARCH_FIRST_LASX_REGNUM))
159 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
161 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
163 /* If kernel dose not support lsx, just return. */
164 if (errno == EINVAL)
165 return;
167 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
169 else
170 loongarch_lsxregset.supply_regset (nullptr, regcache, -1,
171 &regset, sizeof (regset));
175 /* Store to the current thread the valid Loongson SIMD Extension
176 register values in the GDB's register array. */
178 static void
179 store_lsxregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
181 elf_lsxregset_t regset;
183 if ((regnum == -1)
184 || (regnum >= LOONGARCH_FIRST_LSX_REGNUM && regnum < LOONGARCH_FIRST_LASX_REGNUM))
186 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
188 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
190 /* If kernel dose not support lsx, just return. */
191 if (errno == EINVAL)
192 return;
194 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
196 else
198 loongarch_lsxregset.collect_regset (nullptr, regcache, regnum,
199 &regset, sizeof (regset));
200 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LSX, (long) &iovec) < 0)
201 perror_with_name (_("Couldn't set NT_LARCH_LSX registers"));
206 /* Fill GDB's register array with the Loongson Advanced SIMD Extension
207 register values from the current thread. */
209 static void
210 fetch_lasxregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
212 elf_lasxregset_t regset;
214 if ((regnum == -1)
215 || (regnum >= LOONGARCH_FIRST_LASX_REGNUM
216 && regnum < LOONGARCH_FIRST_LASX_REGNUM + LOONGARCH_LINUX_NUM_LASXREGSET))
218 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
220 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
222 /* If kernel dose not support lasx, just return. */
223 if (errno == EINVAL)
224 return;
226 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
228 else
229 loongarch_lasxregset.supply_regset (nullptr, regcache, -1,
230 &regset, sizeof (regset));
234 /* Store to the current thread the valid Loongson Advanced SIMD Extension
235 register values in the GDB's register array. */
237 static void
238 store_lasxregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
240 elf_lasxregset_t regset;
242 if ((regnum == -1)
243 || (regnum >= LOONGARCH_FIRST_LASX_REGNUM
244 && regnum < LOONGARCH_FIRST_LASX_REGNUM + LOONGARCH_LINUX_NUM_LASXREGSET))
246 struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
248 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
250 /* If kernel dose not support lasx, just return. */
251 if (errno == EINVAL)
252 return;
254 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
256 else
258 loongarch_lasxregset.collect_regset (nullptr, regcache, regnum,
259 &regset, sizeof (regset));
260 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LASX, (long) &iovec) < 0)
261 perror_with_name (_("Couldn't set NT_LARCH_LASX registers"));
267 /* Fill GDB's register array with the lbt register values
268 from the current thread. */
270 static void
271 fetch_lbt_from_thread (struct regcache *regcache, int regnum, pid_t tid)
273 gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
275 if (regnum == -1
276 || (regnum >= LOONGARCH_FIRST_SCR_REGNUM
277 && regnum <= LOONGARCH_FTOP_REGNUM))
279 struct iovec iov;
281 iov.iov_base = regset;
282 iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
284 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
286 /* If kernel dose not support lbt, just return. */
287 if (errno == EINVAL)
288 return;
289 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
291 else
292 loongarch_lbtregset.supply_regset (nullptr, regcache, -1,
293 regset, LOONGARCH_LBT_REGS_SIZE);
297 /* Store to the current thread the valid lbt register values
298 in the GDB's register array. */
300 static void
301 store_lbt_to_thread (struct regcache *regcache, int regnum, pid_t tid)
303 gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
305 if (regnum == -1
306 || (regnum >= LOONGARCH_FIRST_SCR_REGNUM
307 && regnum <= LOONGARCH_FTOP_REGNUM))
309 struct iovec iov;
311 iov.iov_base = regset;
312 iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
314 if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
316 /* If kernel dose not support lbt, just return. */
317 if (errno == EINVAL)
318 return;
319 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
321 else
323 loongarch_lbtregset.collect_regset (nullptr, regcache, regnum,
324 regset, LOONGARCH_LBT_REGS_SIZE);
325 if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
326 perror_with_name (_("Couldn't set NT_LARCH_LBT registers"));
331 /* Implement the "fetch_registers" target_ops method. */
333 void
334 loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
335 int regnum)
337 pid_t tid = get_ptrace_pid (regcache->ptid ());
339 fetch_gregs_from_thread(regcache, regnum, tid);
340 fetch_fpregs_from_thread(regcache, regnum, tid);
341 fetch_lsxregs_from_thread(regcache, regnum, tid);
342 fetch_lasxregs_from_thread(regcache, regnum, tid);
343 fetch_lbt_from_thread (regcache, regnum, tid);
346 /* Implement the "store_registers" target_ops method. */
348 void
349 loongarch_linux_nat_target::store_registers (struct regcache *regcache,
350 int regnum)
352 pid_t tid = get_ptrace_pid (regcache->ptid ());
354 store_gregs_to_thread (regcache, regnum, tid);
355 store_fpregs_to_thread(regcache, regnum, tid);
356 store_lsxregs_to_thread(regcache, regnum, tid);
357 store_lasxregs_to_thread(regcache, regnum, tid);
358 store_lbt_to_thread (regcache, regnum, tid);
361 /* Return the address in the core dump or inferior of register REGNO. */
363 CORE_ADDR
364 loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch,
365 int regnum, int store_p)
367 if (regnum >= 0 && regnum < 32)
368 return regnum;
369 else if (regnum == LOONGARCH_PC_REGNUM)
370 return LOONGARCH_PC_REGNUM;
371 else
372 return -1;
375 static loongarch_linux_nat_target the_loongarch_linux_nat_target;
377 /* Wrapper functions. These are only used by libthread_db. */
379 void
380 supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregset)
382 loongarch_gregset.supply_regset (nullptr, regcache, -1, gregset,
383 sizeof (gdb_gregset_t));
386 void
387 fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregset,
388 int regnum)
390 loongarch_gregset.collect_regset (nullptr, regcache, regnum, gregset,
391 sizeof (gdb_gregset_t));
394 void
395 supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset)
397 loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset,
398 sizeof (gdb_fpregset_t));
401 void
402 fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset,
403 int regnum)
405 loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset,
406 sizeof (gdb_fpregset_t));
409 /* Initialize LoongArch Linux native support. */
411 void _initialize_loongarch_linux_nat ();
412 void
413 _initialize_loongarch_linux_nat ()
415 linux_target = &the_loongarch_linux_nat_target;
416 add_inf_child_target (&the_loongarch_linux_nat_target);