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"
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
36 /* Add our register access methods. */
37 void fetch_registers (struct regcache
*, int) override
;
38 void store_registers (struct regcache
*, int) override
;
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. */
50 fetch_gregs_from_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
54 if (regnum
== -1 || (regnum
>= 0 && regnum
< 32)
55 || regnum
== LOONGARCH_ORIG_A0_REGNUM
56 || regnum
== LOONGARCH_PC_REGNUM
57 || regnum
== LOONGARCH_BADV_REGNUM
)
61 iov
.iov_base
= ®set
;
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"));
67 loongarch_gregset
.supply_regset (nullptr, regcache
, -1,
68 ®set
, 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. */
76 store_gregs_to_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
80 if (regnum
== -1 || (regnum
>= 0 && regnum
< 32)
81 || regnum
== LOONGARCH_ORIG_A0_REGNUM
82 || regnum
== LOONGARCH_PC_REGNUM
83 || regnum
== LOONGARCH_BADV_REGNUM
)
87 iov
.iov_base
= ®set
;
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"));
94 loongarch_gregset
.collect_regset (nullptr, regcache
, regnum
,
95 ®set
, 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. */
106 fetch_fpregs_from_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
108 elf_fpregset_t regset
;
111 || (regnum
>= LOONGARCH_FIRST_FP_REGNUM
&& regnum
<= LOONGARCH_FCSR_REGNUM
))
113 struct iovec iovec
= { .iov_base
= ®set
, .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"));
118 loongarch_fpregset
.supply_regset (nullptr, regcache
, -1,
119 ®set
, sizeof (regset
));
123 /* Store to the current thread the valid fp, fcc and fcsr
124 register values in the GDB's register array. */
127 store_fpregs_to_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
129 elf_fpregset_t regset
;
132 || (regnum
>= LOONGARCH_FIRST_FP_REGNUM
&& regnum
<= LOONGARCH_FCSR_REGNUM
))
134 struct iovec iovec
= { .iov_base
= ®set
, .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"));
140 loongarch_fpregset
.collect_regset (nullptr, regcache
, regnum
,
141 ®set
, 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. */
152 fetch_lsxregs_from_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
154 elf_lsxregset_t regset
;
157 || (regnum
>= LOONGARCH_FIRST_LSX_REGNUM
&& regnum
< LOONGARCH_FIRST_LASX_REGNUM
))
159 struct iovec iovec
= { .iov_base
= ®set
, .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. */
167 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
170 loongarch_lsxregset
.supply_regset (nullptr, regcache
, -1,
171 ®set
, sizeof (regset
));
175 /* Store to the current thread the valid Loongson SIMD Extension
176 register values in the GDB's register array. */
179 store_lsxregs_to_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
181 elf_lsxregset_t regset
;
184 || (regnum
>= LOONGARCH_FIRST_LSX_REGNUM
&& regnum
< LOONGARCH_FIRST_LASX_REGNUM
))
186 struct iovec iovec
= { .iov_base
= ®set
, .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. */
194 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
198 loongarch_lsxregset
.collect_regset (nullptr, regcache
, regnum
,
199 ®set
, 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. */
210 fetch_lasxregs_from_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
212 elf_lasxregset_t regset
;
215 || (regnum
>= LOONGARCH_FIRST_LASX_REGNUM
216 && regnum
< LOONGARCH_FIRST_LASX_REGNUM
+ LOONGARCH_LINUX_NUM_LASXREGSET
))
218 struct iovec iovec
= { .iov_base
= ®set
, .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. */
226 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
229 loongarch_lasxregset
.supply_regset (nullptr, regcache
, -1,
230 ®set
, sizeof (regset
));
234 /* Store to the current thread the valid Loongson Advanced SIMD Extension
235 register values in the GDB's register array. */
238 store_lasxregs_to_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
240 elf_lasxregset_t regset
;
243 || (regnum
>= LOONGARCH_FIRST_LASX_REGNUM
244 && regnum
< LOONGARCH_FIRST_LASX_REGNUM
+ LOONGARCH_LINUX_NUM_LASXREGSET
))
246 struct iovec iovec
= { .iov_base
= ®set
, .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. */
254 perror_with_name (_("Couldn't get NT_LARCH_LSX registers"));
258 loongarch_lasxregset
.collect_regset (nullptr, regcache
, regnum
,
259 ®set
, 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. */
271 fetch_lbt_from_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
273 gdb_byte regset
[LOONGARCH_LBT_REGS_SIZE
];
276 || (regnum
>= LOONGARCH_FIRST_SCR_REGNUM
277 && regnum
<= LOONGARCH_FTOP_REGNUM
))
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. */
289 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
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. */
301 store_lbt_to_thread (struct regcache
*regcache
, int regnum
, pid_t tid
)
303 gdb_byte regset
[LOONGARCH_LBT_REGS_SIZE
];
306 || (regnum
>= LOONGARCH_FIRST_SCR_REGNUM
307 && regnum
<= LOONGARCH_FTOP_REGNUM
))
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. */
319 perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
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. */
334 loongarch_linux_nat_target::fetch_registers (struct regcache
*regcache
,
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. */
349 loongarch_linux_nat_target::store_registers (struct regcache
*regcache
,
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. */
364 loongarch_linux_nat_target::register_u_offset (struct gdbarch
*gdbarch
,
365 int regnum
, int store_p
)
367 if (regnum
>= 0 && regnum
< 32)
369 else if (regnum
== LOONGARCH_PC_REGNUM
)
370 return LOONGARCH_PC_REGNUM
;
375 static loongarch_linux_nat_target the_loongarch_linux_nat_target
;
377 /* Wrapper functions. These are only used by libthread_db. */
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
));
387 fill_gregset (const struct regcache
*regcache
, gdb_gregset_t
*gregset
,
390 loongarch_gregset
.collect_regset (nullptr, regcache
, regnum
, gregset
,
391 sizeof (gdb_gregset_t
));
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
));
402 fill_fpregset (const struct regcache
*regcache
, gdb_fpregset_t
*fpregset
,
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 ();
413 _initialize_loongarch_linux_nat ()
415 linux_target
= &the_loongarch_linux_nat_target
;
416 add_inf_child_target (&the_loongarch_linux_nat_target
);