-/* Functions specific to running gdb native on HPPA running Linux.
- Copyright 2004 Free Software Foundation, Inc.
+/* Functions specific to running GDB native on HPPA running GNU/Linux.
+
+ Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "gdbcore.h"
#include "regcache.h"
#include "gdb_string.h"
+#include "inferior.h"
+#include "target.h"
+#include "linux-nat.h"
#include <sys/procfs.h>
-#include <string.h>
-#include <asm/offsets.h>
+#include <sys/ptrace.h>
+#include <linux/version.h>
+
+#include <asm/ptrace.h>
+#include "hppa-linux-offsets.h"
+
+#include "hppa-tdep.h"
/* Prototypes for supply_gregset etc. */
#include "gregset.h"
PT_FR31, PT_FR31 + 4,
};
-CORE_ADDR
-register_addr (int regno, CORE_ADDR blockend)
+static CORE_ADDR
+hppa_linux_register_addr (int regno, CORE_ADDR blockend)
{
CORE_ADDR addr;
- if ((unsigned) regno >= NUM_REGS)
- error ("Invalid register number %d.", regno);
+ if ((unsigned) regno >= ARRAY_SIZE (u_offsets))
+ error (_("Invalid register number %d."), regno);
if (u_offsets[regno] == -1)
addr = 0;
* cr8,9,12,13
* cr10, cr15
*/
-#define GR_REGNUM(_n) (R0_REGNUM+_n)
-#define TR_REGNUM(_n) (TR0_REGNUM+_n)
+#define GR_REGNUM(_n) (HPPA_R0_REGNUM+_n)
+#define TR_REGNUM(_n) (HPPA_TR0_REGNUM+_n)
static const int greg_map[] =
{
GR_REGNUM(0), GR_REGNUM(1), GR_REGNUM(2), GR_REGNUM(3),
GR_REGNUM(24), GR_REGNUM(25), GR_REGNUM(26), GR_REGNUM(27),
GR_REGNUM(28), GR_REGNUM(29), GR_REGNUM(30), GR_REGNUM(31),
- SR4_REGNUM+1, SR4_REGNUM+2, SR4_REGNUM+3, SR4_REGNUM+4,
- SR4_REGNUM, SR4_REGNUM+5, SR4_REGNUM+6, SR4_REGNUM+7,
+ HPPA_SR4_REGNUM+1, HPPA_SR4_REGNUM+2, HPPA_SR4_REGNUM+3, HPPA_SR4_REGNUM+4,
+ HPPA_SR4_REGNUM, HPPA_SR4_REGNUM+5, HPPA_SR4_REGNUM+6, HPPA_SR4_REGNUM+7,
- PCOQ_HEAD_REGNUM, PCOQ_TAIL_REGNUM,
- PCSQ_HEAD_REGNUM, PCSQ_TAIL_REGNUM,
+ HPPA_PCOQ_HEAD_REGNUM, HPPA_PCOQ_TAIL_REGNUM,
+ HPPA_PCSQ_HEAD_REGNUM, HPPA_PCSQ_TAIL_REGNUM,
- SAR_REGNUM, IIR_REGNUM, ISR_REGNUM, IOR_REGNUM,
- IPSW_REGNUM, RCR_REGNUM,
+ HPPA_SAR_REGNUM, HPPA_IIR_REGNUM, HPPA_ISR_REGNUM, HPPA_IOR_REGNUM,
+ HPPA_IPSW_REGNUM, HPPA_RCR_REGNUM,
TR_REGNUM(0), TR_REGNUM(1), TR_REGNUM(2), TR_REGNUM(3),
TR_REGNUM(4), TR_REGNUM(5), TR_REGNUM(6), TR_REGNUM(7),
- PID0_REGNUM, PID1_REGNUM, PID2_REGNUM, PID3_REGNUM,
- CCR_REGNUM, EIEM_REGNUM,
+ HPPA_PID0_REGNUM, HPPA_PID1_REGNUM, HPPA_PID2_REGNUM, HPPA_PID3_REGNUM,
+ HPPA_CCR_REGNUM, HPPA_EIEM_REGNUM,
};
+
+
+/* Fetch one register. */
+
+static void
+fetch_register (struct regcache *regcache, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int tid;
+ int val;
+
+ if (gdbarch_cannot_fetch_register (gdbarch, regno))
+ {
+ regcache_raw_supply (regcache, regno, NULL);
+ return;
+ }
+
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+
+ errno = 0;
+ val = ptrace (PTRACE_PEEKUSER, tid, hppa_linux_register_addr (regno, 0), 0);
+ if (errno != 0)
+ error (_("Couldn't read register %s (#%d): %s."),
+ gdbarch_register_name (gdbarch, regno),
+ regno, safe_strerror (errno));
+
+ regcache_raw_supply (regcache, regno, &val);
+}
+
+/* Store one register. */
+
+static void
+store_register (const struct regcache *regcache, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int tid;
+ int val;
+
+ if (gdbarch_cannot_store_register (gdbarch, regno))
+ return;
+
+ /* GNU/Linux LWP ID's are process ID's. */
+ tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid); /* Not a threaded program. */
+
+ errno = 0;
+ regcache_raw_collect (regcache, regno, &val);
+ ptrace (PTRACE_POKEUSER, tid, hppa_linux_register_addr (regno, 0), val);
+ if (errno != 0)
+ error (_("Couldn't write register %s (#%d): %s."),
+ gdbarch_register_name (gdbarch, regno),
+ regno, safe_strerror (errno));
+}
+
+/* Fetch registers from the child process. Fetch all registers if
+ regno == -1, otherwise fetch all general registers or all floating
+ point registers depending upon the value of regno. */
+
+static void
+hppa_linux_fetch_inferior_registers (struct regcache *regcache, int regno)
+{
+ if (-1 == regno)
+ {
+ for (regno = 0;
+ regno < gdbarch_num_regs (get_regcache_arch (regcache));
+ regno++)
+ fetch_register (regcache, regno);
+ }
+ else
+ {
+ fetch_register (regcache, regno);
+ }
+}
+
+/* Store registers back into the inferior. Store all registers if
+ regno == -1, otherwise store all general registers or all floating
+ point registers depending upon the value of regno. */
+
+static void
+hppa_linux_store_inferior_registers (struct regcache *regcache, int regno)
+{
+ if (-1 == regno)
+ {
+ for (regno = 0;
+ regno < gdbarch_num_regs (get_regcache_arch (regcache));
+ regno++)
+ store_register (regcache, regno);
+ }
+ else
+ {
+ store_register (regcache, regno);
+ }
+}
+
+/* Fill GDB's register array with the general-purpose register values
+ in *gregsetp. */
+
void
-supply_gregset (gdb_gregset_t *gregsetp)
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
{
int i;
- greg_t *regp = (elf_greg_t *) gregsetp;
+ const greg_t *regp = (const elf_greg_t *) gregsetp;
for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++)
{
int regno = greg_map[i];
- int size = register_size (current_gdbarch, regno);
- /* When running a 64 bit kernel, a greg_t may be larger than the
- actual register, so just pick off the LS bits of big-endian word. */
- supply_register (regno, ((char *) (regp + 1)) - size);
+ regcache_raw_supply (regcache, regno, regp);
}
}
+/* Fill register regno (if it is a general-purpose register) in
+ *gregsetp with the appropriate value from GDB's register array.
+ If regno is -1, do this for all registers. */
+
void
-fill_gregset (gdb_gregset_t *gregsetp, int regno)
+fill_gregset (const struct regcache *regcache,
+ gdb_gregset_t *gregsetp, int regno)
{
int i;
- greg_t *regp = (greg_t *) gregsetp;
- memset (gregsetp, 0, sizeof (*gregsetp));
- for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++, regp++)
+ for (i = 0; i < sizeof (greg_map) / sizeof (greg_map[0]); i++)
{
- int regi = greg_map[i];
+ int mregno = greg_map[i];
- if (regno == -1 || regi == regno)
+ if (regno == -1 || regno == mregno)
{
- int rawsize = register_size (current_gdbarch, regi);
- regcache_collect (regi, ((char *) (regp + 1)) - rawsize);
+ regcache_raw_collect(regcache, mregno, &(*gregsetp)[i]);
}
}
}
idea of the current floating point register values. */
void
-supply_fpregset (gdb_fpregset_t *fpregsetp)
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
{
- register int regi;
- char *from;
+ int regi;
+ const char *from;
for (regi = 0; regi <= 31; regi++)
{
- from = (char *) &((*fpregsetp)[regi]);
- supply_register (2*regi + HPPA_FP0_REGNUM, from);
- supply_register (2*regi + HPPA_FP0_REGNUM + 1, from + 4);
+ from = (const char *) &((*fpregsetp)[regi]);
+ regcache_raw_supply (regcache, 2*regi + HPPA_FP0_REGNUM, from);
+ regcache_raw_supply (regcache, 2*regi + HPPA_FP0_REGNUM + 1, from + 4);
}
}
them all. */
void
-fill_fpregset (gdb_fpregset_t *fpregsetp, int regno)
+fill_fpregset (const struct regcache *regcache,
+ gdb_fpregset_t *fpregsetp, int regno)
{
int i;
- for (i = 0; i < NUM_REGS; i++)
+ for (i = HPPA_FP0_REGNUM; i < HPPA_FP0_REGNUM + 32 * 2; i++)
{
- if (regno == -1 || regno == i)
- {
- /* Gross. fpregset_t is double, registers[x] has single
- precision reg. */
- char *to = (char *) &((*fpregsetp)[(i - HPPA_FP0_REGNUM) / 2]);
- if ((i - HPPA_FP0_REGNUM) & 1)
- to += 4;
- regcache_collect (i, to);
- }
+ /* Gross. fpregset_t is double, registers[x] has single
+ precision reg. */
+ char *to = (char *) &((*fpregsetp)[(i - HPPA_FP0_REGNUM) / 2]);
+ if ((i - HPPA_FP0_REGNUM) & 1)
+ to += 4;
+ regcache_raw_collect (regcache, i, to);
}
}
+
+void _initialize_hppa_linux_nat (void);
+
+void
+_initialize_hppa_linux_nat (void)
+{
+ struct target_ops *t;
+
+ /* Fill in the generic GNU/Linux methods. */
+ t = linux_target ();
+
+ /* Add our register access methods. */
+ t->to_fetch_registers = hppa_linux_fetch_inferior_registers;
+ t->to_store_registers = hppa_linux_store_inferior_registers;
+
+ /* Register the target. */
+ linux_nat_add_target (t);
+}