diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 354a6361b7..8f7810b0bc 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -679,6 +679,7 @@ ALL_TARGET_OBS = \ arch/arm-get-next-pcs.o \ arch/arm-linux.o \ arch/i386.o \ + arch/ppc-fbsd-common.o \ arch/ppc-linux-common.o \ arm-bsd-tdep.o \ arm-fbsd-tdep.o \ @@ -1414,6 +1415,7 @@ HFILES_NO_SRCDIR = \ arch/aarch64-insn.h \ arch/arm.h \ arch/i386.h \ + arch/ppc-fbsd-common.h \ arch/ppc-linux-common.h \ arch/ppc-linux-tdesc.h \ cli/cli-cmds.h \ diff --git a/gdb/arch/ppc-fbsd-common.c b/gdb/arch/ppc-fbsd-common.c new file mode 100644 index 0000000000..2d4fea631b --- /dev/null +++ b/gdb/arch/ppc-fbsd-common.c @@ -0,0 +1,59 @@ +/* Common target dependent code for FreeBSD on PPC systems. + + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include "common-defs.h" +#include "arch/ppc-fbsd-common.h" + +extern struct target_desc *tdesc_powerpc_32; +extern struct target_desc *tdesc_powerpc_altivec32; +extern struct target_desc *tdesc_powerpc_vsx32; +extern struct target_desc *tdesc_powerpc_64; +extern struct target_desc *tdesc_powerpc_altivec64; +extern struct target_desc *tdesc_powerpc_vsx64; + +const struct target_desc * +ppc_fbsd_match_description (struct ppc_fbsd_features features) +{ + struct target_desc *tdesc = NULL; + + if (features.wordsize == 8) + { + if (features.vsx) + tdesc = tdesc_powerpc_vsx64; + else if (features.altivec) + tdesc = tdesc_powerpc_altivec64; + else + tdesc = tdesc_powerpc_64; + } + else + { + gdb_assert (features.wordsize == 4); + + if (features.vsx) + tdesc = tdesc_powerpc_vsx32; + else if (features.altivec) + tdesc = tdesc_powerpc_altivec32; + else + tdesc = tdesc_powerpc_32; + } + + gdb_assert (tdesc != NULL); + + return tdesc; +} diff --git a/gdb/arch/ppc-fbsd-common.h b/gdb/arch/ppc-fbsd-common.h new file mode 100644 index 0000000000..0a1b199307 --- /dev/null +++ b/gdb/arch/ppc-fbsd-common.h @@ -0,0 +1,86 @@ +/* Common target dependent code for FreeBSD on PPC systems. + + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of GDB. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef ARCH_PPC_FBSD_COMMON_H +#define ARCH_PPC_FBSD_COMMON_H + +struct target_desc; + +#define PPC_FBSD_SIZEOF_GREGSET_32 148 +#define PPC_FBSD_SIZEOF_GREGSET_64 296 +#define PPC_FBSD_SIZEOF_FPREGSET 264 + +/* PT_GETVRREGS returns data as defined in machine/pcb.h: + 32 128-bit registers + 8 spare bytes + VRSAVE (4 bytes) + VSCR (4 bytes). */ +#define PPC_FBSD_SIZEOF_VRREGSET (32*16 + 8 + 4 + 4) + +/* This is the layout of the POWER7 VSX registers and the way they overlap + with the existing FPR and VMX registers. + + VSR doubleword 0 VSR doubleword 1 + ---------------------------------------------------------------- + VSR[0] | FPR[0] | | + ---------------------------------------------------------------- + VSR[1] | FPR[1] | | + ---------------------------------------------------------------- + | ... | | + | ... | | + ---------------------------------------------------------------- + VSR[30] | FPR[30] | | + ---------------------------------------------------------------- + VSR[31] | FPR[31] | | + ---------------------------------------------------------------- + VSR[32] | VR[0] | + ---------------------------------------------------------------- + VSR[33] | VR[1] | + ---------------------------------------------------------------- + | ... | + | ... | + ---------------------------------------------------------------- + VSR[62] | VR[30] | + ---------------------------------------------------------------- + VSR[63] | VR[31] | + ---------------------------------------------------------------- + + VSX has 64 128bit registers. The first 32 registers overlap with + the FP registers (doubleword 0) and hence extend them with additional + 64 bits (doubleword 1). The other 32 regs overlap with the VMX + registers. */ +#define PPC_FBSD_SIZEOF_VSXREGSET (32*8) + +/* Features used to determine the target description. */ +struct ppc_fbsd_features +{ + unsigned int wordsize; + bool altivec; + bool vsx; +}; + +/* Base value for ppc_fbsd_features variables. */ +const struct ppc_fbsd_features ppc_fbsd_no_features = { + 0, + false, + false +}; + +/* Return a target description that matches FEATURES. */ +const struct target_desc * +ppc_fbsd_match_description (struct ppc_fbsd_features features); + +#endif /* ARCH_PPC_FBSD_COMMON_H */ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index f197160896..dc617a1ab4 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -465,7 +465,8 @@ powerpc*-*-freebsd*) # Target: FreeBSD/powerpc gdb_target_obs="rs6000-tdep.o ppc-sysv-tdep.o ppc64-tdep.o \ ppc-fbsd-tdep.o \ - ravenscar-thread.o ppc-ravenscar-thread.o" + ravenscar-thread.o ppc-ravenscar-thread.o \ + arch/ppc-fbsd-common.o" ;; powerpc-*-netbsd* | powerpc-*-knetbsd*-gnu) diff --git a/gdb/ppc-fbsd-nat.c b/gdb/ppc-fbsd-nat.c index 44661e9a24..d81b62139a 100644 --- a/gdb/ppc-fbsd-nat.c +++ b/gdb/ppc-fbsd-nat.c @@ -21,6 +21,7 @@ #include "gdbcore.h" #include "inferior.h" #include "regcache.h" +#include "regset.h" #include #include @@ -37,10 +38,37 @@ #include "inf-ptrace.h" #include "bsd-kvm.h" +#include "arch/ppc-fbsd-common.h" + +/* These definitions should really come from machine/ptrace.h, + but we provide them in case gdb is built on an machine whose + FreeBSD version still doesn't have them. */ + +#ifndef PT_FIRSTMACH +#define PT_FIRSTMACH 64 +#endif + +/* PTRACE requests for Altivec registers. */ +#ifndef PT_GETVRREGS +#define PT_GETVRREGS (PT_FIRSTMACH + 0) +#define PT_SETVRREGS (PT_FIRSTMACH + 1) +#endif + +/* PTRACE requests for POWER7 VSX registers. */ +#ifndef PT_GETVSRREGS +#define PT_GETVSRREGS (PT_FIRSTMACH + 2) +#define PT_SETVSRREGS (PT_FIRSTMACH + 3) +#endif + +typedef char gdb_vrregset_t[PPC_FBSD_SIZEOF_VRREGSET]; +typedef char gdb_vsxregset_t[PPC_FBSD_SIZEOF_VSXREGSET]; + struct ppc_fbsd_nat_target final : public fbsd_nat_target { void fetch_registers (struct regcache *, int) override; void store_registers (struct regcache *, int) override; + + const struct target_desc *read_description () override; }; static ppc_fbsd_nat_target the_ppc_fbsd_nat_target; @@ -120,6 +148,109 @@ getfpregs_supplies (struct gdbarch *gdbarch, int regno) || regno == tdep->ppc_fpscr_regnum); } +/* The kernel ptrace interface for AltiVec registers uses the + registers set mechanism, as opposed to the interface for all the + other registers, that stores/fetches each register individually. */ +static void +fetch_altivec_registers (struct regcache *regcache, int tid, + int regno) +{ + int ret; + gdb_vrregset_t regs; + const struct regset *vrregset = ppc_fbsd_vrregset (); + + ret = ptrace (PT_GETVRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to fetch AltiVec registers")); + + vrregset->supply_regset (vrregset, regcache, regno, ®s, + PPC_FBSD_SIZEOF_VRREGSET); +} + +/* The kernel ptrace interface for POWER7 VSX registers uses the + registers set mechanism, as opposed to the interface for all the + other registers, that stores/fetches each register individually. */ +static void +fetch_vsx_registers (struct regcache *regcache, int tid, int regno) +{ + int ret; + gdb_vsxregset_t regs; + const struct regset *vsxregset = ppc_fbsd_vsxregset (); + + ret = ptrace (PT_GETVSRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to fetch VSX registers")); + + vsxregset->supply_regset (vsxregset, regcache, regno, ®s, + PPC_FBSD_SIZEOF_VSXREGSET); +} + +static void +store_altivec_registers (const struct regcache *regcache, int tid, + int regno) +{ + int ret; + gdb_vrregset_t regs; + const struct regset *vrregset = ppc_fbsd_vrregset (); + + ret = ptrace (PT_GETVRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to fetch AltiVec registers")); + + vrregset->collect_regset (vrregset, regcache, regno, ®s, + PPC_FBSD_SIZEOF_VRREGSET); + + ret = ptrace (PT_SETVRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to store AltiVec registers")); +} + +static void +store_vsx_registers (const struct regcache *regcache, int tid, int regno) +{ + int ret; + gdb_vsxregset_t regs; + const struct regset *vsxregset = ppc_fbsd_vsxregset (); + + ret = ptrace (PT_GETVSRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to fetch VSX registers")); + + vsxregset->collect_regset (vsxregset, regcache, regno, ®s, + PPC_FBSD_SIZEOF_VSXREGSET); + + ret = ptrace (PT_SETVSRREGS, tid, (PTRACE_TYPE_ARG3) ®s, 0); + if (ret < 0) + perror_with_name (_("Unable to store VSX registers")); +} + +static int +ppc_fbsd_target_wordsize (int tid) +{ + int wordsize = 4; + +#ifdef __powerpc64__ + /* Check for 64-bit inferior process. This is the case when the host is + 64-bit, and PT_GETREGS returns less data than the length of gdb_gregset_t. */ + + gdb_gregset_t regs0; + gdb_gregset_t regs1; + + /* Initialize regs0 with 00's and regs1 with ff's. If, after ptrace fills + them, they have the same contents, it means ptrace returned data for a + 64-bit inferior. */ + memset (®s0, 0, sizeof (regs0)); + memset (®s1, 0xff, sizeof (regs1)); + + if (ptrace (PT_GETREGS, tid, (PTRACE_TYPE_ARG3) ®s0, 0) >= 0 + && ptrace (PT_GETREGS, tid, (PTRACE_TYPE_ARG3) ®s1, 0) >= 0) + if (memcmp (®s0, ®s1, sizeof (regs0)) == 0) + wordsize = 8; +#endif + + return wordsize; +} + /* Fetch register REGNO from the child process. If REGNO is -1, do it for all registers. */ @@ -128,13 +259,15 @@ ppc_fbsd_nat_target::fetch_registers (struct regcache *regcache, int regno) { gdb_gregset_t regs; pid_t pid = ptid_get_lwp (regcache->ptid ()); + struct gdbarch *gdbarch = regcache->arch (); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (ptrace (PT_GETREGS, pid, (PTRACE_TYPE_ARG3) ®s, 0) == -1) perror_with_name (_("Couldn't get registers")); supply_gregset (regcache, ®s); - if (regno == -1 || getfpregs_supplies (regcache->arch (), regno)) + if (regno == -1 || getfpregs_supplies (gdbarch, regno)) { const struct regset *fpregset = ppc_fbsd_fpregset (); gdb_fpregset_t fpregs; @@ -142,8 +275,16 @@ ppc_fbsd_nat_target::fetch_registers (struct regcache *regcache, int regno) if (ptrace (PT_GETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1) perror_with_name (_("Couldn't get FP registers")); - ppc_supply_fpregset (fpregset, regcache, regno, &fpregs, sizeof fpregs); + ppc_supply_fpregset (fpregset, regcache, regno, &fpregs, sizeof (fpregs)); } + + if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1) + if (regno == -1 || altivec_register_p (gdbarch, regno)) + fetch_altivec_registers (regcache, pid, regno); + + if (tdep->ppc_vsr0_upper_regnum != -1) + if (regno == -1 || vsx_register_p (gdbarch, regno)) + fetch_vsx_registers (regcache, pid, regno); } /* Store register REGNO back into the child process. If REGNO is -1, @@ -154,6 +295,8 @@ ppc_fbsd_nat_target::store_registers (struct regcache *regcache, int regno) { gdb_gregset_t regs; pid_t pid = ptid_get_lwp (regcache->ptid ()); + struct gdbarch *gdbarch = regcache->arch (); + struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); if (ptrace (PT_GETREGS, pid, (PTRACE_TYPE_ARG3) ®s, 0) == -1) perror_with_name (_("Couldn't get registers")); @@ -163,7 +306,7 @@ ppc_fbsd_nat_target::store_registers (struct regcache *regcache, int regno) if (ptrace (PT_SETREGS, pid, (PTRACE_TYPE_ARG3) ®s, 0) == -1) perror_with_name (_("Couldn't write registers")); - if (regno == -1 || getfpregs_supplies (regcache->arch (), regno)) + if (regno == -1 || getfpregs_supplies (gdbarch, regno)) { gdb_fpregset_t fpregs; @@ -175,6 +318,46 @@ ppc_fbsd_nat_target::store_registers (struct regcache *regcache, int regno) if (ptrace (PT_SETFPREGS, pid, (PTRACE_TYPE_ARG3) &fpregs, 0) == -1) perror_with_name (_("Couldn't set FP registers")); } + + if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1) + if (regno == -1 || altivec_register_p (gdbarch, regno)) + store_altivec_registers (regcache, pid, regno); + + if (tdep->ppc_vsr0_upper_regnum != -1) + if (regno == -1 || vsx_register_p (gdbarch, regno)) + store_vsx_registers (regcache, pid, regno); +} + +const struct target_desc * +ppc_fbsd_nat_target::read_description () +{ + int tid = ptid_get_lwp (inferior_ptid); + + struct ppc_fbsd_features features = ppc_fbsd_no_features; + + features.wordsize = ppc_fbsd_target_wordsize (tid); + + gdb_vsxregset_t vsxregset; + + if (ptrace (PT_GETVSRREGS, tid, (PTRACE_TYPE_ARG3) &vsxregset, 0) >= 0) + features.vsx = true; + + /* EINVAL means that the PT_GETVSRREGS request isn't supported. + Anything else needs to be reported. */ + else if (errno != EINVAL) + perror_with_name (_("Unable to fetch VSX registers")); + + gdb_vrregset_t vrregset; + + if (ptrace (PT_GETVRREGS, tid, (PTRACE_TYPE_ARG3) &vrregset, 0) >= 0) + features.altivec = true; + + /* EINVAL means that the PT_GETVRREGS request isn't supported. + Anything else needs to be reported. */ + else if (errno != EINVAL) + perror_with_name (_("Unable to fetch AltiVec registers")); + + return ppc_fbsd_match_description (features); } /* Architecture specific function that reconstructs the diff --git a/gdb/ppc-fbsd-tdep.c b/gdb/ppc-fbsd-tdep.c index 495ccca8f1..a1fa7b8aaa 100644 --- a/gdb/ppc-fbsd-tdep.c +++ b/gdb/ppc-fbsd-tdep.c @@ -36,6 +36,21 @@ #include "fbsd-tdep.h" #include "solib-svr4.h" +#include "arch/ppc-fbsd-common.h" + +static void +ppc_fbsd_collect_vrregset (const struct regset *regset, + const struct regcache *regcache, + int regnum, void *buf, size_t len) +{ + gdb_byte *vrregs = (gdb_byte *) buf; + + /* Zero out the unused bytes in ppc32_fbsd_vrregmap + in case they get displayed somewhere (e.g. in core files). */ + memset (&vrregs[32 * 16], 0, 8); + + regcache_collect_regset (regset, regcache, regnum, buf, len); +} /* 32-bit regset descriptions. */ @@ -105,6 +120,31 @@ static const struct regset ppc32_fbsd_fpregset = { ppc_collect_fpregset }; +static const struct regcache_map_entry ppc32_fbsd_vrregmap[] = { + { 32, PPC_VR0_REGNUM, 16 }, + { 1, REGCACHE_MAP_SKIP, 8}, + { 1, PPC_VRSAVE_REGNUM, 4 }, + { 1, PPC_VSCR_REGNUM, 4 }, + { 0 } +}; + +static const struct regset ppc32_fbsd_vrregset = { + ppc32_fbsd_vrregmap, + regcache_supply_regset, + ppc_fbsd_collect_vrregset +}; + +static const struct regcache_map_entry ppc32_fbsd_vsxregmap[] = { + { 32, PPC_VSR0_UPPER_REGNUM, 8 }, + { 0 } +}; + +static const struct regset ppc32_fbsd_vsxregset = { + ppc32_fbsd_vsxregmap, + regcache_supply_regset, + regcache_collect_regset +}; + const struct regset * ppc_fbsd_gregset (int wordsize) { @@ -117,6 +157,18 @@ ppc_fbsd_fpregset (void) return &ppc32_fbsd_fpregset; } +const struct regset * +ppc_fbsd_vrregset (void) +{ + return &ppc32_fbsd_vrregset; +} + +const struct regset * +ppc_fbsd_vsxregset (void) +{ + return &ppc32_fbsd_vsxregset; +} + /* Iterate over core file register note sections. */ static void @@ -126,12 +178,56 @@ ppcfbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, const struct regcache *regcache) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + int have_altivec = tdep->ppc_vr0_regnum != -1; + int have_vsx = tdep->ppc_vsr0_upper_regnum != -1; if (tdep->wordsize == 4) - cb (".reg", 148, &ppc32_fbsd_gregset, NULL, cb_data); + cb (".reg", PPC_FBSD_SIZEOF_GREGSET_32, &ppc32_fbsd_gregset, NULL, cb_data); else - cb (".reg", 296, &ppc64_fbsd_gregset, NULL, cb_data); - cb (".reg2", 264, &ppc32_fbsd_fpregset, NULL, cb_data); + cb (".reg", PPC_FBSD_SIZEOF_GREGSET_64, &ppc64_fbsd_gregset, NULL, cb_data); + cb (".reg2", PPC_FBSD_SIZEOF_FPREGSET, &ppc32_fbsd_fpregset, NULL, cb_data); + + if (have_altivec) + cb (".reg-ppc-vmx", PPC_FBSD_SIZEOF_VRREGSET, ppc_fbsd_vrregset (), + "ppc Altivec", cb_data); + + if (have_vsx) + cb (".reg-ppc-vsx", PPC_FBSD_SIZEOF_VSXREGSET, ppc_fbsd_vsxregset (), + "POWER7 VSX", cb_data); +} + +static const struct target_desc * +ppc_fbsd_core_read_description (struct gdbarch *gdbarch, + struct target_ops *target, + bfd *abfd) +{ + struct ppc_fbsd_features features = ppc_fbsd_no_features; + asection *altivec = bfd_get_section_by_name (abfd, ".reg-ppc-vmx"); + asection *vsx = bfd_get_section_by_name (abfd, ".reg-ppc-vsx"); + asection *section = bfd_get_section_by_name (abfd, ".reg"); + + if (! section) + return NULL; + + switch (bfd_section_size (abfd, section)) + { + case PPC_FBSD_SIZEOF_GREGSET_32: + features.wordsize = 4; + break; + case PPC_FBSD_SIZEOF_GREGSET_64: + features.wordsize = 8; + break; + default: + return NULL; + } + + if (altivec) + features.altivec = true; + + if (vsx) + features.vsx = true; + + return ppc_fbsd_match_description (features); } /* Default page size. */ @@ -317,6 +413,8 @@ ppcfbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) set_gdbarch_gcore_bfd_target (gdbarch, "elf64-powerpc"); } + set_gdbarch_core_read_description (gdbarch, ppc_fbsd_core_read_description); + set_gdbarch_iterate_over_regset_sections (gdbarch, ppcfbsd_iterate_over_regset_sections); diff --git a/gdb/ppc-fbsd-tdep.h b/gdb/ppc-fbsd-tdep.h index 356b5941ba..7b28bab73c 100644 --- a/gdb/ppc-fbsd-tdep.h +++ b/gdb/ppc-fbsd-tdep.h @@ -25,5 +25,7 @@ struct regset; /* From ppc-fbsd-tdep.c ... */ const struct regset *ppc_fbsd_gregset (int); const struct regset *ppc_fbsd_fpregset (void); +const struct regset *ppc_fbsd_vrregset (void); +const struct regset *ppc_fbsd_vsxregset (void); #endif /* PPC_FBSD_TDEP_H */