@@ -18,8 +18,10 @@
#include "server.h"
#include "linux-low.h"
+#include "elf/common.h"
#include "nat/gdb_ptrace.h"
+#include <sys/uio.h>
#include <endian.h>
#include "nat/mips-linux-watch.h"
@@ -33,6 +35,18 @@ extern const struct target_desc *tdesc_mips_linux;
void init_registers_mips_dsp_linux (void);
extern const struct target_desc *tdesc_mips_dsp_linux;
+/* Defined in auto-generated file mips-fpu64-linux.c. */
+void init_registers_mips_fpu64_linux (void);
+extern const struct target_desc *tdesc_mips_fpu64_linux;
+
+/* Defined in auto-generated file mips-fpu64-dsp-linux.c. */
+void init_registers_mips_fpu64_dsp_linux (void);
+extern const struct target_desc *tdesc_mips_fpu64_dsp_linux;
+
+/* Defined in auto-generated file mips-msa-linux.c. */
+void init_registers_mips_msa_linux (void);
+extern const struct target_desc *tdesc_mips_msa_linux;
+
/* Defined in auto-generated file mips64-linux.c. */
void init_registers_mips64_linux (void);
extern const struct target_desc *tdesc_mips64_linux;
@@ -41,15 +55,30 @@ extern const struct target_desc *tdesc_mips64_linux;
void init_registers_mips64_dsp_linux (void);
extern const struct target_desc *tdesc_mips64_dsp_linux;
+/* Defined in auto-generated file mips64-msa-linux.c. */
+void init_registers_mips64_msa_linux (void);
+extern const struct target_desc *tdesc_mips64_msa_linux;
+
#ifdef __mips64
#define tdesc_mips_linux tdesc_mips64_linux
#define tdesc_mips_dsp_linux tdesc_mips64_dsp_linux
+#define tdesc_mips_fpu64_linux tdesc_mips64_linux
+#define tdesc_mips_fpu64_dsp_linux tdesc_mips64_dsp_linux
+#define tdesc_mips_msa_linux tdesc_mips64_msa_linux
#endif
#ifndef PTRACE_GET_THREAD_AREA
#define PTRACE_GET_THREAD_AREA 25
#endif
+#ifndef PTRACE_GETREGSET
+#define PTRACE_GETREGSET 0x4204
+#endif
+
+#ifndef PTRACE_SETREGSET
+#define PTRACE_SETREGSET 0x4205
+#endif
+
#ifdef HAVE_SYS_REG_H
#include <sys/reg.h>
#endif
@@ -64,6 +93,9 @@ extern const struct target_desc *tdesc_mips64_dsp_linux;
#define DSP_CONTROL 77
#endif
+#define ST0_FR (1 << 26)
+#define FIR_F64 (1 << 22)
+
union mips_register
{
unsigned char buf[8];
@@ -117,17 +149,68 @@ static unsigned char mips_dsp_regset_bitmap[(mips_dsp_num_regs + 7) / 8] = {
};
static int have_dsp = -1;
+static int have_fpu64 = -1;
+static int have_msa = -1;
-/* Try peeking at an arbitrarily chosen DSP register and pick the available
- user register set accordingly. */
+/* Try peeking at registers and pick the available user register set
+ accordingly. */
static const struct target_desc *
mips_read_description (void)
{
+ const struct target_desc *tdescs[2][2] =
+ {
+ /* have_fpu64 = 0 have_fpu64 = 1 */
+ { tdesc_mips_linux, tdesc_mips_fpu64_linux }, /* have_dsp = 0 */
+ { tdesc_mips_dsp_linux, tdesc_mips_fpu64_dsp_linux }, /* have_dsp = 1 */
+ };
+
+ if (have_fpu64 < 0)
+ {
+ int pid = lwpid_of (current_thread);
+ long fir;
+
+ /* Try peeking at FIR.F64 bit */
+ errno = 0;
+ fir = ptrace (PTRACE_PEEKUSER, pid, FPC_EIR, 0);
+ switch (errno)
+ {
+ case 0:
+ have_fpu64 = !!(fir & FIR_F64);
+ break;
+ case EIO:
+ have_fpu64 = 0;
+ have_msa = 0;
+ break;
+ default:
+ perror_with_name ("ptrace");
+ break;
+ }
+ }
+
+ /* Check for MSA, which requires FR=1 */
+ if (have_msa < 0)
+ {
+ int pid = lwpid_of (current_thread);
+ int res;
+ uint32_t regs[32*4 + 8];
+ struct iovec iov;
+
+ /* this'd probably be better */
+ //have_msa = (getauxval(AT_HWCAP) & 0x2) != 0;
+
+ /* Test MSAIR */
+ iov.iov_base = regs;
+ iov.iov_len = sizeof(regs);
+ res = ptrace (PTRACE_GETREGSET, pid, NT_MIPS_MSA, &iov);
+ have_msa = (res >= 0) && regs[32*4 + 0];
+ }
+
if (have_dsp < 0)
{
int pid = lwpid_of (current_thread);
+ /* Try peeking at an arbitrarily chosen DSP register */
errno = 0;
ptrace (PTRACE_PEEKUSER, pid, DSP_CONTROL, 0);
switch (errno)
@@ -144,7 +227,8 @@ mips_read_description (void)
}
}
- return have_dsp ? tdesc_mips_dsp_linux : tdesc_mips_linux;
+ return have_msa ? tdesc_mips_msa_linux
+ : tdescs[have_dsp][have_fpu64];
}
static void
@@ -770,22 +854,24 @@ static void
mips_fill_fpregset (struct regcache *regcache, void *buf)
{
union mips_register *regset = (union mips_register *) buf;
- int i, use_64bit, first_fp, big_endian;
+ int i, use_64bit, fp_use_64bit, first_fp, big_endian;
use_64bit = (register_size (regcache->tdesc, 0) == 8);
first_fp = find_regno (regcache->tdesc, "f0");
+ fp_use_64bit = (register_size (regcache->tdesc, first_fp) >= 8);
big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
/* See GDB for a discussion of this peculiar layout. */
for (i = 0; i < 32; i++)
- if (use_64bit)
+ if (fp_use_64bit)
collect_register (regcache, first_fp + i, regset[i].buf);
else
collect_register (regcache, first_fp + i,
regset[i & ~1].buf + 4 * (big_endian != (i & 1)));
mips_collect_register_32bit (regcache, use_64bit,
- find_regno (regcache->tdesc, "fcsr"), regset[32].buf);
+ find_regno (regcache->tdesc, "fcsr"),
+ regset[32].buf);
mips_collect_register_32bit (regcache, use_64bit,
find_regno (regcache->tdesc, "fir"),
regset[32].buf + 4);
@@ -795,15 +881,16 @@ static void
mips_store_fpregset (struct regcache *regcache, const void *buf)
{
const union mips_register *regset = (const union mips_register *) buf;
- int i, use_64bit, first_fp, big_endian;
+ int i, use_64bit, fp_use_64bit, first_fp, big_endian;
use_64bit = (register_size (regcache->tdesc, 0) == 8);
first_fp = find_regno (regcache->tdesc, "f0");
+ fp_use_64bit = (register_size (regcache->tdesc, first_fp) >= 8);
big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
/* See GDB for a discussion of this peculiar layout. */
for (i = 0; i < 32; i++)
- if (use_64bit)
+ if (fp_use_64bit)
supply_register (regcache, first_fp + i, regset[i].buf);
else
supply_register (regcache, first_fp + i,
@@ -818,12 +905,104 @@ mips_store_fpregset (struct regcache *regcache, const void *buf)
}
#endif /* HAVE_PTRACE_GETREGS */
+static void
+mips_fill_msa_regset (struct regcache *regcache, void *buf)
+{
+ unsigned char *bufp = buf;
+ int i, first_fp, fir, fcsr, msair, msacsr, config5;
+ unsigned char tmp[16];
+
+ if (!have_msa)
+ return;
+
+ first_fp = find_regno (regcache->tdesc, "f0");
+ fir = find_regno (regcache->tdesc, "fir");
+ fcsr = find_regno (regcache->tdesc, "fcsr");
+ msair = find_regno (regcache->tdesc, "msair");
+ msacsr = find_regno (regcache->tdesc, "msacsr");
+ config5 = find_regno (regcache->tdesc, "config5");
+
+ /* full vector including float */
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ for (i = 0; i < 32; i++)
+ {
+ collect_register (regcache, first_fp + i, tmp);
+ /* swap 64-bit halves, so it's a single word */
+ memcpy(bufp, tmp + 8, 8);
+ memcpy(bufp + 8, tmp, 8);
+ bufp += 16;
+ }
+ else
+ for (i = 0; i < 32; i++)
+ {
+ collect_register (regcache, first_fp + i, bufp);
+ bufp += 16;
+ }
+
+ collect_register (regcache, fir, bufp);
+ bufp += 4;
+ collect_register (regcache, fcsr, bufp);
+ bufp += 4;
+ collect_register (regcache, msair, bufp);
+ bufp += 4;
+ collect_register (regcache, msacsr, bufp);
+ bufp += 4;
+ collect_register (regcache, config5, bufp);
+}
+
+static void
+mips_store_msa_regset (struct regcache *regcache, const void *buf)
+{
+ const unsigned char *bufp = buf;
+ int i, first_fp, fir, fcsr, msair, msacsr, config5;
+ unsigned char tmp[16];
+
+ if (!have_msa)
+ return;
+
+ first_fp = find_regno (regcache->tdesc, "f0");
+ fir = find_regno (regcache->tdesc, "fir");
+ fcsr = find_regno (regcache->tdesc, "fcsr");
+ msair = find_regno (regcache->tdesc, "msair");
+ msacsr = find_regno (regcache->tdesc, "msacsr");
+ config5 = find_regno (regcache->tdesc, "config5");
+
+ /* full vector including float */
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ for (i = 0; i < 32; i++)
+ {
+ /* swap 64-bit halves, as it's a single word */
+ memcpy(tmp, bufp + 8, 8);
+ memcpy(tmp + 8, bufp, 8);
+ supply_register (regcache, first_fp + i, tmp);
+ bufp += 16;
+ }
+ else
+ for (i = 0; i < 32; i++)
+ {
+ supply_register (regcache, first_fp + i, bufp);
+ bufp += 16;
+ }
+
+ supply_register (regcache, fir, bufp);
+ bufp += 4;
+ supply_register (regcache, fcsr, bufp);
+ bufp += 4;
+ supply_register (regcache, msair, bufp);
+ bufp += 4;
+ supply_register (regcache, msacsr, bufp);
+ bufp += 4;
+ supply_register (regcache, config5, bufp);
+}
+
static struct regset_info mips_regsets[] = {
#ifdef HAVE_PTRACE_GETREGS
{ PTRACE_GETREGS, PTRACE_SETREGS, 0, 38 * 8, GENERAL_REGS,
mips_fill_gregset, mips_store_gregset },
{ PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS,
mips_fill_fpregset, mips_store_fpregset },
+ { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_MIPS_MSA, 34*16, EXTENDED_REGS,
+ mips_fill_msa_regset, mips_store_msa_regset },
#endif /* HAVE_PTRACE_GETREGS */
NULL_REGSET
};
@@ -903,8 +1082,12 @@ initialize_low_arch (void)
/* Initialize the Linux target descriptions. */
init_registers_mips_linux ();
init_registers_mips_dsp_linux ();
+ init_registers_mips_fpu64_linux ();
+ init_registers_mips_fpu64_dsp_linux ();
+ init_registers_mips_msa_linux ();
init_registers_mips64_linux ();
init_registers_mips64_dsp_linux ();
+ init_registers_mips64_msa_linux ();
initialize_regsets_info (&mips_regsets_info);
}