This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[rfc] [3/9] Cell multi-arch: Support TARGET_OBJECT_SPU object type


Hello,

this patch adds support for the TARGET_OBJECT_SPU object type to
the Linux native and gdbserver targets and core files.  (This object
type is already supported by the SPU native target today.)

This object type is used to access all SPU-related information 
(including memory and registers) on a PowerPC target.  It is 
implemented by accessing the spufs special file system (native
and gdbserver) or the "SPU/" note sections in core files.

Bye,
Ulrich


ChangeLog:

	* linux-nat.c: Include <sys/vfs.h>.
	(SPUFS_MAGIC): Define.
	(spu_enumerate_spu_ids): New function.
	(linux_proc_xfer_spu): New function.
	(linux_xfer_partial): Handle TARGET_OBJECT_SPU.

	(iterate_over_spus): New function.
	(struct linux_spu_corefile_data): New data type.
	(linux_spu_corefile_callback): New function.
	(linux_spu_make_corefile_notes): New function.
	(linux_nat_make_corefile_notes): Call it.

	* corelow.c (struct spuid_list): New data type.
	(add_to_spuid_list): New function.
	(core_xfer_partial): Handle TARGET_OBJECT_SPU.

gdbserver/ChangeLog:

	* linux-low.c: Include <sys/stat.h> and <sys/vfs.h>.
	(SPUFS_MAGIC): Define.
	(spu_enumerate_spu_ids): New function.
	(linux_qxfer_spu): New function.
	(linux_target_ops): Install linux_qxfer_spu.



Index: src/gdb/corelow.c
===================================================================
--- src.orig/gdb/corelow.c
+++ src/gdb/corelow.c
@@ -592,6 +592,36 @@ core_files_info (struct target_ops *t)
   print_section_info (core_data, core_bfd);
 }
 
