[rfc/rft] ppc gdbserver: autodetect AltiVec and SPE

Ulrich Weigand
2008-02-28T16:57:36Z

Daniel Jacobowitz wrote:
> > Agreed. I'll see if I can put a patch along those lines together.
> > However, I don't have any non-AltiVec or SPE systems available;
> > would you be able to test on those?
>
> Yes, definitely. We've got both.

That would be great, thanks!

What you do think about this patch? It uses the AT_HWCAP auxv entry
to check for AltiVec and SPE. One minor annoyance is that you need
to know the target wordsize in order to correctly interpret the auxv
array. Therefore, we have to do the 64/32 check first, and only then
can we read auxv.

AltiVec and SPE regset access is now enabled uncoditionally, but the
fill/store routines make sure the corresponding HWCAP is set to avoid
accessing registers that are not present in the select register map.

With SPE there were a number of additional issues:
- The ppc_regmap used conditional defines to disable access to
floating point registers; I'm now using two different regmaps
and switch them in the arch_setup routine.
- As the regmap was defined only for 32-bit host machines, I've
disabled SPE support on 64-bit hosts. I don't think there are
any 64-bit machines with SPE anyway, right?
- There was a conditional check for fpscr in ppc_cannot_store_register;
but as this routine is never called for fpscr in SPE mode, the check
seems superfluous and I just removed it.

Tested on powerpc-linux and powerpc64-linux (-m64 and -m32) with no
regressions; properly detects AltiVec support.

Bye,
Ulrich

ChangeLog:

* configure.srv [powerpc64-*-linux*]: Remove powerpc-e500.o from
srv_regobj. Remove rs6000/powerpc-e500.xml and rs6000/power-spe.xml
from reg_xmlfiles.
* linux-ppc-low.c: Include <elf.h>.
(PPC_FEATURE_HAS_ALTIVEC, PPC_FEATURE_HAS_SPE): Define.
(ppc_hwcap): New global variable.
(ppc_regmap): Remove __SPE__ #ifdef sections.
(ppc_regmap_e500): New global variable.
(ppc_cannot_store_register): Remove __SPE__ special case.
(ppc_get_hwcap): New function.
(ppc_arch_setup): Use it to determine whether inferior supports
AltiVec or SPE registers. Set the_low_target.regmap if appropriate.
(ppc_fill_vrregset, ppc_store_vrregset): Define unconditionally.
Do not access registers if target does not support AltiVec.
(ppc_fill_evrregset, ppc_store_evrregset): Define unconditionally.
Do not access registers if target does not support SPE.
(target_regsets): Unconditionally include AltiVec and SPE regsets.

diff -urNp gdb-orig/gdb/gdbserver/configure.srv gdb-head/gdb/gdbserver/configure.srv
--- gdb-orig/gdb/gdbserver/configure.srv 2008-02-28 06:56:04.000000000 +0100
+++ gdb-head/gdb/gdbserver/configure.srv 2008-02-28 17:17:47.488329501 +0100
@@ -106,15 +106,13 @@ case "${target}" in
srv_linux_usrregs=yes
srv_linux_thread_db=yes
;;
- powerpc64-*-linux*) srv_regobj="reg-ppc.o powerpc-32.o powerpc-e500.o"
+ powerpc64-*-linux*) srv_regobj="reg-ppc.o powerpc-32.o"
srv_regobj="${srv_regobj} reg-ppc64.o powerpc-64.o"
srv_tgtobj="linux-low.o linux-ppc-low.o"
srv_xmlfiles="rs6000/powerpc-32.xml"
srv_xmlfiles="${srv_xmlfiles} rs6000/power-altivec.xml"
srv_xmlfiles="${srv_xmlfiles} rs6000/power-core.xml"
srv_xmlfiles="${srv_xmlfiles} rs6000/power-fpu.xml"
- srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-e500.xml"
- srv_xmlfiles="${srv_xmlfiles} rs6000/power-spe.xml"
srv_xmlfiles="${srv_xmlfiles} rs6000/powerpc-64.xml"
srv_xmlfiles="${srv_xmlfiles} rs6000/power64-core.xml"
srv_linux_usrregs=yes
diff -urNp gdb-orig/gdb/gdbserver/linux-ppc-low.c gdb-head/gdb/gdbserver/linux-ppc-low.c
--- gdb-orig/gdb/gdbserver/linux-ppc-low.c 2008-02-28 06:56:04.000000000 +0100
+++ gdb-head/gdb/gdbserver/linux-ppc-low.c 2008-02-28 17:18:50.462849876 +0100
@@ -21,8 +21,16 @@
#include "server.h"
#include "linux-low.h"

