aboutsummaryrefslogtreecommitdiff
path: root/sim/bfin/interp.c
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2011-03-06 00:20:21 +0000
committerMike Frysinger <vapier@gentoo.org>2011-03-06 00:20:21 +0000
commitef016f835f292f01f065412fcfd84c50bfff1fea (patch)
treefe72facf7bcdc58af74f3a37d30f0f25d501f6a0 /sim/bfin/interp.c
parent7dcf22fd41de725f3280c80a1274bcef309d6891 (diff)
downloadgdb-ef016f835f292f01f065412fcfd84c50bfff1fea.zip
gdb-ef016f835f292f01f065412fcfd84c50bfff1fea.tar.gz
gdb-ef016f835f292f01f065412fcfd84c50bfff1fea.tar.bz2
sim: bfin: new port
This can boot Das U-Boot and a Linux kernel. It also supports Linux userspace FLAT and FDPIC (dynamic and static) ELFs. Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Diffstat (limited to 'sim/bfin/interp.c')
-rw-r--r--sim/bfin/interp.c1241
1 files changed, 1241 insertions, 0 deletions
diff --git a/sim/bfin/interp.c b/sim/bfin/interp.c
new file mode 100644
index 0000000..1f8681d
--- /dev/null
+++ b/sim/bfin/interp.c
@@ -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);
+}