OSDN Git Service

sim: bfin: new port
[pf3gnuchains/sourceware.git] / sim / bfin / interp.c
diff --git a/sim/bfin/interp.c b/sim/bfin/interp.c
new file mode 100644 (file)
index 0000000..1f8681d
--- /dev/null
@@ -0,0 +1,1241 @@
+/* Simulator for Analog Devices Blackfin processors.
+
+   Copyright (C) 2005-2011 Free Software Foundation, Inc.
+   Contributed by Analog Devices, Inc.
+
+   This file is part of simulators.
+
+   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 "config.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "gdb/callback.h"
+#include "gdb/signals.h"
+#include "sim-main.h"
+#include "sim-hw.h"
+
+#include "targ-vals.h"
+
+/* The numbers here do not matter.  They just need to be unique.  */
+#define CB_SYS_ioctl        201
+#define CB_SYS_mmap2        202
+#define CB_SYS_munmap       203
+#define CB_SYS_dup2         204
+#define CB_SYS_getuid       205
+#define CB_SYS_getuid32     206
+#define CB_SYS_getgid       207
+#define CB_SYS_getgid32     208
+#define CB_SYS_setuid       209
+#define CB_SYS_setuid32     210
+#define CB_SYS_setgid       211
+#define CB_SYS_setgid32     212
+#define CB_SYS_pread        213
+#define CB_SYS__llseek      214
+#define CB_SYS_getcwd       215
+#define CB_SYS_stat64       216
+#define CB_SYS_lstat64      217
+#define CB_SYS_fstat64      218
+#define CB_SYS_ftruncate64  219
+#define CB_SYS_gettimeofday 220
+#define CB_SYS_access       221
+#include "linux-targ-map.h"
+#include "linux-fixed-code.h"
+
+#include "elf/common.h"
+#include "elf/external.h"
+#include "elf/internal.h"
+#include "elf/bfin.h"
+#include "elf-bfd.h"
+
+#include "dv-bfin_cec.h"
+#include "dv-bfin_mmu.h"
+
+#ifndef HAVE_GETUID
+# define getuid() 0
+#endif
+#ifndef HAVE_GETGID
+# define getgid() 0
+#endif
+#ifndef HAVE_GETEUID
+# define geteuid() 0
+#endif
+#ifndef HAVE_GETEGID
+# define getegid() 0
+#endif
+#ifndef HAVE_SETUID
+# define setuid(uid) -1
+#endif
+#ifndef HAVE_SETGID
+# define setgid(gid) -1
+#endif
+
+static const char stat_map_32[] =
+/* Linux kernel 32bit layout:  */
+"st_dev,2:space,2:st_ino,4:st_mode,2:st_nlink,2:st_uid,2:st_gid,2:st_rdev,2:"
+"space,2:st_size,4:st_blksize,4:st_blocks,4:st_atime,4:st_atimensec,4:"
+"st_mtime,4:st_mtimensec,4:st_ctime,4:st_ctimensec,4:space,4:space,4";
+/* uClibc public ABI 32bit layout:
+"st_dev,8:space,2:space,2:st_ino,4:st_mode,4:st_nlink,4:st_uid,4:st_gid,4:"
+"st_rdev,8:space,2:space,2:st_size,4:st_blksiez,4:st_blocks,4:st_atime,4:"
+"st_atimensec,4:st_mtime,4:st_mtimensec,4:st_ctime,4:st_ctimensec,4:space,4:"
+"space,4";  */
+static const char stat_map_64[] =
+"st_dev,8:space,4:space,4:st_mode,4:st_nlink,4:st_uid,4:st_gid,4:st_rdev,8:"
+"space,4:st_size,8:st_blksize,4:st_blocks,8:st_atime,4:st_atimensec,4:"
+"st_mtime,4:st_mtimensec,4:st_ctime,4:st_ctimensec,4:st_ino,8";
+
+/* Count the number of arguments in an argv.  */
+static int
+count_argc (const char * const *argv)
+{
+  int i;
+
+  if (! argv)
+    return -1;
+
+  for (i = 0; argv[i] != NULL; ++i)
+    continue;
+  return i;
+}
+
+/* Read/write functions for system call interface.  */
+
+static int
+syscall_read_mem (host_callback *cb, struct cb_syscall *sc,
+                 unsigned long taddr, char *buf, int bytes)
+{
+  SIM_DESC sd = (SIM_DESC) sc->p1;
+  SIM_CPU *cpu = (SIM_CPU *) sc->p2;
+
+  MAYBE_TRACE (CORE, cpu, "DBUS FETCH (syscall) %i bytes @ 0x%08lx", bytes, taddr);
+
+  return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes);
+}
+
+static int
+syscall_write_mem (host_callback *cb, struct cb_syscall *sc,
+                 unsigned long taddr, const char *buf, int bytes)
+{
+  SIM_DESC sd = (SIM_DESC) sc->p1;
+  SIM_CPU *cpu = (SIM_CPU *) sc->p2;
+
+  MAYBE_TRACE (CORE, cpu, "DBUS STORE (syscall) %i bytes @ 0x%08lx", bytes, taddr);
+
+  return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes);
+}
+
+/* Simulate a monitor trap, put the result into r0 and errno into r1
+   return offset by which to adjust pc.  */
+
+void
+bfin_syscall (SIM_CPU *cpu)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  const char * const *argv = (void *)STATE_PROG_ARGV (sd);
+  host_callback *cb = STATE_CALLBACK (sd);
+  bu32 args[6];
+  CB_SYSCALL sc;
+  char *p;
+  char _tbuf[512], *tbuf = _tbuf;
+  int fmt_ret_hex = 0;
+
+  CB_SYSCALL_INIT (&sc);
+
+  if (STATE_ENVIRONMENT (sd) == USER_ENVIRONMENT)
+    {
+      /* Linux syscall.  */
+      sc.func = PREG (0);
+      sc.arg1 = args[0] = DREG (0);
+      sc.arg2 = args[1] = DREG (1);
+      sc.arg3 = args[2] = DREG (2);
+      sc.arg4 = args[3] = DREG (3);
+      /*sc.arg5 =*/ args[4] = DREG (4);
+      /*sc.arg6 =*/ args[5] = DREG (5);
+    }
+  else
+    {
+      /* libgloss syscall.  */
+      sc.func = PREG (0);
+      sc.arg1 = args[0] = GET_LONG (DREG (0));
+      sc.arg2 = args[1] = GET_LONG (DREG (0) + 4);
+      sc.arg3 = args[2] = GET_LONG (DREG (0) + 8);
+      sc.arg4 = args[3] = GET_LONG (DREG (0) + 12);
+      /*sc.arg5 =*/ args[4] = GET_LONG (DREG (0) + 16);
+      /*sc.arg6 =*/ args[5] = GET_LONG (DREG (0) + 20);
+    }
+  sc.p1 = (PTR) sd;
+  sc.p2 = (PTR) cpu;
+  sc.read_mem = syscall_read_mem;
+  sc.write_mem = syscall_write_mem;
+
+  /* Common cb_syscall() handles most functions.  */
+  switch (cb_target_to_host_syscall (cb, sc.func))
+    {
+    case CB_SYS_exit:
+      tbuf += sprintf (tbuf, "exit(%i)", args[0]);
+      sim_engine_halt (sd, cpu, NULL, PCREG, sim_exited, sc.arg1);
+
+#ifdef CB_SYS_argc
+    case CB_SYS_argc:
+      tbuf += sprintf (tbuf, "argc()");
+      sc.result = count_argc (argv);
+      break;
+    case CB_SYS_argnlen:
+      {
+      tbuf += sprintf (tbuf, "argnlen(%u)", args[0]);
+       if (sc.arg1 < count_argc (argv))
+         sc.result = strlen (argv[sc.arg1]);
+       else
+         sc.result = -1;
+      }
+      break;
+    case CB_SYS_argn:
+      {
+       tbuf += sprintf (tbuf, "argn(%u)", args[0]);
+       if (sc.arg1 < count_argc (argv))
+         {
+           const char *argn = argv[sc.arg1];
+           int len = strlen (argn);
+           int written = sc.write_mem (cb, &sc, sc.arg2, argn, len + 1);
+           if (written == len + 1)
+             sc.result = sc.arg2;
+           else
+             sc.result = -1;
+         }
+       else
+         sc.result = -1;
+      }
+      break;
+#endif
+
+    case CB_SYS_gettimeofday:
+      {
+       struct timeval _tv, *tv = &_tv;
+       struct timezone _tz, *tz = &_tz;
+
+       tbuf += sprintf (tbuf, "gettimeofday(%#x, %#x)", args[0], args[1]);
+
+       if (sc.arg1 == 0)
+         tv = NULL;
+       if (sc.arg2 == 0)
+         tz = NULL;
+       sc.result = gettimeofday (tv, tz);
+
+       if (sc.result == 0)
+         {
+           bu32 t;
+
+           if (tv)
+             {
+               t = tv->tv_sec;
+               sc.write_mem (cb, &sc, sc.arg1, (void *)&t, 4);
+               t = tv->tv_usec;
+               sc.write_mem (cb, &sc, sc.arg1 + 4, (void *)&t, 4);
+             }
+
+           if (sc.arg2)
+             {
+               t = tz->tz_minuteswest;
+               sc.write_mem (cb, &sc, sc.arg1, (void *)&t, 4);
+               t = tz->tz_dsttime;
+               sc.write_mem (cb, &sc, sc.arg1 + 4, (void *)&t, 4);
+             }
+         }
+       else
+         goto sys_finish;
+      }
+      break;
+
+    case CB_SYS_ioctl:
+      /* XXX: hack just enough to get basic stdio w/uClibc ...  */
+      tbuf += sprintf (tbuf, "ioctl(%i, %#x, %u)", args[0], args[1], args[2]);
+      if (sc.arg2 == 0x5401)
+       {
+         sc.result = !isatty (sc.arg1);
+         sc.errcode = 0;
+       }
+      else
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EINVAL;
+       }
+      break;
+
+    case CB_SYS_mmap2:
+      {
+       static bu32 heap = BFIN_DEFAULT_MEM_SIZE / 2;
+
+       fmt_ret_hex = 1;
+       tbuf += sprintf (tbuf, "mmap2(%#x, %u, %#x, %#x, %i, %u)",
+                        args[0], args[1], args[2], args[3], args[4], args[5]);
+
+       sc.errcode = 0;
+
+       if (sc.arg4 & 0x20 /*MAP_ANONYMOUS*/)
+         /* XXX: We don't handle zeroing, but default is all zeros.  */;
+       else if (args[4] >= MAX_CALLBACK_FDS)
+         sc.errcode = TARGET_ENOSYS;
+       else
+         {
+           char *data = xmalloc (sc.arg2);
+
+           /* XXX: Should add a cb->pread.  */
+           if (pread (cb->fdmap[args[4]], data, sc.arg2, args[5] << 12) == sc.arg2)
+             sc.write_mem (cb, &sc, heap, data, sc.arg2);
+           else
+             sc.errcode = TARGET_EINVAL;
+
+           free (data);
+         }
+
+       if (sc.errcode)
+         {
+           sc.result = -1;
+           break;
+         }
+
+       sc.result = heap;
+       heap += sc.arg2;
+       /* Keep it page aligned.  */
+       heap = ALIGN (heap, 4096);
+
+       break;
+      }
+
+    case CB_SYS_munmap:
+      /* XXX: meh, just lie for mmap().  */
+      tbuf += sprintf (tbuf, "munmap(%#x, %u)", args[0], args[1]);
+      sc.result = 0;
+      break;
+
+    case CB_SYS_dup2:
+      tbuf += sprintf (tbuf, "dup2(%i, %i)", args[0], args[1]);
+      if (sc.arg1 >= MAX_CALLBACK_FDS || sc.arg2 >= MAX_CALLBACK_FDS)
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EINVAL;
+       }
+      else
+       {
+         sc.result = dup2 (cb->fdmap[sc.arg1], cb->fdmap[sc.arg2]);
+         goto sys_finish;
+       }
+      break;
+
+    case CB_SYS__llseek:
+      tbuf += sprintf (tbuf, "llseek(%i, %u, %u, %#x, %u)",
+                      args[0], args[1], args[2], args[3], args[4]);
+      sc.func = TARGET_LINUX_SYS_lseek;
+      if (sc.arg2)
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EINVAL;
+       }
+      else
+       {
+         sc.arg2 = sc.arg3;
+         sc.arg3 = args[4];
+         cb_syscall (cb, &sc);
+         if (sc.result != -1)
+           {
+             bu32 z = 0;
+             sc.write_mem (cb, &sc, args[3], (void *)&sc.result, 4);
+             sc.write_mem (cb, &sc, args[3] + 4, (void *)&z, 4);
+           }
+       }
+      break;
+
+    /* XXX: Should add a cb->pread.  */
+    case CB_SYS_pread:
+      tbuf += sprintf (tbuf, "pread(%i, %#x, %u, %i)",
+                      args[0], args[1], args[2], args[3]);
+      if (sc.arg1 >= MAX_CALLBACK_FDS)
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EINVAL;
+       }
+      else
+       {
+         long old_pos, read_result, read_errcode;
+
+         /* Get current filepos.  */
+         sc.func = TARGET_LINUX_SYS_lseek;
+         sc.arg2 = 0;
+         sc.arg3 = SEEK_CUR;
+         cb_syscall (cb, &sc);
+         if (sc.result == -1)
+           break;
+         old_pos = sc.result;
+
+         /* Move to the new pos.  */
+         sc.func = TARGET_LINUX_SYS_lseek;
+         sc.arg2 = args[3];
+         sc.arg3 = SEEK_SET;
+         cb_syscall (cb, &sc);
+         if (sc.result == -1)
+           break;
+
+         /* Read the data.  */
+         sc.func = TARGET_LINUX_SYS_read;
+         sc.arg2 = args[1];
+         sc.arg3 = args[2];
+         cb_syscall (cb, &sc);
+         read_result = sc.result;
+         read_errcode = sc.errcode;
+
+         /* Move back to the old pos.  */
+         sc.func = TARGET_LINUX_SYS_lseek;
+         sc.arg2 = old_pos;
+         sc.arg3 = SEEK_SET;
+         cb_syscall (cb, &sc);
+
+         sc.result = read_result;
+         sc.errcode = read_errcode;
+       }
+      break;
+
+    case CB_SYS_getcwd:
+      tbuf += sprintf (tbuf, "getcwd(%#x, %u)", args[0], args[1]);
+
+      p = alloca (sc.arg2);
+      if (getcwd (p, sc.arg2) == NULL)
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EINVAL;
+       }
+      else
+       {
+         sc.write_mem (cb, &sc, sc.arg1, p, sc.arg2);
+         sc.result = sc.arg1;
+       }
+      break;
+
+    case CB_SYS_stat64:
+      tbuf += sprintf (tbuf, "stat64(%#x, %u)", args[0], args[1]);
+      cb->stat_map = stat_map_64;
+      sc.func = TARGET_LINUX_SYS_stat;
+      cb_syscall (cb, &sc);
+      cb->stat_map = stat_map_32;
+      break;
+    case CB_SYS_lstat64:
+      tbuf += sprintf (tbuf, "lstat64(%#x, %u)", args[0], args[1]);
+      cb->stat_map = stat_map_64;
+      sc.func = TARGET_LINUX_SYS_lstat;
+      cb_syscall (cb, &sc);
+      cb->stat_map = stat_map_32;
+      break;
+    case CB_SYS_fstat64:
+      tbuf += sprintf (tbuf, "fstat64(%#x, %u)", args[0], args[1]);
+      cb->stat_map = stat_map_64;
+      sc.func = TARGET_LINUX_SYS_fstat;
+      cb_syscall (cb, &sc);
+      cb->stat_map = stat_map_32;
+      break;
+
+    case CB_SYS_ftruncate64:
+      tbuf += sprintf (tbuf, "ftruncate64(%u, %u)", args[0], args[1]);
+      sc.func = TARGET_LINUX_SYS_ftruncate;
+      cb_syscall (cb, &sc);
+      break;
+
+    case CB_SYS_getuid:
+    case CB_SYS_getuid32:
+      tbuf += sprintf (tbuf, "getuid()");
+      sc.result = getuid ();
+      goto sys_finish;
+    case CB_SYS_getgid:
+    case CB_SYS_getgid32:
+      tbuf += sprintf (tbuf, "getgid()");
+      sc.result = getgid ();
+      goto sys_finish;
+    case CB_SYS_setuid:
+      sc.arg1 &= 0xffff;
+    case CB_SYS_setuid32:
+      tbuf += sprintf (tbuf, "setuid(%u)", args[0]);
+      sc.result = setuid (sc.arg1);
+      goto sys_finish;
+    case CB_SYS_setgid:
+      sc.arg1 &= 0xffff;
+    case CB_SYS_setgid32:
+      tbuf += sprintf (tbuf, "setgid(%u)", args[0]);
+      sc.result = setgid (sc.arg1);
+      goto sys_finish;
+
+    case CB_SYS_getpid:
+      tbuf += sprintf (tbuf, "getpid()");
+      sc.result = getpid ();
+      goto sys_finish;
+    case CB_SYS_kill:
+      tbuf += sprintf (tbuf, "kill(%u, %i)", args[0], args[1]);
+      /* Only let the app kill itself.  */
+      if (sc.arg1 != getpid ())
+       {
+         sc.result = -1;
+         sc.errcode = TARGET_EPERM;
+       }
+      else
+       {
+         sc.result = kill (sc.arg1, sc.arg2);
+         goto sys_finish;
+       }
+      break;
+
+    case CB_SYS_open:
+      tbuf += sprintf (tbuf, "open(%#x, %#x, %o)", args[0], args[1], args[2]);
+      goto case_default;
+    case CB_SYS_close:
+      tbuf += sprintf (tbuf, "close(%i)", args[0]);
+      goto case_default;
+    case CB_SYS_read:
+      tbuf += sprintf (tbuf, "read(%i, %#x, %u)", args[0], args[1], args[2]);
+      goto case_default;
+    case CB_SYS_write:
+      tbuf += sprintf (tbuf, "write(%i, %#x, %u)", args[0], args[1], args[2]);
+      goto case_default;
+    case CB_SYS_lseek:
+      tbuf += sprintf (tbuf, "lseek(%i, %i, %i)", args[0], args[1], args[2]);
+      goto case_default;
+    case CB_SYS_unlink:
+      tbuf += sprintf (tbuf, "unlink(%#x)", args[0]);
+      goto case_default;
+    case CB_SYS_truncate:
+      tbuf += sprintf (tbuf, "truncate(%#x, %i)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_ftruncate:
+      tbuf += sprintf (tbuf, "ftruncate(%i, %i)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_rename:
+      tbuf += sprintf (tbuf, "rename(%#x, %#x)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_stat:
+      tbuf += sprintf (tbuf, "stat(%#x, %#x)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_fstat:
+      tbuf += sprintf (tbuf, "fstat(%i, %#x)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_lstat:
+      tbuf += sprintf (tbuf, "lstat(%i, %#x)", args[0], args[1]);
+      goto case_default;
+    case CB_SYS_pipe:
+      tbuf += sprintf (tbuf, "pipe(%#x, %#x)", args[0], args[1]);
+      goto case_default;
+
+    default:
+      tbuf += sprintf (tbuf, "???_%i(%#x, %#x, %#x, %#x, %#x, %#x)", sc.func,
+                      args[0], args[1], args[2], args[3], args[4], args[5]);
+    case_default:
+      cb_syscall (cb, &sc);
+      break;
+
+    sys_finish:
+      if (sc.result == -1)
+       {
+         cb->last_errno = errno;
+         sc.errcode = cb->get_errno (cb);
+       }
+    }
+
+  TRACE_EVENTS (cpu, "syscall_%i(%#x, %#x, %#x, %#x, %#x, %#x) = %li (error = %i)",
+               sc.func, args[0], args[1], args[2], args[3], args[4], args[5],
+               sc.result, sc.errcode);
+
+  tbuf += sprintf (tbuf, " = ");
+  if (STATE_ENVIRONMENT (sd) == USER_ENVIRONMENT)
+    {
+      if (sc.result == -1)
+       {
+         tbuf += sprintf (tbuf, "-1 (error = %i)", sc.errcode);
+         if (sc.errcode == cb_host_to_target_errno (cb, ENOSYS))
+           {
+             sim_io_eprintf (sd, "bfin-sim: %#x: unimplemented syscall %i\n",
+                             PCREG, sc.func);
+           }
+         SET_DREG (0, -sc.errcode);
+       }
+      else
+       {
+         if (fmt_ret_hex)
+           tbuf += sprintf (tbuf, "%#lx", sc.result);
+         else
+           tbuf += sprintf (tbuf, "%lu", sc.result);
+         SET_DREG (0, sc.result);
+       }
+    }
+  else
+    {
+      tbuf += sprintf (tbuf, "%lu (error = %i)", sc.result, sc.errcode);
+      SET_DREG (0, sc.result);
+      /* Blackfin libgloss only expects R0 to be updated, not R1.  */
+      /*SET_DREG (1, sc.errcode);*/
+    }
+
+  TRACE_SYSCALL (cpu, "%s", _tbuf);
+}
+
+void
+trace_register (SIM_DESC sd,
+               sim_cpu *cpu,
+               const char *fmt,
+               ...)
+{
+  va_list ap;
+  trace_printf (sd, cpu, "%s %s",
+               "reg:     ",
+               TRACE_PREFIX (CPU_TRACE_DATA (cpu)));
+  va_start (ap, fmt);
+  trace_vprintf (sd, cpu, fmt, ap);
+  va_end (ap);
+  trace_printf (sd, cpu, "\n");
+}
+
+/* Execute a single instruction.  */
+
+static sim_cia
+step_once (SIM_CPU *cpu)
+{
+  SIM_DESC sd = CPU_STATE (cpu);
+  bu32 insn_len, oldpc = PCREG;
+  int i;
+  bool ssstep;
+
+  if (TRACE_ANY_P (cpu))
+    trace_prefix (sd, cpu, NULL_CIA, oldpc, TRACE_LINENUM_P (cpu),
+                 NULL, 0, " "); /* Use a space for gcc warnings.  */
+
+  /* Handle hardware single stepping when lower than EVT3, and when SYSCFG
+     has already had the SSSTEP bit enabled.  */
+  ssstep = false;
+  if (STATE_ENVIRONMENT (sd) == OPERATING_ENVIRONMENT
+      && (SYSCFGREG & SYSCFG_SSSTEP))
+    {
+      int ivg = cec_get_ivg (cpu);
+      if (ivg == -1 || ivg > 3)
+       ssstep = true;
+    }
+
+#if 0
+  /* XXX: Is this what happens on the hardware ?  */
+  if (cec_get_ivg (cpu) == EVT_EMU)
+    cec_return (cpu, EVT_EMU);
+#endif
+
+  BFIN_CPU_STATE.did_jump = false;
+
+  insn_len = interp_insn_bfin (cpu, oldpc);
+
+  /* If we executed this insn successfully, then we always decrement
+     the loop counter.  We don't want to update the PC though if the
+     last insn happened to be a change in code flow (jump/etc...).  */
+  if (!BFIN_CPU_STATE.did_jump)
+    SET_PCREG (hwloop_get_next_pc (cpu, oldpc, insn_len));
+  for (i = 1; i >= 0; --i)
+    if (LCREG (i) && oldpc == LBREG (i))
+      {
+       SET_LCREG (i, LCREG (i) - 1);
+       if (LCREG (i))
+         break;
+      }
+
+  ++ PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu));
+
+  /* Handle hardware single stepping only if we're still lower than EVT3.
+     XXX: May not be entirely correct wrt EXCPT insns.  */
+  if (ssstep)
+    {
+      int ivg = cec_get_ivg (cpu);
+      if (ivg == -1 || ivg > 3)
+       {
+         INSN_LEN = 0;
+         cec_exception (cpu, VEC_STEP);
+       }
+    }
+
+  return oldpc;
+}
+
+void
+sim_engine_run (SIM_DESC sd,
+               int next_cpu_nr, /* ignore  */
+               int nr_cpus, /* ignore  */
+               int siggnal) /* ignore  */
+{
+  bu32 ticks;
+  SIM_CPU *cpu;
+
+  SIM_ASSERT (STATE_MAGIC (sd) == SIM_MAGIC_NUMBER);
+
+  cpu = STATE_CPU (sd, 0);
+
+  while (1)
+    {
+      step_once (cpu);
+      /* Process any events -- can't use tickn because it may
+         advance right over the next event.  */
+      for (ticks = 0; ticks < CYCLE_DELAY; ++ticks)
+       if (sim_events_tick (sd))
+         sim_events_process (sd);
+    }
+}
+
+/* Cover function of sim_state_free to free the cpu buffers as well.  */
+
+static void
+free_state (SIM_DESC sd)
+{
+  if (STATE_MODULES (sd) != NULL)
+    sim_module_uninstall (sd);
+  sim_cpu_free_all (sd);
+  sim_state_free (sd);
+}
+
+/* Create an instance of the simulator.  */
+
+static void
+bfin_initialize_cpu (SIM_DESC sd, SIM_CPU *cpu)
+{
+  memset (&cpu->state, 0, sizeof (cpu->state));
+
+  PROFILE_TOTAL_INSN_COUNT (CPU_PROFILE_DATA (cpu)) = 0;
+
+  bfin_model_cpu_init (sd, cpu);
+
+  /* Set default stack to top of scratch pad.  */
+  SET_SPREG (BFIN_DEFAULT_MEM_SIZE);
+  SET_KSPREG (BFIN_DEFAULT_MEM_SIZE);
+  SET_USPREG (BFIN_DEFAULT_MEM_SIZE);
+
+  /* This is what the hardware likes.  */
+  SET_SYSCFGREG (0x30);
+}
+
+SIM_DESC
+sim_open (SIM_OPEN_KIND kind, host_callback *callback,
+         struct bfd *abfd, char **argv)
+{
+  char c;
+  int i;
+  SIM_DESC sd = sim_state_alloc (kind, callback);
+
+  /* The cpu data is kept in a separately allocated chunk of memory.  */
+  if (sim_cpu_alloc_all (sd, 1, /*cgen_cpu_max_extra_bytes ()*/0) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  {
+    /* XXX: Only first core gets profiled ?  */
+    SIM_CPU *cpu = STATE_CPU (sd, 0);
+    STATE_WATCHPOINTS (sd)->pc = &PCREG;
+    STATE_WATCHPOINTS (sd)->sizeof_pc = sizeof (PCREG);
+  }
+
+  if (sim_pre_argv_init (sd, argv[0]) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* XXX: Default to the Virtual environment.  */
+  if (STATE_ENVIRONMENT (sd) == ALL_ENVIRONMENT)
+    STATE_ENVIRONMENT (sd) = VIRTUAL_ENVIRONMENT;
+
+  /* These options override any module options.
+     Obviously ambiguity should be avoided, however the caller may wish to
+     augment the meaning of an option.  */
+#define e_sim_add_option_table(sd, options) \
+  do { \
+    extern const OPTION options[]; \
+    sim_add_option_table (sd, NULL, options); \
+  } while (0)
+  e_sim_add_option_table (sd, bfin_mmu_options);
+  e_sim_add_option_table (sd, bfin_mach_options);
+
+  /* getopt will print the error message so we just have to exit if this fails.
+     FIXME: Hmmm...  in the case of gdb we need getopt to call
+     print_filtered.  */
+  if (sim_parse_args (sd, argv) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Allocate external memory if none specified by user.
+     Use address 4 here in case the user wanted address 0 unmapped.  */
+  if (sim_core_read_buffer (sd, NULL, read_map, &c, 4, 1) == 0)
+    {
+      bu16 emuexcpt = 0x25;
+      sim_do_commandf (sd, "memory-size 0x%lx", BFIN_DEFAULT_MEM_SIZE);
+      sim_write (sd, 0, (void *)&emuexcpt, 2);
+    }
+
+  /* Check for/establish the a reference program image.  */
+  if (sim_analyze_program (sd,
+                          (STATE_PROG_ARGV (sd) != NULL
+                           ? *STATE_PROG_ARGV (sd)
+                           : NULL), abfd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* Establish any remaining configuration options.  */
+  if (sim_config (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  if (sim_post_argv_init (sd) != SIM_RC_OK)
+    {
+      free_state (sd);
+      return 0;
+    }
+
+  /* CPU specific initialization.  */
+  for (i = 0; i < MAX_NR_PROCESSORS; ++i)
+    {
+      SIM_CPU *cpu = STATE_CPU (sd, i);
+      bfin_initialize_cpu (sd, cpu);
+    }
+
+  return sd;
+}
+
+void
+sim_close (SIM_DESC sd, int quitting)
+{
+  sim_module_uninstall (sd);
+}
+
+/* Some utils don't like having a NULL environ.  */
+static const char * const simple_env[] = { "HOME=/", "PATH=/bin", NULL };
+
+static bu32 fdpic_load_offset;
+
+static bool
+bfin_fdpic_load (SIM_DESC sd, SIM_CPU *cpu, struct bfd *abfd, bu32 *sp,
+                bu32 *elf_addrs, char **ldso_path)
+{
+  bool ret;
+  int i;
+
+  Elf_Internal_Ehdr *iehdr;
+  Elf32_External_Ehdr ehdr;
+  Elf_Internal_Phdr *phdrs;
+  unsigned char *data;
+  long phdr_size;
+  int phdrc;
+  bu32 nsegs;
+
+  bu32 max_load_addr;
+
+  unsigned char null[4] = { 0, 0, 0, 0 };
+
+  ret = false;
+  *ldso_path = NULL;
+
+  /* See if this an FDPIC ELF.  */
+  phdrs = NULL;
+  if (!abfd)
+    goto skip_fdpic_init;
+  if (bfd_seek (abfd, 0, SEEK_SET) != 0)
+    goto skip_fdpic_init;
+  if (bfd_bread (&ehdr, sizeof (ehdr), abfd) != sizeof (ehdr))
+    goto skip_fdpic_init;
+  iehdr = elf_elfheader (abfd);
+  if (!(iehdr->e_flags & EF_BFIN_FDPIC))
+    goto skip_fdpic_init;
+
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    sim_io_printf (sd, "Loading FDPIC ELF %s\n Load base: %#x\n ELF entry: %#x\n",
+                  bfd_get_filename (abfd), fdpic_load_offset, elf_addrs[0]);
+
+  /* Grab the Program Headers to set up the loadsegs on the stack.  */
+  phdr_size = bfd_get_elf_phdr_upper_bound (abfd);
+  if (phdr_size == -1)
+    goto skip_fdpic_init;
+  phdrs = xmalloc (phdr_size);
+  phdrc = bfd_get_elf_phdrs (abfd, phdrs);
+  if (phdrc == -1)
+    goto skip_fdpic_init;
+
+  /* Push the Ehdr onto the stack.  */
+  *sp -= sizeof (ehdr);
+  elf_addrs[3] = *sp;
+  sim_write (sd, *sp, (void *)&ehdr, sizeof (ehdr));
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    sim_io_printf (sd, " Elf_Ehdr: %#x\n", *sp);
+
+  /* Since we're relocating things ourselves, we need to relocate
+     the start address as well.  */
+  elf_addrs[0] = bfd_get_start_address (abfd) + fdpic_load_offset;
+
+  /* And the Exec's Phdrs onto the stack.  */
+  if (STATE_PROG_BFD (sd) == abfd)
+    {
+      elf_addrs[4] = elf_addrs[0];
+
+      phdr_size = iehdr->e_phentsize * iehdr->e_phnum;
+      if (bfd_seek (abfd, iehdr->e_phoff, SEEK_SET) != 0)
+       goto skip_fdpic_init;
+      data = xmalloc (phdr_size);
+      if (bfd_bread (data, phdr_size, abfd) != phdr_size)
+       goto skip_fdpic_init;
+      *sp -= phdr_size;
+      elf_addrs[1] = *sp;
+      elf_addrs[2] = phdrc;
+      sim_write (sd, *sp, data, phdr_size);
+      free (data);
+      if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+       sim_io_printf (sd, " Elf_Phdrs: %#x\n", *sp);
+    }
+
+  /* Now push all the loadsegs.  */
+  nsegs = 0;
+  max_load_addr = 0;
+  for (i = phdrc; i >= 0; --i)
+    if (phdrs[i].p_type == PT_LOAD)
+      {
+       Elf_Internal_Phdr *p = &phdrs[i];
+       bu32 paddr, vaddr, memsz, filesz;
+
+       paddr = p->p_paddr + fdpic_load_offset;
+       vaddr = p->p_vaddr;
+       memsz = p->p_memsz;
+       filesz = p->p_filesz;
+
+       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+         sim_io_printf (sd, " PHDR %i: vma %#x lma %#x filesz %#x memsz %#x\n",
+                        i, vaddr, paddr, filesz, memsz);
+
+       data = xmalloc (memsz);
+       if (memsz != filesz)
+         memset (data + filesz, 0, memsz - filesz);
+
+       if (bfd_seek (abfd, p->p_offset, SEEK_SET) == 0
+           && bfd_bread (data, filesz, abfd) == filesz)
+         sim_write (sd, paddr, data, memsz);
+
+       free (data);
+
+       max_load_addr = MAX (paddr + memsz, max_load_addr);
+
+       *sp -= 12;
+       sim_write (sd, *sp+0, (void *)&paddr, 4); /* loadseg.addr  */
+       sim_write (sd, *sp+4, (void *)&vaddr, 4); /* loadseg.p_vaddr  */
+       sim_write (sd, *sp+8, (void *)&memsz, 4); /* loadseg.p_memsz  */
+       ++nsegs;
+      }
+    else if (phdrs[i].p_type == PT_DYNAMIC)
+      {
+       elf_addrs[5] = phdrs[i].p_paddr + fdpic_load_offset;
+       if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+         sim_io_printf (sd, " PT_DYNAMIC: %#x\n", elf_addrs[5]);
+      }
+    else if (phdrs[i].p_type == PT_INTERP)
+      {
+       uint32_t off = phdrs[i].p_offset;
+       uint32_t len = phdrs[i].p_filesz;
+
+       *ldso_path = xmalloc (len);
+       if (bfd_seek (abfd, off, SEEK_SET) != 0
+           || bfd_bread (*ldso_path, len, abfd) != len)
+         {
+           free (*ldso_path);
+           *ldso_path = NULL;
+         }
+       else if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+         sim_io_printf (sd, " PT_INTERP: %s\n", *ldso_path);
+      }
+
+  /* Update the load offset with a few extra pages.  */
+  fdpic_load_offset = ALIGN (MAX (max_load_addr, fdpic_load_offset), 0x10000);
+  fdpic_load_offset += 0x10000;
+
+  /* Push the summary loadmap info onto the stack last.  */
+  *sp -= 4;
+  sim_write (sd, *sp+0, null, 2); /* loadmap.version  */
+  sim_write (sd, *sp+2, (void *)&nsegs, 2); /* loadmap.nsegs  */
+
+  ret = true;
+ skip_fdpic_init:
+  free (phdrs);
+
+  return ret;
+}
+
+static void
+bfin_user_init (SIM_DESC sd, SIM_CPU *cpu, struct bfd *abfd,
+               const char * const *argv, const char * const *env)
+{
+  /* XXX: Missing host -> target endian ...  */
+  /* Linux starts the user app with the stack:
+       argc
+       argv[0]          -- pointers to the actual strings
+       argv[1..N]
+       NULL
+       env[0]
+       env[1..N]
+       NULL
+       auxvt[0].type    -- ELF Auxiliary Vector Table
+       auxvt[0].value
+       auxvt[1..N]
+       AT_NULL
+       0
+       argv[0..N][0..M] -- actual argv/env strings
+       env[0..N][0..M]
+       FDPIC loadmaps   -- for FDPIC apps
+     So set things up the same way.  */
+  int i, argc, envc;
+  bu32 argv_flat, env_flat;
+
+  bu32 sp, sp_flat;
+
+  /* start, at_phdr, at_phnum, at_base, at_entry, pt_dynamic  */
+  bu32 elf_addrs[6];
+  bu32 auxvt, auxvt_size;
+  bu32 exec_loadmap, ldso_loadmap;
+  char *ldso_path;
+
+  unsigned char null[4] = { 0, 0, 0, 0 };
+
+  host_callback *cb = STATE_CALLBACK (sd);
+
+  elf_addrs[0] = elf_addrs[4] = bfd_get_start_address (abfd);
+  elf_addrs[1] = elf_addrs[2] = elf_addrs[3] = elf_addrs[5] = 0;
+
+  /* Keep the load addresses consistent between runs.  Also make sure we make
+     space for the fixed code region (part of the Blackfin Linux ABI).  */
+  fdpic_load_offset = 0x1000;
+
+  /* First try to load this as an FDPIC executable.  */
+  sp = SPREG;
+  if (!bfin_fdpic_load (sd, cpu, STATE_PROG_BFD (sd), &sp, elf_addrs, &ldso_path))
+    goto skip_fdpic_init;
+  exec_loadmap = sp;
+
+  /* If that worked, then load the fixed code region.  We only do this for
+     FDPIC ELFs atm because they are PIEs and let us relocate them without
+     manual fixups.  FLAT files however require location processing which
+     we do not do ourselves, and they link with a VMA of 0.  */
+  sim_write (sd, 0x400, bfin_linux_fixed_code, sizeof (bfin_linux_fixed_code));
+
+  /* If the FDPIC needs an interpreter, then load it up too.  */
+  if (ldso_path)
+    {
+      const char *ldso_full_path = concat (simulator_sysroot, ldso_path, NULL);
+      struct bfd *ldso_bfd;
+
+      ldso_bfd = bfd_openr (ldso_full_path, STATE_TARGET (sd));
+      if (!ldso_bfd)
+       {
+         sim_io_eprintf (sd, "bfin-sim: bfd open failed: %s\n", ldso_full_path);
+         goto static_fdpic;
+       }
+      if (!bfd_check_format (ldso_bfd, bfd_object))
+       sim_io_eprintf (sd, "bfin-sim: bfd format not valid: %s\n", ldso_full_path);
+      bfd_set_arch_info (ldso_bfd, STATE_ARCHITECTURE (sd));
+
+      if (!bfin_fdpic_load (sd, cpu, ldso_bfd, &sp, elf_addrs, &ldso_path))
+       sim_io_eprintf (sd, "bfin-sim: FDPIC ldso failed to load: %s\n", ldso_full_path);
+      if (ldso_path)
+       sim_io_eprintf (sd, "bfin-sim: FDPIC ldso (%s) needs an interpreter (%s) !?\n",
+                       ldso_full_path, ldso_path);
+
+      ldso_loadmap = sp;
+    }
+  else
+ static_fdpic:
+    ldso_loadmap = 0;
+
+  /* Finally setup the registers required by the FDPIC ABI.  */
+  SET_DREG (7, 0); /* Zero out FINI funcptr -- ldso will set this up.  */
+  SET_PREG (0, exec_loadmap); /* Exec loadmap addr.  */
+  SET_PREG (1, ldso_loadmap); /* Interp loadmap addr.  */
+  SET_PREG (2, elf_addrs[5]); /* PT_DYNAMIC map addr.  */
+
+  auxvt = 1;
+  SET_SPREG (sp);
+ skip_fdpic_init:
+  sim_pc_set (cpu, elf_addrs[0]);
+
+  /* Figure out how much storage the argv/env strings need.  */
+  argc = count_argc (argv);
+  if (argc == -1)
+    argc = 0;
+  argv_flat = argc; /* NUL bytes  */
+  for (i = 0; i < argc; ++i)
+    argv_flat += strlen (argv[i]);
+
+  if (!env)
+    env = simple_env;
+  envc = count_argc (env);
+  env_flat = envc; /* NUL bytes  */
+  for (i = 0; i < envc; ++i)
+    env_flat += strlen (env[i]);
+
+  /* Push the Auxiliary Vector Table between argv/env and actual strings.  */
+  sp_flat = sp = ALIGN (SPREG - argv_flat - env_flat - 4, 4);
+  if (auxvt)
+    {
+# define AT_PUSH(at, val) \
+  auxvt_size += 8; \
+  sp -= 4; \
+  auxvt = (val); \
+  sim_write (sd, sp, (void *)&auxvt, 4); \
+  sp -= 4; \
+  auxvt = (at); \
+  sim_write (sd, sp, (void *)&auxvt, 4)
+  auxvt_size = 0;
+      unsigned int egid = getegid (), gid = getgid ();
+      unsigned int euid = geteuid (), uid = getuid ();
+      AT_PUSH (AT_NULL, 0);
+      AT_PUSH (AT_SECURE, egid != gid || euid != uid);
+      AT_PUSH (AT_EGID, egid);
+      AT_PUSH (AT_GID, gid);
+      AT_PUSH (AT_EUID, euid);
+      AT_PUSH (AT_UID, uid);
+      AT_PUSH (AT_ENTRY, elf_addrs[4]);
+      AT_PUSH (AT_FLAGS, 0);
+      AT_PUSH (AT_BASE, elf_addrs[3]);
+      AT_PUSH (AT_PHNUM, elf_addrs[2]);
+      AT_PUSH (AT_PHENT, sizeof (Elf32_External_Phdr));
+      AT_PUSH (AT_PHDR, elf_addrs[1]);
+      AT_PUSH (AT_CLKTCK, 100); /* XXX: This ever not 100 ?  */
+      AT_PUSH (AT_PAGESZ, 4096);
+      AT_PUSH (AT_HWCAP, 0);
+#undef AT_PUSH
+    }
+  SET_SPREG (sp);
+
+  /* Push the argc/argv/env after the auxvt.  */
+  sp -= ((1 + argc + 1 + envc + 1) * 4);
+  SET_SPREG (sp);
+
+  /* First push the argc value.  */
+  sim_write (sd, sp, (void *)&argc, 4);
+  sp += 4;
+
+  /* Then the actual argv strings so we know where to point argv[].  */
+  for (i = 0; i < argc; ++i)
+    {
+      unsigned len = strlen (argv[i]) + 1;
+      sim_write (sd, sp_flat, (void *)argv[i], len);
+      sim_write (sd, sp, (void *)&sp_flat, 4);
+      sp_flat += len;
+      sp += 4;
+    }
+  sim_write (sd, sp, null, 4);
+  sp += 4;
+
+  /* Then the actual env strings so we know where to point env[].  */
+  for (i = 0; i < envc; ++i)
+    {
+      unsigned len = strlen (env[i]) + 1;
+      sim_write (sd, sp_flat, (void *)env[i], len);
+      sim_write (sd, sp, (void *)&sp_flat, 4);
+      sp_flat += len;
+      sp += 4;
+    }
+
+  /* Set some callbacks.  */
+  cb->syscall_map = cb_linux_syscall_map;
+  cb->errno_map = cb_linux_errno_map;
+  cb->open_map = cb_linux_open_map;
+  cb->signal_map = cb_linux_signal_map;
+  cb->stat_map = stat_map_32;
+}
+
+static void
+bfin_os_init (SIM_DESC sd, SIM_CPU *cpu, const char * const *argv)
+{
+  /* Pass the command line via a string in R0 like Linux expects.  */
+  int i;
+  bu8 byte;
+  bu32 cmdline = BFIN_L1_SRAM_SCRATCH;
+
+  SET_DREG (0, cmdline);
+  if (argv && argv[0])
+    {
+      i = 1;
+      byte = ' ';
+      while (argv[i])
+       {
+         bu32 len = strlen (argv[i]);
+         sim_write (sd, cmdline, (void *)argv[i], len);
+         cmdline += len;
+         sim_write (sd, cmdline, &byte, 1);
+         ++cmdline;
+         ++i;
+       }
+    }
+  byte = 0;
+  sim_write (sd, cmdline, &byte, 1);
+}
+
+SIM_RC
+sim_create_inferior (SIM_DESC sd, struct bfd *abfd,
+                    char **argv, char **env)
+{
+  SIM_CPU *cpu = STATE_CPU (sd, 0);
+  SIM_ADDR addr;
+
+  /* Set the PC.  */
+  if (abfd != NULL)
+    addr = bfd_get_start_address (abfd);
+  else
+    addr = 0;
+  sim_pc_set (cpu, addr);
+
+  /* Standalone mode (i.e. `bfin-...-run`) will take care of the argv
+     for us in sim_open() -> sim_parse_args().  But in debug mode (i.e.
+     'target sim' with `bfin-...-gdb`), we need to handle it.  */
+  if (STATE_OPEN_KIND (sd) == SIM_OPEN_DEBUG)
+    {
+      free (STATE_PROG_ARGV (sd));
+      STATE_PROG_ARGV (sd) = dupargv (argv);
+    }
+
+  switch (STATE_ENVIRONMENT (sd))
+    {
+    case USER_ENVIRONMENT:
+      bfin_user_init (sd, cpu, abfd, (void *)argv, (void *)env);
+      break;
+    case OPERATING_ENVIRONMENT:
+      bfin_os_init (sd, cpu, (void *)argv);
+      break;
+    default:
+      /* Nothing to do for virtual/all envs.  */
+      break;
+    }
+
+  return SIM_RC_OK;
+}
+
+void
+sim_do_command (SIM_DESC sd, char *cmd)
+{
+  if (sim_args_command (sd, cmd) != SIM_RC_OK)
+    sim_io_eprintf (sd, "Unknown command `%s'\n", cmd);
+}