/* S390 native-dependent code for GDB, the GNU debugger.
- Copyright (C) 2001, 2003, 2004, 2005, 2006
+ Copyright (C) 2001, 2003, 2004, 2005, 2006, 2007, 2009
Free Software Foundation, Inc
#include "inferior.h"
#include "target.h"
#include "linux-nat.h"
+#include "auxv.h"
#include "s390-tdep.h"
#include <asm/types.h>
#include <sys/procfs.h>
#include <sys/ucontext.h>
+#include <elf.h>
+
+#ifndef HWCAP_S390_HIGH_GPRS
+#define HWCAP_S390_HIGH_GPRS 512
+#endif
/* Map registers to gregset/ptrace offsets.
we have to fix up the 64-bit registers we get from the kernel
to make them look like 32-bit registers. */
#ifdef __s390x__
-#define SUBOFF(i) \
- ((gdbarch_ptr_bit (current_gdbarch) == 32 \
+#define SUBOFF(gdbarch, i) \
+ ((gdbarch_ptr_bit (gdbarch) == 32 \
&& ((i) == S390_PSWA_REGNUM \
|| ((i) >= S390_R0_REGNUM && (i) <= S390_R15_REGNUM)))? 4 : 0)
#else
-#define SUBOFF(i) 0
+#define SUBOFF(gdbarch, i) 0
#endif
void
supply_gregset (struct regcache *regcache, const gregset_t *regp)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
int i;
for (i = 0; i < S390_NUM_REGS; i++)
if (regmap_gregset[i] != -1)
regcache_raw_supply (regcache, i,
- (const char *)regp + regmap_gregset[i] + SUBOFF (i));
+ (const char *)regp + regmap_gregset[i]
+ + SUBOFF (gdbarch, i));
}
/* Fill register REGNO (if it is a general-purpose register) in
void
fill_gregset (const struct regcache *regcache, gregset_t *regp, int regno)
{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
int i;
for (i = 0; i < S390_NUM_REGS; i++)
if (regmap_gregset[i] != -1)
if (regno == -1 || regno == i)
regcache_raw_collect (regcache, i,
- (char *)regp + regmap_gregset[i] + SUBOFF (i));
+ (char *)regp + regmap_gregset[i]
+ + SUBOFF (gdbarch, i));
}
/* Fill GDB's register array with the floating-point register values
/* Fetch register REGNUM from the child process. If REGNUM is -1, do
this for all registers. */
static void
-s390_linux_fetch_inferior_registers (struct regcache *regcache, int regnum)
+s390_linux_fetch_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
{
int tid = s390_inferior_tid ();
/* Store register REGNUM back into the child process. If REGNUM is
-1, do this for all registers. */
static void
-s390_linux_store_inferior_registers (struct regcache *regcache, int regnum)
+s390_linux_store_inferior_registers (struct target_ops *ops,
+ struct regcache *regcache, int regnum)
{
int tid = s390_inferior_tid ();
{
per_lowcore_bits per_lowcore;
ptrace_area parea;
+ int result;
/* Speed up common case. */
if (!watch_base)
if (ptrace (PTRACE_PEEKUSR_AREA, s390_inferior_tid (), &parea) < 0)
perror_with_name (_("Couldn't retrieve watchpoint status"));
- return per_lowcore.perc_storage_alteration == 1
- && per_lowcore.perc_store_real_address == 0;
+ result = (per_lowcore.perc_storage_alteration == 1
+ && per_lowcore.perc_store_real_address == 0);
+
+ if (result)
+ {
+ /* Do not report this watchpoint again. */
+ memset (&per_lowcore, 0, sizeof (per_lowcore));
+ if (ptrace (PTRACE_POKEUSR_AREA, s390_inferior_tid (), &parea) < 0)
+ perror_with_name (_("Couldn't clear watchpoint status"));
+ }
+
+ return result;
}
static void
-s390_fix_watch_points (void)
+s390_fix_watch_points (ptid_t ptid)
{
- int tid = s390_inferior_tid ();
+ int tid;
per_struct per_info;
ptrace_area parea;
CORE_ADDR watch_lo_addr = (CORE_ADDR)-1, watch_hi_addr = 0;
struct watch_area *area;
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
for (area = watch_base; area; area = area->next)
{
watch_lo_addr = min (watch_lo_addr, area->lo_addr);
static int
s390_insert_watchpoint (CORE_ADDR addr, int len, int type)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
struct watch_area *area = xmalloc (sizeof (struct watch_area));
+
if (!area)
return -1;
area->next = watch_base;
watch_base = area;
- s390_fix_watch_points ();
+ ALL_LWPS (lp, ptid)
+ s390_fix_watch_points (ptid);
return 0;
}
static int
s390_remove_watchpoint (CORE_ADDR addr, int len, int type)
{
+ struct lwp_info *lp;
+ ptid_t ptid;
struct watch_area *area, **parea;
for (parea = &watch_base; *parea; parea = &(*parea)->next)
*parea = area->next;
xfree (area);
- s390_fix_watch_points ();
+ ALL_LWPS (lp, ptid)
+ s390_fix_watch_points (ptid);
return 0;
}
static int
s390_can_use_hw_breakpoint (int type, int cnt, int othertype)
{
- return 1;
+ return type == bp_hardware_watchpoint;
}
static int
return 1;
}
+static int
+s390_target_wordsize (void)
+{
+ int wordsize = 4;
+
+ /* Check for 64-bit inferior process. This is the case when the host is
+ 64-bit, and in addition bit 32 of the PSW mask is set. */
+#ifdef __s390x__
+ long pswm;
+
+ errno = 0;
+ pswm = (long) ptrace (PTRACE_PEEKUSER, s390_inferior_tid (), PT_PSWMASK, 0);
+ if (errno == 0 && (pswm & 0x100000000ul) != 0)
+ wordsize = 8;
+#endif
+
+ return wordsize;
+}
+
+static int
+s390_auxv_parse (struct target_ops *ops, gdb_byte **readptr,
+ gdb_byte *endptr, CORE_ADDR *typep, CORE_ADDR *valp)
+{
+ int sizeof_auxv_field = s390_target_wordsize ();
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+ gdb_byte *ptr = *readptr;
+
+ if (endptr == ptr)
+ return 0;
+
+ if (endptr - ptr < sizeof_auxv_field * 2)
+ return -1;
+
+ *typep = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+ ptr += sizeof_auxv_field;
+ *valp = extract_unsigned_integer (ptr, sizeof_auxv_field, byte_order);
+ ptr += sizeof_auxv_field;
+
+ *readptr = ptr;
+ return 1;
+}
+
+#ifdef __s390x__
+static unsigned long
+s390_get_hwcap (void)
+{
+ CORE_ADDR field;
+
+ if (target_auxv_search (¤t_target, AT_HWCAP, &field))
+ return (unsigned long) field;
+
+ return 0;
+}
+#endif
+
+static const struct target_desc *
+s390_read_description (struct target_ops *ops)
+{
+#ifdef __s390x__
+ /* If GDB itself is compiled as 64-bit, we are running on a machine in
+ z/Architecture mode. If the target is running in 64-bit addressing
+ mode, report s390x architecture. If the target is running in 31-bit
+ addressing mode, but the kernel supports using 64-bit registers in
+ that mode, report s390 architecture with 64-bit GPRs. */
+
+ if (s390_target_wordsize () == 8)
+ return tdesc_s390x_linux64;
+
+ if (s390_get_hwcap () & HWCAP_S390_HIGH_GPRS)
+ return tdesc_s390_linux64;
+#endif
+
+ /* If GDB itself is compiled as 31-bit, or if we're running a 31-bit inferior
+ on a 64-bit kernel that does not support using 64-bit registers in 31-bit
+ mode, report s390 architecture with 32-bit GPRs. */
+ return tdesc_s390_linux32;
+}
void _initialize_s390_nat (void);
t->to_insert_watchpoint = s390_insert_watchpoint;
t->to_remove_watchpoint = s390_remove_watchpoint;
+ /* Detect target architecture. */
+ t->to_read_description = s390_read_description;
+ t->to_auxv_parse = s390_auxv_parse;
+
/* Register the target. */
linux_nat_add_target (t);
+ linux_nat_set_new_thread (t, s390_fix_watch_points);
}