Re: [csl-arm] VFP and iWMMXt support

Daniel Jacobowitz
2005-03-30T15:01:54Z

On Tue, Mar 29, 2005 at 03:51:51AM +0100, Paul Brook wrote:
> I've applied the attached patch to the csl-arm-20050325-branch.
>
> It adds support for the iWMMXt and VFP Arm coprocessors.
> This includes a new extension to the remote protocol to handle optional
> register sets. The extension may need some more work before it's ready for
> mainline.
>
> Tested with cross to arm-none-eabi.

This patch, also applied to the branch, adds native support for iWMMXt
registers on arm-linux. It requires a brand new kernel. I didn't do
VFP, because no one's implemented the necessary kernel primitives yet.

The principle is the same as in the previous patch. Before this moves
to mainline I intend to finish converting Linux to inf-ptrace, and then
NATIVE_XFER_AVAILABLE_REGISTERS will not be necessary.

--
Daniel Jacobowitz
CodeSourcery, LLC

2005-03-30 Daniel Jacobowitz <dan@codesourcery.com>

* Makefile.in (arm-linux-nat.o): Update dependencies.
* arm-linux-nat.c: Include "gdb_assert.h".
(PTRACE_GETWMMXREGS, PTRACE_SETWMMXREGS): Define.
(arm_linux_has_wmmx_registers): New flag.
(GET_THREAD_ID): Remove stray punctuation.
(IWMMXT_REGS_SIZE): Define.
(fetch_wmmx_regs, store_wmmx_regs): New functions.
(fetch_inferior_registers, store_inferior_registers): Call them.
(arm_linux_available_registers): New function.
* inftarg.c (child_xfer_partial): Handle
TARGET_OBJECT_AVAILABLE_REGISTERS.
* config/arm/nm-linux.h (arm_linux_available_registers): Add
prototype.
(NATIVE_XFER_AVAILABLE_REGISTERS): Define.

2005-03-30 Daniel Jacobowitz <dan@codesourcery.com>

* linux-arm-low.c (arm_fill_wmmxregset, arm_store_wmmxregset):
Remove stray text.
(arm_available_registers): Remove debugging output. Use hex.
* regcache.c (num_registers): Make global.
* server.c (handle_p_packet, handle_P_packet): Check the value
of regnum.

Index: gdb/Makefile.in
===================================================================
RCS file: /cvs/src/src/gdb/Makefile.in,v
retrieving revision 1.707.2.4
diff -u -p -r1.707.2.4 Makefile.in
--- gdb/Makefile.in 29 Mar 2005 02:52:04 -0000 1.707.2.4
+++ gdb/Makefile.in 30 Mar 2005 14:53:20 -0000
@@ -1732,7 +1732,7 @@ arch-utils.o: arch-utils.c $(defs_h) $(a
$(floatformat_h)
arm-linux-nat.o: arm-linux-nat.c $(defs_h) $(inferior_h) $(gdbcore_h) \
$(gdb_string_h) $(regcache_h) $(arm_tdep_h) $(gregset_h)\
- $(gdb_proc_service_h)
+ $(gdb_proc_service_h) $(gdb_assert_h)
arm-linux-tdep.o: arm-linux-tdep.c $(defs_h) $(target_h) $(value_h) \
$(gdbtypes_h) $(floatformat_h) $(gdbcore_h) $(frame_h) $(regcache_h) \
$(doublest_h) $(solib_svr4_h) $(osabi_h) $(arm_tdep_h) \
Index: gdb/arm-linux-nat.c
===================================================================
RCS file: /cvs/src/src/gdb/arm-linux-nat.c,v
retrieving revision 1.22.2.3
diff -u -p -r1.22.2.3 arm-linux-nat.c
--- gdb/arm-linux-nat.c 29 Mar 2005 02:52:04 -0000 1.22.2.3
+++ gdb/arm-linux-nat.c 30 Mar 2005 14:53:20 -0000
@@ -23,6 +23,7 @@
#include "gdbcore.h"
#include "gdb_string.h"
#include "regcache.h"
+#include "gdb_assert.h"

#include "arm-tdep.h"

@@ -41,6 +42,13 @@
#define PTRACE_GET_THREAD_AREA 22
#endif

+#ifndef PTRACE_GETWMMXREGS
+#define PTRACE_GETWMMXREGS 18
+#define PTRACE_SETWMMXREGS 19
+#endif
+
+static int arm_linux_has_wmmx_registers = 1;
+
extern int arm_apcs_32;

#define typeNone 0x00
@@ -99,7 +107,7 @@ get_thread_id (ptid_t ptid)
tid = PIDGET (ptid);
return tid;
}
-#define GET_THREAD_ID(PTID) get_thread_id ((PTID));
+#define GET_THREAD_ID(PTID) get_thread_id (PTID)

