@@ -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)
@@ -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)
{
@@ -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);