xref: /openbsd/gnu/usr.bin/binutils/gdb/ppcobsd-nat.c (revision 3cab2bb3)
1 /* Native-dependent code for OpenBSD/powerpc.
2 
3    Copyright (C) 2004, 2005, 2006 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.  */
21 
22 #include "defs.h"
23 #include "gdbcore.h"
24 #include "inferior.h"
25 #include "regcache.h"
26 
27 #include "gdb_assert.h"
28 #include <stddef.h>
29 #include <sys/types.h>
30 #include <sys/ptrace.h>
31 #include <sys/signal.h>
32 #include <machine/frame.h>
33 #include <machine/pcb.h>
34 #include <machine/reg.h>
35 
36 #include "obsd-nat.h"
37 #include "ppc-tdep.h"
38 #include "ppcobsd-tdep.h"
39 #include "inf-ptrace.h"
40 #include "bsd-kvm.h"
41 
42 /* OpenBSD/powerpc didn't have PT_GETFPREGS/PT_SETFPREGS until release
43    4.0.  On older releases the floating-point registers are handled by
44    PT_GETREGS/PT_SETREGS, but fpscr wasn't available..  */
45 
46 #ifdef PT_GETFPREGS
47 
48 /* Returns true if PT_GETFPREGS fetches this register.  */
49 
50 static int
51 getfpregs_supplies (int regnum)
52 {
53   struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
54 
55   /* FIXME: jimb/2004-05-05: Some PPC variants don't have floating
56      point registers.  Traditionally, GDB's register set has still
57      listed the floating point registers for such machines, so this
58      code is harmless.  However, the new E500 port actually omits the
59      floating point registers entirely from the register set --- they
60      don't even have register numbers assigned to them.
61 
62      It's not clear to me how best to update this code, so this assert
63      will alert the first person to encounter the NetBSD/E500
64      combination to the problem.  */
65   gdb_assert (ppc_floating_point_unit_p (current_gdbarch));
66 
67   return ((regnum >= tdep->ppc_fp0_regnum
68            && regnum < tdep->ppc_fp0_regnum + ppc_num_fprs)
69 	  || regnum == tdep->ppc_fpscr_regnum);
70 }
71 
72 #endif /* PT_GETFPREGS */
73 
74 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
75    for all registers.  */
76 
77 static void
78 ppcobsd_fetch_registers (int regnum)
79 {
80   struct reg regs;
81   int pid;
82 
83   /* Cater for systems like OpenBSD, that implement threads as
84      separate processes.  */
85   pid = ptid_get_lwp (inferior_ptid);
86   if (pid == 0)
87     pid = ptid_get_pid (inferior_ptid);
88 
89   if (ptrace (PT_GETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
90     perror_with_name (_("Couldn't get registers"));
91 
92   ppc_supply_gregset (&ppcobsd_gregset, current_regcache, -1,
93 		      &regs, sizeof regs);
94 #ifndef PT_GETFPREGS
95   ppc_supply_fpregset (&ppcobsd_gregset, current_regcache, -1,
96 		       &regs, sizeof regs);
97 #endif
98 
99 #ifdef PT_GETFPREGS
100   if (regnum == -1 || getfpregs_supplies (regnum))
101     {
102       struct fpreg fpregs;
103 
104       if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
105 	perror_with_name (_("Couldn't get floating point status"));
106 
107       ppc_supply_fpregset (&ppcobsd_fpregset, current_regcache, -1,
108 			   &fpregs, sizeof fpregs);
109     }
110 #endif
111 }
112 
113 /* Store register REGNUM back into the inferior.  If REGNUM is -1, do
114    this for all registers.  */
115 
116 static void
117 ppcobsd_store_registers (int regnum)
118 {
119   struct reg regs;
120   int pid;
121 
122   /* Cater for systems like OpenBSD, that implement threads as
123      separate processes.  */
124   pid = ptid_get_lwp (inferior_ptid);
125   if (pid == 0)
126     pid = ptid_get_pid (inferior_ptid);
127 
128   if (ptrace (PT_GETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
129     perror_with_name (_("Couldn't get registers"));
130 
131   ppc_collect_gregset (&ppcobsd_gregset, current_regcache,
132 		       regnum, &regs, sizeof regs);
133 #ifndef PT_GETFPREGS
134   ppc_collect_fpregset (&ppcobsd_gregset, current_regcache,
135 			regnum, &regs, sizeof regs);
136 #endif
137 
138   if (ptrace (PT_SETREGS, pid, (PTRACE_TYPE_ARG3) &regs, 0) == -1)
139     perror_with_name (_("Couldn't write registers"));
140 
141 #ifdef PT_GETFPREGS
142   if (regnum == -1 || getfpregs_supplies (regnum))
143     {
144       struct fpreg fpregs;
145 
146       if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
147 	perror_with_name (_("Couldn't get floating point status"));
148 
149       ppc_collect_fpregset (&ppcobsd_fpregset, current_regcache,
150 			    regnum, &fpregs, sizeof fpregs);
151 
152       if (ptrace (PT_SETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1)
153 	perror_with_name (_("Couldn't write floating point status"));
154     }
155 #endif
156 }
157 
158 
159 static int
160 ppcobsd_supply_pcb (struct regcache *regcache, struct pcb *pcb)
161 {
162   struct gdbarch_tdep *tdep = gdbarch_tdep (get_regcache_arch (regcache));
163   struct switchframe sf;
164   struct callframe cf;
165   int i, regnum;
166 
167   /* The following is true for OpenBSD 3.7:
168 
169      The pcb contains %r1 (the stack pointer) at the point of the
170      context switch in cpu_switch().  At that point we have a stack
171      frame as described by `struct switchframe', and below that a call
172      frame as described by `struct callframe'.  From this information
173      we reconstruct the register state as it would look when we are in
174      cpu_switch().  */
175 
176   /* The stack pointer shouldn't be zero.  */
177   if (pcb->pcb_sp == 0)
178     return 0;
179 
180   read_memory (pcb->pcb_sp, (char *)&sf, sizeof sf);
181   regcache_raw_supply (regcache, tdep->ppc_cr_regnum, &sf.cr);
182   regcache_raw_supply (regcache, tdep->ppc_gp0_regnum + 2, &sf.fixreg2);
183   for (i = 0, regnum = tdep->ppc_gp0_regnum + 13; i < 19; i++, regnum++)
184     regcache_raw_supply (regcache, regnum, &sf.fixreg[i]);
185 
186   read_memory (sf.sp, (char *)&cf, sizeof cf);
187   regcache_raw_supply (regcache, SP_REGNUM, &cf.sp);
188   regcache_raw_supply (regcache, tdep->ppc_gp0_regnum + 30, &cf.r30);
189   regcache_raw_supply (regcache, tdep->ppc_gp0_regnum + 31, &cf.r31);
190 
191   read_memory (cf.sp, (char *)&cf, sizeof cf);
192   regcache_raw_supply (regcache, PC_REGNUM, &cf.lr);
193 
194   return 1;
195 }
196 
197 
198 /* Provide a prototype to silence -Wmissing-prototypes.  */
199 void _initialize_ppcobsd_nat (void);
200 
201 void
202 _initialize_ppcobsd_nat (void)
203 {
204   struct target_ops *t;
205 
206   /* Add in local overrides.  */
207   t = inf_ptrace_target ();
208   t->to_fetch_registers = ppcobsd_fetch_registers;
209   t->to_store_registers = ppcobsd_store_registers;
210   t->to_pid_to_str = obsd_pid_to_str;
211   t->to_find_new_threads = obsd_find_new_threads;
212   t->to_wait = obsd_wait;
213   add_target (t);
214 
215   /* General-purpose registers.  */
216   ppcobsd_reg_offsets.r0_offset = offsetof (struct reg, gpr);
217   ppcobsd_reg_offsets.pc_offset = offsetof (struct reg, pc);
218   ppcobsd_reg_offsets.ps_offset = offsetof (struct reg, ps);
219   ppcobsd_reg_offsets.cr_offset = offsetof (struct reg, cnd);
220   ppcobsd_reg_offsets.lr_offset = offsetof (struct reg, lr);
221   ppcobsd_reg_offsets.ctr_offset = offsetof (struct reg, cnt);
222   ppcobsd_reg_offsets.xer_offset = offsetof (struct reg, xer);
223   ppcobsd_reg_offsets.mq_offset = offsetof (struct reg, mq);
224 
225   /* Floating-point registers.  */
226   ppcobsd_reg_offsets.f0_offset = offsetof (struct reg, fpr);
227   ppcobsd_reg_offsets.fpscr_offset = -1;
228 #ifdef PT_GETFPREGS
229   ppcobsd_fpreg_offsets.f0_offset = offsetof (struct fpreg, fpr);
230   ppcobsd_fpreg_offsets.fpscr_offset = offsetof (struct fpreg, fpscr);
231 #endif
232 
233   /* AltiVec registers.  */
234   ppcobsd_reg_offsets.vr0_offset = offsetof (struct vreg, vreg);
235   ppcobsd_reg_offsets.vscr_offset = offsetof (struct vreg, vscr);
236   ppcobsd_reg_offsets.vrsave_offset = offsetof (struct vreg, vrsave);
237 
238   /* Support debugging kernel virtual memory images.  */
239   bsd_kvm_add_target (ppcobsd_supply_pcb);
240 }
241