diff options
author | Ulrich Weigand <uweigand@de.ibm.com> | 2006-11-22 13:49:53 +0000 |
---|---|---|
committer | Ulrich Weigand <uweigand@de.ibm.com> | 2006-11-22 13:49:53 +0000 |
commit | 771b4502d5e67d90e46529beb9e73ec37151828d (patch) | |
tree | 412025f292ae66f4a2a13bfcebfe3cf48e123107 /gdb/spu-linux-nat.c | |
parent | 0b2b0195d1b3b75d9fd0e5313466a0141790071d (diff) | |
download | gdb-771b4502d5e67d90e46529beb9e73ec37151828d.zip gdb-771b4502d5e67d90e46529beb9e73ec37151828d.tar.gz gdb-771b4502d5e67d90e46529beb9e73ec37151828d.tar.bz2 |
* 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.c | 557 |
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 0000000..fd3673c --- /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); +} + |