+#include <elf.h>
#include <asm/ptrace.h>

+/* These are in <asm/cputable.h> in current kernels. */
+#define PPC_FEATURE_HAS_ALTIVEC 0x10000000
+#define PPC_FEATURE_HAS_SPE 0x00800000
+
+static unsigned long ppc_hwcap;
+
+
/* Defined in auto-generated file reg-ppc.c. */
void init_registers_ppc (void);
/* Defined in auto-generated file powerpc-32.c. */
@@ -69,16 +77,6 @@ static int ppc_regmap[] =
PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
-#ifdef __SPE__
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
- -1, -1, -1, -1,
-#else
PT_FPR0*4, PT_FPR0*4 + 8, PT_FPR0*4+16, PT_FPR0*4+24,
PT_FPR0*4+32, PT_FPR0*4+40, PT_FPR0*4+48, PT_FPR0*4+56,
PT_FPR0*4+64, PT_FPR0*4+72, PT_FPR0*4+80, PT_FPR0*4+88,
@@ -87,20 +85,36 @@ static int ppc_regmap[] =
PT_FPR0*4+160, PT_FPR0*4+168, PT_FPR0*4+176, PT_FPR0*4+184,
PT_FPR0*4+192, PT_FPR0*4+200, PT_FPR0*4+208, PT_FPR0*4+216,
PT_FPR0*4+224, PT_FPR0*4+232, PT_FPR0*4+240, PT_FPR0*4+248,
-#endif
PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
-#ifdef __SPE__
- PT_CTR * 4, PT_XER * 4, -1
-#else
PT_CTR * 4, PT_XER * 4, PT_FPSCR * 4
-#endif
+ };
+
+static int ppc_regmap_e500[] =
+ {PT_R0 * 4, PT_R1 * 4, PT_R2 * 4, PT_R3 * 4,
+ PT_R4 * 4, PT_R5 * 4, PT_R6 * 4, PT_R7 * 4,
+ PT_R8 * 4, PT_R9 * 4, PT_R10 * 4, PT_R11 * 4,
+ PT_R12 * 4, PT_R13 * 4, PT_R14 * 4, PT_R15 * 4,
+ PT_R16 * 4, PT_R17 * 4, PT_R18 * 4, PT_R19 * 4,
+ PT_R20 * 4, PT_R21 * 4, PT_R22 * 4, PT_R23 * 4,
+ PT_R24 * 4, PT_R25 * 4, PT_R26 * 4, PT_R27 * 4,
+ PT_R28 * 4, PT_R29 * 4, PT_R30 * 4, PT_R31 * 4,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ -1, -1, -1, -1,
+ PT_NIP * 4, PT_MSR * 4, PT_CCR * 4, PT_LNK * 4,
+ PT_CTR * 4, PT_XER * 4, -1
};
#endif

static int
ppc_cannot_store_register (int regno)
{
-#if !defined (__powerpc64__) && !defined (__SPE__)
+#ifndef __powerpc64__
/* Some kernels do not allow us to store fpscr. */
if (regno == find_regno ("fpscr"))
return 2;
@@ -167,6 +181,42 @@ ppc_set_pc (CORE_ADDR pc)
}
}

+
+static int
+ppc_get_hwcap (unsigned long *valp)
+{
+ int wordsize = register_size (0);
+ unsigned char *data = alloca (2 * wordsize);
+ int offset = 0;
+
+ while ((*the_target->read_auxv) (offset, data, 2 * wordsize) == 2 * wordsize)
+ {
+ if (wordsize == 4)
+ {
+ unsigned int *data_p = (unsigned int *)data;
+ if (data_p[0] == AT_HWCAP)
+ {
+ *valp = data_p[1];
+ return 1;
+ }
+ }
+ else
+ {
+ unsigned long *data_p = (unsigned long *)data;
+ if (data_p[0] == AT_HWCAP)
+ {
+ *valp = data_p[1];
+ return 1;
+ }
+ }
+
+ offset += 2 * wordsize;
+ }
+
+ *valp = 0;
+ return 0;
+}
+
static void
ppc_arch_setup (void)
{
@@ -174,28 +224,37 @@ ppc_arch_setup (void)
long msr;

/* On a 64-bit host, assume 64-bit inferior process. */
-#ifdef __ALTIVEC__
- init_registers_powerpc_64 ();
-#else
init_registers_ppc64 ();
-#endif

/* Only if the high bit of the MSR is set, we actually have
a 64-bit inferior. */
collect_register_by_name ("msr", &msr);
if (msr < 0)
- return;
+ {
+ ppc_get_hwcap (&ppc_hwcap);
+ if (ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC)
+ init_registers_powerpc_64 ();
+
+ return;
+ }
#endif

/* OK, we have a 32-bit inferior. */
-#ifdef __ALTIVEC__
- init_registers_powerpc_32 ();
-#else
-#ifdef __SPE__
- init_registers_powerpc_e500 ();
-#else
init_registers_ppc ();
-#endif
+
+ ppc_get_hwcap (&ppc_hwcap);
+ if (ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC)
+ init_registers_powerpc_32 ();
+
+ /* On 32-bit machines, check for SPE registers.
+ Set the low target's regmap field as appropriately. */
+#ifndef __powerpc64__
+ the_low_target.regmap = ppc_regmap;
+ if (ppc_hwcap & PPC_FEATURE_HAS_SPE)
+ {
+ init_registers_powerpc_e500 ();
+ the_low_target.regmap = ppc_regmap_e500;
+ }
#endif
}