+struct spuid_list
+{
+  gdb_byte *buf;
+  ULONGEST offset;
+  LONGEST len;
+  ULONGEST pos;
+  ULONGEST written;
+};
+
+static void
+add_to_spuid_list (bfd *abfd, asection *asect, void *list_p)
+{
+  struct spuid_list *list = list_p;
+  enum bfd_endian byte_order
+    = bfd_big_endian (abfd)? BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
+  int fd, pos = 0;
+
+  sscanf (bfd_section_name (abfd, asect), "SPU/%d/regs%n", &fd, &pos);
+  if (pos == 0)
+    return;
+
+  if (list->pos >= list->offset && list->pos + 4 <= list->offset + list->len)
+    {
+      store_unsigned_integer (list->buf + list->pos - list->offset,
+			      4, byte_order, fd);
+      list->written += 4;
+    }
+  list->pos += 4;
+}
+
 static LONGEST
 core_xfer_partial (struct target_ops *ops, enum target_object object,
 		   const char *annex, gdb_byte *readbuf,
@@ -682,6 +712,53 @@ core_xfer_partial (struct target_ops *op
 	}
       /* FALL THROUGH */
 
+    case TARGET_OBJECT_SPU:
+      if (readbuf && annex)
+	{
+	  /* When the SPU contexts are stored in a core file, BFD
+	     represents this with a fake section called "SPU/<annex>".  */
+
+	  struct bfd_section *section;
+	  bfd_size_type size;
+	  char *contents;
+
+	  char sectionstr[100];
+	  xsnprintf (sectionstr, sizeof sectionstr, "SPU/%s", annex);
+
+	  section = bfd_get_section_by_name (core_bfd, sectionstr);
+	  if (section == NULL)
+	    return -1;
+
+	  size = bfd_section_size (core_bfd, section);
+	  if (offset >= size)
+	    return 0;
+	  size -= offset;
+	  if (size > len)
+	    size = len;
+	  if (size > 0
+	      && !bfd_get_section_contents (core_bfd, section, readbuf,
+					    (file_ptr) offset, size))
+	    {
+	      warning (_("Couldn't read SPU section in core file."));
+	      return -1;
+	    }
+
+	  return size;
+	}
+      else if (readbuf)
+	{
+	  /* NULL annex requests list of all present spuids.  */
+	  struct spuid_list list;
+	  list.buf = readbuf;
+	  list.offset = offset;
+	  list.len = len;
+	  list.pos = 0;
+	  list.written = 0;
+	  bfd_map_over_sections (core_bfd, add_to_spuid_list, &list);
+	  return list.written;
+	}
+      return -1;
+
     default:
       if (ops->beneath != NULL)
 	return ops->beneath->to_xfer_partial (ops->beneath, object, annex,
Index: src/gdb/gdbserver/linux-low.c
===================================================================
--- src.orig/gdb/gdbserver/linux-low.c
+++ src/gdb/gdbserver/linux-low.c
@@ -40,6 +40,12 @@
 #include <pwd.h>
 #include <sys/types.h>
 #include <dirent.h>
+#include <sys/stat.h>
+#include <sys/vfs.h>
+
+#ifndef SPUFS_MAGIC
+#define SPUFS_MAGIC 0x23c9b64e
+#endif
 
 #ifndef PTRACE_GETSIGINFO
 # define PTRACE_GETSIGINFO 0x4202
@@ -3025,6 +3031,100 @@ linux_supports_multi_process (void)
   return 1;
 }
 
+
+/* Enumerate spufs IDs for process PID.  */
+static int
+spu_enumerate_spu_ids (long pid, unsigned char *buf, CORE_ADDR offset, int len)
+{
+  int pos = 0;
+  int written = 0;
+  char path[128];
+  DIR *dir;
+  struct dirent *entry;
+
+  sprintf (path, "/proc/%ld/fd", pid);
+  dir = opendir (path);
+  if (!dir)
+    return -1;
+
+  rewinddir (dir);
+  while ((entry = readdir (dir)) != NULL)
+    {
+      struct stat st;
+      struct statfs stfs;
+      int fd;
+
+      fd = atoi (entry->d_name);
+      if (!fd)
+        continue;
+
+      sprintf (path, "/proc/%ld/fd/%d", pid, fd);
+      if (stat (path, &st) != 0)
+        continue;
+      if (!S_ISDIR (st.st_mode))
+        continue;
+
+      if (statfs (path, &stfs) != 0)
+        continue;
+      if (stfs.f_type != SPUFS_MAGIC)
+        continue;
+
+      if (pos >= offset && pos + 4 <= offset + len)
+        {
+          *(unsigned int *)(buf + pos - offset) = fd;
+          written += 4;
+        }
+      pos += 4;
+    }
+
+  closedir (dir);
+  return written;
+}
+
+/* Implements the to_xfer_partial interface for the TARGET_OBJECT_SPU
+   object type, using the /proc file system.  */
+static int
+linux_qxfer_spu (const char *annex, unsigned char *readbuf,
+		 unsigned const char *writebuf,
+		 CORE_ADDR offset, int len)
+{
+  long pid = lwpid_of (get_thread_lwp (current_inferior));
+  char buf[128];
+  int fd = 0;
+  int ret = 0;
+
+  if (!writebuf && !readbuf)
+    return -1;
+
+  if (!*annex)
+    {
+      if (!readbuf)
+	return -1;
+      else
+	return spu_enumerate_spu_ids (pid, readbuf, offset, len);
+    }
+
+  sprintf (buf, "/proc/%ld/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 0;
+    }
+
+  if (writebuf)
+    ret = write (fd, writebuf, (size_t) len);
+  else
+    ret = read (fd, readbuf, (size_t) len);
+
+  close (fd);
+  return ret;
+}
+
 static struct target_ops linux_target_ops = {
   linux_create_inferior,
   linux_attach,
@@ -3055,7 +3155,7 @@ static struct target_ops linux_target_op
 #else
   NULL,
 #endif
-  NULL,
+  linux_qxfer_spu,
   hostio_last_error_from_errno,
   linux_qxfer_osdata,
   linux_xfer_siginfo,
Index: src/gdb/linux-nat.c
===================================================================
--- src.orig/gdb/linux-nat.c
+++ src/gdb/linux-nat.c
@@ -54,6 +54,11 @@
 #include "gdb_dirent.h"
 #include "xml-support.h"
 #include "terminal.h"
+#include <sys/vfs.h>
+
+#ifndef SPUFS_MAGIC
+#define SPUFS_MAGIC 0x23c9b64e
+#endif
 
 #ifdef HAVE_PERSONALITY
 # include <sys/personality.h>
@@ -3661,6 +3666,119 @@ linux_nat_corefile_thread_callback (stru
   return 0;
 }
 
+/* Enumerate spufs IDs for process PID.  */
+
+static void
+iterate_over_spus (int pid, void (*callback) (void *, int), void *data)
+{
+  char path[128];
+  DIR *dir;
+  struct dirent *entry;
+
+  xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+  dir = opendir (path);
+  if (!dir)
+    return;
+
+  rewinddir (dir);
+  while ((entry = readdir (dir)) != NULL)
+    {
+      struct stat st;
+      struct statfs stfs;
+      int fd;
+
+      fd = atoi (entry->d_name);
+      if (!fd)
+	continue;
+
+      xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+      if (stat (path, &st) != 0)
+	continue;
+      if (!S_ISDIR (st.st_mode))
+	continue;
+
+      if (statfs (path, &stfs) != 0)
+	continue;
+      if (stfs.f_type != SPUFS_MAGIC)
+	continue;
+
+      callback (data, fd);
+    }
+
+  closedir (dir);
+}
+
+/* Generate corefile notes for SPU contexts.  */
+
+struct linux_spu_corefile_data
+{
+  bfd *obfd;
+  char *note_data;
+  int *note_size;
+};
+
+static void
+linux_spu_corefile_callback (void *data, int fd)
+{
+  struct linux_spu_corefile_data *args = data;
+  int i;
+
+  static const char *spu_files[] =
+    {
+      "object-id",
+      "mem",
+      "regs",
+      "fpcr",
+      "lslr",
+      "decr",
+      "decr_status",
+      "signal1",
+      "signal1_type",
+      "signal2",
+      "signal2_type",
+      "event_mask",
+      "event_status",
+      "mbox_info",
+      "ibox_info",
+      "wbox_info",
+      "dma_info",
+      "proxydma_info",
+   };
+
+  for (i = 0; i < sizeof (spu_files) / sizeof (spu_files[0]); i++)
+    {
+      char annex[32], note_name[32];
+      gdb_byte *spu_data;
+      LONGEST spu_len;
+
+      xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[i]);
+      spu_len = target_read_alloc (&current_target, TARGET_OBJECT_SPU,
+				   annex, &spu_data);
+      if (spu_len > 0)
+	{
+	  xsnprintf (note_name, sizeof note_name, "SPU/%s", annex);
+	  args->note_data = elfcore_write_note (args->obfd, args->note_data,
+						args->note_size, note_name,
+						NT_SPU, spu_data, spu_len);
+	  xfree (spu_data);
+	}
+    }
+}
+
+static char *
+linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size)
+{
+  struct linux_spu_corefile_data args;
+  args.obfd = obfd;
+  args.note_data = note_data;
+  args.note_size = note_size;
+
+  iterate_over_spus (PIDGET (inferior_ptid),
+		     linux_spu_corefile_callback, &args);
+
+  return args.note_data;
+}
+
 /* Fills the "to_make_corefile_note" target vector.  Builds the note
    section for a corefile, and returns it in a malloc buffer.  */
 