static void
fetch_nwfpe_single (unsigned int fn, FPA11 * fpa11)
@@ -550,6 +558,97 @@ store_regs (void)
}
}

+/* Fetch all WMMX registers of the process and store into
+ regcache. */
+
+#define IWMMXT_REGS_SIZE (16 * 8 + 6 * 4)
+
+static void
+fetch_wmmx_regs (void)
+{
+ char regbuf[IWMMXT_REGS_SIZE];
+ int ret, regno, tid, first;
+
+ /* Get the thread id for the ptrace call. */
+ tid = GET_THREAD_ID (inferior_ptid);
+
+ ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf);
+ if (ret < 0)
+ {
+ warning (_("Unable to fetch WMMX registers."));
+ return;
+ }
+
+ first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum;
+
+ for (regno = 0; regno < NUM_IWMMXT_COP0REGS; regno++)
+ regcache_raw_supply (current_regcache, first + regno,
+ &regbuf[regno * 8]);
+
+ first += NUM_IWMMXT_COP0REGS;
+
+ for (regno = 0; regno < 2; regno++)
+ regcache_raw_supply (current_regcache, first + regno, NULL);
+
+ for (regno = 2; regno < 4; regno++)
+ regcache_raw_supply (current_regcache, first + regno,
+ &regbuf[16 * 8 + (regno - 2) * 4]);
+
+ for (regno = 4; regno < 8; regno++)
+ regcache_raw_supply (current_regcache, first + regno, NULL);
+
+ for (regno = 8; regno < 12; regno++)
+ regcache_raw_supply (current_regcache, first + regno,
+ &regbuf[16 * 8 + 2 * 4 + (regno - 8) * 4]);
+
+ for (regno = 12; regno < 16; regno++)
+ regcache_raw_supply (current_regcache, first + regno, NULL);
+}
+
+static void
+store_wmmx_regs (void)
+{
+ char regbuf[IWMMXT_REGS_SIZE];
+ int ret, regno, tid, first;
+
+ /* Get the thread id for the ptrace call. */
+ tid = GET_THREAD_ID (inferior_ptid);
+
+ ret = ptrace (PTRACE_GETWMMXREGS, tid, 0, regbuf);
+ if (ret < 0)
+ {
+ warning (_("Unable to fetch WMMX registers."));
+ return;
+ }
+
+ first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum;
+
+ for (regno = 0; regno < NUM_IWMMXT_COP0REGS; regno++)
+ if (register_cached (first + regno))
+ regcache_raw_collect (current_regcache, first + regno,
+ &regbuf[regno * 8]);
+
+ first += 18;
+ for (regno = 0; regno < 2; regno++)
+ if (register_cached (first + regno))
+ regcache_raw_collect (current_regcache, first + regno,
+ &regbuf[16 * 8 + regno * 4]);
+
+ first += 6;
+ for (regno = 0; regno < 4; regno++)
+ if (register_cached (first + regno))
+ regcache_raw_collect (current_regcache, first + regno,
+ &regbuf[16 * 8 + 2 * 4 + regno * 4]);
+
+ ret = ptrace (PTRACE_SETWMMXREGS, tid, 0, regbuf);
+
+ if (ret < 0)
+ {
+ warning (_("Unable to store WMMX registers."));
+ return;
+ }
+}
+
/* Fetch registers from the child process. Fetch all registers if
regno == -1, otherwise fetch all general registers or all floating
point registers depending upon the value of regno. */
@@ -561,14 +660,21 @@ fetch_inferior_registers (int regno)
{
fetch_regs ();
fetch_fpa_regs ();
+ if (arm_linux_has_wmmx_registers)
+ fetch_wmmx_regs ();
}
else
{
- if (regno < ARM_F0_REGNUM || regno > ARM_FPS_REGNUM)
+ if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM)
fetch_register (regno);
-
- if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM)
+ else if (regno >= ARM_F0_REGNUM && regno <= ARM_FPS_REGNUM)
fetch_fpa_register (regno);
+ else if (arm_linux_has_wmmx_registers)
+ {
+ int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum;
+ if (regno >= first && regno < first + NUM_IWMMXT_REGS)
+ fetch_wmmx_regs ();
+ }
}
}

