1282 lines
34 KiB
C
1282 lines
34 KiB
C
/* Simulator for Analog Devices Blackfin processors.
|
|
|
|
Copyright (C) 2005-2015 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 cb_linux_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 cb_linux_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";
|
|
static const char cb_libgloss_stat_map_32[] =
|
|
"st_dev,2:st_ino,2:st_mode,4:st_nlink,2:st_uid,2:st_gid,2:st_rdev,2:"
|
|
"st_size,4:st_atime,4:space,4:st_mtime,4:space,4:st_ctime,4:"
|
|
"space,4:st_blksize,4:st_blocks,4:space,8";
|
|
static const char *stat_map_32, *stat_map_64;
|
|
|
|
/* 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[1024 * 3], *tbuf = _tbuf, tstr[1024];
|
|
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
|
|
{
|
|
#ifdef HAVE_PREAD
|
|
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);
|
|
#else
|
|
sc.errcode = TARGET_ENOSYS;
|
|
#endif
|
|
}
|
|
|
|
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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "stat64(%#x:\"%s\", %u)", args[0], tstr, 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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "lstat64(%#x:\"%s\", %u)", args[0], tstr, 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
|
|
{
|
|
#ifdef HAVE_KILL
|
|
sc.result = kill (sc.arg1, sc.arg2);
|
|
goto sys_finish;
|
|
#else
|
|
sc.result = -1;
|
|
sc.errcode = TARGET_ENOSYS;
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case CB_SYS_open:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "open(%#x:\"%s\", %#x, %o)",
|
|
args[0], tstr, 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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[1]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "write(%i, %#x:\"%s\", %u)",
|
|
args[0], args[1], tstr, 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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "unlink(%#x:\"%s\")", args[0], tstr);
|
|
goto case_default;
|
|
case CB_SYS_truncate:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "truncate(%#x:\"%s\", %i)", args[0], tstr, 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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "rename(%#x:\"%s\", ", args[0], tstr);
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[1]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "%#x:\"%s\")", args[1], tstr);
|
|
goto case_default;
|
|
case CB_SYS_stat:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "stat(%#x:\"%s\", %#x)", args[0], tstr, 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:
|
|
if (cb_get_string (cb, &sc, tstr, sizeof (tstr), args[0]))
|
|
strcpy (tstr, "???");
|
|
tbuf += sprintf (tbuf, "lstat(%#x:\"%s\", %#x)", args[0], tstr, 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);
|
|
SET_DREG (1, sc.result2);
|
|
SET_DREG (2, 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;
|
|
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)
|
|
unsigned int egid = getegid (), gid = getgid ();
|
|
unsigned int euid = geteuid (), uid = getuid ();
|
|
bu32 auxvt_size = 0;
|
|
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 = cb_linux_stat_map_32;
|
|
stat_map_64 = cb_linux_stat_map_64;
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
bfin_virtual_init (SIM_DESC sd, SIM_CPU *cpu)
|
|
{
|
|
host_callback *cb = STATE_CALLBACK (sd);
|
|
|
|
cb->stat_map = stat_map_32 = cb_libgloss_stat_map_32;
|
|
stat_map_64 = NULL;
|
|
}
|
|
|
|
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)
|
|
{
|
|
freeargv (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:
|
|
bfin_virtual_init (sd, cpu);
|
|
break;
|
|
}
|
|
|
|
return SIM_RC_OK;
|
|
}
|