From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 466 invoked by alias); 20 Feb 2002 20:21:24 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 32735 invoked from network); 20 Feb 2002 20:21:18 -0000 Received: from unknown (HELO cygnus.com) (205.180.230.5) by sources.redhat.com with SMTP; 20 Feb 2002 20:21:18 -0000 Received: from localhost.redhat.com (cse.cygnus.com [205.180.230.236]) by runyon.cygnus.com (8.8.7-cygnus/8.8.7) with ESMTP id MAA20496 for ; Wed, 20 Feb 2002 12:21:13 -0800 (PST) Received: by localhost.redhat.com (Postfix, from userid 469) id 63249112E3; Wed, 20 Feb 2002 15:20:45 -0500 (EST) From: Elena Zannoni MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Message-ID: <15476.1308.919907.110811@localhost.redhat.com> Date: Wed, 20 Feb 2002 12:21:00 -0000 To: gdb-patches@sources.redhat.com Subject: [RFA] ppc-linux-nat.c AltiVec regs ptrace X-Mailer: VM 7.00 under Emacs 20.7.1 X-SW-Source: 2002-02/txt/msg00562.txt.bz2 This patch adds support for fetching and storing the AltiVec registers on PPC. I previously submitted a different patch based on a different ptrace interface that was rejected by the ppc linux kernel maintainer. A new ptrace interface has since been added to the kernel: http://ppc.bkbits.net:8080/linuxppc_2_4/cset@1.543.1.14 This was the old patch, which is now obsolete: http://sources.redhat.com/ml/gdb-patches/2001-11/msg00548.html The Altivec registers are defined like this: #define ELF_NVRREG 33 /* includes vscr */ /* Altivec registers */ typedef __vector128 elf_vrreg_t; typedef elf_vrreg_t elf_vrregset_t[ELF_NVRREG]; There are 32 vector registers 16 bytes longs, plus a VSCR register which is only 4 bytes long, but is fetched as a 16 bytes quantity. Up to here we have the elf_vrregset_t structure. Appended to this there is space for the VRSAVE register: 4 bytes. Even though this vrsave register is not included in the regset typedef, it is handled by the ptrace requests. The layout is like this: |.|.|.|.|.....|.|.|.|.||.|.|.|x||.| <-------> <-------><-------><-> VR0 VR31 VSCR VRSAVE (where x is the actual value of the vscr reg) I have added a typedef to the ppc-linux-nat.c file to deal with such a layout. I am not sure whether this is the best place to put that. Maybe in gregset.h? Elena 2002-02-19 Elena Zannoni * ppc-linux-nat.c (PTRACE_GETVRREGS, PTRACE_SETVRREGS): Define. (have_ptrace_getvrregs): Define for run time checks. (gdb_vrregset_t): New type for Altivec register handling. (fetch_register, store_register): Fetch/store altivec register when needed. (fetch_altivec_register, store_altivec-register): New functions. (supply_vrregset, fill_vrregset): New functions. (fetch_altivec_registers, store_altivec_registers): New functions. (fetch_ppc_registers, store_ppc_registers): Fetch/store altivec registers as well. Index: ppc-linux-nat.c =================================================================== RCS file: /cvs/uberbaum/gdb/ppc-linux-nat.c,v retrieving revision 1.15 diff -u -p -r1.15 ppc-linux-nat.c --- ppc-linux-nat.c 2002/02/18 15:08:40 1.15 +++ ppc-linux-nat.c 2002/02/20 19:27:29 @@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Boston, MA 02111-1307, USA.*/ #include "defs.h" #include "frame.h" @@ -51,6 +51,39 @@ #define PTRACE_XFER_TYPE int #endif +/* This is necessary because of a glibc vs. kernel headers mess up. + Glibc's sys/ptrace.h defines PTRACE_GETFPXREGS and + PTRACE_SETFPXREGS to the same numbers that ppc's asm/ptrace.h + defines PTRACE_GETVRREGS and PTRACE_SETVRREGS to be. To complicate + matters further, the presence of such macros in glibc's ptrace.h + file doesn't guarantee that there is kernel support for + fetching/storing the AltiVec registers. So we have to fall back to + do a runtime check as well. A similar trick is done for the + i386. */ + +#ifdef HAVE_PTRACE_GETFPXREGS +/* These definitions don't really have any functional impact, they are here + to ease understanding of the ptrace calls. Glibc doesn't know about the + vrregs yet.*/ +#define PTRACE_GETVRREGS 18 +#define PTRACE_SETVRREGS 19 + +/* This oddity is because the linux kernel defines elf_vrregset_t as + an array of 33 16 bytes long elements. I.e. it leaves out vrsave. + However the PTRACE_GETVRREGS and PTRACE_SETVRREGS requests return the + vrsave as an extra 4 bytes at the end. I opted for creating a flat + array of chars, so that it is easier to manipulate for gdb. */ +#define SIZEOF_VRREGS 33*16+4 +typedef char gdb_vrregset_t[SIZEOF_VRREGS]; +#endif + +int have_ptrace_getvrregs +#ifdef HAVE_PTRACE_GETFPXREGS + = 1; +#else + = 0; +#endif + int kernel_u_size (void) { @@ -109,6 +142,53 @@ ppc_ptrace_cannot_fetch_store_register ( return (ppc_register_u_addr (regno) == -1); } +#ifdef HAVE_PTRACE_GETFPXREGS +/* The linux 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_register (int tid, int regno) +{ + int ret; + int offset = 0; + gdb_vrregset_t regs; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + int vrregsize = REGISTER_RAW_SIZE (tdep->ppc_vr0_regnum); + + ret = ptrace (PTRACE_GETVRREGS, tid, 0, ®s); + if (ret < 0) + { + if (errno == EIO) + { + have_ptrace_getvrregs = 0; + return; + } + perror_with_name ("Unable to fetch AltiVec register"); + } + + /* VSCR is fetched as a 16 bytes quantity, but it is really + 4 bytes long on the hardware. We deal only with the lower + 4 bytes of the vector. + VRSAVE is at the end of the array in a 4 bytes slot, so there is no + need to define an offset for it. */ + if (regno == (tdep->ppc_vrsave_regnum - 1)) + offset = vrregsize - REGISTER_RAW_SIZE (tdep->ppc_vrsave_regnum); + + supply_register (regno, + regs + (regno - tdep->ppc_vr0_regnum) * vrregsize + offset); +} + +#else +static void +fetch_altivec_register (int tid, int regno) +{ + internal_error (__FILE__, __LINE__, + "fetch_altivec_register: HAVE_PTRACE_GETFPXREGS not defined +\nand have_ptrace_getvrregs set."); + return; +} +#endif /* HAVE_PTRACE_GETFPXREGS */ + static void fetch_register (int tid, int regno) { @@ -119,6 +199,21 @@ fetch_register (int tid, int regno) char *buf = alloca (MAX_REGISTER_RAW_SIZE); CORE_ADDR regaddr = ppc_register_u_addr (regno); + if (altivec_register_p (regno)) + { + /* If this is the first time through, or if it is not the first time + through, and we have comfirmed that there is kernel support for + such a ptrace request, then go and fetch the register. */ + if (have_ptrace_getvrregs) + { + fetch_altivec_register (tid, regno); + return; + } + /* If we have discovered that there is no ptrace support for + AltiVec registers, fall through and return zeroes, because regaddr + will be -1 in this case. */ + } + if (regaddr == -1) { memset (buf, '\0', REGISTER_RAW_SIZE (regno)); /* Supply zeroes */ @@ -142,14 +237,70 @@ fetch_register (int tid, int regno) supply_register (regno, buf); } +static void +supply_vrregset (gdb_vrregset_t *vrregsetp) +{ + int i; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum; + int vrregsize = REGISTER_RAW_SIZE (tdep->ppc_vr0_regnum); + int offset = vrregsize - REGISTER_RAW_SIZE (tdep->ppc_vrsave_regnum); + + for (i = 0; i < num_of_vrregs - 1; i++) + { + /* The last 2 registers of this set are only 32 bit long, not 128. + However an offset is necessary only for VSCR because it occupies + a whole vector, while VRSAVE occupies a full 4 bytes slot. */ + if (i == (tdep->ppc_vrsave_regnum - 1)) + supply_register (tdep->ppc_vr0_regnum + i, + *vrregsetp + i * vrregsize + offset); + else + supply_register (tdep->ppc_vr0_regnum + i, *vrregsetp + i * vrregsize); + } +} + +#ifdef HAVE_PTRACE_GETFPXREGS + +static void +fetch_altivec_registers (int tid) +{ + int ret; + gdb_vrregset_t regs; + + ret = ptrace (PTRACE_GETVRREGS, tid, 0, ®s); + if (ret < 0) + { + if (errno == EIO) + { + have_ptrace_getvrregs = 0; + return; + } + perror_with_name ("Unable to fetch AltiVec registers"); + } + supply_vrregset (®s); +} +#else +static void +fetch_altivec_registers (int tid) +{ + internal_error (__FILE__, __LINE__, + "fetch_altivec_registers: HAVE_PTRACE_GETFPXREGS not defined +\nand have_ptrace_getvrregs set"); + return; +} +#endif /* HAVE_PTRACE_GETFPXREGS */ + static void fetch_ppc_registers (int tid) { int i; - int last_register = gdbarch_tdep (current_gdbarch)->ppc_mq_regnum; - - for (i = 0; i <= last_register; i++) + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + for (i = 0; i <= tdep->ppc_mq_regnum; i++) fetch_register (tid, i); + if (have_ptrace_getvrregs) + if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1) + fetch_altivec_registers (tid); } /* Fetch registers from the child process. Fetch all registers if @@ -158,21 +309,65 @@ fetch_ppc_registers (int tid) void fetch_inferior_registers (int regno) { - /* Overload thread id onto process id */ + /* Overload thread id onto process id */ int tid = TIDGET (inferior_ptid); /* No thread id, just use process id */ if (tid == 0) tid = PIDGET (inferior_ptid); - if (regno == -1) + if (regno == -1) fetch_ppc_registers (tid); else fetch_register (tid, regno); } +#ifdef HAVE_PTRACE_GETFPXREGS /* Store one register. */ static void +store_altivec_register (int tid, int regno) +{ + int ret; + int offset = 0; + gdb_vrregset_t regs; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + int vrregsize = REGISTER_RAW_SIZE (tdep->ppc_vr0_regnum); + + ret = ptrace (PTRACE_GETVRREGS, tid, 0, ®s); + if (ret < 0) + { + if (errno == EIO) + { + have_ptrace_getvrregs = 0; + return; + } + perror_with_name ("Unable to fetch AltiVec register"); + } + + /* VSCR is fetched as a 16 bytes quantity, but it is really + 4 bytes long on the hardware. */ + if (regno == (tdep->ppc_vrsave_regnum - 1)) + offset = vrregsize - REGISTER_RAW_SIZE (tdep->ppc_vrsave_regnum); + + regcache_collect (regno, + regs + (regno - tdep->ppc_vr0_regnum) * vrregsize + offset); + + ret = ptrace (PTRACE_SETVRREGS, tid, 0, ®s); + if (ret < 0) + perror_with_name ("Unable to store AltiVec register"); +} +#else +static void +store_altivec_register (int tid) +{ + internal_error (__FILE__, __LINE__, + "store_altivec_register: HAVE_PTRACE_GETFPXREGS not defined, +\nand have_ptrace_getvrregs set"); + return; +} +#endif + +static void store_register (int tid, int regno) { /* This isn't really an address. But ptrace thinks of it as one. */ @@ -182,11 +377,15 @@ store_register (int tid, int regno) unsigned int offset; /* Offset of registers within the u area. */ char *buf = alloca (MAX_REGISTER_RAW_SIZE); - if (regaddr == -1) + if (altivec_register_p (regno)) { + store_altivec_register (tid, regno); return; } + if (regaddr == -1) + return; + regcache_collect (regno, buf); for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (PTRACE_XFER_TYPE)) { @@ -204,13 +403,71 @@ store_register (int tid, int regno) } static void +fill_vrregset (gdb_vrregset_t *vrregsetp) +{ + int i; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + int num_of_vrregs = tdep->ppc_vrsave_regnum - tdep->ppc_vr0_regnum; + int vrregsize = REGISTER_RAW_SIZE (tdep->ppc_vr0_regnum); + int offset = vrregsize - REGISTER_RAW_SIZE (tdep->ppc_vrsave_regnum); + + for (i = 0; i < num_of_vrregs; i++) + { + /* The last 2 registers of this set are only 32 bit long, not 128. + Only VSCR is fetched as a 16 bytes quantity. */ + if (i == (tdep->ppc_vrsave_regnum - 1)) + regcache_collect (tdep->ppc_vr0_regnum + i, + *vrregsetp + i * vrregsize + offset); + else + regcache_collect (tdep->ppc_vr0_regnum + i, *vrregsetp + i * vrregsize); + } +} + +#ifdef HAVE_PTRACE_GETFPXREGS +static void +store_altivec_registers (int tid) +{ + int ret; + gdb_vrregset_t regs; + + ret = ptrace (PTRACE_GETVRREGS, tid, 0, (int) ®s); + if (ret < 0) + { + if (errno == EIO) + { + have_ptrace_getvrregs = 0; + return; + } + perror_with_name ("Couldn't get AltiVec registers"); + } + + fill_vrregset (®s); + + if (ptrace (PTRACE_SETVRREGS, tid, 0, (int) ®s) < 0) + perror_with_name ("Couldn't write AltiVec registers"); +} +#else +static void +store_altivec_registers (int tid) +{ + internal_error (__FILE__, __LINE__, + "store_altivec_registers: HAVE_PTRACE_GETFPXREGS not defined +\nand have_ptrace_getvrregs set"); + return; +} +#endif + +static void store_ppc_registers (int tid) { int i; - int last_register = gdbarch_tdep (current_gdbarch)->ppc_mq_regnum; + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); - for (i = 0; i <= last_register; i++) + for (i = 0; i <= tdep->ppc_mq_regnum; i++) store_register (tid, i); + if (have_ptrace_getvrregs) + if (tdep->ppc_vr0_regnum != -1 && tdep->ppc_vrsave_regnum != -1) + store_altivec_registers (tid); } void @@ -281,6 +538,7 @@ void supply_fpregset (gdb_fpregset_t * fpregsetp) { int regi; + for (regi = 0; regi < 32; regi++) supply_register (FP0_REGNUM + regi, (char *) (*fpregsetp + regi)); }