xref: /netbsd-src/external/gpl3/gdb/dist/gdbserver/linux-xtensa-low.cc (revision f1c2b495c8d0ed769f039187bdd4f963026e012b)
1 /* GNU/Linux/Xtensa specific low level interface, for the remote server for GDB.
2    Copyright (C) 2007-2024 Free Software Foundation, Inc.
3 
4    This file is part of GDB.
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18 
19 
20 #include "linux-low.h"
21 
22 /* Linux target op definitions for the Xtensa architecture.  */
23 
24 class xtensa_target : public linux_process_target
25 {
26 public:
27 
28   const regs_info *get_regs_info () override;
29 
30   const gdb_byte *sw_breakpoint_from_kind (int kind, int *size) override;
31 
32 protected:
33 
34   void low_arch_setup () override;
35 
36   bool low_cannot_fetch_register (int regno) override;
37 
38   bool low_cannot_store_register (int regno) override;
39 
40   bool low_supports_breakpoints () override;
41 
42   CORE_ADDR low_get_pc (regcache *regcache) override;
43 
44   void low_set_pc (regcache *regcache, CORE_ADDR newpc) override;
45 
46   bool low_breakpoint_at (CORE_ADDR pc) override;
47 };
48 
49 /* The singleton target ops object.  */
50 
51 static xtensa_target the_xtensa_target;
52 
53 bool
54 xtensa_target::low_cannot_fetch_register (int regno)
55 {
56   gdb_assert_not_reached ("linux target op low_cannot_fetch_register "
57 			  "is not implemented by the target");
58 }
59 
60 bool
61 xtensa_target::low_cannot_store_register (int regno)
62 {
63   gdb_assert_not_reached ("linux target op low_cannot_store_register "
64 			  "is not implemented by the target");
65 }
66 
67 bool
68 xtensa_target::low_supports_breakpoints ()
69 {
70   return true;
71 }
72 
73 CORE_ADDR
74 xtensa_target::low_get_pc (regcache *regcache)
75 {
76   return linux_get_pc_32bit (regcache);
77 }
78 
79 void
80 xtensa_target::low_set_pc (regcache *regcache, CORE_ADDR pc)
81 {
82   linux_set_pc_32bit (regcache, pc);
83 }
84 
85 /* Defined in auto-generated file reg-xtensa.c.  */
86 void init_registers_xtensa (void);
87 extern const struct target_desc *tdesc_xtensa;
88 
89 #include <asm/ptrace.h>
90 #include <xtensa-config.h>
91 #include "arch/xtensa.h"
92 #include "gdb_proc_service.h"
93 
94 #include "xtensa-xtregs.c"
95 
96 enum regnum {
97 	R_PC=0,	R_PS,
98 	R_LBEG,	R_LEND,	R_LCOUNT,
99 	R_SAR,
100 	R_WS, R_WB,
101 	R_THREADPTR,
102 	R_A0 = 64
103 };
104 
105 static void
106 xtensa_fill_gregset (struct regcache *regcache, void *buf)
107 {
108   elf_greg_t* rset = (elf_greg_t*)buf;
109   const struct target_desc *tdesc = regcache->tdesc;
110   int ar0_regnum;
111   char *ptr;
112   int i;
113 
114   /* Take care of AR registers.  */
115 
116   ar0_regnum = find_regno (tdesc, "ar0");
117   ptr = (char*)&rset[R_A0];
118 
119   for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
120     {
121       collect_register (regcache, i, ptr);
122       ptr += register_size (tdesc, i);
123     }
124 
125   if (XSHAL_ABI == XTHAL_ABI_CALL0)
126     {
127       int a0_regnum = find_regno (tdesc, "a0");
128       ptr = (char *) &rset[R_A0 + 4 * rset[R_WB]];
129 
130       for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
131 	{
132 	  if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
133 	    ptr = (char *) &rset[R_A0];
134 	  collect_register (regcache, i, ptr);
135 	  ptr += register_size (tdesc, i);
136 	}
137     }
138 
139   /* Loop registers, if hardware has it.  */
140 
141 #if XCHAL_HAVE_LOOPS
142   collect_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
143   collect_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
144   collect_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
145 #endif
146 
147   collect_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
148   collect_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
149   collect_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
150   collect_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
151   collect_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
152 
153 #if XCHAL_HAVE_THREADPTR
154   collect_register_by_name (regcache, "threadptr",
155 			    (char *) &rset[R_THREADPTR]);
156 #endif
157 }
158 
159 static void
160 xtensa_store_gregset (struct regcache *regcache, const void *buf)
161 {
162   const elf_greg_t* rset = (const elf_greg_t*)buf;
163   const struct target_desc *tdesc = regcache->tdesc;
164   int ar0_regnum;
165   char *ptr;
166   int i;
167 
168   /* Take care of AR registers.  */
169 
170   ar0_regnum = find_regno (tdesc, "ar0");
171   ptr = (char *)&rset[R_A0];
172 
173   for (i = ar0_regnum; i < ar0_regnum + XCHAL_NUM_AREGS; i++)
174     {
175       supply_register (regcache, i, ptr);
176       ptr += register_size (tdesc, i);
177     }
178 
179   if (XSHAL_ABI == XTHAL_ABI_CALL0)
180     {
181       int a0_regnum = find_regno (tdesc, "a0");
182       ptr = (char *) &rset[R_A0 + (4 * rset[R_WB]) % XCHAL_NUM_AREGS];
183 
184       for (i = a0_regnum; i < a0_regnum + C0_NREGS; i++)
185 	{
186 	  if ((4 * rset[R_WB] + i - a0_regnum) == XCHAL_NUM_AREGS)
187 	    ptr = (char *) &rset[R_A0];
188 	  supply_register (regcache, i, ptr);
189 	  ptr += register_size (tdesc, i);
190 	}
191     }
192 
193   /* Loop registers, if hardware has it.  */
194 
195 #if XCHAL_HAVE_LOOPS
196   supply_register_by_name (regcache, "lbeg", (char*)&rset[R_LBEG]);
197   supply_register_by_name (regcache, "lend", (char*)&rset[R_LEND]);
198   supply_register_by_name (regcache, "lcount", (char*)&rset[R_LCOUNT]);
199 #endif
200 
201   supply_register_by_name (regcache, "sar", (char*)&rset[R_SAR]);
202   supply_register_by_name (regcache, "pc", (char*)&rset[R_PC]);
203   supply_register_by_name (regcache, "ps", (char*)&rset[R_PS]);
204   supply_register_by_name (regcache, "windowbase", (char*)&rset[R_WB]);
205   supply_register_by_name (regcache, "windowstart", (char*)&rset[R_WS]);
206 
207 #if XCHAL_HAVE_THREADPTR
208   supply_register_by_name (regcache, "threadptr",
209 			   (char *) &rset[R_THREADPTR]);
210 #endif
211 }
212 
213 /* Xtensa GNU/Linux PTRACE interface includes extended register set.  */
214 
215 static void
216 xtensa_fill_xtregset (struct regcache *regcache, void *buf)
217 {
218   const xtensa_regtable_t *ptr;
219 
220   for (ptr = xtensa_regmap_table; ptr->name; ptr++)
221     {
222       collect_register_by_name (regcache, ptr->name,
223 				(char*)buf + ptr->ptrace_offset);
224     }
225 }
226 
227 static void
228 xtensa_store_xtregset (struct regcache *regcache, const void *buf)
229 {
230   const xtensa_regtable_t *ptr;
231 
232   for (ptr = xtensa_regmap_table; ptr->name; ptr++)
233     {
234       supply_register_by_name (regcache, ptr->name,
235 				(char*)buf + ptr->ptrace_offset);
236     }
237 }
238 
239 static struct regset_info xtensa_regsets[] = {
240   { PTRACE_GETREGS, PTRACE_SETREGS, 0, sizeof (elf_gregset_t),
241     GENERAL_REGS,
242     xtensa_fill_gregset, xtensa_store_gregset },
243   { PTRACE_GETXTREGS, PTRACE_SETXTREGS, 0, XTENSA_ELF_XTREG_SIZE,
244     EXTENDED_REGS,
245     xtensa_fill_xtregset, xtensa_store_xtregset },
246   NULL_REGSET
247 };
248 
249 #if XCHAL_HAVE_BE
250 #define XTENSA_BREAKPOINT {0xd2,0x0f}
251 #else
252 #define XTENSA_BREAKPOINT {0x2d,0xf0}
253 #endif
254 
255 static const gdb_byte xtensa_breakpoint[] = XTENSA_BREAKPOINT;
256 #define xtensa_breakpoint_len 2
257 
258 /* Implementation of target ops method "sw_breakpoint_from_kind".  */
259 
260 const gdb_byte *
261 xtensa_target::sw_breakpoint_from_kind (int kind, int *size)
262 {
263   *size = xtensa_breakpoint_len;
264   return xtensa_breakpoint;
265 }
266 
267 bool
268 xtensa_target::low_breakpoint_at (CORE_ADDR where)
269 {
270     unsigned long insn;
271 
272     read_memory (where, (unsigned char *) &insn, xtensa_breakpoint_len);
273     return memcmp((char *) &insn,
274 		  xtensa_breakpoint, xtensa_breakpoint_len) == 0;
275 }
276 
277 /* Called by libthread_db.  */
278 
279 ps_err_e
280 ps_get_thread_area (struct ps_prochandle *ph,
281 		    lwpid_t lwpid, int idx, void **base)
282 {
283   xtensa_elf_gregset_t regs;
284 
285   if (ptrace (PTRACE_GETREGS, lwpid, NULL, &regs) != 0)
286     return PS_ERR;
287 
288   /* IDX is the bias from the thread pointer to the beginning of the
289      thread descriptor.  It has to be subtracted due to implementation
290      quirks in libthread_db.  */
291   *base = (void *) ((char *) regs.threadptr - idx);
292 
293   return PS_OK;
294 }
295 
296 static struct regsets_info xtensa_regsets_info =
297   {
298     xtensa_regsets, /* regsets */
299     0, /* num_regsets */
300     NULL, /* disabled_regsets */
301   };
302 
303 static struct regs_info myregs_info =
304   {
305     NULL, /* regset_bitmap */
306     NULL, /* usrregs */
307     &xtensa_regsets_info
308   };
309 
310 void
311 xtensa_target::low_arch_setup ()
312 {
313   current_process ()->tdesc = tdesc_xtensa;
314 }
315 
316 const regs_info *
317 xtensa_target::get_regs_info ()
318 {
319   return &myregs_info;
320 }
321 
322 /* The linux target ops object.  */
323 
324 linux_process_target *the_linux_target = &the_xtensa_target;
325 
326 void
327 initialize_low_arch (void)
328 {
329   /* Initialize the Linux target descriptions.  */
330   init_registers_xtensa ();
331 
332   initialize_regsets_info (&xtensa_regsets_info);
333 }
334