OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-old/] [gdb-7.1/] [gdb/] [spu-multiarch.c] - Rev 862

Go to most recent revision | Compare with Previous | Blame | View Log

/* Cell SPU GNU/Linux multi-architecture debugging support.
   Copyright (C) 2009, 2010 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 3 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, see <http://www.gnu.org/licenses/>.  */
 
#include "defs.h"
#include "gdbcore.h"
#include "gdbcmd.h"
#include "gdb_string.h"
#include "gdb_assert.h"
#include "arch-utils.h"
#include "observer.h"
#include "inferior.h"
#include "regcache.h"
#include "symfile.h"
#include "objfiles.h"
#include "solib.h"
#include "solist.h"
 
#include "ppc-tdep.h"
#include "ppc-linux-tdep.h"
#include "spu-tdep.h"
 
/* This module's target vector.  */
static struct target_ops spu_ops;
 
/* Number of SPE objects loaded into the current inferior.  */
static int spu_nr_solib;
 
/* Stand-alone SPE executable?  */
#define spu_standalone_p() \
  (symfile_objfile && symfile_objfile->obfd \
   && bfd_get_arch (symfile_objfile->obfd) == bfd_arch_spu)
 
/* PPU side system calls.  */
#define INSTR_SC	0x44000002
#define NR_spu_run	0x0116
 
/* 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 (ptid_t ptid, int *fd, CORE_ADDR *addr)
{
  enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch);
  struct gdbarch_tdep *tdep;
  struct regcache *regcache;
  char buf[4];
  CORE_ADDR pc;
  ULONGEST regval;
 
  /* If we're not on PPU, there's nothing to detect.  */
  if (gdbarch_bfd_arch_info (target_gdbarch)->arch != bfd_arch_powerpc)
    return 0;
 
  /* Get PPU-side registers.  */
  regcache = get_thread_arch_regcache (ptid, target_gdbarch);
  tdep = gdbarch_tdep (target_gdbarch);
 
  /* Fetch instruction preceding current NIP.  */
  if (target_read_memory (regcache_read_pc (regcache) - 4, buf, 4) != 0)
    return 0;
  /* It should be a "sc" instruction.  */
  if (extract_unsigned_integer (buf, 4, byte_order) != INSTR_SC)
    return 0;
  /* System call number should be NR_spu_run.  */
  regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum, &regval);
  if (regval != NR_spu_run)
    return 0;
 
  /* Register 3 contains fd, register 4 the NPC param pointer.  */
  regcache_cooked_read_unsigned (regcache, PPC_ORIG_R3_REGNUM, &regval);
  *fd = (int) regval;
  regcache_cooked_read_unsigned (regcache, tdep->ppc_gp0_regnum + 4, &regval);
  *addr = (CORE_ADDR) regval;
  return 1;
}
 
/* Find gdbarch for SPU context SPUFS_FD.  */
static struct gdbarch *
spu_gdbarch (int spufs_fd)
{
  struct gdbarch_info info;
  gdbarch_info_init (&info);
  info.bfd_arch_info = bfd_lookup_arch (bfd_arch_spu, bfd_mach_spu);
  info.byte_order = BFD_ENDIAN_BIG;
  info.osabi = GDB_OSABI_LINUX;
  info.tdep_info = (void *) &spufs_fd;
  return gdbarch_find_by_info (info);
}
 
/* Override the to_thread_architecture routine.  */
static struct gdbarch *
spu_thread_architecture (struct target_ops *ops, ptid_t ptid)
{
  int spufs_fd;
  CORE_ADDR spufs_addr;
 
  if (parse_spufs_run (ptid, &spufs_fd, &spufs_addr))
    return spu_gdbarch (spufs_fd);
 
  return target_gdbarch;
}
 
/* Override the to_region_ok_for_hw_watchpoint routine.  */
static int
spu_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
{
  struct target_ops *ops_beneath = find_target_beneath (&spu_ops);
  while (ops_beneath && !ops_beneath->to_region_ok_for_hw_watchpoint)
    ops_beneath = find_target_beneath (ops_beneath);
 
  /* We cannot watch SPU local store.  */
  if (SPUADDR_SPU (addr) != -1)
    return 0;
 
  if (ops_beneath)
    return ops_beneath->to_region_ok_for_hw_watchpoint (addr, len);
 
  return 0;
}
 
