summaryrefslogtreecommitdiff
path: root/gdb/spu-linux-nat.c
diff options
context:
space:
mode:
authorUlrich Weigand <uweigand@de.ibm.com>2006-11-22 13:49:53 +0000
committerUlrich Weigand <uweigand@de.ibm.com>2006-11-22 13:49:53 +0000
commiteea6bc6554e56c11a5fcc26f7823771b0497dfc4 (patch)
treedad2bd0a9360d3ba8490f7d755d4940047bf56b4 /gdb/spu-linux-nat.c
parentef7b9179e4aa301fc6b290c3472984976e4daef5 (diff)
downloadgdb-eea6bc6554e56c11a5fcc26f7823771b0497dfc4.tar.gz
* config/powerpc/spu-linux.mh: New file.
* config/spu/spu.mt: New file. * configure.ac: Provide gdb_native configuration variable. * configure: Regenerate. * configure.host: Support powerpc64 to spu 'pseudo-native' mode. * configure.tgt: Add "spu" target_cpu and "spu*-*-*" target. * Makefile.in (spu_tdep_h): New variable. (ALLDEPFILES): Add spu-linux-nat.c and spu-tdep.c (spu-linux-nat.o, spu-tdep.o): Add dependencies. * spu-linux-nat.c: New file. * spu-tdep.c: New file. * spu-tdep.h: New file.
Diffstat (limited to 'gdb/spu-linux-nat.c')
-rw-r--r--gdb/spu-linux-nat.c557
1 files changed, 557 insertions, 0 deletions
diff --git a/gdb/spu-linux-nat.c b/gdb/spu-linux-nat.c
new file mode 100644
index 00000000000..fd3673c6a23
--- /dev/null
+++ b/gdb/spu-linux-nat.c
@@ -0,0 +1,557 @@
+/* SPU native-dependent code for GDB, the GNU debugger.
+ Copyright (C) 2006 Free Software Foundation, Inc.
+
+ Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
+
+ 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 2 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, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "gdb_string.h"
+#include "target.h"
+#include "inferior.h"
+#include "inf-ptrace.h"
+#include "regcache.h"
+#include "symfile.h"
+#include "gdb_wait.h"
+
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include "spu-tdep.h"
+
+/* PPU side system calls. */
+#define INSTR_SC 0x44000002
+#define NR_spu_run 0x0116
+
+
+/* Fetch PPU register REGNO. */
+static CORE_ADDR
+fetch_ppc_register (int regno)
+{
+ PTRACE_TYPE_RET res;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+#ifndef __powerpc64__
+ /* If running as a 32-bit process on a 64-bit system, we attempt
+ to get the full 64-bit register content of the target process.
+ If the PPC special ptrace call fails, we're on a 32-bit system;
+ just fall through to the regular ptrace call in that case. */
+ {
+ gdb_byte buf[8];
+
+ errno = 0;
+ ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+ (PTRACE_TYPE_ARG3) (regno * 8), buf);
+ if (errno == 0)
+ ptrace (PPC_PTRACE_PEEKUSR_3264, tid,
+ (PTRACE_TYPE_ARG3) (regno * 8 + 4), buf + 4);
+ if (errno == 0)
+ return (CORE_ADDR) *(unsigned long long *)buf;
+ }
+#endif
+
+ errno = 0;
+ res = ptrace (PT_READ_U, tid,
+ (PTRACE_TYPE_ARG3) (regno * sizeof (PTRACE_TYPE_RET)), 0);
+ if (errno != 0)
+ {
+ char mess[128];
+ xsnprintf (mess, sizeof mess, "reading PPC register #%d", regno);
+ perror_with_name (_(mess));
+ }
+
+ return (CORE_ADDR) (unsigned long) res;
+}
+
+/* Fetch WORD from PPU memory at (aligned) MEMADDR in thread TID. */
+static int
+fetch_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET *word)
+{
+ errno = 0;
+
+#ifndef __powerpc64__
+ if (memaddr >> 32)
+ {
+ unsigned long long addr_8 = (unsigned long long) memaddr;
+ ptrace (PPC_PTRACE_PEEKTEXT_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+ }
+ else
+#endif
+ *word = ptrace (PT_READ_I, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, 0);
+
+ return errno;
+}
+
+/* Store WORD into PPU memory at (aligned) MEMADDR in thread TID. */
+static int
+store_ppc_memory_1 (int tid, CORE_ADDR memaddr, PTRACE_TYPE_RET word)
+{
+ errno = 0;
+
+#ifndef __powerpc64__
+ if (memaddr >> 32)
+ {
+ unsigned long long addr_8 = (unsigned long long) memaddr;
+ ptrace (PPC_PTRACE_POKEDATA_3264, tid, (PTRACE_TYPE_ARG3) &addr_8, word);
+ }
+ else
+#endif
+ ptrace (PT_WRITE_D, tid, (PTRACE_TYPE_ARG3) (size_t) memaddr, word);
+
+ return errno;
+}
+
+/* Fetch LEN bytes of PPU memory at MEMADDR to MYADDR. */
+static int
+fetch_ppc_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len)
+{
+ int i, ret;
+
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+ int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+ / sizeof (PTRACE_TYPE_RET));
+ PTRACE_TYPE_RET *buffer;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+ buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+ if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[i])) != 0)
+ return ret;
+
+ memcpy (myaddr,
+ (char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+ len);
+
+ return 0;
+}
+
+/* Store LEN bytes from MYADDR to PPU memory at MEMADDR. */
+static int
+store_ppc_memory (CORE_ADDR memaddr, const gdb_byte *myaddr, int len)
+{
+ int i, ret;
+
+ CORE_ADDR addr = memaddr & -(CORE_ADDR) sizeof (PTRACE_TYPE_RET);
+ int count = ((((memaddr + len) - addr) + sizeof (PTRACE_TYPE_RET) - 1)
+ / sizeof (PTRACE_TYPE_RET));
+ PTRACE_TYPE_RET *buffer;
+
+ int tid = TIDGET (inferior_ptid);
+ if (tid == 0)
+ tid = PIDGET (inferior_ptid);
+
+ buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+
+ if (addr != memaddr || len < (int) sizeof (PTRACE_TYPE_RET))
+ if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[0])) != 0)
+ return ret;
+
+ if (count > 1)
+ if ((ret = fetch_ppc_memory_1 (tid, addr + (count - 1)
+ * sizeof (PTRACE_TYPE_RET),
+ &buffer[count - 1])) != 0)
+ return ret;
+
+ memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
+ myaddr, len);
+
+ for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
+ if ((ret = store_ppc_memory_1 (tid, addr, buffer[i])) != 0)
+ return ret;
+
+ return 0;
+}
+
+
+/* If the PPU thread is currently stopped on a spu_run system call,
+ return to FD and ADDR the file handle and NPC parameter address
+ used with the system call. Return non-zero if successful. */
+static int
+parse_spufs_run (int *fd, CORE_ADDR *addr)
+{
+ gdb_byte buf[4];
+ CORE_ADDR pc = fetch_ppc_register (32); /* nip */
+
+ /* Fetch instruction preceding current NIP. */
+ if (fetch_ppc_memory (pc-4, buf, 4) != 0)
+ return 0;
+ /* It should be a "sc" instruction. */
+ if (extract_unsigned_integer (buf, 4) != INSTR_SC)
+ return 0;
+ /* System call number should be NR_spu_run. */
+ if (fetch_ppc_register (0) != NR_spu_run)
+ return 0;
+
+ /* Register 3 contains fd, register 4 the NPC param pointer. */
+ *fd = fetch_ppc_register (34); /* orig_gpr3 */
+ *addr = fetch_ppc_register (4);
+ return 1;
+}
+
+
+/* Copy LEN bytes at OFFSET in spufs file ANNEX into/from READBUF or WRITEBUF,
+ using the /proc file system. */
+static LONGEST
+spu_proc_xfer_spu (const char *annex, gdb_byte *readbuf,
+ const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ char buf[128];
+ int fd = 0;
+ int ret = -1;
+ int pid = PIDGET (inferior_ptid);
+
+ if (!annex)
+ return 0;
+
+ xsnprintf (buf, sizeof buf, "/proc/%d/fd/%s", pid, annex);
+ fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
+ if (fd <= 0)
+ return -1;
+
+ if (offset != 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ {
+ close (fd);
+ return -1;
+ }
+
+ if (writebuf)
+ ret = write (fd, writebuf, (size_t) len);
+ else if (readbuf)
+ ret = read (fd, readbuf, (size_t) len);
+
+ close (fd);
+ return ret;
+}
+
+
+/* Inferior memory should contain an SPE executable image at location ADDR.
+ Allocate a BFD representing that executable. Return NULL on error. */
+
+static void *
+spu_bfd_iovec_open (struct bfd *nbfd, void *open_closure)
+{
+ return open_closure;
+}
+
+static int
+spu_bfd_iovec_close (struct bfd *nbfd, void *stream)
+{
+ xfree (stream);
+ return 1;
+}
+
+static file_ptr
+spu_bfd_iovec_pread (struct bfd *abfd, void *stream, void *buf,
+ file_ptr nbytes, file_ptr offset)
+{
+ CORE_ADDR addr = *(CORE_ADDR *)stream;
+
+ if (fetch_ppc_memory (addr + offset, buf, nbytes) != 0)
+ {
+ bfd_set_error (bfd_error_invalid_operation);
+ return -1;
+ }
+
+ return nbytes;
+}
+
+static bfd *
+spu_bfd_open (CORE_ADDR addr)
+{
+ struct bfd *nbfd;
+
+ CORE_ADDR *open_closure = xmalloc (sizeof (CORE_ADDR));
+ *open_closure = addr;
+
+ nbfd = bfd_openr_iovec (xstrdup ("<in-memory>"), "elf32-spu",
+ spu_bfd_iovec_open, open_closure,
+ spu_bfd_iovec_pread, spu_bfd_iovec_close);
+ if (!nbfd)
+ return NULL;
+
+ if (!bfd_check_format (nbfd, bfd_object))
+ {
+ bfd_close (nbfd);
+ return NULL;
+ }
+
+ return nbfd;
+}
+
+/* INFERIOR_FD is a file handle passed by the inferior to the
+ spu_run system call. Assuming the SPE context was allocated
+ by the libspe library, try to retrieve the main SPE executable
+ file from its copy within the target process. */
+static void
+spu_symbol_file_add_from_memory (int inferior_fd)
+{
+ CORE_ADDR addr;
+ struct bfd *nbfd;
+
+ char id[128];
+ char annex[32];
+ int len;
+
+ /* Read object ID. */
+ xsnprintf (annex, sizeof annex, "%d/object-id", inferior_fd);
+ len = spu_proc_xfer_spu (annex, id, NULL, 0, sizeof id);
+ if (len <= 0 || len >= sizeof id)
+ return;
+ id[len] = 0;
+ if (sscanf (id, "0x%llx", &addr) != 1)
+ return;
+
+ /* Open BFD representing SPE executable and read its symbols. */
+ nbfd = spu_bfd_open (addr);
+ if (nbfd)
+ symbol_file_add_from_bfd (nbfd, 0, NULL, 1, 0);
+}
+
+
+/* Override the post_startup_inferior routine to continue running
+ the inferior until the first spu_run system call. */
+static void
+spu_child_post_startup_inferior (ptid_t ptid)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ int tid = TIDGET (ptid);
+ if (tid == 0)
+ tid = PIDGET (ptid);
+
+ while (!parse_spufs_run (&fd, &addr))
+ {
+ ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0);
+ waitpid (tid, NULL, __WALL | __WNOTHREAD);
+ }
+}
+
+/* Override the post_attach routine to try load the SPE executable
+ file image from its copy inside the target process. */
+static void
+spu_child_post_attach (int pid)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* Like child_post_startup_inferior, if we happened to attach to
+ the inferior while it wasn't currently in spu_run, continue
+ running it until we get back there. */
+ while (!parse_spufs_run (&fd, &addr))
+ {
+ ptrace (PT_SYSCALL, pid, (PTRACE_TYPE_ARG3) 0, 0);
+ waitpid (pid, NULL, __WALL | __WNOTHREAD);
+ }
+
+ /* If the user has not provided an executable file, try to extract
+ the image from inside the target process. */
+ if (!get_exec_file (0))
+ spu_symbol_file_add_from_memory (fd);
+}
+
+/* Wait for child PTID to do something. Return id of the child,
+ minus_one_ptid in case of error; store status into *OURSTATUS. */
+static ptid_t
+spu_child_wait (ptid_t ptid, struct target_waitstatus *ourstatus)
+{
+ int save_errno;
+ int status;
+ pid_t pid;
+
+ do
+ {
+ set_sigint_trap (); /* Causes SIGINT to be passed on to the
+ attached process. */
+ set_sigio_trap ();
+
+ pid = waitpid (PIDGET (ptid), &status, 0);
+ if (pid == -1 && errno == ECHILD)
+ /* Try again with __WCLONE to check cloned processes. */
+ pid = waitpid (PIDGET (ptid), &status, __WCLONE);
+
+ save_errno = errno;
+
+ /* Make sure we don't report an event for the exit of the
+ original program, if we've detached from it. */
+ if (pid != -1 && !WIFSTOPPED (status) && pid != PIDGET (inferior_ptid))
+ {
+ pid = -1;
+ save_errno = EINTR;
+ }
+
+ clear_sigio_trap ();
+ clear_sigint_trap ();
+ }
+ while (pid == -1 && save_errno == EINTR);
+
+ if (pid == -1)
+ {
+ warning ("Child process unexpectedly missing: %s",
+ safe_strerror (save_errno));
+
+ /* Claim it exited with unknown signal. */
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
+ return minus_one_ptid;
+ }
+
+ store_waitstatus (ourstatus, status);
+ return pid_to_ptid (pid);
+}
+
+/* Override the fetch_inferior_register routine. */
+static void
+spu_fetch_inferior_registers (int regno)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return;
+
+ /* The ID register holds the spufs file handle. */
+ if (regno == -1 || regno == SPU_ID_REGNUM)
+ {
+ char buf[4];
+ store_unsigned_integer (buf, 4, fd);
+ regcache_raw_supply (current_regcache, SPU_ID_REGNUM, buf);
+ }
+
+ /* The NPC register is found at ADDR. */
+ if (regno == -1 || regno == SPU_PC_REGNUM)
+ {
+ gdb_byte buf[4];
+ if (fetch_ppc_memory (addr, buf, 4) == 0)
+ regcache_raw_supply (current_regcache, SPU_PC_REGNUM, buf);
+ }
+
+ /* The GPRs are found in the "regs" spufs file. */
+ if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+ {
+ gdb_byte buf[16 * SPU_NUM_GPRS];
+ char annex[32];
+ int i;
+
+ xsnprintf (annex, sizeof annex, "%d/regs", fd);
+ if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf)
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ regcache_raw_supply (current_regcache, i, buf + i*16);
+ }
+}
+
+/* Override the store_inferior_register routine. */
+static void
+spu_store_inferior_registers (int regno)
+{
+ int fd;
+ CORE_ADDR addr;
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return;
+
+ /* The NPC register is found at ADDR. */
+ if (regno == -1 || regno == SPU_PC_REGNUM)
+ {
+ gdb_byte buf[4];
+ regcache_raw_collect (current_regcache, SPU_PC_REGNUM, buf);
+ store_ppc_memory (addr, buf, 4);
+ }
+
+ /* The GPRs are found in the "regs" spufs file. */
+ if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
+ {
+ gdb_byte buf[16 * SPU_NUM_GPRS];
+ char annex[32];
+ int i;
+
+ for (i = 0; i < SPU_NUM_GPRS; i++)
+ regcache_raw_collect (current_regcache, i, buf + i*16);
+
+ xsnprintf (annex, sizeof annex, "%d/regs", fd);
+ spu_proc_xfer_spu (annex, NULL, buf, 0, sizeof buf);
+ }
+}
+
+/* Override the to_xfer_partial routine. */
+static LONGEST
+spu_xfer_partial (struct target_ops *ops,
+ enum target_object object, const char *annex,
+ gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST offset, LONGEST len)
+{
+ if (object == TARGET_OBJECT_MEMORY)
+ {
+ int fd;
+ CORE_ADDR addr;
+ char mem_annex[32];
+
+ /* We must be stopped on a spu_run system call. */
+ if (!parse_spufs_run (&fd, &addr))
+ return 0;
+
+ /* Use the "mem" spufs file to access SPU local store. */
+ xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
+ return spu_proc_xfer_spu (mem_annex, readbuf, writebuf, offset, len);
+ }
+
+ return 0;
+}
+
+/* Override the to_can_use_hw_breakpoint routine. */
+static int
+spu_can_use_hw_breakpoint (int type, int cnt, int othertype)
+{
+ return 0;
+}
+
+
+/* Initialize SPU native target. */
+void
+_initialize_spu_nat (void)
+{
+ /* Generic ptrace methods. */
+ struct target_ops *t;
+ t = inf_ptrace_target ();
+
+ /* Add SPU methods. */
+ t->to_post_attach = spu_child_post_attach;
+ t->to_post_startup_inferior = spu_child_post_startup_inferior;
+ t->to_wait = spu_child_wait;
+ t->to_fetch_registers = spu_fetch_inferior_registers;
+ t->to_store_registers = spu_store_inferior_registers;
+ t->to_xfer_partial = spu_xfer_partial;
+ t->to_can_use_hw_breakpoint = spu_can_use_hw_breakpoint;
+
+ /* Register SPU target. */
+ add_target (t);
+}
+