-/* 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 Linux.
+ Copyright 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GDB.
#include "inferior.h"
#include "target.h"
#include "gdbcore.h"
+#include "regcache.h"
#include <signal.h>
#include <sys/ptrace.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;
}
void
-supply_gregset (gregsetp)
- gregset_t *gregsetp;
+supply_gregset (gregset_t *gregsetp)
{
int regi;
greg_t *regp = (greg_t *) gregsetp;
}
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_) \
+ memcpy (regp + _idx_, ®isters[REGISTER_BYTE (_regi_)], \
+ REGISTER_RAW_SIZE (_regi_))
+
+ 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)
+{
+ register int regi;
+ char *from;
+
+ for (regi = IA64_FR0_REGNUM; regi <= IA64_FR127_REGNUM; regi++)
+ {
+ from = (char *) &((*fpregsetp)[regi - IA64_FR0_REGNUM]);
+ supply_register (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;
+ char *to;
+ char *from;
+
+ for (regi = IA64_FR0_REGNUM; regi <= IA64_FR127_REGNUM; regi++)
+ {
+ if ((regno == -1) || (regno == regi))
+ {
+ from = (char *) ®isters[REGISTER_BYTE (regi)];
+ to = (char *) &((*fpregsetp)[regi - IA64_FR0_REGNUM]);
+ memcpy (to, from, REGISTER_RAW_SIZE (regi));
+ }
+ }
+}
+
+#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_ARG3_TYPE) (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_ARG3_TYPE) (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)
{
- fprintf(stderr, "Warning: fill_gregset not implemented!\n");
- /* FIXME: Implement later */
+ 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;
+}
+
+CORE_ADDR
+ia64_linux_stopped_by_watchpoint (ptid_t ptid)
+{
+ CORE_ADDR psr;
+ int tid;
+ struct siginfo siginfo;
+
+ tid = TIDGET(ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ errno = 0;
+ ptrace (PTRACE_GETSIGINFO, tid, (PTRACE_ARG3_TYPE) 0, &siginfo);
+
+ if (errno != 0 || (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);
+
+ return (CORE_ADDR) siginfo.si_addr;
}