/* Override the to_fetch_registers routine.  */
static void
spu_fetch_registers (struct target_ops *ops,
		     struct regcache *regcache, int regno)
{
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
  struct target_ops *ops_beneath = find_target_beneath (ops);
  int spufs_fd;
  CORE_ADDR spufs_addr;
 
  /* This version applies only if we're currently in spu_run.  */
  if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
    {
      while (ops_beneath && !ops_beneath->to_fetch_registers)
	ops_beneath = find_target_beneath (ops_beneath);
 
      gdb_assert (ops_beneath);
      ops_beneath->to_fetch_registers (ops_beneath, regcache, regno);
      return;
    }
 
  /* We must be stopped on a spu_run system call.  */
  if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_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, byte_order, spufs_fd);
      regcache_raw_supply (regcache, SPU_ID_REGNUM, buf);
    }
 
  /* The NPC register is found in PPC memory at SPUFS_ADDR.  */
  if (regno == -1 || regno == SPU_PC_REGNUM)
    {
      char buf[4];
 
      if (target_read (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
		       buf, spufs_addr, sizeof buf) == sizeof buf)
	regcache_raw_supply (regcache, SPU_PC_REGNUM, buf);
    }
 
  /* The GPRs are found in the "regs" spufs file.  */
  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
    {
      char buf[16 * SPU_NUM_GPRS], annex[32];
      int i;
 
      xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
      if (target_read (ops_beneath, TARGET_OBJECT_SPU, annex,
		       buf, 0, sizeof buf) == sizeof buf)
	for (i = 0; i < SPU_NUM_GPRS; i++)
	  regcache_raw_supply (regcache, i, buf + i*16);
    }
}
 
/* Override the to_store_registers routine.  */
static void
spu_store_registers (struct target_ops *ops,
		     struct regcache *regcache, int regno)
{
  struct gdbarch *gdbarch = get_regcache_arch (regcache);
  struct target_ops *ops_beneath = find_target_beneath (ops);
  int spufs_fd;
  CORE_ADDR spufs_addr;
 
  /* This version applies only if we're currently in spu_run.  */
  if (gdbarch_bfd_arch_info (gdbarch)->arch != bfd_arch_spu)
    {
      while (ops_beneath && !ops_beneath->to_fetch_registers)
	ops_beneath = find_target_beneath (ops_beneath);
 
      gdb_assert (ops_beneath);
      ops_beneath->to_store_registers (ops_beneath, regcache, regno);
      return;
    }
 
  /* We must be stopped on a spu_run system call.  */
  if (!parse_spufs_run (inferior_ptid, &spufs_fd, &spufs_addr))
    return;
 
  /* The NPC register is found in PPC memory at SPUFS_ADDR.  */
  if (regno == -1 || regno == SPU_PC_REGNUM)
    {
      char buf[4];
      regcache_raw_collect (regcache, SPU_PC_REGNUM, buf);
 
      target_write (ops_beneath, TARGET_OBJECT_MEMORY, NULL,
		    buf, spufs_addr, sizeof buf);
    }
 
