1/* Target-dependent code for PowerPC systems running NetBSD.
2   Copyright 2002, 2003 Free Software Foundation, Inc.
3   Contributed by Wasabi Systems, 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., 59 Temple Place - Suite 330,
20   Boston, MA 02111-1307, USA.  */
21
22#include "defs.h"
23#include "gdbcore.h"
24#include "regcache.h"
25#include "target.h"
26#include "breakpoint.h"
27#include "value.h"
28#include "osabi.h"
29
30#include "ppc-tdep.h"
31#include "ppcnbsd-tdep.h"
32#include "nbsd-tdep.h"
33
34#include "solib-svr4.h"
35
36#define REG_FIXREG_OFFSET(x)	((x) * 4)
37#define REG_LR_OFFSET		(32 * 4)
38#define REG_CR_OFFSET		(33 * 4)
39#define REG_XER_OFFSET		(34 * 4)
40#define REG_CTR_OFFSET		(35 * 4)
41#define REG_PC_OFFSET		(36 * 4)
42#define SIZEOF_STRUCT_REG	(37 * 4)
43
44#define FPREG_FPR_OFFSET(x)	((x) * 8)
45#define FPREG_FPSCR_OFFSET	(32 * 8)
46#define SIZEOF_STRUCT_FPREG	(33 * 8)
47
48void
49ppcnbsd_supply_reg (char *regs, int regno)
50{
51  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
52  int i;
53
54  for (i = 0; i <= 31; i++)
55    {
56      if (regno == i || regno == -1)
57	supply_register (i, regs + REG_FIXREG_OFFSET (i));
58    }
59
60  if (regno == tdep->ppc_lr_regnum || regno == -1)
61    supply_register (tdep->ppc_lr_regnum, regs + REG_LR_OFFSET);
62
63  if (regno == tdep->ppc_cr_regnum || regno == -1)
64    supply_register (tdep->ppc_cr_regnum, regs + REG_CR_OFFSET);
65
66  if (regno == tdep->ppc_xer_regnum || regno == -1)
67    supply_register (tdep->ppc_xer_regnum, regs + REG_XER_OFFSET);
68
69  if (regno == tdep->ppc_ctr_regnum || regno == -1)
70    supply_register (tdep->ppc_ctr_regnum, regs + REG_CTR_OFFSET);
71
72  if (regno == PC_REGNUM || regno == -1)
73    supply_register (PC_REGNUM, regs + REG_PC_OFFSET);
74}
75
76void
77ppcnbsd_fill_reg (char *regs, int regno)
78{
79  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
80  int i;
81
82  for (i = 0; i <= 31; i++)
83    {
84      if (regno == i || regno == -1)
85	regcache_collect (i, regs + REG_FIXREG_OFFSET (i));
86    }
87
88  if (regno == tdep->ppc_lr_regnum || regno == -1)
89    regcache_collect (tdep->ppc_lr_regnum, regs + REG_LR_OFFSET);
90
91  if (regno == tdep->ppc_cr_regnum || regno == -1)
92    regcache_collect (tdep->ppc_cr_regnum, regs + REG_CR_OFFSET);
93
94  if (regno == tdep->ppc_xer_regnum || regno == -1)
95    regcache_collect (tdep->ppc_xer_regnum, regs + REG_XER_OFFSET);
96
97  if (regno == tdep->ppc_ctr_regnum || regno == -1)
98    regcache_collect (tdep->ppc_ctr_regnum, regs + REG_CTR_OFFSET);
99
100  if (regno == PC_REGNUM || regno == -1)
101    regcache_collect (PC_REGNUM, regs + REG_PC_OFFSET);
102}
103
104void
105ppcnbsd_supply_fpreg (char *fpregs, int regno)
106{
107  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
108  int i;
109
110  for (i = FP0_REGNUM; i <= FP0_REGNUM + 31; i++)
111    {
112      if (regno == i || regno == -1)
113	supply_register (i, fpregs + FPREG_FPR_OFFSET (i - FP0_REGNUM));
114    }
115
116  if (regno == tdep->ppc_fpscr_regnum || regno == -1)
117    supply_register (tdep->ppc_fpscr_regnum, fpregs + FPREG_FPSCR_OFFSET);
118}
119
120void
121ppcnbsd_fill_fpreg (char *fpregs, int regno)
122{
123  struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
124  int i;
125
126  for (i = FP0_REGNUM; i <= FP0_REGNUM + 31; i++)
127    {
128      if (regno == i || regno == -1)
129	regcache_collect (i, fpregs + FPREG_FPR_OFFSET (i - FP0_REGNUM));
130    }
131
132  if (regno == tdep->ppc_fpscr_regnum || regno == -1)
133    regcache_collect (tdep->ppc_fpscr_regnum, fpregs + FPREG_FPSCR_OFFSET);
134}
135
136static void
137fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
138                      CORE_ADDR ignore)
139{
140  char *regs, *fpregs;
141
142  /* We get everything from one section.  */
143  if (which != 0)
144    return;
145
146  regs = core_reg_sect;
147  fpregs = core_reg_sect + SIZEOF_STRUCT_REG;
148
149  /* Integer registers.  */
150  ppcnbsd_supply_reg (regs, -1);
151
152  /* Floating point registers.  */
153  ppcnbsd_supply_fpreg (fpregs, -1);
154}
155
156static void
157fetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size, int which,
158                         CORE_ADDR ignore)
159{
160  switch (which)
161    {
162    case 0:  /* Integer registers.  */
163      if (core_reg_size != SIZEOF_STRUCT_REG)
164	warning ("Wrong size register set in core file.");
165      else
166	ppcnbsd_supply_reg (core_reg_sect, -1);
167      break;
168
169    case 2:  /* Floating point registers.  */
170      if (core_reg_size != SIZEOF_STRUCT_FPREG)
171	warning ("Wrong size FP register set in core file.");
172      else
173	ppcnbsd_supply_fpreg (core_reg_sect, -1);
174      break;
175
176    default:
177      /* Don't know what kind of register request this is; just ignore it.  */
178      break;
179    }
180}
181
182static struct core_fns ppcnbsd_core_fns =
183{
184  bfd_target_unknown_flavour,		/* core_flavour */
185  default_check_format,			/* check_format */
186  default_core_sniffer,			/* core_sniffer */
187  fetch_core_registers,			/* core_read_registers */
188  NULL					/* next */
189};
190
191static struct core_fns ppcnbsd_elfcore_fns =
192{
193  bfd_target_elf_flavour,		/* core_flavour */
194  default_check_format,			/* check_format */
195  default_core_sniffer,			/* core_sniffer */
196  fetch_elfcore_registers,		/* core_read_registers */
197  NULL					/* next */
198};
199
200static int
201ppcnbsd_pc_in_sigtramp (CORE_ADDR pc, char *func_name)
202{
203  /* FIXME: Need to add support for kernel-provided signal trampolines.  */
204  return (nbsd_pc_in_sigtramp (pc, func_name));
205}
206
207/* NetBSD is confused.  It appears that 1.5 was using the correct SVr4
208   convention but, 1.6 switched to the below broken convention.  For
209   the moment use the broken convention.  Ulgh!.  */
210
211static enum return_value_convention
212ppcnbsd_return_value (struct gdbarch *gdbarch, struct type *valtype,
213		      struct regcache *regcache, void *readbuf,
214		      const void *writebuf)
215{
216  if ((TYPE_CODE (valtype) == TYPE_CODE_STRUCT
217       || TYPE_CODE (valtype) == TYPE_CODE_UNION)
218      && !((TYPE_LENGTH (valtype) == 16 || TYPE_LENGTH (valtype) == 8)
219	    && TYPE_VECTOR (valtype))
220      && !(TYPE_LENGTH (valtype) == 1
221	   || TYPE_LENGTH (valtype) == 2
222	   || TYPE_LENGTH (valtype) == 4
223	   || TYPE_LENGTH (valtype) == 8))
224    return RETURN_VALUE_STRUCT_CONVENTION;
225  else
226    return ppc_sysv_abi_broken_return_value (gdbarch, valtype, regcache,
227					     readbuf, writebuf);
228}
229
230static void
231ppcnbsd_init_abi (struct gdbarch_info info,
232                  struct gdbarch *gdbarch)
233{
234  set_gdbarch_pc_in_sigtramp (gdbarch, ppcnbsd_pc_in_sigtramp);
235  /* For NetBSD, this is an on again, off again thing.  Some systems
236     do use the broken struct convention, and some don't.  */
237  set_gdbarch_return_value (gdbarch, ppcnbsd_return_value);
238  set_solib_svr4_fetch_link_map_offsets (gdbarch,
239                                nbsd_ilp32_solib_svr4_fetch_link_map_offsets);
240}
241
242void
243_initialize_ppcnbsd_tdep (void)
244{
245  gdbarch_register_osabi (bfd_arch_powerpc, 0, GDB_OSABI_NETBSD_ELF,
246			  ppcnbsd_init_abi);
247
248  add_core_fns (&ppcnbsd_core_fns);
249  add_core_fns (&ppcnbsd_elfcore_fns);
250}
251