This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
[PATCH 04/24] Add MIPS Config5 register related support
- From: Bhushan Attarde <bhushan dot attarde at imgtec dot com>
- To: <gdb-patches at sourceware dot org>
- Cc: <Maciej dot Rozycki at imgtec dot com>, <Matthew dot Fortune at imgtec dot com>, <James dot Hogan at imgtec dot com>, <Andrew dot Bennett at imgtec dot com>, <Jaydeep dot Patil at imgtec dot com>, Bhushan Attarde <bhushan dot attarde at imgtec dot com>
- Date: Mon, 27 Jun 2016 20:19:31 +0530
- Subject: [PATCH 04/24] Add MIPS Config5 register related support
- Authentication-results: sourceware.org; auth=none
- References: <1467038991-6600-1-git-send-email-bhushan dot attarde at imgtec dot com>
This patch has below changes:
1. Add a Config5 register binding in order to get access to the FRE
floating point mode bit. The internal register number is allocated
dynamically only when provided by the target description, and is tracked in
struct mips_regnum.
2. MIPS native: Use FP regset to support Config5
Use the NT_FPREGSET register set in preference to PTRACE_GETFPREGS for
getting the floating point registers. This register set also provides the
Config5 register which PTRACE_GETFPREGS doesn't.
3. Define type for the MIPS CP0 Config5 register, allowing the important
fields to be decoded.
gdb/ChangeLog:
* mips-tdep.c (mips_config5_type): New function.
(mips_linux_reg_names): Add config5.
(mips_gdbarch_init): Initialise config5 register number and bind
to named config5 register in target description.
(mips_value_to_register): Return config5_type for config5 register.
* mips-tdep.h (struct mips_regnum): New config5 field.
(gdbarch_tdep): New config5_type field.
* mips-linux-nat.c: Include "elf/common.h".
(PTRACE_GETREGSET, PTRACE_SETREGSET): New fallback definitions.
(have_ptrace_getregset_gp, have_ptrace_getregset_fp): New variables.
(mips64_linux_regsets_fetch_registers): Use PTRACE_GETREGSET with
NT_FPREGSET in preferance to PTRACE_GETFPREGS to get FP and
Config5 registers.
(mips64_linux_regsets_store_registers): Use PTRACE_SETREGSET with
NT_FPREGSET in preferance to PTRACE_SETFPREGS to get FP and
Config5 registers
---
gdb/mips-linux-nat.c | 221 ++++++++++++++++++++++++++++++++++++++++++++++-----
gdb/mips-tdep.c | 35 ++++++++
gdb/mips-tdep.h | 4 +
3 files changed, 239 insertions(+), 21 deletions(-)
diff --git a/gdb/mips-linux-nat.c b/gdb/mips-linux-nat.c
index bfe9fcb..627c652 100644
--- a/gdb/mips-linux-nat.c
+++ b/gdb/mips-linux-nat.c
@@ -27,6 +27,7 @@
#include "linux-nat.h"
#include "mips-linux-tdep.h"
#include "target-descriptions.h"
+#include "elf/common.h"
#include "gdb_proc_service.h"
#include "gregset.h"
@@ -46,10 +47,22 @@
#define PTRACE_GET_THREAD_AREA 25
#endif
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET 0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET 0x4205
+#endif
+
/* Assume that we have PTRACE_GETREGS et al. support. If we do not,
we'll clear this and use PTRACE_PEEKUSER instead. */
static int have_ptrace_regsets = 1;
+/* Does the current host support PTRACE_GETREGSET? */
+static int have_ptrace_getregset_gp = 1;
+static int have_ptrace_getregset_fp = 1;
+
/* Saved function pointers to fetch and store a single register using
PTRACE_PEEKUSER and PTRACE_POKEUSER. */
@@ -217,18 +230,25 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
int is_fp, is_dsp;
int have_dsp;
int regi;
int tid;
- if (regno >= mips_regnum (gdbarch)->fp0
- && regno <= mips_regnum (gdbarch)->fp0 + 32)
+ /* FP registers can be obtained in several ways.
+ is_fp will be cleared once the registers have been obtained. */
+ if (regno == -1)
+ is_fp = 1;
+ else if (regno >= mips_regnum (gdbarch)->fp0
+ && regno <= mips_regnum (gdbarch)->fp0 + 32)
is_fp = 1;
else if (regno == mips_regnum (gdbarch)->fp_control_status)
is_fp = 1;
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
is_fp = 1;
+ else if (regno == mips_regnum (gdbarch)->config5)
+ is_fp = 1;
else
is_fp = 0;
@@ -266,23 +286,93 @@ mips64_linux_regsets_fetch_registers (struct target_ops *ops,
(const mips64_elf_gregset_t *) ®s);
}
- if (regno == -1 || is_fp)
+ if (is_fp)
{
- mips64_elf_fpregset_t fp_regs;
+ const struct mips_regnum *rn = mips_regnum (gdbarch);
+ int float_regnum = rn->fp0;
- if (ptrace (PTRACE_GETFPREGS, tid, 0L,
- (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+ /* Try the FP regset next as it may contain Config5 */
+ if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
{
- if (errno == EIO)
+ unsigned char fp_regs[34][8];
+ struct iovec iovec;
+ int ret;
+
+ iovec.iov_base = &fp_regs;
+ iovec.iov_len = sizeof (fp_regs);
+
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
{
- have_ptrace_regsets = 0;
- return;
+ if (errno == EIO)
+ have_ptrace_getregset_gp = 0;
+ else if (errno == EINVAL)
+ have_ptrace_getregset_fp = 0;
+ else
+ perror_with_name (_("Unable to fetch FP registers."));
+ }
+ else
+ {
+ for (regi = 0; regi < 32; regi++)
+ {
+ if (register_size (gdbarch, float_regnum + regi) == 8)
+ {
+ /* FR = 1
+ copy entire double */
+ regcache_raw_supply (regcache, float_regnum + regi,
+ (char *) fp_regs[regi]);
+ }
+ else if (regi & 1)
+ {
+ /* FR = 0
+ odd single from top of even double */
+ regcache_raw_supply (regcache, float_regnum + regi,
+ (char *) fp_regs[regi - 1] +
+ 4 - 4*big_endian);
+ }
+ else
+ {
+ /* FR = 0
+ even single from bottom of even double */
+ regcache_raw_supply (regcache, float_regnum + regi,
+ (char *) fp_regs[regi] + 4 * big_endian);
+ }
+ }
+
+ if (iovec.iov_len >= 32*8 + 4)
+ regcache_raw_supply (regcache, rn->fp_control_status,
+ (char *) (fp_regs + 32) + 0);
+ if (iovec.iov_len >= 32*8 + 8)
+ regcache_raw_supply (regcache, rn->fp_implementation_revision,
+ (char *) (fp_regs + 32) + 4);
+ if (iovec.iov_len >= 33*8 + 4 && rn->config5 >= 0)
+ regcache_raw_supply (regcache, rn->config5,
+ (char *) (fp_regs + 33) + 0);
+
+ /* we've got fp registers now */
+ is_fp = 0;
}
- perror_with_name (_("Couldn't get FP registers"));
}
- mips64_supply_fpregset (regcache,
- (const mips64_elf_fpregset_t *) &fp_regs);
+ /* Fall back to GETFPREGS. */
+ if (is_fp)
+ {
+ mips64_elf_fpregset_t fp_regs;
+
+ if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+ (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+ {
+ if (errno == EIO)
+ {
+ have_ptrace_regsets = 0;
+ return;
+ }
+ perror_with_name (_("Couldn't get FP registers"));
+ }
+
+ mips64_supply_fpregset (regcache,
+ (const mips64_elf_fpregset_t *) &fp_regs);
+ }
}
if (is_dsp)
@@ -305,11 +395,16 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
struct regcache *regcache, int regno)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int big_endian = (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG);
int is_fp, is_dsp;
int have_dsp;
int regi;
int tid;
+ /* FP registers can be written in several ways.
+ is_fp will be cleared once the registers have been written. */
+ if (regno == -1)
+ is_fp = 1;
if (regno >= mips_regnum (gdbarch)->fp0
&& regno <= mips_regnum (gdbarch)->fp0 + 32)
is_fp = 1;
@@ -317,6 +412,8 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
is_fp = 1;
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
is_fp = 1;
+ else if (regno == mips_regnum (gdbarch)->config5)
+ is_fp = 1;
else
is_fp = 0;
@@ -349,19 +446,101 @@ mips64_linux_regsets_store_registers (struct target_ops *ops,
perror_with_name (_("Couldn't set registers"));
}
- if (regno == -1 || is_fp)
+ if (is_fp)
{
- mips64_elf_fpregset_t fp_regs;
+ const struct mips_regnum *rn = mips_regnum (gdbarch);
+ int float_regnum = rn->fp0;
+
+ /* Try the FP regset next as it may contain Config5 */
+ if (have_ptrace_getregset_gp && have_ptrace_getregset_fp)
+ {
+ unsigned char fp_regs[34][8];
+ struct iovec iovec;
+ int ret;
+
+ iovec.iov_base = &fp_regs;
+ iovec.iov_len = sizeof (fp_regs);
- if (ptrace (PTRACE_GETFPREGS, tid, 0L,
- (PTRACE_TYPE_ARG3) &fp_regs) == -1)
- perror_with_name (_("Couldn't get FP registers"));
+ ret = ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ have_ptrace_getregset_gp = 0;
+ else if (errno == EINVAL)
+ have_ptrace_getregset_fp = 0;
+ else
+ perror_with_name (_("Unable to fetch FP registers."));
+ }
+ else
+ {
+ for (regi = 0; regi < 32; regi++)
+ {
+ if (register_size (gdbarch, float_regnum + regi) == 8)
+ {
+ /* FR = 1
+ copy entire double */
+ regcache_raw_collect (regcache, float_regnum + regi,
+ (char *) fp_regs[regi]);
+ }
+ else if (regi & 1)
+ {
+ /* FR = 0
+ odd single from top of even double */
+ regcache_raw_collect (regcache, float_regnum + regi,
+ (char *) fp_regs[regi - 1] +
+ 4 - 4*big_endian);
+ }
+ else
+ {
+ /* FR = 0
+ even single from bottom of even double */
+ regcache_raw_collect (regcache, float_regnum + regi,
+ (char *) fp_regs[regi] + 4 * big_endian);
+ }
+ }
+
+ regcache_raw_collect (regcache, rn->fp_control_status,
+ (char *) (fp_regs + 32) + 0);
+ regcache_raw_collect (regcache, rn->fp_implementation_revision,
+ (char *) (fp_regs + 32) + 4);
+ if (rn->config5 >= 0)
+ regcache_raw_collect (regcache, rn->config5,
+ (char *) (fp_regs + 33) + 0);
+
+ /* don't modify iovec length from amount of data returned */
+ ret = ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, &iovec);
+ if (ret < 0)
+ {
+ if (errno == EIO)
+ have_ptrace_getregset_gp = 0;
+ else if (errno == EINVAL)
+ have_ptrace_getregset_fp = 0;
+ else
+ perror_with_name (_("Unable to store FP/MSA registers."));
+ }
+ else
+ {
+ /* we've got fp registers now */
+ is_fp = 0;
+ }
+ }
+ }
- mips64_fill_fpregset (regcache, &fp_regs, regno);
+ /* Fall back to SETFPREGS. */
+ if (is_fp)
+ {
+ mips64_elf_fpregset_t fp_regs;
+
+ if (ptrace (PTRACE_GETFPREGS, tid, 0L,
+ (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+ perror_with_name (_("Couldn't get FP registers"));
- if (ptrace (PTRACE_SETFPREGS, tid, 0L,
- (PTRACE_TYPE_ARG3) &fp_regs) == -1)
- perror_with_name (_("Couldn't set FP registers"));
+ mips64_fill_fpregset (regcache, &fp_regs, regno);
+
+ if (ptrace (PTRACE_SETFPREGS, tid, 0L,
+ (PTRACE_TYPE_ARG3) &fp_regs) == -1)
+ perror_with_name (_("Couldn't set FP registers"));
+ }
}
if (is_dsp)
diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index ae4dc2e..28cfb57 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -993,6 +993,25 @@ mips_register_to_value (struct frame_info *frame, int regnum,
}
}
+static struct type *
+mips_config5_type (struct gdbarch *gdbarch)
+{
+ struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+ if (tdep->config5_type == NULL)
+ {
+ struct type *t;
+ /* top half has flags */
+ t = arch_flags_type (gdbarch, "__gdb_builtin_type_config5", 4);
+ append_flags_type_flag (t, 8, "FRE");
+
+ TYPE_NAME (t) = "config5";
+ tdep->config5_type = t;
+ }
+
+ return tdep->config5_type;
+}
+
static void
mips_value_to_register (struct frame_info *frame, int regnum,
struct type *type, const gdb_byte *from)
@@ -1080,6 +1099,8 @@ mips_register_type (struct gdbarch *gdbarch, int regnum)
else if (rawnum == mips_regnum (gdbarch)->fp_control_status
|| rawnum == mips_regnum (gdbarch)->fp_implementation_revision)
return builtin_type (gdbarch)->builtin_int32;
+ else if (rawnum == mips_regnum (gdbarch)->config5)
+ return mips_config5_type (gdbarch);
else if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
&& gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
&& rawnum >= MIPS_FIRST_EMBED_REGNUM
@@ -1152,6 +1173,9 @@ mips_pseudo_register_type (struct gdbarch *gdbarch, int regnum)
&& rawnum < mips_regnum (gdbarch)->dspacc + 6)))
return builtin_type (gdbarch)->builtin_int32;
+ if (rawnum == mips_regnum (gdbarch)->config5)
+ return mips_config5_type (gdbarch);
+
if (gdbarch_osabi (gdbarch) != GDB_OSABI_IRIX
&& gdbarch_osabi (gdbarch) != GDB_OSABI_LINUX
&& rawnum >= MIPS_EMBED_FP0_REGNUM + 32
@@ -8236,6 +8260,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
info.tdep_info = &tdep_info;
/* Fill in the OS dependent register numbers and names. */
+ mips_regnum.config5 = -1;
if (info.osabi == GDB_OSABI_IRIX)
{
mips_regnum.fp0 = 32;
@@ -8350,6 +8375,15 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
valid_p &= tdesc_numbered_register (feature, tdesc_data,
mips_regnum.cause, "cause");
+ /* Optionally, Config5 contains FP mode bits */
+ if (tdesc_unnumbered_register (feature, "config5"))
+ {
+ /* Allocate a new register. */
+ mips_regnum.config5 = num_regs++;
+ tdesc_numbered_register (feature, tdesc_data,
+ mips_regnum.config5, "config5");
+ }
+
if (!valid_p)
{
tdesc_data_cleanup (tdesc_data);
@@ -8666,6 +8700,7 @@ mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
tdep->register_size = 0;
tdep->fp_register_size = info.tdep_info->fp_register_size;
tdep->fp_register_size_fixed_p = 0;
+ tdep->config5_type = NULL;
if (info.target_desc)
{
diff --git a/gdb/mips-tdep.h b/gdb/mips-tdep.h
index 6c1d9ca..39af72b 100644
--- a/gdb/mips-tdep.h
+++ b/gdb/mips-tdep.h
@@ -63,6 +63,7 @@ struct mips_regnum
int fp0;
int fp_implementation_revision;
int fp_control_status;
+ int config5;
int badvaddr; /* Bad vaddr for addressing exception. */
int cause; /* Describes last exception. */
int hi; /* Multiply/divide temp. */
@@ -119,6 +120,9 @@ struct gdbarch_tdep
int fp_register_size_fixed_p;
int fp_register_size;
+ /* ISA-specific data types. */
+ struct type *config5_type;
+
/* Return the expected next PC if FRAME is stopped at a syscall
instruction. */
CORE_ADDR (*syscall_next_pc) (struct frame_info *frame);
--
1.9-rc2