  /* The GPRs are found in the "regs" spufs file.  */
  if (regno == -1 || (regno >= 0 && regno < SPU_NUM_GPRS))
    {
      char buf[16 * SPU_NUM_GPRS], annex[32];
      int i;
 
      for (i = 0; i < SPU_NUM_GPRS; i++)
	regcache_raw_collect (regcache, i, buf + i*16);
 
      xsnprintf (annex, sizeof annex, "%d/regs", spufs_fd);
      target_write (ops_beneath, TARGET_OBJECT_SPU, annex,
		    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)
{
  struct target_ops *ops_beneath = find_target_beneath (ops);
  while (ops_beneath && !ops_beneath->to_xfer_partial)
    ops_beneath = find_target_beneath (ops_beneath);
  gdb_assert (ops_beneath);
 
  /* Use the "mem" spufs file to access SPU local store.  */
  if (object == TARGET_OBJECT_MEMORY)
    {
      int fd = SPUADDR_SPU (offset);
      CORE_ADDR addr = SPUADDR_ADDR (offset);
      char mem_annex[32];
 
      if (fd >= 0 && addr < SPU_LS_SIZE)
	{
	  xsnprintf (mem_annex, sizeof mem_annex, "%d/mem", fd);
	  return ops_beneath->to_xfer_partial (ops_beneath, TARGET_OBJECT_SPU,
					       mem_annex, readbuf, writebuf,
					       addr, len);
	}
    }
 
  return ops_beneath->to_xfer_partial (ops_beneath, object, annex,
				       readbuf, writebuf, offset, len);
}
 
/* Override the to_search_memory routine.  */
static int
spu_search_memory (struct target_ops* ops,
		   CORE_ADDR start_addr, ULONGEST search_space_len,
		   const gdb_byte *pattern, ULONGEST pattern_len,
		   CORE_ADDR *found_addrp)
{
  struct target_ops *ops_beneath = find_target_beneath (ops);
  while (ops_beneath && !ops_beneath->to_search_memory)
    ops_beneath = find_target_beneath (ops_beneath);
 
  /* For SPU local store, always fall back to the simple method.  Likewise
     if we do not have any target-specific special implementation.  */
  if (!ops_beneath || SPUADDR_SPU (start_addr) >= 0)
    return simple_search_memory (ops,
				 start_addr, search_space_len,
				 pattern, pattern_len, found_addrp);
 
  return ops_beneath->to_search_memory (ops_beneath,
					start_addr, search_space_len,
					pattern, pattern_len, found_addrp);
}
 
 
/* Push and pop the SPU multi-architecture support target.  */
 
static void
spu_multiarch_activate (void)
{
  /* If GDB was configured without SPU architecture support,
     we cannot install SPU multi-architecture support either.  */
  if (spu_gdbarch (-1) == NULL)
    return;
 
  push_target (&spu_ops);
 
  /* Make sure the thread architecture is re-evaluated.  */
  registers_changed ();
}
 
static void
spu_multiarch_deactivate (void)
{
  unpush_target (&spu_ops);
 
  /* Make sure the thread architecture is re-evaluated.  */
  registers_changed ();
}
 
static void
spu_multiarch_inferior_created (struct target_ops *ops, int from_tty)
{
  if (spu_standalone_p ())
    spu_multiarch_activate ();
}
 
static void
spu_multiarch_solib_loaded (struct so_list *so)
{
  if (!spu_standalone_p ())
    if (so->abfd && bfd_get_arch (so->abfd) == bfd_arch_spu)
      if (spu_nr_solib++ == 0)
	spu_multiarch_activate ();
}
 
static void
spu_multiarch_solib_unloaded (struct so_list *so)
{
  if (!spu_standalone_p ())
    if (so->abfd && bfd_get_arch (so->abfd) == bfd_arch_spu)
      if (--spu_nr_solib == 0)
	spu_multiarch_deactivate ();
}
 
static void
spu_mourn_inferior (struct target_ops *ops)
{
  struct target_ops *ops_beneath = find_target_beneath (ops);
  while (ops_beneath && !ops_beneath->to_mourn_inferior)
    ops_beneath = find_target_beneath (ops_beneath);
 
  gdb_assert (ops_beneath);
  ops_beneath->to_mourn_inferior (ops_beneath);
  spu_multiarch_deactivate ();
}
 
 
/* Initialize the SPU multi-architecture support target.  */
 
static void
init_spu_ops (void)
{
  spu_ops.to_shortname = "spu";
  spu_ops.to_longname = "SPU multi-architecture support.";
  spu_ops.to_doc = "SPU multi-architecture support.";
  spu_ops.to_mourn_inferior = spu_mourn_inferior;
  spu_ops.to_fetch_registers = spu_fetch_registers;
  spu_ops.to_store_registers = spu_store_registers;
  spu_ops.to_xfer_partial = spu_xfer_partial;
  spu_ops.to_search_memory = spu_search_memory;
  spu_ops.to_region_ok_for_hw_watchpoint = spu_region_ok_for_hw_watchpoint;
  spu_ops.to_thread_architecture = spu_thread_architecture;
  spu_ops.to_stratum = arch_stratum;
  spu_ops.to_magic = OPS_MAGIC;
}
 
void
_initialize_spu_multiarch (void)
{
  /* Install ourselves on the target stack.  */
  init_spu_ops ();
  add_target (&spu_ops);
 
  /* Install observers to watch for SPU objects.  */
  observer_attach_inferior_created (spu_multiarch_inferior_created);
  observer_attach_solib_loaded (spu_multiarch_solib_loaded);
  observer_attach_solib_unloaded (spu_multiarch_solib_unloaded);
}
 
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.