@@ -3722,6 +3840,8 @@ linux_nat_make_corefile_notes (bfd *obfd
       xfree (auxv);
     }
 
+  note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size);
+
   make_cleanup (xfree, note_data);
   return note_data;
 }
@@ -4055,6 +4175,100 @@ linux_proc_xfer_partial (struct target_o
   return ret;
 }
 
+
+/* Enumerate spufs IDs for process PID.  */
+static LONGEST
+spu_enumerate_spu_ids (int pid, gdb_byte *buf, ULONGEST offset, LONGEST len)
+{
+  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
+  LONGEST pos = 0;
+  LONGEST written = 0;
+  char path[128];
+  DIR *dir;
+  struct dirent *entry;
+
+  xsnprintf (path, sizeof path, "/proc/%d/fd", pid);
+  dir = opendir (path);
+  if (!dir)
+    return -1;
+
+  rewinddir (dir);
+  while ((entry = readdir (dir)) != NULL)
+    {
+      struct stat st;
+      struct statfs stfs;
+      int fd;
+
+      fd = atoi (entry->d_name);
+      if (!fd)
+	continue;
+
+      xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd);
+      if (stat (path, &st) != 0)
+	continue;
+      if (!S_ISDIR (st.st_mode))
+	continue;
+
+      if (statfs (path, &stfs) != 0)
+	continue;
+      if (stfs.f_type != SPUFS_MAGIC)
+	continue;
+
+      if (pos >= offset && pos + 4 <= offset + len)
+	{
+	  store_unsigned_integer (buf + pos - offset, 4, byte_order, fd);
+	  written += 4;
+	}
+      pos += 4;
+    }
+
+  closedir (dir);
+  return written;
+}
+
+/* Implement the to_xfer_partial interface for the TARGET_OBJECT_SPU
+   object type, using the /proc file system.  */
+static LONGEST
+linux_proc_xfer_spu (struct target_ops *ops, enum target_object object,
+		     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)
+    {
+      if (!readbuf)
+	return -1;
+      else
+	return spu_enumerate_spu_ids (pid, readbuf, offset, len);
+    }
+
+  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 0;
+    }
+
+  if (writebuf)
+    ret = write (fd, writebuf, (size_t) len);
+  else if (readbuf)
+    ret = read (fd, readbuf, (size_t) len);
+
+  close (fd);
+  return ret;
+}
+
+
 /* Parse LINE as a signal set and add its set bits to SIGS.  */
 
 static void
@@ -4260,6 +4474,10 @@ linux_xfer_partial (struct target_ops *o
     return linux_nat_xfer_osdata (ops, object, annex, readbuf, writebuf,
                                offset, len);
 
+  if (object == TARGET_OBJECT_SPU)
+    return linux_proc_xfer_spu (ops, object, annex, readbuf, writebuf,
+				offset, len);
+
   /* GDB calculates all the addresses in possibly larget width of the address.
      Address width needs to be masked before its final use - either by
      linux_proc_xfer_partial or inf_ptrace_xfer_partial.
-- 
  Dr. Ulrich Weigand
  GNU Toolchain for Linux on System z and Cell BE
  Ulrich.Weigand@de.ibm.com


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]