xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/loongarch-linux-nat.c (revision 6881a4007f077b54e5f51159c52b9b25f57deb0d)
1 /* Native-dependent code for GNU/Linux on LoongArch processors.
2 
3    Copyright (C) 2022-2023 Free Software Foundation, Inc.
4    Contributed by Loongson Ltd.
5 
6    This file is part of GDB.
7 
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.
12 
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.
17 
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/>.  */
20 
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"
29 
30 #include <asm/ptrace.h>
31 
32 /* LoongArch Linux native additions to the default Linux support.  */
33 
34 class loongarch_linux_nat_target final : public linux_nat_trad_target
35 {
36 public:
37   /* Add our register access methods.  */
38   void fetch_registers (struct regcache *, int) override;
39   void store_registers (struct regcache *, int) override;
40 
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;
45 };
46 
47 /* Fill GDB's register array with the general-purpose, orig_a0, pc and badv
48    register values from the current thread.  */
49 
50 static void
51 fetch_gregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
52 {
53   elf_gregset_t regset;
54 
55   if (regnum == -1 || (regnum >= 0 && regnum < 32)
56       || regnum == LOONGARCH_ORIG_A0_REGNUM
57       || regnum == LOONGARCH_PC_REGNUM
58       || regnum == LOONGARCH_BADV_REGNUM)
59   {
60     struct iovec iov;
61 
62     iov.iov_base = &regset;
63     iov.iov_len = sizeof (regset);
64 
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, regnum,
69 				       &regset, sizeof (regset));
70   }
71 }
72 
73 /* Store to the current thread the valid general-purpose, orig_a0, pc and badv
74    register values in the GDB's register array.  */
75 
76 static void
77 store_gregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
78 {
79   elf_gregset_t regset;
80 
81   if (regnum == -1 || (regnum >= 0 && regnum < 32)
82       || regnum == LOONGARCH_ORIG_A0_REGNUM
83       || regnum == LOONGARCH_PC_REGNUM
84       || regnum == LOONGARCH_BADV_REGNUM)
85   {
86     struct iovec iov;
87 
88     iov.iov_base = &regset;
89     iov.iov_len = sizeof (regset);
90 
91     if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, (long) &iov) < 0)
92       perror_with_name (_("Couldn't get NT_PRSTATUS registers"));
93     else
94       {
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"));
99       }
100   }
101 }
102 
103 /* Fill GDB's register array with the fp, fcc and fcsr
104    register values from the current thread.  */
105 
106 static void
107 fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
108 {
109   elf_fpregset_t regset;
110 
111   if ((regnum == -1)
112       || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
113     {
114       struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
115 
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, regnum,
120 					  &regset, sizeof (regset));
121     }
122 }
123 
124 /* Store to the current thread the valid fp, fcc and fcsr
125    register values in the GDB's register array.  */
126 
127 static void
128 store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
129 {
130   elf_fpregset_t regset;
131 
132   if ((regnum == -1)
133       || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
134     {
135       struct iovec iovec = { .iov_base = &regset, .iov_len = sizeof (regset) };
136 
137       if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
138 	perror_with_name (_("Couldn't get NT_FPREGSET registers"));
139       else
140 	{
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"));
145 	}
146     }
147 }
148 
149 /* Implement the "fetch_registers" target_ops method.  */
150 
151 void
152 loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
153 					     int regnum)
154 {
155   pid_t tid = get_ptrace_pid (regcache->ptid ());
156 
157   fetch_gregs_from_thread(regcache, regnum, tid);
158   fetch_fpregs_from_thread(regcache, regnum, tid);
159 }
160 
161 /* Implement the "store_registers" target_ops method.  */
162 
163 void
164 loongarch_linux_nat_target::store_registers (struct regcache *regcache,
165 					     int regnum)
166 {
167   pid_t tid = get_ptrace_pid (regcache->ptid ());
168 
169   store_gregs_to_thread (regcache, regnum, tid);
170   store_fpregs_to_thread(regcache, regnum, tid);
171 }
172 
173 /* Return the address in the core dump or inferior of register REGNO.  */
174 
175 CORE_ADDR
176 loongarch_linux_nat_target::register_u_offset (struct gdbarch *gdbarch,
177 					       int regnum, int store_p)
178 {
179   if (regnum >= 0 && regnum < 32)
180     return regnum;
181   else if (regnum == LOONGARCH_PC_REGNUM)
182     return LOONGARCH_PC_REGNUM;
183   else
184     return -1;
185 }
186 
187 static loongarch_linux_nat_target the_loongarch_linux_nat_target;
188 
189 /* Wrapper functions.  These are only used by libthread_db.  */
190 
191 void
192 supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregset)
193 {
194   loongarch_gregset.supply_regset (nullptr, regcache, -1, gregset,
195 				   sizeof (gdb_gregset_t));
196 }
197 
198 void
199 fill_gregset (const struct regcache *regcache, gdb_gregset_t *gregset,
200 	      int regnum)
201 {
202   loongarch_gregset.collect_regset (nullptr, regcache, regnum, gregset,
203 				    sizeof (gdb_gregset_t));
204 }
205 
206 void
207 supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset)
208 {
209   loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset,
210 				    sizeof (gdb_fpregset_t));
211 }
212 
213 void
214 fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset,
215 	       int regnum)
216 {
217   loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset,
218 				     sizeof (gdb_fpregset_t));
219 }
220 
221 /* Initialize LoongArch Linux native support.  */
222 
223 void _initialize_loongarch_linux_nat ();
224 void
225 _initialize_loongarch_linux_nat ()
226 {
227   linux_target = &the_loongarch_linux_nat_target;
228   add_inf_child_target (&the_loongarch_linux_nat_target);
229 }
230