diff options
author | Mike Frysinger <vapier@gentoo.org> | 2011-03-06 00:20:21 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2011-03-06 00:20:21 +0000 |
commit | ef016f835f292f01f065412fcfd84c50bfff1fea (patch) | |
tree | fe72facf7bcdc58af74f3a37d30f0f25d501f6a0 /sim/bfin/interp.c | |
parent | 7dcf22fd41de725f3280c80a1274bcef309d6891 (diff) | |
download | gdb-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.c | 1241 |
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); +} |