@@ -583,14 +689,21 @@ store_inferior_registers (int regno)
{
store_regs ();
store_fpa_regs ();
+ if (arm_linux_has_wmmx_registers)
+ store_wmmx_regs ();
}
else
{
- if ((regno < ARM_F0_REGNUM) || (regno > ARM_FPS_REGNUM))
+ if (regno < ARM_F0_REGNUM || regno == ARM_PS_REGNUM)
store_register (regno);
-
- if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM))
- store_fpa_register (regno);
+ else if ((regno >= ARM_F0_REGNUM) && (regno <= ARM_FPS_REGNUM))
+ store_fpa_register (regno);
+ else if (arm_linux_has_wmmx_registers)
+ {
+ int first = gdbarch_tdep (current_gdbarch)->first_iwmmxt_regnum;
+ if (regno >= first && regno < first + NUM_IWMMXT_REGS)
+ store_wmmx_regs ();
+ }
}
}

@@ -740,6 +853,50 @@ get_linux_version (unsigned int *vmajor,
return ((*vmajor << 16) | (*vminor << 8) | *vrelease);
}

+LONGEST
+arm_linux_available_registers (struct target_ops *ops,
+ int /* enum target_object */ object,
+ const char *annex,
+ void *readbuf,
+ const void *writebuf,
+ ULONGEST offset,
+ LONGEST len)
+{
+ char *result = NULL;
+ int total_len;
+
+ gdb_assert (object == TARGET_OBJECT_AVAILABLE_REGISTERS);
+ gdb_assert (readbuf && !writebuf);
+
+ if (arm_linux_has_wmmx_registers)
+ {
+ int ret;
+ char regbuf[IWMMXT_REGS_SIZE];
+
+ ret = ptrace (PTRACE_GETWMMXREGS, GET_THREAD_ID (inferior_ptid), 0,
+ regbuf);
+ if (ret < 0)
+ /* Should we be checking the error code? */
+ arm_linux_has_wmmx_registers = 0;
+ }
+
+ if (arm_linux_has_wmmx_registers)
+ result = "iwmmxt";
+
+ if (result == NULL)
+ return 0;
+
+ total_len = strlen (result);
+ if (total_len > offset)
+ {
+ int bytes_read = min (total_len - offset, len);
+ memcpy (readbuf, result + offset, bytes_read);
+ return bytes_read;
+ }
+
+ return 0;
+}
+
void
_initialize_arm_linux_nat (void)
{
Index: gdb/inftarg.c
===================================================================
RCS file: /cvs/src/src/gdb/inftarg.c,v
retrieving revision 1.41
diff -u -p -r1.41 inftarg.c
--- gdb/inftarg.c 12 Feb 2005 00:39:19 -0000 1.41
+++ gdb/inftarg.c 30 Mar 2005 14:53:20 -0000
@@ -559,6 +559,13 @@ child_xfer_partial (struct target_ops *o
return NATIVE_XFER_AUXV (ops, object, annex, readbuf, writebuf,
offset, len);

+ case TARGET_OBJECT_AVAILABLE_REGISTERS:
+#ifndef NATIVE_XFER_AVAILABLE_REGISTERS
+#define NATIVE_XFER_AVAILABLE_REGISTERS(OPS,OBJECT,ANNEX,WRITEBUF,READBUF,OFFSET,LEN) (-1)
+#endif
+ return NATIVE_XFER_AVAILABLE_REGISTERS (ops, object, annex, readbuf,
+ writebuf, offset, len);
+
default:
return -1;
}
Index: gdb/config/arm/nm-linux.h
===================================================================
RCS file: /cvs/src/src/gdb/config/arm/nm-linux.h,v
retrieving revision 1.10
diff -u -p -r1.10 nm-linux.h
--- gdb/config/arm/nm-linux.h 29 Jul 2004 20:22:49 -0000 1.10
+++ gdb/config/arm/nm-linux.h 30 Mar 2005 14:53:20 -0000
@@ -38,4 +38,20 @@ extern int kernel_u_size (void);
/* Override copies of {fetch,store}_inferior_registers in infptrace.c. */
#define FETCH_INFERIOR_REGISTERS

+/* This function is called like a to_xfer_partial hook,
+ but must be called with TARGET_OBJECT_AVAILABLE_REGISTERS. */
+
+struct target_ops;
+
+extern LONGEST arm_linux_available_registers
+ (struct target_ops *ops,
+ int /* enum target_object */ object,
+ const char *annex,
+ void *readbuf,
+ const void *writebuf,
+ ULONGEST offset,
+ LONGEST len);
+
+#define NATIVE_XFER_AVAILABLE_REGISTERS arm_linux_available_registers
+
#endif /* NM_ARMLINUX_H */
Index: gdb/gdbserver/linux-arm-low.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/linux-arm-low.c,v
retrieving revision 1.7.14.2
diff -u -p -r1.7.14.2 linux-arm-low.c
--- gdb/gdbserver/linux-arm-low.c 29 Mar 2005 02:52:06 -0000 1.7.14.2
+++ gdb/gdbserver/linux-arm-low.c 30 Mar 2005 14:53:20 -0000
@@ -155,11 +155,6 @@ arm_fill_wmmxregset (void *buf)

for (i = 0; i < 4; i++)
collect_register (arm_num_regs + i + 16 + 8, ((char *) buf) + 16 * 8 + 8 + i * 4);
-
- ((int*)buf)[0],
- ((int*)buf)[1],
- ((int*)buf)[2],
- ((int*)buf)[3]);
}

