summaryrefslogtreecommitdiff
path: root/gdb/ppc-linux-nat.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/ppc-linux-nat.c')
-rw-r--r--gdb/ppc-linux-nat.c222
1 files changed, 220 insertions, 2 deletions
diff --git a/gdb/ppc-linux-nat.c b/gdb/ppc-linux-nat.c
index db070b9a7fe..ed197f1163f 100644
--- a/gdb/ppc-linux-nat.c
+++ b/gdb/ppc-linux-nat.c
@@ -76,6 +76,11 @@
#define PTRACE_SETVRREGS 19
#endif
+/* PTRACE requests for POWER7 VSX registers. */
+#ifndef PTRACE_GETVSXREGS
+#define PTRACE_GETVSXREGS 27
+#define PTRACE_SETVSXREGS 28
+#endif
/* Similarly for the ptrace requests for getting / setting the SPE
registers (ev0 -- ev31, acc, and spefscr). See the description of
@@ -127,6 +132,41 @@
typedef char gdb_vrregset_t[SIZEOF_VRREGS];
+/* This is the layout of the POWER7 VSX registers and the way they overlap
+ with the existing FPR and VMX registers.
+
+ VSR doubleword 0 VSR doubleword 1
+ ----------------------------------------------------------------
+ VSR[0] | FPR[0] | |
+ ----------------------------------------------------------------
+ VSR[1] | FPR[1] | |
+ ----------------------------------------------------------------
+ | ... | |
+ | ... | |
+ ----------------------------------------------------------------
+ VSR[30] | FPR[30] | |
+ ----------------------------------------------------------------
+ VSR[31] | FPR[31] | |
+ ----------------------------------------------------------------
+ VSR[32] | VR[0] |
+ ----------------------------------------------------------------
+ VSR[33] | VR[1] |
+ ----------------------------------------------------------------
+ | ... |
+ | ... |
+ ----------------------------------------------------------------
+ VSR[62] | VR[30] |
+ ----------------------------------------------------------------
+ VSR[63] | VR[31] |
+ ----------------------------------------------------------------
+
+ VSX has 64 128bit registers. The first 32 registers overlap with
+ the FP registers (doubleword 0) and hence extend them with additional
+ 64 bits (doubleword 1). The other 32 regs overlap with the VMX
+ registers. */
+#define SIZEOF_VSXREGS 32*8
+
+typedef char gdb_vsxregset_t[SIZEOF_VSXREGS];
/* On PPC processors that support the the Signal Processing Extension
(SPE) APU, the general-purpose registers are 64 bits long.
@@ -152,6 +192,12 @@ struct gdb_evrregset_t
unsigned long spefscr;
};
+/* Non-zero if our kernel may support the PTRACE_GETVSXREGS and
+ PTRACE_SETVSXREGS requests, for reading and writing the VSX
+ POWER7 registers 0 through 31. Zero if we've tried one of them and
+ gotten an error. Note that VSX registers 32 through 63 overlap
+ with VR registers 0 through 31. */
+int have_ptrace_getsetvsxregs = 1;
/* Non-zero if our kernel may support the PTRACE_GETVRREGS and
PTRACE_SETVRREGS requests, for reading and writing the Altivec
@@ -239,6 +285,34 @@ ppc_register_u_addr (struct gdbarch *gdbarch, int regno)
return u_addr;
}
+/* The Linux kernel ptrace interface for POWER7 VSX registers uses the
+ registers set mechanism, as opposed to the interface for all the
+ other registers, that stores/fetches each register individually. */
+static void
+fetch_vsx_register (struct regcache *regcache, int tid, int regno)
+{
+ int ret;
+ gdb_vsxregset_t regs;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+ ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_getsetvsxregs = 0;
+ return;
+ }
+ perror_with_name (_("Unable to fetch VSX register"));
+ }
+
+ regcache_raw_supply (regcache, regno,
+ regs + (regno - tdep->ppc_vsr0_upper_regnum)
+ * vsxregsize);
+}
+
/* The Linux kernel ptrace interface for AltiVec registers uses the
registers set mechanism, as opposed to the interface for all the
other registers, that stores/fetches each register individually. */
@@ -373,6 +447,14 @@ fetch_register (struct regcache *regcache, int tid, int regno)
AltiVec registers, fall through and return zeroes, because
regaddr will be -1 in this case. */
}
+ if (vsx_register_p (gdbarch, regno))
+ {
+ if (have_ptrace_getsetvsxregs)
+ {
+ fetch_vsx_register (regcache, tid, regno);
+ return;
+ }
+ }
else if (spe_register_p (gdbarch, regno))
{
fetch_spe_register (regcache, tid, regno);
@@ -429,6 +511,21 @@ fetch_register (struct regcache *regcache, int tid, int regno)
}
static void
+supply_vsxregset (struct regcache *regcache, gdb_vsxregset_t *vsxregsetp)
+{
+ int i;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+ for (i = 0; i < ppc_num_vshrs; i++)
+ {
+ regcache_raw_supply (regcache, tdep->ppc_vsr0_upper_regnum + i,
+ *vsxregsetp + i * vsxregsize);
+ }
+}
+
+static void
supply_vrregset (struct regcache *regcache, gdb_vrregset_t *vrregsetp)
{
int i;
@@ -454,6 +551,25 @@ supply_vrregset (struct regcache *regcache, gdb_vrregset_t *vrregsetp)
}
static void
+fetch_vsx_registers (struct regcache *regcache, int tid)
+{
+ int ret;
+ gdb_vsxregset_t regs;
+
+ ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_getsetvsxregs = 0;
+ return;
+ }
+ perror_with_name (_("Unable to fetch VSX registers"));
+ }
+ supply_vsxregset (regcache, &regs);
+}
+
+static void
fetch_altivec_registers (struct regcache *regcache, int tid)
{
int ret;
@@ -507,6 +623,9 @@ fetch_ppc_registers (struct regcache *regcache, int tid)
if (have_ptrace_getvrregs)
if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
fetch_altivec_registers (regcache, tid);
+ if (have_ptrace_getsetvsxregs)
+ if (tdep->ppc_vsr0_upper_regnum != -1)
+ fetch_vsx_registers (regcache, tid);
if (tdep->ppc_ev0_upper_regnum >= 0)
fetch_spe_register (regcache, tid, -1);
}
@@ -530,6 +649,35 @@ ppc_linux_fetch_inferior_registers (struct regcache *regcache, int regno)
fetch_register (regcache, tid, regno);
}
+/* Store one VSX register. */
+static void
+store_vsx_register (const struct regcache *regcache, int tid, int regno)
+{
+ int ret;
+ gdb_vsxregset_t regs;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+ ret = ptrace (PTRACE_SETVSXREGS, tid, 0, &regs);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_getsetvsxregs = 0;
+ return;
+ }
+ perror_with_name (_("Unable to fetch VSX register"));
+ }
+
+ regcache_raw_collect (regcache, regno, regs +
+ (regno - tdep->ppc_vsr0_upper_regnum) * vsxregsize);
+
+ ret = ptrace (PTRACE_SETVSXREGS, tid, 0, &regs);
+ if (ret < 0)
+ perror_with_name (_("Unable to store VSX register"));
+}
+
/* Store one register. */
static void
store_altivec_register (const struct regcache *regcache, int tid, int regno)
@@ -668,6 +816,11 @@ store_register (const struct regcache *regcache, int tid, int regno)
store_altivec_register (regcache, tid, regno);
return;
}
+ if (vsx_register_p (gdbarch, regno))
+ {
+ store_vsx_register (regcache, tid, regno);
+ return;
+ }
else if (spe_register_p (gdbarch, regno))
{
store_spe_register (regcache, tid, regno);
@@ -722,6 +875,19 @@ store_register (const struct regcache *regcache, int tid, int regno)
}
static void
+fill_vsxregset (const struct regcache *regcache, gdb_vsxregset_t *vsxregsetp)
+{
+ int i;
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+ int vsxregsize = register_size (gdbarch, tdep->ppc_vsr0_upper_regnum);
+
+ for (i = 0; i < ppc_num_vshrs; i++)
+ regcache_raw_collect (regcache, tdep->ppc_vsr0_upper_regnum + i,
+ *vsxregsetp + i * vsxregsize);
+}
+
+static void
fill_vrregset (const struct regcache *regcache, gdb_vrregset_t *vrregsetp)
{
int i;
@@ -745,6 +911,29 @@ fill_vrregset (const struct regcache *regcache, gdb_vrregset_t *vrregsetp)
}
static void
+store_vsx_registers (const struct regcache *regcache, int tid)
+{
+ int ret;
+ gdb_vsxregset_t regs;
+
+ ret = ptrace (PTRACE_GETVSXREGS, tid, 0, &regs);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_getsetvsxregs = 0;
+ return;
+ }
+ perror_with_name (_("Couldn't get VSX registers"));
+ }
+
+ fill_vsxregset (regcache, &regs);
+
+ if (ptrace (PTRACE_SETVSXREGS, tid, 0, &regs) < 0)
+ perror_with_name (_("Couldn't write VSX registers"));
+}
+
+static void
store_altivec_registers (const struct regcache *regcache, int tid)
{
int ret;
@@ -802,6 +991,9 @@ store_ppc_registers (const struct regcache *regcache, int tid)
if (have_ptrace_getvrregs)
if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1)
store_altivec_registers (regcache, tid);
+ if (have_ptrace_getsetvsxregs)
+ if (tdep->ppc_vsr0_upper_regnum != -1)
+ store_vsx_registers (regcache, tid);
if (tdep->ppc_ev0_upper_regnum >= 0)
store_spe_register (regcache, tid, -1);
}
@@ -1037,6 +1229,7 @@ static const struct target_desc *
ppc_linux_read_description (struct target_ops *ops)
{
int altivec = 0;
+ int vsx = 0;
int tid = TIDGET (inferior_ptid);
if (tid == 0)
@@ -1055,6 +1248,19 @@ ppc_linux_read_description (struct target_ops *ops)
perror_with_name (_("Unable to fetch SPE registers"));
}
+ if (have_ptrace_getsetvsxregs)
+ {
+ gdb_vsxregset_t vsxregset;
+
+ if (ptrace (PTRACE_GETVSXREGS, tid, 0, &vsxregset) >= 0)
+ vsx = 1;
+
+ /* EIO means that the PTRACE_GETVSXREGS request isn't supported.
+ Anything else needs to be reported. */
+ else if (errno != EIO)
+ perror_with_name (_("Unable to fetch VSX registers"));
+ }
+
if (have_ptrace_getvrregs)
{
gdb_vrregset_t vrregset;
@@ -1076,11 +1282,23 @@ ppc_linux_read_description (struct target_ops *ops)
errno = 0;
msr = (long) ptrace (PTRACE_PEEKUSER, tid, PT_MSR * 8, 0);
if (errno == 0 && msr < 0)
- return altivec? tdesc_powerpc_altivec64l : tdesc_powerpc_64l;
+ {
+ if (vsx)
+ return tdesc_powerpc_vsx64l;
+ else if (altivec)
+ return tdesc_powerpc_altivec64l;
+
+ return tdesc_powerpc_64l;
+ }
}
#endif
- return altivec? tdesc_powerpc_altivec32l : tdesc_powerpc_32l;
+ if (vsx)
+ return tdesc_powerpc_vsx32l;
+ else if (altivec)
+ return tdesc_powerpc_altivec32l;
+
+ return tdesc_powerpc_32l;
}
void _initialize_ppc_linux_nat (void);