@@ -232,8 +291,6 @@ static void ppc_fill_gregset (void *buf)
ppc_collect_ptrace_register (i, (char *) buf + ppc_regmap[i]);
}

-#ifdef __ALTIVEC__
-
#ifndef PTRACE_GETVRREGS
#define PTRACE_GETVRREGS 18
#define PTRACE_SETVRREGS 19
@@ -247,6 +304,9 @@ ppc_fill_vrregset (void *buf)
int i, base;
char *regset = buf;

+ if (!(ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC))
+ return;
+
base = find_regno ("vr0");
for (i = 0; i < 32; i++)
collect_register (base + i, &regset[i * 16]);
@@ -261,6 +321,9 @@ ppc_store_vrregset (const void *buf)
int i, base;
const char *regset = buf;

+ if (!(ppc_hwcap & PPC_FEATURE_HAS_ALTIVEC))
+ return;
+
base = find_regno ("vr0");
for (i = 0; i < 32; i++)
supply_register (base + i, &regset[i * 16]);
@@ -269,10 +332,6 @@ ppc_store_vrregset (const void *buf)
supply_register_by_name ("vrsave", &regset[33 * 16]);
}

-#endif /* __ALTIVEC__ */
-
-#ifdef __SPE__
-
#ifndef PTRACE_GETEVRREGS
#define PTRACE_GETEVRREGS 20
#define PTRACE_SETEVRREGS 21
@@ -291,6 +350,9 @@ ppc_fill_evrregset (void *buf)
int i, ev0;
struct gdb_evrregset_t *regset = buf;

+ if (!(ppc_hwcap & PPC_FEATURE_HAS_SPE))
+ return;
+
ev0 = find_regno ("ev0h");
for (i = 0; i < 32; i++)
collect_register (ev0 + i, &regset->evr[i]);
@@ -305,6 +367,9 @@ ppc_store_evrregset (const void *buf)
int i, ev0;
const struct gdb_evrregset_t *regset = buf;

+ if (!(ppc_hwcap & PPC_FEATURE_HAS_SPE))
+ return;
+
ev0 = find_regno ("ev0h");
for (i = 0; i < 32; i++)
supply_register (ev0 + i, &regset->evr[i]);
@@ -312,21 +377,16 @@ ppc_store_evrregset (const void *buf)
supply_register_by_name ("acc", &regset->acc);
supply_register_by_name ("spefscr", &regset->spefscr);
}
-#endif /* __SPE__ */

struct regset_info target_regsets[] = {
/* List the extra register sets before GENERAL_REGS. That way we will
fetch them every time, but still fall back to PTRACE_PEEKUSER for the
general registers. Some kernels support these, but not the newer
PPC_PTRACE_GETREGS. */
-#ifdef __ALTIVEC__
{ PTRACE_GETVRREGS, PTRACE_SETVRREGS, SIZEOF_VRREGS, EXTENDED_REGS,
ppc_fill_vrregset, ppc_store_vrregset },
-#endif
-#ifdef __SPE__
{ PTRACE_GETEVRREGS, PTRACE_SETEVRREGS, 32 * 4 + 8 + 4, EXTENDED_REGS,
ppc_fill_evrregset, ppc_store_evrregset },
-#endif
{ 0, 0, 0, GENERAL_REGS, ppc_fill_gregset, NULL },
{ 0, 0, -1, -1, NULL, NULL }
};

--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com