/* Low level interface to SPUs, for the remote server for GDB.
- Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2006-2015 Free Software Foundation, Inc.
Contributed by Ulrich Weigand <uweigand@de.ibm.com>.
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
+ 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,
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. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "server.h"
-#include <sys/wait.h>
-#include <stdio.h>
+#include "gdb_wait.h"
#include <sys/ptrace.h>
#include <fcntl.h>
-#include <string.h>
-#include <stdlib.h>
#include <unistd.h>
-#include <errno.h>
#include <sys/syscall.h>
+#include "filestuff.h"
+#include "hostio.h"
/* Some older glibc versions do not define this. */
#ifndef __WNOTHREAD
#define __WNOTHREAD 0x20000000 /* Don't wait on children of other
- threads in this group */
+ threads in this group */
#endif
#define PTRACE_TYPE_RET long
#define INSTR_SC 0x44000002
#define NR_spu_run 0x0116
-/* Get current thread ID (Linux task ID). */
-#define current_tid ((struct inferior_list_entry *)current_inferior)->id
-
/* These are used in remote-utils.c. */
int using_threads = 0;
-int debug_threads = 0;
+/* Defined in auto-generated file reg-spu.c. */
+void init_registers_spu (void);
+extern const struct target_desc *tdesc_spu;
+
+/* Software breakpoint instruction. */
+static const gdb_byte breakpoint[] = { 0x00, 0x00, 0x3f, 0xff };
/* Fetch PPU register REGNO. */
static CORE_ADDR
{
PTRACE_TYPE_RET res;
- int tid = current_tid;
+ int tid = ptid_get_lwp (current_ptid);
#ifndef __powerpc64__
/* If running as a 32-bit process on a 64-bit system, we attempt
errno = 0;
res = ptrace (PT_READ_U, tid,
- (PTRACE_TYPE_ARG3) (regno * sizeof (PTRACE_TYPE_RET)), 0);
+ (PTRACE_TYPE_ARG3) (regno * sizeof (PTRACE_TYPE_RET)), 0);
if (errno != 0)
{
char mess[128];
/ sizeof (PTRACE_TYPE_RET));
PTRACE_TYPE_RET *buffer;
- int tid = current_tid;
+ int tid = ptid_get_lwp (current_ptid);
- buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+ buffer = XALLOCAVEC (PTRACE_TYPE_RET, count);
for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[i])) != 0)
return ret;
/ sizeof (PTRACE_TYPE_RET));
PTRACE_TYPE_RET *buffer;
- int tid = current_tid;
+ int tid = ptid_get_lwp (current_ptid);
- buffer = (PTRACE_TYPE_RET *) alloca (count * sizeof (PTRACE_TYPE_RET));
+ buffer = XALLOCAVEC (PTRACE_TYPE_RET, count);
if (addr != memaddr || len < (int) sizeof (PTRACE_TYPE_RET))
if ((ret = fetch_ppc_memory_1 (tid, addr, &buffer[0])) != 0)
return ret;
memcpy ((char *) buffer + (memaddr & (sizeof (PTRACE_TYPE_RET) - 1)),
- myaddr, len);
+ myaddr, len);
for (i = 0; i < count; i++, addr += sizeof (PTRACE_TYPE_RET))
if ((ret = store_ppc_memory_1 (tid, addr, buffer[i])) != 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
+static int
parse_spufs_run (int *fd, CORE_ADDR *addr)
{
- char buf[4];
+ unsigned int insn;
CORE_ADDR pc = fetch_ppc_register (32); /* nip */
/* Fetch instruction preceding current NIP. */
- if (fetch_ppc_memory (pc-4, buf, 4) != 0)
+ if (fetch_ppc_memory (pc-4, (char *) &insn, 4) != 0)
return 0;
/* It should be a "sc" instruction. */
- if (*(unsigned int *)buf != INSTR_SC)
+ if (insn != INSTR_SC)
return 0;
/* System call number should be NR_spu_run. */
if (fetch_ppc_register (0) != NR_spu_run)
if (!annex)
return 0;
- sprintf (buf, "/proc/%ld/fd/%s", current_tid, annex);
+ sprintf (buf, "/proc/%ld/fd/%s", ptid_get_lwp (current_ptid), annex);
fd = open (buf, writebuf? O_WRONLY : O_RDONLY);
if (fd <= 0)
return -1;
&& lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
{
close (fd);
- return -1;
+ return 0;
}
if (writebuf)
spu_create_inferior (char *program, char **allargs)
{
int pid;
+ ptid_t ptid;
+ struct process_info *proc;
pid = fork ();
if (pid < 0)
if (pid == 0)
{
+ close_most_fds ();
ptrace (PTRACE_TRACEME, 0, 0, 0);
setpgid (0, 0);
execv (program, allargs);
+ if (errno == ENOENT)
+ execvp (program, allargs);
fprintf (stderr, "Cannot exec %s: %s.\n", program,
strerror (errno));
_exit (0177);
}
- add_thread (pid, NULL, pid);
+ proc = add_process (pid, 0);
+ proc->tdesc = tdesc_spu;
+
+ ptid = ptid_build (pid, pid, 0);
+ add_thread (ptid, NULL);
return pid;
}
int
spu_attach (unsigned long pid)
{
+ ptid_t ptid;
+ struct process_info *proc;
+
if (ptrace (PTRACE_ATTACH, pid, 0, 0) != 0)
{
fprintf (stderr, "Cannot attach to process %ld: %s (%d)\n", pid,
_exit (0177);
}
- add_thread (pid, NULL, pid);
+ proc = add_process (pid, 1);
+ proc->tdesc = tdesc_spu;
+ ptid = ptid_build (pid, pid, 0);
+ add_thread (ptid, NULL);
return 0;
}
/* Kill the inferior process. */
-static void
-spu_kill (void)
+static int
+spu_kill (int pid)
{
- ptrace (PTRACE_KILL, current_tid, 0, 0);
+ int status, ret;
+ struct process_info *process = find_process_pid (pid);
+ if (process == NULL)
+ return -1;
+
+ ptrace (PTRACE_KILL, pid, 0, 0);
+
+ do {
+ ret = waitpid (pid, &status, 0);
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ break;
+ } while (ret != -1 || errno != ECHILD);
+
+ clear_inferiors ();
+ remove_process (process);
+ return 0;
}
/* Detach from inferior process. */
+static int
+spu_detach (int pid)
+{
+ struct process_info *process = find_process_pid (pid);
+ if (process == NULL)
+ return -1;
+
+ ptrace (PTRACE_DETACH, pid, 0, 0);
+
+ clear_inferiors ();
+ remove_process (process);
+ return 0;
+}
+
+static void
+spu_mourn (struct process_info *process)
+{
+ remove_process (process);
+}
+
static void
-spu_detach (void)
+spu_join (int pid)
{
- ptrace (PTRACE_DETACH, current_tid, 0, 0);
+ int status, ret;
+
+ do {
+ ret = waitpid (pid, &status, 0);
+ if (WIFEXITED (status) || WIFSIGNALED (status))
+ break;
+ } while (ret != -1 || errno != ECHILD);
}
/* Return nonzero if the given thread is still alive. */
static int
-spu_thread_alive (unsigned long tid)
+spu_thread_alive (ptid_t ptid)
{
- return tid == current_tid;
+ return ptid_equal (ptid, current_ptid);
}
/* Resume process. */
static void
-spu_resume (struct thread_resume *resume_info)
+spu_resume (struct thread_resume *resume_info, size_t n)
{
- while (resume_info->thread != -1
- && resume_info->thread != current_tid)
- resume_info++;
+ struct thread_info *thr = get_first_thread ();
+ size_t i;
- block_async_io ();
- enable_async_io ();
+ for (i = 0; i < n; i++)
+ if (ptid_equal (resume_info[i].thread, minus_one_ptid)
+ || ptid_equal (resume_info[i].thread, ptid_of (thr)))
+ break;
- if (resume_info->leave_stopped)
+ if (i == n)
return;
/* We don't support hardware single-stepping right now, assume
GDB knows to use software single-stepping. */
- if (resume_info->step)
+ if (resume_info[i].kind == resume_step)
fprintf (stderr, "Hardware single-step not supported.\n");
regcache_invalidate ();
errno = 0;
- ptrace (PTRACE_CONT, current_tid, 0, resume_info->sig);
+ ptrace (PTRACE_CONT, ptid_get_lwp (ptid_of (thr)), 0, resume_info[i].sig);
if (errno)
perror_with_name ("ptrace");
}
/* Wait for process, returns status. */
-static unsigned char
-spu_wait (char *status)
+static ptid_t
+spu_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
- int tid = current_tid;
+ int pid = ptid_get_pid (ptid);
int w;
int ret;
- enable_async_io ();
- unblock_async_io ();
-
while (1)
{
- ret = waitpid (tid, &w, WNOHANG | __WALL | __WNOTHREAD);
+ ret = waitpid (pid, &w, WNOHANG | __WALL | __WNOTHREAD);
if (ret == -1)
{
while (!parse_spufs_run (&fd, &addr))
{
- ptrace (PT_SYSCALL, tid, (PTRACE_TYPE_ARG3) 0, 0);
- waitpid (tid, NULL, __WALL | __WNOTHREAD);
+ ptrace (PT_SYSCALL, pid, (PTRACE_TYPE_ARG3) 0, 0);
+ waitpid (pid, NULL, __WALL | __WNOTHREAD);
}
}
- disable_async_io ();
-
if (WIFEXITED (w))
{
fprintf (stderr, "\nChild exited with retcode = %x \n", WEXITSTATUS (w));
- *status = 'W';
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = WEXITSTATUS (w);
clear_inferiors ();
- return ((unsigned char) WEXITSTATUS (w));
+ return pid_to_ptid (ret);
}
else if (!WIFSTOPPED (w))
{
fprintf (stderr, "\nChild terminated with signal = %x \n", WTERMSIG (w));
- *status = 'X';
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (WTERMSIG (w));
clear_inferiors ();
- return ((unsigned char) WTERMSIG (w));
+ return pid_to_ptid (ret);
}
/* After attach, we may have received a SIGSTOP. Do not return this
as signal to GDB, or else it will try to continue with SIGSTOP ... */
if (!server_waiting)
{
- *status = 'T';
- return 0;
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = GDB_SIGNAL_0;
+ return ptid_build (ret, ret, 0);
}
- *status = 'T';
- return ((unsigned char) WSTOPSIG (w));
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+ ourstatus->value.sig = gdb_signal_from_host (WSTOPSIG (w));
+ return ptid_build (ret, ret, 0);
}
/* Fetch inferior registers. */
static void
-spu_fetch_registers (int regno)
+spu_fetch_registers (struct regcache *regcache, int regno)
{
int fd;
CORE_ADDR addr;
- /* ??? Some callers use 0 to mean all registers. */
- if (regno == 0)
- regno = -1;
-
/* 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)
- supply_register (SPU_ID_REGNUM, (char *)&fd);
+ supply_register (regcache, SPU_ID_REGNUM, (char *)&fd);
/* The NPC register is found at ADDR. */
if (regno == -1 || regno == SPU_PC_REGNUM)
{
char buf[4];
if (fetch_ppc_memory (addr, buf, 4) == 0)
- supply_register (SPU_PC_REGNUM, buf);
+ supply_register (regcache, SPU_PC_REGNUM, buf);
}
/* The GPRs are found in the "regs" spufs file. */
sprintf (annex, "%d/regs", fd);
if (spu_proc_xfer_spu (annex, buf, NULL, 0, sizeof buf) == sizeof buf)
for (i = 0; i < SPU_NUM_CORE_REGS; i++)
- supply_register (i, buf + i*16);
+ supply_register (regcache, i, buf + i*16);
}
}
/* Store inferior registers. */
static void
-spu_store_registers (int regno)
+spu_store_registers (struct regcache *regcache, int regno)
{
int fd;
CORE_ADDR addr;
if (regno == -1 || regno == SPU_PC_REGNUM)
{
char buf[4];
- collect_register (SPU_PC_REGNUM, buf);
+ collect_register (regcache, SPU_PC_REGNUM, buf);
store_ppc_memory (addr, buf, 4);
}
int i;
for (i = 0; i < SPU_NUM_CORE_REGS; i++)
- collect_register (i, buf + i*16);
+ collect_register (regcache, i, buf + i*16);
sprintf (annex, "%d/regs", fd);
spu_proc_xfer_spu (annex, NULL, buf, 0, sizeof buf);
{
int fd, ret;
CORE_ADDR addr;
- char annex[32];
+ char annex[32], lslr_annex[32], buf[32];
+ CORE_ADDR lslr;
/* We must be stopped on a spu_run system call. */
if (!parse_spufs_run (&fd, &addr))
/* Use the "mem" spufs file to access SPU local store. */
sprintf (annex, "%d/mem", fd);
ret = spu_proc_xfer_spu (annex, myaddr, NULL, memaddr, len);
+ if (ret > 0)
+ return ret == len ? 0 : EIO;
+
+ /* SPU local store access wraps the address around at the
+ local store limit. We emulate this here. To avoid needing
+ an extra access to retrieve the LSLR, we only do that after
+ trying the original address first, and getting end-of-file. */
+ sprintf (lslr_annex, "%d/lslr", fd);
+ memset (buf, 0, sizeof buf);
+ if (spu_proc_xfer_spu (lslr_annex, (unsigned char *)buf, NULL,
+ 0, sizeof buf) <= 0)
+ return ret;
+
+ lslr = strtoul (buf, NULL, 16);
+ ret = spu_proc_xfer_spu (annex, myaddr, NULL, memaddr & lslr, len);
+
return ret == len ? 0 : EIO;
}
{
int fd, ret;
CORE_ADDR addr;
- char annex[32];
+ char annex[32], lslr_annex[32], buf[32];
+ CORE_ADDR lslr;
/* We must be stopped on a spu_run system call. */
if (!parse_spufs_run (&fd, &addr))
/* Use the "mem" spufs file to access SPU local store. */
sprintf (annex, "%d/mem", fd);
ret = spu_proc_xfer_spu (annex, NULL, myaddr, memaddr, len);
+ if (ret > 0)
+ return ret == len ? 0 : EIO;
+
+ /* SPU local store access wraps the address around at the
+ local store limit. We emulate this here. To avoid needing
+ an extra access to retrieve the LSLR, we only do that after
+ trying the original address first, and getting end-of-file. */
+ sprintf (lslr_annex, "%d/lslr", fd);
+ memset (buf, 0, sizeof buf);
+ if (spu_proc_xfer_spu (lslr_annex, (unsigned char *)buf, NULL,
+ 0, sizeof buf) <= 0)
+ return ret;
+
+ lslr = strtoul (buf, NULL, 16);
+ ret = spu_proc_xfer_spu (annex, NULL, myaddr, memaddr & lslr, len);
+
return ret == len ? 0 : EIO;
}
/* Send signal to inferior. */
static void
-spu_send_signal (int signo)
+spu_request_interrupt (void)
{
- syscall (SYS_tkill, current_tid, signo);
+ struct thread_info *thr = get_first_thread ();
+
+ syscall (SYS_tkill, lwpid_of (thr), SIGINT);
}
-static const char *
-spu_arch_string (void)
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+spu_sw_breakpoint_from_kind (int kind, int *size)
{
- return "spu";
+ *size = sizeof breakpoint;
+ return breakpoint;
}
-\f
static struct target_ops spu_target_ops = {
spu_create_inferior,
+ NULL, /* arch_setup */
spu_attach,
spu_kill,
spu_detach,
+ spu_mourn,
+ spu_join,
spu_thread_alive,
spu_resume,
spu_wait,
spu_fetch_registers,
spu_store_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
spu_read_memory,
spu_write_memory,
spu_look_up_symbols,
- spu_send_signal,
+ spu_request_interrupt,
NULL,
+ NULL, /* supports_z_point_type */
NULL,
NULL,
+ NULL, /* stopped_by_sw_breakpoint */
+ NULL, /* supports_stopped_by_sw_breakpoint */
+ NULL, /* stopped_by_hw_breakpoint */
+ NULL, /* supports_stopped_by_hw_breakpoint */
+ NULL, /* supports_hardware_single_step */
NULL,
NULL,
NULL,
NULL,
- spu_arch_string,
+ spu_proc_xfer_spu,
+ hostio_last_error_from_errno,
+ NULL, /* qxfer_osdata */
+ NULL, /* qxfer_siginfo */
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* handle_new_gdb_connection */
+ NULL, /* handle_monitor_command */
+ NULL, /* core_of_thread */
+ NULL, /* read_loadmap */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* read_pc */
+ NULL, /* write_pc */
+ NULL, /* thread_stopped */
+ NULL, /* get_tib_address */
+ NULL, /* pause_all */
+ NULL, /* unpause_all */
+ NULL, /* stabilize_threads */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* supports_disable_randomization */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* qxfer_libraries_svr4 */
+ NULL, /* support_agent */
+ NULL, /* support_btrace */
+ NULL, /* enable_btrace */
+ NULL, /* disable_btrace */
+ NULL, /* read_btrace */
+ NULL, /* read_btrace_conf */
+ NULL, /* supports_range_stepping */
+ NULL, /* pid_to_exec_file */
+ NULL, /* multifs_open */
+ NULL, /* multifs_unlink */
+ NULL, /* multifs_readlink */
+ NULL, /* breakpoint_kind_from_pc */
+ spu_sw_breakpoint_from_kind,
};
void
initialize_low (void)
{
- static const unsigned char breakpoint[] = { 0x00, 0x00, 0x3f, 0xff };
-
set_target_ops (&spu_target_ops);
- set_breakpoint_data (breakpoint, sizeof breakpoint);
- init_registers ();
+ init_registers_spu ();
}