-/* Functions specific to running gdb native on IA64 running Linux.
- Copyright 1999 Free Software Foundation, Inc.
+/* Functions specific to running gdb native on IA-64 running
+ GNU/Linux.
+
+ Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005
+ Free Software Foundation, Inc.
This file is part of GDB.
Boston, MA 02111-1307, USA. */
#include "defs.h"
+#include "gdb_string.h"
#include "inferior.h"
#include "target.h"
#include "gdbcore.h"
+#include "regcache.h"
+#include "ia64-tdep.h"
#include <signal.h>
#include <sys/ptrace.h>
-#include <sys/wait.h>
+#include "gdb_wait.h"
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
#endif
+#include <sys/syscall.h>
#include <sys/user.h>
#include <asm/ptrace_offsets.h>
#include <sys/procfs.h>
+/* Prototypes for supply_gregset etc. */
+#include "gregset.h"
+
/* These must match the order of the register names.
Some sort of lookup table is needed because the offsets associated
PT_PR,
PT_CR_IIP, /* ip */
PT_CR_IPSR, /* psr */
- PT_CR_IFS, /* cfm */
+ PT_CFM, /* cfm */
/* kernel registers not visible via ptrace interface (?) */
-1, -1, -1, -1, -1, -1, -1, -1,
/* hole */
};
CORE_ADDR
-register_addr (regno, blockend)
- int regno;
- CORE_ADDR blockend;
+register_addr (int regno, CORE_ADDR blockend)
{
CORE_ADDR addr;
to be changed by (roughly) N as well. (It could be N-1 or N+1
depending upon where the NaT collection bits fall.)
- OTOH, the linux kernel provides read/write access to bsp (and
+ OTOH, the Linux kernel provides read/write access to bsp (and
currently read/write access to bspstore as well). But it
is definitely the case that if you change one, the other
will change at the same time. It is more useful to gdb to
}
void
-supply_gregset (gregsetp)
- gregset_t *gregsetp;
+supply_gregset (gregset_t *gregsetp)
{
int regi;
greg_t *regp = (greg_t *) gregsetp;
for (regi = IA64_GR0_REGNUM; regi <= IA64_GR31_REGNUM; regi++)
{
- supply_register (regi, (char *) (regp + (regi - IA64_GR0_REGNUM)));
+ regcache_raw_supply (current_regcache, regi,
+ (char *) (regp + (regi - IA64_GR0_REGNUM)));
}
/* FIXME: NAT collection bits are at index 32; gotta deal with these
somehow... */
- supply_register (IA64_PR_REGNUM, (char *) (regp + 33));
+ regcache_raw_supply (current_regcache, IA64_PR_REGNUM, (char *) (regp + 33));
for (regi = IA64_BR0_REGNUM; regi <= IA64_BR7_REGNUM; regi++)
{
- supply_register (regi, (char *) (regp + 34 + (regi - IA64_BR0_REGNUM)));
+ regcache_raw_supply (current_regcache, regi,
+ (char *) (regp + 34 + (regi - IA64_BR0_REGNUM)));
}
- supply_register (IA64_IP_REGNUM, (char *) (regp + 42));
- supply_register (IA64_CFM_REGNUM, (char *) (regp + 43));
- supply_register (IA64_PSR_REGNUM, (char *) (regp + 44));
- supply_register (IA64_RSC_REGNUM, (char *) (regp + 45));
- supply_register (IA64_BSP_REGNUM, (char *) (regp + 46));
- supply_register (IA64_BSPSTORE_REGNUM, (char *) (regp + 47));
- supply_register (IA64_RNAT_REGNUM, (char *) (regp + 48));
- supply_register (IA64_CCV_REGNUM, (char *) (regp + 49));
- supply_register (IA64_UNAT_REGNUM, (char *) (regp + 50));
- supply_register (IA64_FPSR_REGNUM, (char *) (regp + 51));
- supply_register (IA64_PFS_REGNUM, (char *) (regp + 52));
- supply_register (IA64_LC_REGNUM, (char *) (regp + 53));
- supply_register (IA64_EC_REGNUM, (char *) (regp + 54));
+ regcache_raw_supply (current_regcache, IA64_IP_REGNUM,
+ (char *) (regp + 42));
+ regcache_raw_supply (current_regcache, IA64_CFM_REGNUM,
+ (char *) (regp + 43));
+ regcache_raw_supply (current_regcache, IA64_PSR_REGNUM,
+ (char *) (regp + 44));
+ regcache_raw_supply (current_regcache, IA64_RSC_REGNUM,
+ (char *) (regp + 45));
+ regcache_raw_supply (current_regcache, IA64_BSP_REGNUM,
+ (char *) (regp + 46));
+ regcache_raw_supply (current_regcache, IA64_BSPSTORE_REGNUM,
+ (char *) (regp + 47));
+ regcache_raw_supply (current_regcache, IA64_RNAT_REGNUM,
+ (char *) (regp + 48));
+ regcache_raw_supply (current_regcache, IA64_CCV_REGNUM,
+ (char *) (regp + 49));
+ regcache_raw_supply (current_regcache, IA64_UNAT_REGNUM,
+ (char *) (regp + 50));
+ regcache_raw_supply (current_regcache, IA64_FPSR_REGNUM,
+ (char *) (regp + 51));
+ regcache_raw_supply (current_regcache, IA64_PFS_REGNUM,
+ (char *) (regp + 52));
+ regcache_raw_supply (current_regcache, IA64_LC_REGNUM,
+ (char *) (regp + 53));
+ regcache_raw_supply (current_regcache, IA64_EC_REGNUM,
+ (char *) (regp + 54));
}
void
-fill_gregset (gregsetp, regno)
- gregset_t *gregsetp;
- int regno;
+fill_gregset (gregset_t *gregsetp, int regno)
+{
+ int regi;
+ greg_t *regp = (greg_t *) gregsetp;
+
+#define COPY_REG(_idx_,_regi_) \
+ if ((regno == -1) || regno == _regi_) \
+ regcache_raw_collect (current_regcache, _regi_, regp + _idx_)
+
+ for (regi = IA64_GR0_REGNUM; regi <= IA64_GR31_REGNUM; regi++)
+ {
+ COPY_REG (regi - IA64_GR0_REGNUM, regi);
+ }
+
+ /* FIXME: NAT collection bits at index 32? */
+
+ COPY_REG (33, IA64_PR_REGNUM);
+
+ for (regi = IA64_BR0_REGNUM; regi <= IA64_BR7_REGNUM; regi++)
+ {
+ COPY_REG (34 + (regi - IA64_BR0_REGNUM), regi);
+ }
+
+ COPY_REG (42, IA64_IP_REGNUM);
+ COPY_REG (43, IA64_CFM_REGNUM);
+ COPY_REG (44, IA64_PSR_REGNUM);
+ COPY_REG (45, IA64_RSC_REGNUM);
+ COPY_REG (46, IA64_BSP_REGNUM);
+ COPY_REG (47, IA64_BSPSTORE_REGNUM);
+ COPY_REG (48, IA64_RNAT_REGNUM);
+ COPY_REG (49, IA64_CCV_REGNUM);
+ COPY_REG (50, IA64_UNAT_REGNUM);
+ COPY_REG (51, IA64_FPSR_REGNUM);
+ COPY_REG (52, IA64_PFS_REGNUM);
+ COPY_REG (53, IA64_LC_REGNUM);
+ COPY_REG (54, IA64_EC_REGNUM);
+}
+
+/* Given a pointer to a floating point register set in /proc format
+ (fpregset_t *), unpack the register contents and supply them as gdb's
+ idea of the current floating point register values. */
+
+void
+supply_fpregset (fpregset_t *fpregsetp)
+{
+ int regi;
+ char *from;
+
+ for (regi = IA64_FR0_REGNUM; regi <= IA64_FR127_REGNUM; regi++)
+ {
+ from = (char *) &((*fpregsetp)[regi - IA64_FR0_REGNUM]);
+ regcache_raw_supply (current_regcache, regi, from);
+ }
+}
+
+/* Given a pointer to a floating point register set in /proc format
+ (fpregset_t *), update the register specified by REGNO from gdb's idea
+ of the current floating point register set. If REGNO is -1, update
+ them all. */
+
+void
+fill_fpregset (fpregset_t *fpregsetp, int regno)
+{
+ int regi;
+
+ for (regi = IA64_FR0_REGNUM; regi <= IA64_FR127_REGNUM; regi++)
+ {
+ if ((regno == -1) || (regno == regi))
+ regcache_raw_collect (current_regcache, regi,
+ &((*fpregsetp)[regi - IA64_FR0_REGNUM]));
+ }
+}
+
+#define IA64_PSR_DB (1UL << 24)
+#define IA64_PSR_DD (1UL << 39)
+
+static void
+enable_watchpoints_in_psr (ptid_t ptid)
+{
+ CORE_ADDR psr;
+
+ psr = read_register_pid (IA64_PSR_REGNUM, ptid);
+ if (!(psr & IA64_PSR_DB))
+ {
+ psr |= IA64_PSR_DB; /* Set the db bit - this enables hardware
+ watchpoints and breakpoints. */
+ write_register_pid (IA64_PSR_REGNUM, psr, ptid);
+ }
+}
+
+static long
+fetch_debug_register (ptid_t ptid, int idx)
+{
+ long val;
+ int tid;
+
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ val = ptrace (PT_READ_U, tid, (PTRACE_TYPE_ARG3) (PT_DBR + 8 * idx), 0);
+
+ return val;
+}
+
+static void
+store_debug_register (ptid_t ptid, int idx, long val)
+{
+ int tid;
+
+ tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ (void) ptrace (PT_WRITE_U, tid, (PTRACE_TYPE_ARG3) (PT_DBR + 8 * idx), val);
+}
+
+static void
+fetch_debug_register_pair (ptid_t ptid, int idx, long *dbr_addr, long *dbr_mask)
+{
+ if (dbr_addr)
+ *dbr_addr = fetch_debug_register (ptid, 2 * idx);
+ if (dbr_mask)
+ *dbr_mask = fetch_debug_register (ptid, 2 * idx + 1);
+}
+
+static void
+store_debug_register_pair (ptid_t ptid, int idx, long *dbr_addr, long *dbr_mask)
+{
+ if (dbr_addr)
+ store_debug_register (ptid, 2 * idx, *dbr_addr);
+ if (dbr_mask)
+ store_debug_register (ptid, 2 * idx + 1, *dbr_mask);
+}
+
+static int
+is_power_of_2 (int val)
+{
+ int i, onecount;
+
+ onecount = 0;
+ for (i = 0; i < 8 * sizeof (val); i++)
+ if (val & (1 << i))
+ onecount++;
+
+ return onecount <= 1;
+}
+
+int
+ia64_linux_insert_watchpoint (ptid_t ptid, CORE_ADDR addr, int len, int rw)
+{
+ int idx;
+ long dbr_addr, dbr_mask;
+ int max_watchpoints = 4;
+
+ if (len <= 0 || !is_power_of_2 (len))
+ return -1;
+
+ for (idx = 0; idx < max_watchpoints; idx++)
+ {
+ fetch_debug_register_pair (ptid, idx, NULL, &dbr_mask);
+ if ((dbr_mask & (0x3UL << 62)) == 0)
+ {
+ /* Exit loop if both r and w bits clear */
+ break;
+ }
+ }
+
+ if (idx == max_watchpoints)
+ return -1;
+
+ dbr_addr = (long) addr;
+ dbr_mask = (~(len - 1) & 0x00ffffffffffffffL); /* construct mask to match */
+ dbr_mask |= 0x0800000000000000L; /* Only match privilege level 3 */
+ switch (rw)
+ {
+ case hw_write:
+ dbr_mask |= (1L << 62); /* Set w bit */
+ break;
+ case hw_read:
+ dbr_mask |= (1L << 63); /* Set r bit */
+ break;
+ case hw_access:
+ dbr_mask |= (3L << 62); /* Set both r and w bits */
+ break;
+ default:
+ return -1;
+ }
+
+ store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+ enable_watchpoints_in_psr (ptid);
+
+ return 0;
+}
+
+int
+ia64_linux_remove_watchpoint (ptid_t ptid, CORE_ADDR addr, int len)
+{
+ int idx;
+ long dbr_addr, dbr_mask;
+ int max_watchpoints = 4;
+
+ if (len <= 0 || !is_power_of_2 (len))
+ return -1;
+
+ for (idx = 0; idx < max_watchpoints; idx++)
+ {
+ fetch_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+ if ((dbr_mask & (0x3UL << 62)) && addr == (CORE_ADDR) dbr_addr)
+ {
+ dbr_addr = 0;
+ dbr_mask = 0;
+ store_debug_register_pair (ptid, idx, &dbr_addr, &dbr_mask);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+ia64_linux_stopped_data_address (CORE_ADDR *addr_p)
+{
+ CORE_ADDR psr;
+ int tid;
+ struct siginfo siginfo;
+ ptid_t ptid = inferior_ptid;
+
+ tid = TIDGET(ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_TYPE_ARG3) 0, &siginfo);
+
+ if (errno != 0 || siginfo.si_signo != SIGTRAP ||
+ (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
+ return 0;
+
+ psr = read_register_pid (IA64_PSR_REGNUM, ptid);
+ psr |= IA64_PSR_DD; /* Set the dd bit - this will disable the watchpoint
+ for the next instruction */
+ write_register_pid (IA64_PSR_REGNUM, psr, ptid);
+
+ *addr_p = (CORE_ADDR)siginfo.si_addr;
+ return 1;
+}
+
+int
+ia64_linux_stopped_by_watchpoint (void)
+{
+ CORE_ADDR addr;
+ return ia64_linux_stopped_data_address (&addr);
+}
+
+LONGEST
+ia64_linux_xfer_unwind_table (struct target_ops *ops,
+ enum target_object object,
+ const char *annex,
+ void *readbuf, const void *writebuf,
+ ULONGEST offset, LONGEST len)
{
- fprintf(stderr, "Warning: fill_gregset not implemented!\n");
- /* FIXME: Implement later */
+ return syscall (__NR_getunwind, readbuf, len);
}