static void
@@ -167,10 +162,6 @@ arm_store_wmmxregset (const void *buf)
{
int i;

- ((int*)buf)[0],
- ((int*)buf)[1],
- ((int*)buf)[2],
- ((int*)buf)[3]);
for (i = 0; i < 16; i++)
supply_register (arm_num_regs + i, ((char *) buf) + i * 8);

@@ -186,11 +177,10 @@ arm_available_registers (void)
{
char buf[64];

- printf ("use_regsets %d target_regsets %d\n", use_regsets_p, target_regsets[1].size);
if (use_regsets_p && target_regsets[1].size > 0)
{
int wr0 = find_regno ("wr0");
- sprintf (buf, "iwmmxt:%d", wr0);
+ sprintf (buf, "iwmmxt:%x", wr0);
return strdup (buf);
}

Index: gdb/gdbserver/regcache.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/regcache.c,v
retrieving revision 1.7.18.1
diff -u -p -r1.7.18.1 regcache.c
--- gdb/gdbserver/regcache.c 29 Mar 2005 02:52:06 -0000 1.7.18.1
+++ gdb/gdbserver/regcache.c 30 Mar 2005 14:53:20 -0000
@@ -38,7 +38,7 @@ struct inferior_regcache_data
static int register_bytes, g_register_bytes;

static struct reg *reg_defs;
-static int num_registers;
+int num_registers;

const char **gdbserver_expedite_regs;

Index: gdb/gdbserver/server.c
===================================================================
RCS file: /cvs/src/src/gdb/gdbserver/server.c,v
retrieving revision 1.23.2.1
diff -u -p -r1.23.2.1 server.c
--- gdb/gdbserver/server.c 29 Mar 2005 02:52:06 -0000 1.23.2.1
+++ gdb/gdbserver/server.c 30 Mar 2005 14:53:21 -0000
@@ -295,6 +295,8 @@ handle_v_requests (char *own_buf, char *
return;
}

+extern int num_registers;
+
/* Handle a register fetch ('p') request. */
void
handle_p_packet (char *own_buf)
@@ -302,7 +304,7 @@ handle_p_packet (char *own_buf)
char *end = own_buf + 1;
int regnum = strtol (own_buf + 1, &end, 16);

- if (*end)
+ if (*end || regnum < 0 || regnum >= num_registers)
{
write_enn (own_buf);
return;
@@ -318,7 +320,7 @@ handle_P_packet (char *own_buf)
char *end = own_buf + 1;
int regnum = strtol (own_buf + 1, &end, 16);

- if (*end != '=')
+ if (*end != '=' || regnum < 0 || regnum >= num_registers)
{
write_enn (own_buf);
return;