diff options
author | Hans-Peter Nilsson <hp@axis.com> | 2005-01-28 04:29:00 +0000 |
---|---|---|
committer | Hans-Peter Nilsson <hp@axis.com> | 2005-01-28 04:29:00 +0000 |
commit | f6bcefefe817b20b493081511cdeb8f87052bd41 (patch) | |
tree | 925f09bdedb36933c713803d9d0824c5b523c5ed /sim/cris/traps.c | |
parent | 97f669eda91b587c590bb5d0bb185d63c126d7fe (diff) | |
download | gdb-f6bcefefe817b20b493081511cdeb8f87052bd41.zip gdb-f6bcefefe817b20b493081511cdeb8f87052bd41.tar.gz gdb-f6bcefefe817b20b493081511cdeb8f87052bd41.tar.bz2 |
* cris: New directory, simulator for Axis Communications CRIS
including CRIS v32, CGEN-based.
* configure.ac: Add corresponding configury.
* configure: Regenerate.
Diffstat (limited to 'sim/cris/traps.c')
-rw-r--r-- | sim/cris/traps.c | 2982 |
1 files changed, 2982 insertions, 0 deletions
diff --git a/sim/cris/traps.c b/sim/cris/traps.c new file mode 100644 index 0000000..ff39a4f --- /dev/null +++ b/sim/cris/traps.c @@ -0,0 +1,2982 @@ +/* CRIS exception, interrupt, and trap (EIT) support + Copyright (C) 2004, 2005 Free Software Foundation, Inc. + Contributed by Axis Communications. + +This file is part of the GNU 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 2, 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, write to the Free Software Foundation, Inc., +59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#include "sim-main.h" +#include "sim-options.h" +#include "targ-vals.h" +#include "bfd.h" +#ifdef HAVE_ERRNO_H +#include <errno.h> +#endif +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif +#ifdef HAVE_FCNTL_H +#include <fcntl.h> +#endif +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif + +/* The verbatim values are from asm-cris/unistd.h. */ + +#define TARGET_SYS_exit 1 +#define TARGET_SYS_read 3 +#define TARGET_SYS_write 4 +#define TARGET_SYS_open 5 +#define TARGET_SYS_close 6 +#define TARGET_SYS_unlink 10 +#define TARGET_SYS_time 13 +#define TARGET_SYS_lseek 19 +#define TARGET_SYS_getpid 20 +#define TARGET_SYS_kill 37 +#define TARGET_SYS_rename 38 +#define TARGET_SYS_pipe 42 +#define TARGET_SYS_brk 45 +#define TARGET_SYS_ioctl 54 +#define TARGET_SYS_fcntl 55 +#define TARGET_SYS_getppid 64 +#define TARGET_SYS_setrlimit 75 +#define TARGET_SYS_gettimeofday 78 +#define TARGET_SYS_readlink 85 +#define TARGET_SYS_munmap 91 +#define TARGET_SYS_truncate 92 +#define TARGET_SYS_ftruncate 93 +#define TARGET_SYS_socketcall 102 +#define TARGET_SYS_fstat 108 +#define TARGET_SYS_wait4 114 +#define TARGET_SYS_sigreturn 119 +#define TARGET_SYS_clone 120 +#define TARGET_SYS_uname 122 +#define TARGET_SYS_mprotect 125 +#define TARGET_SYS_llseek 140 +#define TARGET_SYS__sysctl 149 +#define TARGET_SYS_sched_setparam 154 +#define TARGET_SYS_sched_getparam 155 +#define TARGET_SYS_sched_setscheduler 156 +#define TARGET_SYS_sched_getscheduler 157 +#define TARGET_SYS_sched_yield 158 +#define TARGET_SYS_sched_get_priority_max 159 +#define TARGET_SYS_sched_get_priority_min 160 +#define TARGET_SYS_mremap 163 +#define TARGET_SYS_poll 168 +#define TARGET_SYS_rt_sigaction 174 +#define TARGET_SYS_rt_sigprocmask 175 +#define TARGET_SYS_rt_sigsuspend 179 +#define TARGET_SYS_getcwd 183 +#define TARGET_SYS_ugetrlimit 191 +#define TARGET_SYS_mmap2 192 +#define TARGET_SYS_stat64 195 +#define TARGET_SYS_lstat64 196 +#define TARGET_SYS_fstat64 197 +#define TARGET_SYS_geteuid32 201 +#define TARGET_SYS_getuid32 199 +#define TARGET_SYS_getegid32 202 +#define TARGET_SYS_getgid32 200 +#define TARGET_SYS_fcntl64 221 + +#define TARGET_PROT_READ 0x1 +#define TARGET_PROT_WRITE 0x2 +#define TARGET_PROT_EXEC 0x4 +#define TARGET_PROT_NONE 0x0 + +#define TARGET_MAP_SHARED 0x01 +#define TARGET_MAP_PRIVATE 0x02 +#define TARGET_MAP_TYPE 0x0f +#define TARGET_MAP_FIXED 0x10 +#define TARGET_MAP_ANONYMOUS 0x20 + +#define TARGET_CTL_KERN 1 +#define TARGET_CTL_VM 2 +#define TARGET_CTL_NET 3 +#define TARGET_CTL_PROC 4 +#define TARGET_CTL_FS 5 +#define TARGET_CTL_DEBUG 6 +#define TARGET_CTL_DEV 7 +#define TARGET_CTL_BUS 8 +#define TARGET_CTL_ABI 9 + +#define TARGET_CTL_KERN_VERSION 4 + +/* linux/mman.h */ +#define TARGET_MREMAP_MAYMOVE 1 +#define TARGET_MREMAP_FIXED 2 + +#define TARGET_TCGETS 0x5401 + +#define TARGET_UTSNAME "#38 Sun Apr 1 00:00:00 MET 2001" + +/* Seconds since the above date + 10 minutes. */ +#define TARGET_EPOCH 986080200 + +/* Milliseconds since start of run. We use the number of syscalls to + avoid introducing noise in the execution time. */ +#define TARGET_TIME_MS(cpu) ((cpu)->syscalls) + +/* Seconds as in time(2). */ +#define TARGET_TIME(cpu) (TARGET_EPOCH + TARGET_TIME_MS (cpu) / 1000) + +#define TARGET_SCHED_OTHER 0 + +#define TARGET_RLIMIT_STACK 3 +#define TARGET_RLIMIT_NOFILE 7 + +#define SIM_TARGET_MAX_THREADS 64 +#define SIM_MAX_ALLOC_CHUNK (512*1024*1024) + +/* From linux/sched.h. */ +#define TARGET_CSIGNAL 0x000000ff +#define TARGET_CLONE_VM 0x00000100 +#define TARGET_CLONE_FS 0x00000200 +#define TARGET_CLONE_FILES 0x00000400 +#define TARGET_CLONE_SIGHAND 0x00000800 +#define TARGET_CLONE_PID 0x00001000 +#define TARGET_CLONE_PTRACE 0x00002000 +#define TARGET_CLONE_VFORK 0x00004000 +#define TARGET_CLONE_PARENT 0x00008000 +#define TARGET_CLONE_THREAD 0x00010000 +#define TARGET_CLONE_SIGNAL (TARGET_CLONE_SIGHAND | TARGET_CLONE_THREAD) + +/* From asm-cris/poll.h. */ +#define TARGET_POLLIN 1 + +/* From asm-cris/signal.h. */ +#define TARGET_SIG_BLOCK 0 +#define TARGET_SIG_UNBLOCK 1 +#define TARGET_SIG_SETMASK 2 + +#define TARGET_SIG_DFL 0 +#define TARGET_SIG_IGN 1 +#define TARGET_SIG_ERR ((USI)-1) + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGBUS 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGUSR1 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGUSR2 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGSTKFLT 16 +#define TARGET_SIGCHLD 17 +#define TARGET_SIGCONT 18 +#define TARGET_SIGSTOP 19 +#define TARGET_SIGTSTP 20 +#define TARGET_SIGTTIN 21 +#define TARGET_SIGTTOU 22 +#define TARGET_SIGURG 23 +#define TARGET_SIGXCPU 24 +#define TARGET_SIGXFSZ 25 +#define TARGET_SIGVTALRM 26 +#define TARGET_SIGPROF 27 +#define TARGET_SIGWINCH 28 +#define TARGET_SIGIO 29 +#define TARGET_SIGPOLL SIGIO +/* Actually commented out in the kernel header. */ +#define TARGET_SIGLOST 29 +#define TARGET_SIGPWR 30 +#define TARGET_SIGSYS 31 + +/* From include/asm-cris/signal.h. */ +#define TARGET_SA_NOCLDSTOP 0x00000001 +#define TARGET_SA_NOCLDWAIT 0x00000002 /* not supported yet */ +#define TARGET_SA_SIGINFO 0x00000004 +#define TARGET_SA_ONSTACK 0x08000000 +#define TARGET_SA_RESTART 0x10000000 +#define TARGET_SA_NODEFER 0x40000000 +#define TARGET_SA_RESETHAND 0x80000000 +#define TARGET_SA_INTERRUPT 0x20000000 /* dummy -- ignored */ +#define TARGET_SA_RESTORER 0x04000000 + +/* From linux/wait.h. */ +#define TARGET_WNOHANG 1 +#define TARGET_WUNTRACED 2 +#define TARGET___WNOTHREAD 0x20000000 +#define TARGET___WALL 0x40000000 +#define TARGET___WCLONE 0x80000000 + +static const char stat_map[] = +"st_dev,2:space,10:space,4:st_mode,4:st_nlink,4:st_uid,4" +":st_gid,4:st_rdev,2:space,10:st_size,8:st_blksize,4:st_blocks,4" +":space,4:st_atime,4:space,4:st_mtime,4:space,4:st_ctime,4:space,4" +":st_ino,8"; + +static const CB_TARGET_DEFS_MAP syscall_map[] = +{ + { CB_SYS_open, TARGET_SYS_open }, + { CB_SYS_close, TARGET_SYS_close }, + { CB_SYS_read, TARGET_SYS_read }, + { CB_SYS_write, TARGET_SYS_write }, + { CB_SYS_lseek, TARGET_SYS_lseek }, + { CB_SYS_unlink, TARGET_SYS_unlink }, + { CB_SYS_getpid, TARGET_SYS_getpid }, + { CB_SYS_fstat, TARGET_SYS_fstat64 }, + { CB_SYS_lstat, TARGET_SYS_lstat64 }, + { CB_SYS_stat, TARGET_SYS_stat64 }, + { CB_SYS_pipe, TARGET_SYS_pipe }, + { CB_SYS_time, TARGET_SYS_time }, + { CB_SYS_rename, TARGET_SYS_rename }, + { CB_SYS_truncate, TARGET_SYS_truncate }, + { CB_SYS_ftruncate, TARGET_SYS_ftruncate }, + { 0, -1 } +}; + +/* An older, 32-bit-only stat mapping. */ +static const char stat32_map[] = +"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:space,4:st_mtime,4:space,4:st_ctime,4:space,12"; + +/* Map for calls using the 32-bit struct stat. Primarily used by the + newlib Linux mapping. */ +static const CB_TARGET_DEFS_MAP syscall_stat32_map[] = +{ + { CB_SYS_fstat, TARGET_SYS_fstat }, + { 0, -1 } +}; + +/* Giving the true value for the running sim process will lead to + non-time-invariant behavior. */ +#define TARGET_PID 42 + +/* Unfortunately, we don't get this from cris.cpu at the moment, and if + we did, we'd still don't get a register number with the "16" offset. */ +#define TARGET_SRP_REGNUM (16+11) + +/* Extracted by applying + awk '/^#define/ { printf "#ifdef %s\n { %s, %s },\n#endif\n", $2, $2, $3;}' + on .../include/asm/errno.h in a GNU/Linux/CRIS installation and + adjusting the synonyms. */ + +static const CB_TARGET_DEFS_MAP errno_map[] = +{ +#ifdef EPERM + { EPERM, 1 }, +#endif +#ifdef ENOENT + { ENOENT, 2 }, +#endif +#ifdef ESRCH + { ESRCH, 3 }, +#endif +#ifdef EINTR + { EINTR, 4 }, +#endif +#ifdef EIO + { EIO, 5 }, +#endif +#ifdef ENXIO + { ENXIO, 6 }, +#endif +#ifdef E2BIG + { E2BIG, 7 }, +#endif +#ifdef ENOEXEC + { ENOEXEC, 8 }, +#endif +#ifdef EBADF + { EBADF, 9 }, +#endif +#ifdef ECHILD + { ECHILD, 10 }, +#endif +#ifdef EAGAIN + { EAGAIN, 11 }, +#endif +#ifdef ENOMEM + { ENOMEM, 12 }, +#endif +#ifdef EACCES + { EACCES, 13 }, +#endif +#ifdef EFAULT + { EFAULT, 14 }, +#endif +#ifdef ENOTBLK + { ENOTBLK, 15 }, +#endif +#ifdef EBUSY + { EBUSY, 16 }, +#endif +#ifdef EEXIST + { EEXIST, 17 }, +#endif +#ifdef EXDEV + { EXDEV, 18 }, +#endif +#ifdef ENODEV + { ENODEV, 19 }, +#endif +#ifdef ENOTDIR + { ENOTDIR, 20 }, +#endif +#ifdef EISDIR + { EISDIR, 21 }, +#endif +#ifdef EINVAL + { EINVAL, 22 }, +#endif +#ifdef ENFILE + { ENFILE, 23 }, +#endif +#ifdef EMFILE + { EMFILE, 24 }, +#endif +#ifdef ENOTTY + { ENOTTY, 25 }, +#endif +#ifdef ETXTBSY + { ETXTBSY, 26 }, +#endif +#ifdef EFBIG + { EFBIG, 27 }, +#endif +#ifdef ENOSPC + { ENOSPC, 28 }, +#endif +#ifdef ESPIPE + { ESPIPE, 29 }, +#endif +#ifdef EROFS + { EROFS, 30 }, +#endif +#ifdef EMLINK + { EMLINK, 31 }, +#endif +#ifdef EPIPE + { EPIPE, 32 }, +#endif +#ifdef EDOM + { EDOM, 33 }, +#endif +#ifdef ERANGE + { ERANGE, 34 }, +#endif +#ifdef EDEADLK + { EDEADLK, 35 }, +#endif +#ifdef ENAMETOOLONG + { ENAMETOOLONG, 36 }, +#endif +#ifdef ENOLCK + { ENOLCK, 37 }, +#endif +#ifdef ENOSYS + { ENOSYS, 38 }, +#endif +#ifdef ENOTEMPTY + { ENOTEMPTY, 39 }, +#endif +#ifdef ELOOP + { ELOOP, 40 }, +#endif +#ifdef EWOULDBLOCK + { EWOULDBLOCK, 11 }, +#endif +#ifdef ENOMSG + { ENOMSG, 42 }, +#endif +#ifdef EIDRM + { EIDRM, 43 }, +#endif +#ifdef ECHRNG + { ECHRNG, 44 }, +#endif +#ifdef EL2NSYNC + { EL2NSYNC, 45 }, +#endif +#ifdef EL3HLT + { EL3HLT, 46 }, +#endif +#ifdef EL3RST + { EL3RST, 47 }, +#endif +#ifdef ELNRNG + { ELNRNG, 48 }, +#endif +#ifdef EUNATCH + { EUNATCH, 49 }, +#endif +#ifdef ENOCSI + { ENOCSI, 50 }, +#endif +#ifdef EL2HLT + { EL2HLT, 51 }, +#endif +#ifdef EBADE + { EBADE, 52 }, +#endif +#ifdef EBADR + { EBADR, 53 }, +#endif +#ifdef EXFULL + { EXFULL, 54 }, +#endif +#ifdef ENOANO + { ENOANO, 55 }, +#endif +#ifdef EBADRQC + { EBADRQC, 56 }, +#endif +#ifdef EBADSLT + { EBADSLT, 57 }, +#endif +#ifdef EDEADLOCK + { EDEADLOCK, 35 }, +#endif +#ifdef EBFONT + { EBFONT, 59 }, +#endif +#ifdef ENOSTR + { ENOSTR, 60 }, +#endif +#ifdef ENODATA + { ENODATA, 61 }, +#endif +#ifdef ETIME + { ETIME, 62 }, +#endif +#ifdef ENOSR + { ENOSR, 63 }, +#endif +#ifdef ENONET + { ENONET, 64 }, +#endif +#ifdef ENOPKG + { ENOPKG, 65 }, +#endif +#ifdef EREMOTE + { EREMOTE, 66 }, +#endif +#ifdef ENOLINK + { ENOLINK, 67 }, +#endif +#ifdef EADV + { EADV, 68 }, +#endif +#ifdef ESRMNT + { ESRMNT, 69 }, +#endif +#ifdef ECOMM + { ECOMM, 70 }, +#endif +#ifdef EPROTO + { EPROTO, 71 }, +#endif +#ifdef EMULTIHOP + { EMULTIHOP, 72 }, +#endif +#ifdef EDOTDOT + { EDOTDOT, 73 }, +#endif +#ifdef EBADMSG + { EBADMSG, 74 }, +#endif +#ifdef EOVERFLOW + { EOVERFLOW, 75 }, +#endif +#ifdef ENOTUNIQ + { ENOTUNIQ, 76 }, +#endif +#ifdef EBADFD + { EBADFD, 77 }, +#endif +#ifdef EREMCHG + { EREMCHG, 78 }, +#endif +#ifdef ELIBACC + { ELIBACC, 79 }, +#endif +#ifdef ELIBBAD + { ELIBBAD, 80 }, +#endif +#ifdef ELIBSCN + { ELIBSCN, 81 }, +#endif +#ifdef ELIBMAX + { ELIBMAX, 82 }, +#endif +#ifdef ELIBEXEC + { ELIBEXEC, 83 }, +#endif +#ifdef EILSEQ + { EILSEQ, 84 }, +#endif +#ifdef ERESTART + { ERESTART, 85 }, +#endif +#ifdef ESTRPIPE + { ESTRPIPE, 86 }, +#endif +#ifdef EUSERS + { EUSERS, 87 }, +#endif +#ifdef ENOTSOCK + { ENOTSOCK, 88 }, +#endif +#ifdef EDESTADDRREQ + { EDESTADDRREQ, 89 }, +#endif +#ifdef EMSGSIZE + { EMSGSIZE, 90 }, +#endif +#ifdef EPROTOTYPE + { EPROTOTYPE, 91 }, +#endif +#ifdef ENOPROTOOPT + { ENOPROTOOPT, 92 }, +#endif +#ifdef EPROTONOSUPPORT + { EPROTONOSUPPORT, 93 }, +#endif +#ifdef ESOCKTNOSUPPORT + { ESOCKTNOSUPPORT, 94 }, +#endif +#ifdef EOPNOTSUPP + { EOPNOTSUPP, 95 }, +#endif +#ifdef EPFNOSUPPORT + { EPFNOSUPPORT, 96 }, +#endif +#ifdef EAFNOSUPPORT + { EAFNOSUPPORT, 97 }, +#endif +#ifdef EADDRINUSE + { EADDRINUSE, 98 }, +#endif +#ifdef EADDRNOTAVAIL + { EADDRNOTAVAIL, 99 }, +#endif +#ifdef ENETDOWN + { ENETDOWN, 100 }, +#endif +#ifdef ENETUNREACH + { ENETUNREACH, 101 }, +#endif +#ifdef ENETRESET + { ENETRESET, 102 }, +#endif +#ifdef ECONNABORTED + { ECONNABORTED, 103 }, +#endif +#ifdef ECONNRESET + { ECONNRESET, 104 }, +#endif +#ifdef ENOBUFS + { ENOBUFS, 105 }, +#endif +#ifdef EISCONN + { EISCONN, 106 }, +#endif +#ifdef ENOTCONN + { ENOTCONN, 107 }, +#endif +#ifdef ESHUTDOWN + { ESHUTDOWN, 108 }, +#endif +#ifdef ETOOMANYREFS + { ETOOMANYREFS, 109 }, +#endif +#ifdef ETIMEDOUT + { ETIMEDOUT, 110 }, +#endif +#ifdef ECONNREFUSED + { ECONNREFUSED, 111 }, +#endif +#ifdef EHOSTDOWN + { EHOSTDOWN, 112 }, +#endif +#ifdef EHOSTUNREACH + { EHOSTUNREACH, 113 }, +#endif +#ifdef EALREADY + { EALREADY, 114 }, +#endif +#ifdef EINPROGRESS + { EINPROGRESS, 115 }, +#endif +#ifdef ESTALE + { ESTALE, 116 }, +#endif +#ifdef EUCLEAN + { EUCLEAN, 117 }, +#endif +#ifdef ENOTNAM + { ENOTNAM, 118 }, +#endif +#ifdef ENAVAIL + { ENAVAIL, 119 }, +#endif +#ifdef EISNAM + { EISNAM, 120 }, +#endif +#ifdef EREMOTEIO + { EREMOTEIO, 121 }, +#endif +#ifdef EDQUOT + { EDQUOT, 122 }, +#endif +#ifdef ENOMEDIUM + { ENOMEDIUM, 123 }, +#endif +#ifdef EMEDIUMTYPE + { EMEDIUMTYPE, 124 }, +#endif + { 0, -1 } +}; + +/* Extracted by applying + perl -ne 'if ($_ =~ /^#define/) { split; + printf "#ifdef $_[1]\n { %s, 0x%x },\n#endif\n", + $_[1], $_[2] =~ /^0/ ? oct($_[2]) : $_[2];}' + on pertinent parts of .../include/asm/fcntl.h in a GNU/Linux/CRIS + installation and removing synonyms and unnecessary items. Don't + forget the end-marker. */ + +static const CB_TARGET_DEFS_MAP open_map[] = { +#ifdef O_ACCMODE + { O_ACCMODE, 0x3 }, +#endif +#ifdef O_RDONLY + { O_RDONLY, 0x0 }, +#endif +#ifdef O_WRONLY + { O_WRONLY, 0x1 }, +#endif +#ifdef O_RDWR + { O_RDWR, 0x2 }, +#endif +#ifdef O_CREAT + { O_CREAT, 0x40 }, +#endif +#ifdef O_EXCL + { O_EXCL, 0x80 }, +#endif +#ifdef O_NOCTTY + { O_NOCTTY, 0x100 }, +#endif +#ifdef O_TRUNC + { O_TRUNC, 0x200 }, +#endif +#ifdef O_APPEND + { O_APPEND, 0x400 }, +#endif +#ifdef O_NONBLOCK + { O_NONBLOCK, 0x800 }, +#endif +#ifdef O_NDELAY + { O_NDELAY, 0x0 }, +#endif +#ifdef O_SYNC + { O_SYNC, 0x1000 }, +#endif +#ifdef FASYNC + { FASYNC, 0x2000 }, +#endif +#ifdef O_DIRECT + { O_DIRECT, 0x4000 }, +#endif +#ifdef O_LARGEFILE + { O_LARGEFILE, 0x8000 }, +#endif +#ifdef O_DIRECTORY + { O_DIRECTORY, 0x10000 }, +#endif +#ifdef O_NOFOLLOW + { O_NOFOLLOW, 0x20000 }, +#endif + { -1, -1 } +}; + +/* Needed for the cris_pipe_nonempty and cris_pipe_empty syscalls. */ +static SIM_CPU *current_cpu_for_cb_callback; + +static int syscall_read_mem (host_callback *, struct cb_syscall *, + unsigned long, char *, int); +static int syscall_write_mem (host_callback *, struct cb_syscall *, + unsigned long, const char *, int); +static USI create_map (SIM_DESC, struct cris_sim_mmapped_page **, + USI addr, USI len); +static USI unmap_pages (SIM_DESC, struct cris_sim_mmapped_page **, + USI addr, USI len); +static USI is_mapped (SIM_DESC, struct cris_sim_mmapped_page **, + USI addr, USI len); +static void dump_statistics (SIM_CPU *current_cpu); +static void make_first_thread (SIM_CPU *current_cpu); + +/* Read/write functions for system call interface. */ + +static int +syscall_read_mem (host_callback *cb ATTRIBUTE_UNUSED, + 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; + + return sim_core_read_buffer (sd, cpu, read_map, buf, taddr, bytes); +} + +static int +syscall_write_mem (host_callback *cb ATTRIBUTE_UNUSED, + 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; + + return sim_core_write_buffer (sd, cpu, write_map, buf, taddr, bytes); +} + +/* When we risk running self-modified code (as in trampolines), this is + called from special-case insns. The silicon CRIS CPU:s have enough + cache snooping implemented making this a simulator-only issue. Tests: + gcc.c-torture/execute/931002-1.c execution, -O3 -g + gcc.c-torture/execute/931002-1.c execution, -O3 -fomit-frame-pointer. */ + +void +cris_flush_simulator_decode_cache (SIM_CPU *current_cpu, + USI pc ATTRIBUTE_UNUSED) +{ + SIM_DESC sd = CPU_STATE (current_cpu); + +#if WITH_SCACHE + if (USING_SCACHE_P (sd)) + scache_flush_cpu (current_cpu); +#endif +} + +/* Output statistics at the end of a run. */ +static void +dump_statistics (SIM_CPU *current_cpu) +{ + SIM_DESC sd = CPU_STATE (current_cpu); + CRIS_MISC_PROFILE *profp + = CPU_CRIS_MISC_PROFILE (current_cpu); + unsigned64 total = profp->basic_cycle_count; + const char *textmsg = "Basic clock cycles, total @: %llu\n"; + + /* The --cris-stats={basic|unaligned|schedulable|all} counts affect + what's included in the "total" count only. */ + switch (CPU_CRIS_MISC_PROFILE (current_cpu)->flags + & FLAG_CRIS_MISC_PROFILE_ALL) + { + case FLAG_CRIS_MISC_PROFILE_SIMPLE: + break; + + case (FLAG_CRIS_MISC_PROFILE_UNALIGNED | FLAG_CRIS_MISC_PROFILE_SIMPLE): + textmsg + = "Clock cycles including stall cycles for unaligned accesses @: %llu\n"; + total += profp->unaligned_mem_dword_count; + break; + + case (FLAG_CRIS_MISC_PROFILE_SCHEDULABLE | FLAG_CRIS_MISC_PROFILE_SIMPLE): + textmsg = "Schedulable clock cycles, total @: %llu\n"; + total + += (profp->memsrc_stall_count + + profp->memraw_stall_count + + profp->movemsrc_stall_count + + profp->movemdst_stall_count + + profp->mulsrc_stall_count + + profp->jumpsrc_stall_count + + profp->unaligned_mem_dword_count); + break; + + case FLAG_CRIS_MISC_PROFILE_ALL: + textmsg = "All accounted clock cycles, total @: %llu\n"; + total + += (profp->memsrc_stall_count + + profp->memraw_stall_count + + profp->movemsrc_stall_count + + profp->movemdst_stall_count + + profp->movemaddr_stall_count + + profp->mulsrc_stall_count + + profp->jumpsrc_stall_count + + profp->branch_stall_count + + profp->jumptarget_stall_count + + profp->unaligned_mem_dword_count); + break; + + default: + abort (); + + sim_io_eprintf (sd, + "Internal inconsistency at %s:%d", + __FILE__, __LINE__); + sim_engine_halt (sd, current_cpu, NULL, 0, + sim_stopped, SIM_SIGILL); + } + + /* Historically, these messages have gone to stderr, so we'll keep it + that way. It's also easier to then tell it from normal program + output. FIXME: Add redirect option like "run -e file". */ + sim_io_eprintf (sd, textmsg, total); + + /* For v32, unaligned_mem_dword_count should always be 0. For + v10, memsrc_stall_count should always be 0. */ + sim_io_eprintf (sd, "Memory source stall cycles: %lld\n", + profp->memsrc_stall_count + + profp->unaligned_mem_dword_count); + sim_io_eprintf (sd, "Memory read-after-write stall cycles: %lld\n", + profp->memraw_stall_count); + sim_io_eprintf (sd, "Movem source stall cycles: %lld\n", + profp->movemsrc_stall_count); + sim_io_eprintf (sd, "Movem destination stall cycles: %lld\n", + profp->movemdst_stall_count); + sim_io_eprintf (sd, "Movem address stall cycles: %lld\n", + profp->movemaddr_stall_count); + sim_io_eprintf (sd, "Multiplication source stall cycles: %lld\n", + profp->mulsrc_stall_count); + sim_io_eprintf (sd, "Jump source stall cycles: %lld\n", + profp->jumpsrc_stall_count); + sim_io_eprintf (sd, "Branch misprediction stall cycles: %lld\n", + profp->branch_stall_count); + sim_io_eprintf (sd, "Jump target stall cycles: %lld\n", + profp->jumptarget_stall_count); +} + +/* Check whether any part of [addr .. addr + len - 1] is already mapped. + Return 1 if a overlap detected, 0 otherwise. */ + +static USI +is_mapped (SIM_DESC sd ATTRIBUTE_UNUSED, + struct cris_sim_mmapped_page **rootp, + USI addr, USI len) +{ + struct cris_sim_mmapped_page *mapp; + + if (len == 0 || (len & 8191)) + abort (); + + /* Iterate over the reverse-address sorted pages until we find a page in + or lower than the checked area. */ + for (mapp = *rootp; mapp != NULL && mapp->addr >= addr; mapp = mapp->prev) + if (mapp->addr < addr + len && mapp->addr >= addr) + return 1; + + return 0; +} + +/* Create mmapped memory. */ + +static USI +create_map (SIM_DESC sd, struct cris_sim_mmapped_page **rootp, USI addr, + USI len) +{ + struct cris_sim_mmapped_page *mapp; + struct cris_sim_mmapped_page **higher_prevp = rootp; + USI new_addr = 0x40000000; + + if (addr != 0) + new_addr = addr; + else if (*rootp) + new_addr = rootp[0]->addr + 8192; + + if (len != 8192) + { + USI page_addr; + + if (len & 8191) + /* Which is better: return an error for this, or just round it up? */ + abort (); + + /* Do a recursive call for each page in the request. */ + for (page_addr = new_addr; len != 0; page_addr += 8192, len -= 8192) + if (create_map (sd, rootp, page_addr, 8192) >= (USI) -8191) + abort (); + + return new_addr; + } + + for (mapp = *rootp; + mapp != NULL && mapp->addr > new_addr; + mapp = mapp->prev) + higher_prevp = &mapp->prev; + + /* Allocate the new page, on the next higher page from the last one + allocated, and link in the new descriptor before previous ones. */ + mapp = malloc (sizeof (*mapp)); + + if (mapp == NULL) + return (USI) -ENOMEM; + + sim_core_attach (sd, NULL, 0, access_read_write_exec, 0, + new_addr, len, + 0, NULL, NULL); + + mapp->addr = new_addr; + mapp->prev = *higher_prevp; + *higher_prevp = mapp; + + return new_addr; +} + +/* Unmap one or more pages. */ + +static USI +unmap_pages (SIM_DESC sd, struct cris_sim_mmapped_page **rootp, USI addr, + USI len) +{ + struct cris_sim_mmapped_page *mapp; + struct cris_sim_mmapped_page **higher_prevp = rootp; + + if (len != 8192) + { + USI page_addr; + + if (len & 8191) + /* Which is better: return an error for this, or just round it up? */ + abort (); + + /* Loop backwards to make each call is O(1) over the number of pages + allocated, if we're unmapping from the high end of the pages. */ + for (page_addr = addr + len - 8192; + page_addr >= addr; + page_addr -= 8192) + if (unmap_pages (sd, rootp, page_addr, 8192) != 0) + abort (); + + return 0; + } + + for (mapp = *rootp; mapp != NULL && mapp->addr > addr; mapp = mapp->prev) + higher_prevp = &mapp->prev; + + if (mapp == NULL || mapp->addr != addr) + return EINVAL; + + *higher_prevp = mapp->prev; + sim_core_detach (sd, NULL, 0, 0, addr); + free (mapp); + return 0; +} + +/* The semantic code invokes this for illegal (unrecognized) instructions. */ + +SEM_PC +sim_engine_invalid_insn (SIM_CPU *current_cpu, IADDR cia, SEM_PC vpc) +{ + SIM_DESC sd = CPU_STATE (current_cpu); + + sim_engine_halt (sd, current_cpu, NULL, cia, sim_stopped, SIM_SIGILL); + return vpc; +} + +/* Handlers from the CGEN description that should not be called. */ + +USI +cris_bmod_handler (SIM_CPU *current_cpu ATTRIBUTE_UNUSED, + UINT srcreg ATTRIBUTE_UNUSED, + USI dstreg ATTRIBUTE_UNUSED) +{ + abort (); +} + +void +h_supr_set_handler (SIM_CPU *current_cpu ATTRIBUTE_UNUSED, + UINT index ATTRIBUTE_UNUSED, + USI page ATTRIBUTE_UNUSED, + USI newval ATTRIBUTE_UNUSED) +{ + abort (); +} + +USI +h_supr_get_handler (SIM_CPU *current_cpu ATTRIBUTE_UNUSED, + UINT index ATTRIBUTE_UNUSED, + USI page ATTRIBUTE_UNUSED) +{ + abort (); +} + +/* Swap one context for another. */ + +static void +schedule (SIM_CPU *current_cpu, int next) +{ + /* Need to mark context-switches in the trace output. */ + if ((CPU_CRIS_MISC_PROFILE (current_cpu)->flags + & FLAG_CRIS_MISC_PROFILE_XSIM_TRACE)) + cris_trace_printf (CPU_STATE (current_cpu), current_cpu, + "\t#:%d\n", next); + + /* Copy the current context (if there is one) to its slot. */ + if (current_cpu->thread_data[current_cpu->threadno].cpu_context) + memcpy (current_cpu->thread_data[current_cpu->threadno].cpu_context, + ¤t_cpu->cpu_data_placeholder, + current_cpu->thread_cpu_data_size); + + /* Copy the new context from its slot. */ + memcpy (¤t_cpu->cpu_data_placeholder, + current_cpu->thread_data[next].cpu_context, + current_cpu->thread_cpu_data_size); + + /* Update needed stuff to indicate the new context. */ + current_cpu->threadno = next; + + /* Handle pending signals. */ + if (current_cpu->thread_data[next].sigpending + /* We don't run nested signal handlers. This means that pause(2) + and sigsuspend(2) do not work in sighandlers, but that + shouldn't be too hard a restriction. It also greatly + simplifies the code. */ + && current_cpu->thread_data[next].cpu_context_atsignal == NULL) + { + int sig; + + /* See if there's really a pending, non-blocked handler. We don't + queue signals, so just use the first one in ascending order. */ + for (sig = 0; sig < 64; sig++) + if (current_cpu->thread_data[next].sigdata[sig].pending + && !current_cpu->thread_data[next].sigdata[sig].blocked) + { + bfd_byte regbuf[4]; + USI sp; + int i; + USI blocked; + USI pc = sim_pc_get (current_cpu); + + /* It's simpler to save the CPU context inside the simulator + than on the stack. */ + current_cpu->thread_data[next].cpu_context_atsignal + = (*current_cpu + ->make_thread_cpu_data) (current_cpu, + current_cpu->thread_data[next] + .cpu_context); + + (*CPU_REG_FETCH (current_cpu)) (current_cpu, H_GR_SP, regbuf, 4); + sp = bfd_getl32 (regbuf); + + /* Make sure we have an aligned stack. */ + sp &= ~3; + + /* Make room for the signal frame, aligned. FIXME: Check that + the memory exists, map it in if absent. (BTW, should also + implement on-access automatic stack allocation). */ + sp -= 20; + + /* This isn't the same signal frame as the kernel uses, because + we don't want to bother getting all registers on and off the + stack. */ + + /* First, we store the currently blocked signals. */ + blocked = 0; + for (i = 0; i < 32; i++) + blocked + |= current_cpu->thread_data[next].sigdata[i + 1].blocked << i; + sim_core_write_aligned_4 (current_cpu, pc, 0, sp, blocked); + blocked = 0; + for (i = 0; i < 31; i++) + blocked + |= current_cpu->thread_data[next].sigdata[i + 33].blocked << i; + sim_core_write_aligned_4 (current_cpu, pc, 0, sp + 4, blocked); + + /* Then, the actual instructions. This is CPU-specific, but we + use instructions from the common subset for v10 and v32 which + should be safe for the time being but could be parametrized + if need be. */ + /* MOVU.W [PC+],R9. */ + sim_core_write_aligned_2 (current_cpu, pc, 0, sp + 8, 0x9c5f); + /* .WORD TARGET_SYS_sigreturn. */ + sim_core_write_aligned_2 (current_cpu, pc, 0, sp + 10, + TARGET_SYS_sigreturn); + /* BREAK 13. */ + sim_core_write_aligned_2 (current_cpu, pc, 0, sp + 12, 0xe93d); + + /* NOP (on v32; it's SETF on v10, but is the correct compatible + instruction. Still, it doesn't matter because v10 has no + delay slot for BREAK so it will not be executed). */ + sim_core_write_aligned_2 (current_cpu, pc, 0, sp + 16, 0x05b0); + + /* Modify registers to hold the right values for the sighandler + context: updated stackpointer and return address pointing to + the sigreturn stub. */ + bfd_putl32 (sp, regbuf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, H_GR_SP, regbuf, 4); + bfd_putl32 (sp + 8, regbuf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, TARGET_SRP_REGNUM, + regbuf, 4); + + current_cpu->thread_data[next].sigdata[sig].pending = 0; + + /* Block this signal (for the duration of the sighandler). */ + current_cpu->thread_data[next].sigdata[sig].blocked = 1; + + sim_pc_set (current_cpu, current_cpu->sighandler[sig]); + bfd_putl32 (sig, regbuf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, H_GR_R10, + regbuf, 4); + + /* We ignore a SA_SIGINFO flag in the sigaction call; the code I + needed all this for, specifies a SA_SIGINFO call but treats it + like an ordinary sighandler; only the signal number argument is + inspected. To make future need to implement SA_SIGINFO + correctly possible, we set the siginfo argument register to a + magic (hopefully non-address) number. (NB: then, you should + just need to pass the siginfo argument; it seems you probably + don't need to implement the specific rt_sigreturn.) */ + bfd_putl32 (0xbad5161f, regbuf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, H_GR_R11, + regbuf, 4); + + /* The third argument is unused and the kernel sets it to 0. */ + bfd_putl32 (0, regbuf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, H_GR_R12, + regbuf, 4); + return; + } + + /* No, there actually was no pending signal for this thread. Reset + this flag. */ + current_cpu->thread_data[next].sigpending = 0; + } +} + +/* Reschedule the simplest possible way until something else is absolutely + necessary: + - A. Find the next process (round-robin) that doesn't have at_syscall + set, schedule it. + - B. If there is none, just run the next process, round-robin. + - Clear at_syscall for the current process. */ + +static void +reschedule (SIM_CPU *current_cpu) +{ + int i; + + /* Iterate over all thread slots, because after a few thread creations + and exits, we don't know where the live ones are. */ + for (i = (current_cpu->threadno + 1) % SIM_TARGET_MAX_THREADS; + i != current_cpu->threadno; + i = (i + 1) % SIM_TARGET_MAX_THREADS) + if (current_cpu->thread_data[i].cpu_context + && current_cpu->thread_data[i].at_syscall == 0) + { + schedule (current_cpu, i); + return; + } + + /* Pick any next live thread. */ + for (i = (current_cpu->threadno + 1) % SIM_TARGET_MAX_THREADS; + i != current_cpu->threadno; + i = (i + 1) % SIM_TARGET_MAX_THREADS) + if (current_cpu->thread_data[i].cpu_context) + { + schedule (current_cpu, i); + return; + } + + /* More than one live thread, but we couldn't find the next one? */ + abort (); +} + +/* Set up everything to receive (or IGN) an incoming signal to the + current context. */ + +static int +deliver_signal (SIM_CPU *current_cpu, int sig, unsigned int pid) +{ + int i; + USI pc = sim_pc_get (current_cpu); + + /* Find the thread index of the pid. */ + for (i = 0; i < SIM_TARGET_MAX_THREADS; i++) + /* Apparently it's ok to send signals to zombies (so a check for + current_cpu->thread_data[i].cpu_context != NULL would be + wrong). */ + if (current_cpu->thread_data[i].threadid == pid - TARGET_PID) + { + if (sig < 64) + switch (current_cpu->sighandler[sig]) + { + case TARGET_SIG_DFL: + switch (sig) + { + /* The following according to the glibc + documentation. (The kernel code has non-obvious + execution paths.) */ + case TARGET_SIGFPE: + case TARGET_SIGILL: + case TARGET_SIGSEGV: + case TARGET_SIGBUS: + case TARGET_SIGABRT: + case TARGET_SIGTRAP: + case TARGET_SIGSYS: + + case TARGET_SIGTERM: + case TARGET_SIGINT: + case TARGET_SIGQUIT: + case TARGET_SIGKILL: + case TARGET_SIGHUP: + + case TARGET_SIGALRM: + case TARGET_SIGVTALRM: + case TARGET_SIGPROF: + case TARGET_SIGSTOP: + + case TARGET_SIGPIPE: + case TARGET_SIGLOST: + case TARGET_SIGXCPU: + case TARGET_SIGXFSZ: + case TARGET_SIGUSR1: + case TARGET_SIGUSR2: + sim_io_eprintf (CPU_STATE (current_cpu), + "Exiting pid %d due to signal %d\n", + pid, sig); + sim_engine_halt (CPU_STATE (current_cpu), current_cpu, + NULL, pc, sim_stopped, + sig == TARGET_SIGABRT + ? SIM_SIGABRT : SIM_SIGILL); + return 0; + + /* The default for all other signals is to be ignored. */ + default: + return 0; + } + + case TARGET_SIG_IGN: + switch (sig) + { + case TARGET_SIGKILL: + case TARGET_SIGSTOP: + /* Can't ignore these signals. */ + sim_io_eprintf (CPU_STATE (current_cpu), + "Exiting pid %d due to signal %d\n", + pid, sig); + sim_engine_halt (CPU_STATE (current_cpu), current_cpu, + NULL, pc, sim_stopped, SIM_SIGILL); + return 0; + + default: + return 0; + } + break; + + default: + /* Mark the signal as pending, making schedule () check + closer. The signal will be handled when the thread is + scheduled and the signal is unblocked. */ + current_cpu->thread_data[i].sigdata[sig].pending = 1; + current_cpu->thread_data[i].sigpending = 1; + return 0; + } + else + { + sim_io_eprintf (CPU_STATE (current_cpu), + "Unimplemented signal: %d\n", sig); + sim_engine_halt (CPU_STATE (current_cpu), current_cpu, NULL, pc, + sim_stopped, SIM_SIGILL); + } + } + + return + -cb_host_to_target_errno (STATE_CALLBACK (CPU_STATE (current_cpu)), + ESRCH); +} + +/* Make the vector and the first item, the main thread. */ + +static void +make_first_thread (SIM_CPU *current_cpu) +{ + current_cpu->thread_data + = xcalloc (1, + SIM_TARGET_MAX_THREADS + * sizeof (current_cpu->thread_data[0])); + current_cpu->thread_data[0].cpu_context + = (*current_cpu->make_thread_cpu_data) (current_cpu, + ¤t_cpu + ->cpu_data_placeholder); + current_cpu->thread_data[0].parent_threadid = -1; + + /* For good measure. */ + if (TARGET_SIG_DFL != 0) + abort (); +} + +/* Main function: the handler of the "break 13" syscall insn. */ + +USI +cris_break_13_handler (SIM_CPU *current_cpu, USI callnum, USI arg1, + USI arg2, USI arg3, USI arg4, USI arg5, USI arg6, + USI pc) +{ + CB_SYSCALL s; + SIM_DESC sd = CPU_STATE (current_cpu); + host_callback *cb = STATE_CALLBACK (sd); + int retval; + int threadno = current_cpu->threadno; + + current_cpu->syscalls++; + + CB_SYSCALL_INIT (&s); + s.func = callnum; + s.arg1 = arg1; + s.arg2 = arg2; + s.arg3 = arg3; + + if (callnum == TARGET_SYS_exit && current_cpu->m1threads == 0) + { + if (CPU_CRIS_MISC_PROFILE (current_cpu)->flags + & FLAG_CRIS_MISC_PROFILE_ALL) + dump_statistics (current_cpu); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_exited, arg1); + } + + s.p1 = (PTR) sd; + s.p2 = (PTR) current_cpu; + s.read_mem = syscall_read_mem; + s.write_mem = syscall_write_mem; + + current_cpu_for_cb_callback = current_cpu; + + if (cb_syscall (cb, &s) != CB_RC_OK) + { + abort (); + sim_io_eprintf (sd, "Break 13: invalid %d? Returned %ld\n", callnum, + s.result); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGILL); + } + + retval = s.result == -1 ? -s.errcode : s.result; + + if (s.errcode != 0 && s.errcode == cb_host_to_target_errno (cb, ENOSYS)) + { + /* If the generic simulator call said ENOSYS, then let's try the + ones we know ourselves. + + The convention is to provide *very limited* functionality on an + as-needed basis, only what's covered by the test-suite, tests + added when functionality changes and abort with a descriptive + message for *everything* else. Where there's no test-case, we + just abort. */ + switch (callnum) + { + case 0: + /* It's a pretty safe bet that the "old setup() system call" + number will not be re-used; we can't say the same for higher + numbers. We treat this simulator-generated call as "wait + forever"; we re-run this insn. The wait is ended by a + callback. Sanity check that this is the reason we got + here. */ + if (current_cpu->thread_data == NULL + || (current_cpu->thread_data[threadno].pipe_write_fd == 0)) + goto unimplemented_syscall; + + sim_pc_set (current_cpu, pc); + retval = arg1; + break; + + case TARGET_SYS_fcntl64: + case TARGET_SYS_fcntl: + if (arg2 == 1) + { + /* F_GETFD. + Glibc checks stdin, stdout and stderr fd:s for + close-on-exec security sanity. We just need to provide a + OK return value. If we really need to have a + close-on-exec flag true, we could just do a real fcntl + here. */ + retval = 0; + } + else if (arg2 == 2) + { + /* F_SETFD. Just ignore attempts to set the close-on-exec + flag. */ + retval = 0; + } + break; + + case TARGET_SYS_uname: + { + /* Fill in a few constants to appease glibc. */ + static const char sim_utsname[6][65] = + { + "Linux", + "sim-target", + "2.4.5", + TARGET_UTSNAME, + "cris", + "localdomain" + }; + + if ((s.write_mem) (cb, &s, arg1, (const char *) sim_utsname, + sizeof (sim_utsname)) + != sizeof (sim_utsname)) + retval = -cb_host_to_target_errno (cb, EFAULT); + else + retval = 0; + break; + } + + case TARGET_SYS_geteuid32: + /* We tell the truth with these. Maybe we shouldn't, but it + should match the "stat" information. */ + retval = geteuid (); + break; + + case TARGET_SYS_getuid32: + retval = getuid (); + break; + + case TARGET_SYS_getegid32: + retval = getegid (); + break; + + case TARGET_SYS_getgid32: + retval = getgid (); + break; + + case TARGET_SYS_brk: + /* Most often, we just return the argument, like the Linux + kernel. */ + retval = arg1; + + if (arg1 == 0) + retval = current_cpu->endbrk; + else if (arg1 <= current_cpu->endmem) + current_cpu->endbrk = arg1; + else + { + USI new_end = (arg1 + 8191) & ~8191; + + /* If the simulator wants to brk more than a certain very + large amount, something is wrong. FIXME: Return an error + or abort? Have command-line selectable? */ + if (new_end - current_cpu->endmem > SIM_MAX_ALLOC_CHUNK) + { + current_cpu->endbrk = current_cpu->endmem; + retval = current_cpu->endmem; + break; + } + + sim_core_attach (sd, NULL, 0, access_read_write_exec, 0, + current_cpu->endmem, + new_end - current_cpu->endmem, + 0, NULL, NULL); + current_cpu->endbrk = arg1; + current_cpu->endmem = new_end; + } + break; + + case TARGET_SYS_getpid: + /* Correct until CLONE_THREAD is implemented. */ + retval = current_cpu->thread_data == NULL + ? TARGET_PID + : TARGET_PID + current_cpu->thread_data[threadno].threadid; + break; + + case TARGET_SYS_getppid: + /* Correct until CLONE_THREAD is implemented. */ + retval = current_cpu->thread_data == NULL + ? TARGET_PID - 1 + : (TARGET_PID + + current_cpu->thread_data[threadno].parent_threadid); + break; + + case TARGET_SYS_mmap2: + { + USI addr = arg1; + USI len = arg2; + USI prot = arg3; + USI flags = arg4; + USI fd = arg5; + USI pgoff = arg6; + + /* If the simulator wants to mmap more than the very large + limit, something is wrong. FIXME: Return an error or + abort? Have command-line selectable? */ + if (len > SIM_MAX_ALLOC_CHUNK) + { + retval = -cb_host_to_target_errno (cb, ENOMEM); + break; + } + + if ((prot != (TARGET_PROT_READ | TARGET_PROT_WRITE) + && (prot + != (TARGET_PROT_READ + | TARGET_PROT_WRITE + | TARGET_PROT_EXEC)) + && prot != TARGET_PROT_READ) + || (flags != (TARGET_MAP_ANONYMOUS | TARGET_MAP_PRIVATE) + && flags != TARGET_MAP_PRIVATE + && flags != TARGET_MAP_SHARED) + || (fd != (USI) -1 && prot != TARGET_PROT_READ) + || pgoff != 0 + || ((len & 8191) != 0 && fd == (USI) -1)) + { + sim_io_eprintf (sd, "Unimplemented mmap2 call " + "(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", + (unsigned long) arg1, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5, + (unsigned long) arg6); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + break; + } + else if (fd != (USI) -1) + { + /* Map a file. */ + + USI newaddr; + USI pos; + + /* A non-aligned argument is allowed for files. */ + USI newlen = (len + 8191) & ~8191; + + /* We only support read, which we should already have + checked. Check again anyway. */ + if (prot != TARGET_PROT_READ) + abort (); + + newaddr + = create_map (sd, ¤t_cpu->highest_mmapped_page, addr, + newlen); + + if (newaddr >= (USI) -8191) + { + abort (); + retval = -cb_host_to_target_errno (cb, -(SI) newaddr); + break; + } + + /* Find the current position in the file. */ + s.func = TARGET_SYS_lseek; + s.arg1 = fd; + s.arg2 = 0; + s.arg3 = SEEK_CUR; + if (cb_syscall (cb, &s) != CB_RC_OK) + abort (); + pos = s.result; + + if (s.result < 0) + abort (); + + /* Use the standard read callback to read in "len" + bytes. */ + s.func = TARGET_SYS_read; + s.arg1 = fd; + s.arg2 = newaddr; + s.arg3 = len; + if (cb_syscall (cb, &s) != CB_RC_OK) + abort (); + + if ((USI) s.result != len) + abort (); + + /* After reading, we need to go back to the previous + position in the file. */ + s.func = TARGET_SYS_lseek; + s.arg1 = fd; + s.arg2 = pos; + s.arg3 = SEEK_SET; + if (cb_syscall (cb, &s) != CB_RC_OK) + abort (); + if (pos != (USI) s.result) + abort (); + + retval = newaddr; + } + else + { + USI newaddr + = create_map (sd, ¤t_cpu->highest_mmapped_page, addr, len); + + if (newaddr >= (USI) -8191) + retval = -cb_host_to_target_errno (cb, -(SI) newaddr); + else + retval = newaddr; + } + break; + } + + case TARGET_SYS_mprotect: + { + /* We only cover the case of linuxthreads mprotecting out its + stack guard page. */ + USI addr = arg1; + USI len = arg2; + USI prot = arg3; + + if ((addr & 8191) != 0 + || len != 8192 + || prot != TARGET_PROT_NONE + || !is_mapped (sd, ¤t_cpu->highest_mmapped_page, addr, + len)) + { + sim_io_eprintf (sd, "Unimplemented mprotect call " + "(0x%lx, 0x%lx, 0x%lx)\n", + (unsigned long) arg1, + (unsigned long) arg2, + (unsigned long) arg3); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + break; + } + + /* FIXME: We should account for pages like this that are + "mprotected out". For now, we just tell the simulator + core to remove that page from its map. */ + sim_core_detach (sd, NULL, 0, 0, addr); + retval = 0; + break; + } + + case TARGET_SYS_ioctl: + { + /* We support only a very limited functionality: checking + stdout with TCGETS to perform the isatty function. The + TCGETS ioctl isn't actually performed or the result used by + an isatty () caller in a "hello, world" program; only the + return value is then used. Maybe we shouldn't care about + the environment of the simulator regarding isatty, but + that's been working before, in the xsim simulator. */ + if (arg2 == TARGET_TCGETS && arg1 == 1) + retval = isatty (1) ? 0 : cb_host_to_target_errno (cb, EINVAL); + else + retval = -cb_host_to_target_errno (cb, EINVAL); + break; + } + + case TARGET_SYS_munmap: + { + USI addr = arg1; + USI len = arg2; + USI result + = unmap_pages (sd, ¤t_cpu->highest_mmapped_page, addr, + len); + retval = result != 0 ? -cb_host_to_target_errno (cb, result) : 0; + break; + } + + case TARGET_SYS_wait4: + { + int i; + USI pid = arg1; + USI saddr = arg2; + USI options = arg3; + USI rusagep = arg4; + + /* FIXME: We're not properly implementing __WCLONE, and we + don't really need the special casing so we might as well + make this general. */ + if ((!(pid == (USI) -1 + && options == (TARGET___WCLONE | TARGET_WNOHANG) + && saddr != 0) + && !(pid > 0 + && (options == TARGET___WCLONE + || options == TARGET___WALL))) + || rusagep != 0 + || current_cpu->thread_data == NULL) + { + sim_io_eprintf (sd, "Unimplemented wait4 call " + "(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", + (unsigned long) arg1, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + break; + } + + if (pid == (USI) -1) + for (i = 1; i < SIM_TARGET_MAX_THREADS; i++) + { + if (current_cpu->thread_data[threadno].threadid + == current_cpu->thread_data[i].parent_threadid + && current_cpu->thread_data[i].threadid != 0 + && current_cpu->thread_data[i].cpu_context == NULL) + { + /* A zombied child. Get the exit value and clear the + zombied entry so it will be reused. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, saddr, + current_cpu + ->thread_data[i].exitval); + retval + = current_cpu->thread_data[i].threadid + TARGET_PID; + memset (¤t_cpu->thread_data[i], 0, + sizeof (current_cpu->thread_data[i])); + goto outer_break; + } + } + else + { + /* We're waiting for a specific PID. If we don't find + it zombied on this run, rerun the syscall. */ + for (i = 1; i < SIM_TARGET_MAX_THREADS; i++) + if (pid == current_cpu->thread_data[i].threadid + TARGET_PID + && current_cpu->thread_data[i].cpu_context == NULL) + { + if (saddr != 0) + /* Get the exit value if the caller wants it. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, + saddr, + current_cpu + ->thread_data[i] + .exitval); + + retval + = current_cpu->thread_data[i].threadid + TARGET_PID; + memset (¤t_cpu->thread_data[i], 0, + sizeof (current_cpu->thread_data[i])); + + goto outer_break; + } + + sim_pc_set (current_cpu, pc); + } + + retval = -cb_host_to_target_errno (cb, ECHILD); + outer_break: + break; + } + + case TARGET_SYS_rt_sigaction: + { + USI signum = arg1; + USI old_sa = arg3; + USI new_sa = arg2; + + /* The kernel says: + struct sigaction { + __sighandler_t sa_handler; + unsigned long sa_flags; + void (*sa_restorer)(void); + sigset_t sa_mask; + }; */ + + if (old_sa != 0) + { + sim_core_write_unaligned_4 (current_cpu, pc, 0, old_sa + 0, + current_cpu->sighandler[signum]); + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg3 + 4, 0); + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg3 + 8, 0); + + /* We'll assume _NSIG_WORDS is 2 for the kernel. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg3 + 12, 0); + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg3 + 16, 0); + } + if (new_sa != 0) + { + USI sa_handler + = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa); + USI sa_flags + = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 4); + USI sa_restorer + = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 8); + USI sa_mask_low + = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 12); + USI sa_mask_high + = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 16); + + /* We won't interrupt a syscall so we won't restart it, + but a signal(2) call ends up syscalling rt_sigaction + with this flag, so we have to handle it. The + sa_restorer field contains garbage when not + TARGET_SA_RESTORER, so don't look at it. For the + time being, we don't nest sighandlers, so we + ignore the sa_mask, which simplifies things. */ + if ((sa_flags != 0 + && sa_flags != TARGET_SA_RESTART + && sa_flags != (TARGET_SA_RESTART|TARGET_SA_SIGINFO)) + || sa_handler == 0) + { + sim_io_eprintf (sd, "Unimplemented rt_sigaction " + "syscall (0x%lx, " + "0x%lx: [0x%x, 0x%x, 0x%x, " + "{0x%x, 0x%x}], " + "0x%lx)\n", + (unsigned long) arg1, + (unsigned long) arg2, + sa_handler, sa_flags, sa_restorer, + sa_mask_low, sa_mask_high, + (unsigned long) arg3); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + current_cpu->sighandler[signum] = sa_handler; + + /* Because we may have unblocked signals, one may now be + pending, if there are threads, that is. */ + if (current_cpu->thread_data) + current_cpu->thread_data[threadno].sigpending = 1; + } + retval = 0; + break; + } + + case TARGET_SYS_mremap: + { + USI addr = arg1; + USI old_len = arg2; + USI new_len = arg3; + USI flags = arg4; + USI new_addr = arg5; + USI mapped_addr; + + if (new_len == old_len) + /* The program and/or library is possibly confused but + this is a valid call. Happens with ipps-1.40 on file + svs_all. */ + retval = addr; + else if (new_len < old_len) + { + /* Shrinking is easy. */ + if (unmap_pages (sd, ¤t_cpu->highest_mmapped_page, + addr + new_len, old_len - new_len) != 0) + retval = -cb_host_to_target_errno (cb, EINVAL); + else + retval = addr; + } + else if (! is_mapped (sd, ¤t_cpu->highest_mmapped_page, + addr + old_len, new_len - old_len)) + { + /* If the extension isn't mapped, we can just add it. */ + mapped_addr + = create_map (sd, ¤t_cpu->highest_mmapped_page, + addr + old_len, new_len - old_len); + + if (mapped_addr > (USI) -8192) + retval = -cb_host_to_target_errno (cb, -(SI) mapped_addr); + else + retval = addr; + } + else if (flags & TARGET_MREMAP_MAYMOVE) + { + /* Create a whole new map and copy the contents + block-by-block there. We ignore the new_addr argument + for now. */ + char buf[8192]; + USI prev_addr = addr; + USI prev_len = old_len; + + mapped_addr + = create_map (sd, ¤t_cpu->highest_mmapped_page, + 0, new_len); + + if (mapped_addr > (USI) -8192) + { + retval = -cb_host_to_target_errno (cb, -(SI) new_addr); + break; + } + + retval = mapped_addr; + + for (; old_len > 0; + old_len -= 8192, mapped_addr += 8192, addr += 8192) + { + if (sim_core_read_buffer (sd, current_cpu, read_map, buf, + addr, 8192) != 8192 + || sim_core_write_buffer (sd, current_cpu, 0, buf, + mapped_addr, 8192) != 8192) + abort (); + } + + if (unmap_pages (sd, ¤t_cpu->highest_mmapped_page, + prev_addr, prev_len) != 0) + abort (); + } + else + retval = -cb_host_to_target_errno (cb, -ENOMEM); + break; + } + + case TARGET_SYS_poll: + { + int npollfds = arg2; + int timeout = arg3; + SI ufds = arg1; + SI fd = -1; + HI events = -1; + HI revents = 0; + struct stat buf; + int i; + + /* The kernel says: + struct pollfd { + int fd; + short events; + short revents; + }; */ + + /* Check that this is the expected poll call from + linuxthreads/manager.c; we don't support anything else. + Remember, fd == 0 isn't supported. */ + if (npollfds != 1 + || ((fd = sim_core_read_unaligned_4 (current_cpu, pc, + 0, ufds)) <= 0) + || ((events = sim_core_read_unaligned_2 (current_cpu, pc, + 0, ufds + 4)) + != TARGET_POLLIN) + || ((cb->fstat) (cb, fd, &buf) != 0 + || (buf.st_mode & S_IFIFO) == 0) + || current_cpu->thread_data == NULL) + { + sim_io_eprintf (sd, "Unimplemented poll syscall " + "(0x%lx: [0x%x, 0x%x, x], 0x%lx, 0x%lx)\n", + (unsigned long) arg1, fd, events, + (unsigned long) arg2, (unsigned long) arg3); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGILL); + break; + } + + retval = 0; + + /* Iterate over threads; find a marker that a writer is + sleeping, waiting for a reader. */ + for (i = 0; i < SIM_TARGET_MAX_THREADS; i++) + if (current_cpu->thread_data[i].cpu_context != NULL + && current_cpu->thread_data[i].pipe_read_fd == fd) + { + revents = TARGET_POLLIN; + retval = 1; + break; + } + + /* Timeout decreases with whatever time passed between the + last syscall and this. That's not exactly right for the + first call, but it's close enough that it isn't + worthwhile to complicate matters by making that a special + case. */ + timeout + -= (TARGET_TIME_MS (current_cpu) + - (current_cpu->thread_data[threadno].last_execution)); + + /* Arrange to repeat this syscall until timeout or event, + decreasing timeout at each iteration. */ + if (timeout > 0 && revents == 0) + { + bfd_byte timeout_buf[4]; + + bfd_putl32 (timeout, timeout_buf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, + H_GR_R12, timeout_buf, 4); + sim_pc_set (current_cpu, pc); + retval = arg1; + break; + } + + sim_core_write_unaligned_2 (current_cpu, pc, 0, ufds + 4 + 2, + revents); + break; + } + + case TARGET_SYS_gettimeofday: + if (arg1 != 0) + { + USI ts = TARGET_TIME (current_cpu); + USI tms = TARGET_TIME_MS (current_cpu); + + /* First dword is seconds since TARGET_EPOCH. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg1, ts); + + /* Second dword is microseconds. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg1 + 4, + (tms % 1000) * 1000); + } + if (arg2 != 0) + { + /* Time-zone info is always cleared. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg2, 0); + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg2 + 4, 0); + } + retval = 0; + break; + + case TARGET_SYS_llseek: + { + /* If it fits, tweak parameters to fit the "generic" 32-bit + lseek and use that. */ + SI fd = arg1; + SI offs_hi = arg2; + SI offs_lo = arg3; + SI resultp = arg4; + SI whence = arg5; + retval = 0; + + if (!((offs_hi == 0 && offs_lo >= 0) + || (offs_hi == -1 && offs_lo < 0))) + { + sim_io_eprintf (sd, + "Unimplemented llseek offset," + " fd %d: 0x%x:0x%x\n", + fd, (unsigned) arg2, (unsigned) arg3); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + s.func = TARGET_SYS_lseek; + s.arg2 = offs_lo; + s.arg3 = whence; + if (cb_syscall (cb, &s) != CB_RC_OK) + { + sim_io_eprintf (sd, "Break 13: invalid %d? Returned %ld\n", callnum, + s.result); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGILL); + } + if (s.result < 0) + retval = -s.errcode; + else + { + sim_core_write_unaligned_4 (current_cpu, pc, 0, resultp, + s.result); + sim_core_write_unaligned_4 (current_cpu, pc, 0, resultp + 4, + s.result < 0 ? -1 : 0); + } + break; + } + + /* This one does have a generic callback function, but at the time + of this writing, cb_syscall does not have code for it, and we + need target-specific code for the threads implementation + anyway. */ + case TARGET_SYS_kill: + { + USI pid = arg1; + USI sig = arg2; + + retval = 0; + + /* At kill(2), glibc sets signal masks such that the thread + machinery is initialized. Still, there is and was only + one thread. */ + if (current_cpu->max_threadid == 0) + { + if (pid != TARGET_PID) + { + retval = -cb_host_to_target_errno (cb, EPERM); + break; + } + + /* FIXME: Signal infrastructure (target-to-sim mapping). */ + if (sig == TARGET_SIGABRT) + /* A call "abort ()", i.e. "kill (getpid(), SIGABRT)" is + the end-point for failing GCC test-cases. */ + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGABRT); + else + { + sim_io_eprintf (sd, "Unimplemented signal: %d\n", sig); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + /* This will not be reached. */ + abort (); + } + else + retval = deliver_signal (current_cpu, sig, pid); + break; + } + + case TARGET_SYS_rt_sigprocmask: + { + int i; + USI how = arg1; + USI newsetp = arg2; + USI oldsetp = arg3; + + if (how != TARGET_SIG_BLOCK + && how != TARGET_SIG_SETMASK + && how != TARGET_SIG_UNBLOCK) + { + sim_io_eprintf (sd, "Unimplemented rt_sigprocmask syscall " + "(0x%x, 0x%x, 0x%x)\n", arg1, arg2, arg3); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + retval = 0; + break; + } + + if (newsetp) + { + USI set_low + = sim_core_read_unaligned_4 (current_cpu, pc, 0, + newsetp); + USI set_high + = sim_core_read_unaligned_4 (current_cpu, pc, 0, + newsetp + 4); + + /* The sigmask is kept in the per-thread data, so we may + need to create the first one. */ + if (current_cpu->thread_data == NULL) + make_first_thread (current_cpu); + + if (how == TARGET_SIG_SETMASK) + for (i = 0; i < 64; i++) + current_cpu->thread_data[threadno].sigdata[i].blocked = 0; + + for (i = 0; i < 32; i++) + if ((set_low & (1 << i))) + current_cpu->thread_data[threadno].sigdata[i + 1].blocked + = (how != TARGET_SIG_UNBLOCK); + + for (i = 0; i < 31; i++) + if ((set_high & (1 << i))) + current_cpu->thread_data[threadno].sigdata[i + 33].blocked + = (how != TARGET_SIG_UNBLOCK); + + /* The mask changed, so a signal may be unblocked for + execution. */ + current_cpu->thread_data[threadno].sigpending = 1; + } + + if (oldsetp != 0) + { + USI set_low = 0; + USI set_high = 0; + + for (i = 0; i < 32; i++) + if (current_cpu->thread_data[threadno] + .sigdata[i + 1].blocked) + set_low |= 1 << i; + for (i = 0; i < 31; i++) + if (current_cpu->thread_data[threadno] + .sigdata[i + 33].blocked) + set_high |= 1 << i; + + sim_core_write_unaligned_4 (current_cpu, pc, 0, oldsetp + 0, set_low); + sim_core_write_unaligned_4 (current_cpu, pc, 0, oldsetp + 4, set_high); + } + + retval = 0; + break; + } + + case TARGET_SYS_sigreturn: + { + int i; + bfd_byte regbuf[4]; + int was_sigsuspended; + + if (current_cpu->thread_data == NULL + /* The CPU context is saved with the simulator data, not + on the stack as in the real world. */ + || (current_cpu->thread_data[threadno].cpu_context_atsignal + == NULL)) + { + sim_io_eprintf (sd, "Invalid sigreturn syscall: no signal" + " handler active " + "(0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", + (unsigned long) arg1, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5, + (unsigned long) arg6); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + was_sigsuspended + = current_cpu->thread_data[threadno].sigsuspended; + + /* Restore the sigmask, either from the stack copy made when + the sighandler was called, or from the saved state + specifically for sigsuspend(2). */ + if (was_sigsuspended) + { + current_cpu->thread_data[threadno].sigsuspended = 0; + for (i = 0; i < 64; i++) + current_cpu->thread_data[threadno].sigdata[i].blocked + = current_cpu->thread_data[threadno] + .sigdata[i].blocked_suspendsave; + } + else + { + USI sp; + USI set_low; + USI set_high; + + (*CPU_REG_FETCH (current_cpu)) (current_cpu, + H_GR_SP, regbuf, 4); + sp = bfd_getl32 (regbuf); + set_low + = sim_core_read_unaligned_4 (current_cpu, pc, 0, sp); + set_high + = sim_core_read_unaligned_4 (current_cpu, pc, 0, sp + 4); + + for (i = 0; i < 32; i++) + current_cpu->thread_data[threadno].sigdata[i + 1].blocked + = (set_low & (1 << i)) != 0; + for (i = 0; i < 31; i++) + current_cpu->thread_data[threadno].sigdata[i + 33].blocked + = (set_high & (1 << i)) != 0; + } + + /* The mask changed, so a signal may be unblocked for + execution. */ + current_cpu->thread_data[threadno].sigpending = 1; + + memcpy (¤t_cpu->cpu_data_placeholder, + current_cpu->thread_data[threadno].cpu_context_atsignal, + current_cpu->thread_cpu_data_size); + free (current_cpu->thread_data[threadno].cpu_context_atsignal); + current_cpu->thread_data[threadno].cpu_context_atsignal = NULL; + + /* The return value must come from the saved R10. */ + (*CPU_REG_FETCH (current_cpu)) (current_cpu, H_GR_R10, regbuf, 4); + retval = bfd_getl32 (regbuf); + + /* We must also break the "sigsuspension loop". */ + if (was_sigsuspended) + sim_pc_set (current_cpu, sim_pc_get (current_cpu) + 2); + break; + } + + case TARGET_SYS_rt_sigsuspend: + { + USI newsetp = arg1; + USI setsize = arg2; + + if (setsize != 8) + { + sim_io_eprintf (sd, "Unimplemented rt_sigsuspend syscall" + " arguments (0x%lx, 0x%lx)\n", + (unsigned long) arg1, (unsigned long) arg2); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + /* Don't change the signal mask if we're already in + sigsuspend state (i.e. this syscall is a rerun). */ + else if (!current_cpu->thread_data[threadno].sigsuspended) + { + USI set_low + = sim_core_read_unaligned_4 (current_cpu, pc, 0, + newsetp); + USI set_high + = sim_core_read_unaligned_4 (current_cpu, pc, 0, + newsetp + 4); + int i; + + /* Save the current sigmask and insert the user-supplied + one. */ + for (i = 0; i < 32; i++) + { + current_cpu->thread_data[threadno] + .sigdata[i + 1].blocked_suspendsave + = current_cpu->thread_data[threadno] + .sigdata[i + 1].blocked; + + current_cpu->thread_data[threadno] + .sigdata[i + 1].blocked = (set_low & (1 << i)) != 0; + } + for (i = 0; i < 31; i++) + { + current_cpu->thread_data[threadno] + .sigdata[i + 33].blocked_suspendsave + = current_cpu->thread_data[threadno] + .sigdata[i + 33].blocked; + current_cpu->thread_data[threadno] + .sigdata[i + 33].blocked = (set_high & (1 << i)) != 0; + } + + current_cpu->thread_data[threadno].sigsuspended = 1; + + /* The mask changed, so a signal may be unblocked for + execution. */ + current_cpu->thread_data[threadno].sigpending = 1; + } + + /* Because we don't use arg1 (newsetp) when this syscall is + rerun, it doesn't matter that we overwrite it with the + (constant) return value. */ + retval = -cb_host_to_target_errno (cb, EINTR); + sim_pc_set (current_cpu, pc); + break; + } + + /* Add case labels here for other syscalls using the 32-bit + "struct stat", provided they have a corresponding simulator + function of course. */ + case TARGET_SYS_fstat: + { + /* As long as the infrastructure doesn't cache anything + related to the stat mapping, this trick gets us a dual + "struct stat"-type mapping in the least error-prone way. */ + const char *saved_map = cb->stat_map; + CB_TARGET_DEFS_MAP *saved_syscall_map = cb->syscall_map; + + cb->syscall_map = (CB_TARGET_DEFS_MAP *) syscall_stat32_map; + cb->stat_map = stat32_map; + + if (cb_syscall (cb, &s) != CB_RC_OK) + { + abort (); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + retval = s.result == -1 ? -s.errcode : s.result; + + cb->stat_map = saved_map; + cb->syscall_map = saved_syscall_map; + break; + } + + case TARGET_SYS_getcwd: + { + USI buf = arg1; + USI size = arg2; + + char *cwd = xmalloc (MAXPATHLEN); + if (cwd != getcwd (cwd, MAXPATHLEN)) + abort (); + + /* FIXME: When and if we support chdir, we need something + a bit more elaborate. */ + if (simulator_sysroot[0] != '\0') + strcpy (cwd, "/"); + + retval = -cb_host_to_target_errno (cb, ERANGE); + if (strlen (cwd) + 1 <= size) + { + retval = strlen (cwd) + 1; + if (sim_core_write_buffer (sd, current_cpu, 0, cwd, + buf, retval) + != (unsigned int) retval) + retval = -cb_host_to_target_errno (cb, EFAULT); + } + free (cwd); + break; + } + + case TARGET_SYS_readlink: + { + SI path = arg1; + SI buf = arg2; + SI bufsiz = arg3; + char *pbuf = xmalloc (MAXPATHLEN); + char *lbuf = xmalloc (MAXPATHLEN); + char *lbuf_alloc = lbuf; + int nchars = -1; + int i; + int o = 0; + + if (sim_core_read_unaligned_1 (current_cpu, pc, 0, path) == '/') + { + strcpy (pbuf, simulator_sysroot); + o += strlen (simulator_sysroot); + } + + for (i = 0; i + o < MAXPATHLEN; i++) + { + pbuf[i + o] + = sim_core_read_unaligned_1 (current_cpu, pc, 0, path + i); + if (pbuf[i + o] == 0) + break; + } + + if (i + o == MAXPATHLEN) + { + retval = -cb_host_to_target_errno (cb, ENAMETOOLONG); + break; + } + + /* Intervene calls for certain files expected in the target + proc file system. */ + if (strcmp (pbuf + strlen (simulator_sysroot), + "/proc/" XSTRING (TARGET_PID) "/exe") == 0) + { + char *argv0 + = (STATE_PROG_ARGV (sd) != NULL + ? *STATE_PROG_ARGV (sd) : NULL); + + if (argv0 == NULL || *argv0 == '.') + { + sim_io_eprintf (sd, "Unimplemented readlink syscall " + "(0x%lx: [\"%s\"], 0x%lx)\n", + (unsigned long) arg1, pbuf, + (unsigned long) arg2); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + break; + } + else if (*argv0 == '/') + { + if (strncmp (simulator_sysroot, argv0, + strlen (simulator_sysroot)) == 0) + argv0 += strlen (simulator_sysroot); + + strcpy (lbuf, argv0); + nchars = strlen (argv0) + 1; + } + else + { + if (getcwd (lbuf, MAXPATHLEN) != NULL + && strlen (lbuf) + 2 + strlen (argv0) < MAXPATHLEN) + { + if (strncmp (simulator_sysroot, lbuf, + strlen (simulator_sysroot)) == 0) + lbuf += strlen (simulator_sysroot); + + strcat (lbuf, "/"); + strcat (lbuf, argv0); + nchars = strlen (lbuf) + 1; + } + else + abort (); + } + } + else + nchars = readlink (pbuf, lbuf, MAXPATHLEN); + + /* We trust that the readlink result returns a *relative* + link, or one already adjusted for the file-path-prefix. + (We can't generally tell the difference, so we go with + the easiest decision; no adjustment.) */ + + if (nchars == -1) + { + retval = -cb_host_to_target_errno (cb, errno); + break; + } + + if (bufsiz < nchars) + nchars = bufsiz; + + if (sim_core_write_buffer (sd, current_cpu, write_map, lbuf, + buf, nchars) != (unsigned int) nchars) + retval = -cb_host_to_target_errno (cb, EFAULT); + else + retval = nchars; + + free (pbuf); + free (lbuf_alloc); + break; + } + + case TARGET_SYS_sched_getscheduler: + { + USI pid = arg1; + + /* FIXME: Search (other) existing threads. */ + if (pid != 0 && pid != TARGET_PID) + retval = -cb_host_to_target_errno (cb, ESRCH); + else + retval = TARGET_SCHED_OTHER; + break; + } + + case TARGET_SYS_sched_getparam: + { + USI pid = arg1; + USI paramp = arg2; + + /* The kernel says: + struct sched_param { + int sched_priority; + }; */ + + if (pid != 0 && pid != TARGET_PID) + retval = -cb_host_to_target_errno (cb, ESRCH); + else + { + /* FIXME: Save scheduler setting before threads are + created too. */ + sim_core_write_unaligned_4 (current_cpu, pc, 0, paramp, + current_cpu->thread_data != NULL + ? (current_cpu + ->thread_data[threadno] + .priority) + : 0); + retval = 0; + } + break; + } + + case TARGET_SYS_sched_setparam: + { + USI pid = arg1; + USI paramp = arg2; + + if ((pid != 0 && pid != TARGET_PID) + || sim_core_read_unaligned_4 (current_cpu, pc, 0, + paramp) != 0) + retval = -cb_host_to_target_errno (cb, EINVAL); + else + retval = 0; + break; + } + + case TARGET_SYS_sched_setscheduler: + { + USI pid = arg1; + USI policy = arg2; + USI paramp = arg3; + + if ((pid != 0 && pid != TARGET_PID) + || policy != TARGET_SCHED_OTHER + || sim_core_read_unaligned_4 (current_cpu, pc, 0, + paramp) != 0) + retval = -cb_host_to_target_errno (cb, EINVAL); + else + /* FIXME: Save scheduler setting to be read in later + sched_getparam calls. */ + retval = 0; + break; + } + + case TARGET_SYS_sched_yield: + /* We reschedule to the next thread after a syscall anyway, so + we don't have to do anything here than to set the return + value. */ + retval = 0; + break; + + case TARGET_SYS_sched_get_priority_min: + case TARGET_SYS_sched_get_priority_max: + if (arg1 != 0) + retval = -cb_host_to_target_errno (cb, EINVAL); + else + retval = 0; + break; + + case TARGET_SYS_ugetrlimit: + { + unsigned int curlim, maxlim; + if (arg1 != TARGET_RLIMIT_STACK && arg1 != TARGET_RLIMIT_NOFILE) + { + retval = -cb_host_to_target_errno (cb, EINVAL); + break; + } + + /* The kernel says: + struct rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; + }; */ + if (arg1 == TARGET_RLIMIT_NOFILE) + { + /* Sadly a very low limit. Better not lie, though. */ + maxlim = curlim = MAX_CALLBACK_FDS; + } + else /* arg1 == TARGET_RLIMIT_STACK */ + { + maxlim = 0xffffffff; + curlim = 0x800000; + } + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg2, curlim); + sim_core_write_unaligned_4 (current_cpu, pc, 0, arg2 + 4, maxlim); + retval = 0; + break; + } + + case TARGET_SYS_setrlimit: + if (arg1 != TARGET_RLIMIT_STACK) + { + retval = -cb_host_to_target_errno (cb, EINVAL); + break; + } + /* FIXME: Save values for future ugetrlimit calls. */ + retval = 0; + break; + + /* Provide a very limited subset of the sysctl functions, and + abort for the rest. */ + case TARGET_SYS__sysctl: + { + /* The kernel says: + struct __sysctl_args { + int *name; + int nlen; + void *oldval; + size_t *oldlenp; + void *newval; + size_t newlen; + unsigned long __unused[4]; + }; */ + SI name = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1); + SI name0 = name == 0 + ? 0 : sim_core_read_unaligned_4 (current_cpu, pc, 0, name); + SI name1 = name == 0 + ? 0 : sim_core_read_unaligned_4 (current_cpu, pc, 0, name + 4); + SI nlen + = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1 + 4); + SI oldval + = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1 + 8); + SI oldlenp + = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1 + 12); + SI oldlen = oldlenp == 0 + ? 0 : sim_core_read_unaligned_4 (current_cpu, pc, 0, oldlenp); + SI newval + = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1 + 16); + SI newlen + = sim_core_read_unaligned_4 (current_cpu, pc, 0, arg1 + 20); + + if (name0 == TARGET_CTL_KERN && name1 == TARGET_CTL_KERN_VERSION) + { + SI to_write = oldlen < (SI) sizeof (TARGET_UTSNAME) + ? oldlen : (SI) sizeof (TARGET_UTSNAME); + + sim_core_write_unaligned_4 (current_cpu, pc, 0, oldlenp, + sizeof (TARGET_UTSNAME)); + + if (sim_core_write_buffer (sd, current_cpu, write_map, + TARGET_UTSNAME, oldval, + to_write) + != (unsigned int) to_write) + retval = -cb_host_to_target_errno (cb, EFAULT); + else + retval = 0; + break; + } + + sim_io_eprintf (sd, "Unimplemented _sysctl syscall " + "(0x%lx: [0x%lx, 0x%lx]," + " 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", + (unsigned long) name, + (unsigned long) name0, + (unsigned long) name1, + (unsigned long) nlen, + (unsigned long) oldval, + (unsigned long) oldlenp, + (unsigned long) newval, + (unsigned long) newlen); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + break; + } + + case TARGET_SYS_exit: + { + /* Here for all but the last thread. */ + int i; + int pid + = current_cpu->thread_data[threadno].threadid + TARGET_PID; + int ppid + = (current_cpu->thread_data[threadno].parent_threadid + + TARGET_PID); + int exitsig = current_cpu->thread_data[threadno].exitsig; + + /* Any children are now all orphans. */ + for (i = 0; i < SIM_TARGET_MAX_THREADS; i++) + if (current_cpu->thread_data[i].parent_threadid + == current_cpu->thread_data[threadno].threadid) + /* Make getppid(2) return 1 for them, poor little ones. */ + current_cpu->thread_data[i].parent_threadid = -TARGET_PID + 1; + + /* Free the cpu context data. When the parent has received + the exit status, we'll clear the entry too. */ + free (current_cpu->thread_data[threadno].cpu_context); + current_cpu->thread_data[threadno].cpu_context = NULL; + current_cpu->m1threads--; + if (arg1 != 0) + { + sim_io_eprintf (sd, "Thread %d exited with status %d\n", + pid, arg1); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + /* Still, we may want to support non-zero exit values. */ + current_cpu->thread_data[threadno].exitval = arg1 << 8; + + if (exitsig) + deliver_signal (current_cpu, exitsig, ppid); + break; + } + + case TARGET_SYS_clone: + { + int nthreads = current_cpu->m1threads + 1; + void *thread_cpu_data; + bfd_byte old_sp_buf[4]; + bfd_byte sp_buf[4]; + const bfd_byte zeros[4] = { 0, 0, 0, 0 }; + int i; + + /* That's right, the syscall clone arguments are reversed + compared to sys_clone notes in clone(2) and compared to + other Linux ports (i.e. it's the same order as in the + clone(2) libcall). */ + USI flags = arg2; + USI newsp = arg1; + + if (nthreads == SIM_TARGET_MAX_THREADS) + { + retval = -cb_host_to_target_errno (cb, EAGAIN); + break; + } + + /* FIXME: Implement the low byte. */ + if ((flags & ~TARGET_CSIGNAL) != + (TARGET_CLONE_VM + | TARGET_CLONE_FS + | TARGET_CLONE_FILES + | TARGET_CLONE_SIGHAND) + || newsp == 0) + { + sim_io_eprintf (sd, + "Unimplemented clone syscall (0x%lx, 0x%lx)\n", + (unsigned long) arg1, (unsigned long) arg2); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + + if (current_cpu->thread_data == NULL) + make_first_thread (current_cpu); + + /* The created thread will get the new SP and a cleared R10. + Since it's created out of a copy of the old thread and we + don't have a set-register-function that just take the + cpu_data as a parameter, we set the childs values first, + and write back or overwrite them in the parent after the + copy. */ + (*CPU_REG_FETCH (current_cpu)) (current_cpu, + H_GR_SP, old_sp_buf, 4); + bfd_putl32 (newsp, sp_buf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, + H_GR_SP, sp_buf, 4); + (*CPU_REG_STORE (current_cpu)) (current_cpu, + H_GR_R10, (bfd_byte *) zeros, 4); + thread_cpu_data + = (*current_cpu + ->make_thread_cpu_data) (current_cpu, + ¤t_cpu->cpu_data_placeholder); + (*CPU_REG_STORE (current_cpu)) (current_cpu, + H_GR_SP, old_sp_buf, 4); + + retval = ++current_cpu->max_threadid + TARGET_PID; + + /* Find an unused slot. After a few threads have been created + and exited, the array is expected to be a bit fragmented. + We don't reuse the first entry, though, that of the + original thread. */ + for (i = 1; i < SIM_TARGET_MAX_THREADS; i++) + if (current_cpu->thread_data[i].cpu_context == NULL + /* Don't reuse a zombied entry. */ + && current_cpu->thread_data[i].threadid == 0) + break; + + memcpy (¤t_cpu->thread_data[i], + ¤t_cpu->thread_data[threadno], + sizeof (current_cpu->thread_data[i])); + current_cpu->thread_data[i].cpu_context = thread_cpu_data; + current_cpu->thread_data[i].cpu_context_atsignal = NULL; + current_cpu->thread_data[i].threadid = current_cpu->max_threadid; + current_cpu->thread_data[i].parent_threadid + = current_cpu->thread_data[threadno].threadid; + current_cpu->thread_data[i].pipe_read_fd = 0; + current_cpu->thread_data[i].pipe_write_fd = 0; + current_cpu->thread_data[i].at_syscall = 0; + current_cpu->thread_data[i].sigpending = 0; + current_cpu->thread_data[i].sigsuspended = 0; + current_cpu->thread_data[i].exitsig = flags & TARGET_CSIGNAL; + current_cpu->m1threads = nthreads; + break; + } + + /* Better watch these in case they do something necessary. */ + case TARGET_SYS_socketcall: + retval = -cb_host_to_target_errno (cb, ENOSYS); + break; + + unimplemented_syscall: + default: + sim_io_eprintf (sd, "Unimplemented syscall: %d " + "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", callnum, + arg1, arg2, arg3, arg4, arg5, arg6); + sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, + SIM_SIGILL); + } + } + + /* A system call is a rescheduling point. For the time being, we don't + reschedule anywhere else. */ + if (current_cpu->m1threads != 0 + /* We need to schedule off from an exiting thread that is the + second-last one. */ + || (current_cpu->thread_data != NULL + && current_cpu->thread_data[threadno].cpu_context == NULL)) + { + bfd_byte retval_buf[4]; + + current_cpu->thread_data[threadno].last_execution + = TARGET_TIME_MS (current_cpu); + bfd_putl32 (retval, retval_buf); + (*CPU_REG_STORE (current_cpu)) (current_cpu, H_GR_R10, retval_buf, 4); + + current_cpu->thread_data[threadno].at_syscall = 1; + reschedule (current_cpu); + + (*CPU_REG_FETCH (current_cpu)) (current_cpu, H_GR_R10, retval_buf, 4); + retval = bfd_getl32 (retval_buf); + } + + return retval; +} + +/* Callback from simulator write saying that the pipe at (reader, writer) + is now non-empty (so the writer should wait until the pipe is empty, at + least not write to this or any other pipe). Simplest is to just wait + until the pipe is empty. */ + +static void +cris_pipe_nonempty (host_callback *cb ATTRIBUTE_UNUSED, + int reader, int writer) +{ + SIM_CPU *cpu = current_cpu_for_cb_callback; + const bfd_byte zeros[4] = { 0, 0, 0, 0 }; + + /* It's the current thread: we just have to re-run the current + syscall instruction (presumably "break 13") and change the syscall + to the special simulator-wait code. Oh, and set a marker that + we're waiting, so we can disambiguate the special call from a + program error. + + This function may be called multiple times between cris_pipe_empty, + but we must avoid e.g. decreasing PC every time. Check fd markers + to tell. */ + if (cpu->thread_data == NULL) + { + sim_io_eprintf (CPU_STATE (cpu), + "Terminating simulation due to writing pipe rd:wr %d:%d" + " from one single thread\n", reader, writer); + sim_engine_halt (CPU_STATE (cpu), cpu, + NULL, sim_pc_get (cpu), sim_stopped, SIM_SIGILL); + } + else if (cpu->thread_data[cpu->threadno].pipe_write_fd == 0) + { + cpu->thread_data[cpu->threadno].pipe_write_fd = writer; + cpu->thread_data[cpu->threadno].pipe_read_fd = reader; + /* FIXME: We really shouldn't change registers other than R10 in + syscalls (like R9), here or elsewhere. */ + (*CPU_REG_STORE (cpu)) (cpu, H_GR_R9, (bfd_byte *) zeros, 4); + sim_pc_set (cpu, sim_pc_get (cpu) - 2); + } +} + +/* Callback from simulator close or read call saying that the pipe at + (reader, writer) is now empty (so the writer can write again, perhaps + leave a waiting state). If there are bytes remaining, they couldn't be + consumed (perhaps due to the pipe closing). */ + +static void +cris_pipe_empty (host_callback *cb, + int reader ATTRIBUTE_UNUSED, + int writer) +{ + int i; + SIM_CPU *cpu = current_cpu_for_cb_callback; + bfd_byte r10_buf[4]; + int remaining = cb->pipe_buffer[writer].size; + + /* We need to find the thread that waits for this pipe. */ + for (i = 0; i < SIM_TARGET_MAX_THREADS; i++) + if (cpu->thread_data[i].cpu_context + && cpu->thread_data[i].pipe_write_fd == writer) + { + int retval; + /* Temporarily switch to this cpu context, so we can change the + PC by ordinary calls. */ + + memcpy (cpu->thread_data[cpu->threadno].cpu_context, + &cpu->cpu_data_placeholder, + cpu->thread_cpu_data_size); + memcpy (&cpu->cpu_data_placeholder, + cpu->thread_data[i].cpu_context, + cpu->thread_cpu_data_size); + + /* The return value is supposed to contain the number of written + bytes, which is the number of bytes requested and returned at + the write call. We subtract the remaining bytes from that, + but making sure we still get a positive number. + The return value may also be a negative number; an error + value. We cover this case by comparing against remaining, + which is always >= 0. */ + (*CPU_REG_FETCH (cpu)) (cpu, H_GR_R10, r10_buf, 4); + retval = (int) bfd_getl_signed_32 (r10_buf); + if (retval >= remaining) + bfd_putl32 (retval - remaining, r10_buf); + (*CPU_REG_STORE (cpu)) (cpu, H_GR_R10, r10_buf, 4); + + sim_pc_set (cpu, sim_pc_get (cpu) + 2); + memcpy (cpu->thread_data[i].cpu_context, + &cpu->cpu_data_placeholder, + cpu->thread_cpu_data_size); + memcpy (&cpu->cpu_data_placeholder, + cpu->thread_data[cpu->threadno].cpu_context, + cpu->thread_cpu_data_size); + cpu->thread_data[i].pipe_read_fd = 0; + cpu->thread_data[i].pipe_write_fd = 0; + return; + } + + abort (); +} + +/* We have a simulator-specific notion of time. See TARGET_TIME. */ + +static long +cris_time (host_callback *cb ATTRIBUTE_UNUSED, long *t) +{ + long retval = TARGET_TIME (current_cpu_for_cb_callback); + if (t) + *t = retval; + return retval; +} + +/* Set target-specific callback data. */ + +void +cris_set_callbacks (host_callback *cb) +{ + /* Yeargh, have to cast away constness to avoid warnings. */ + cb->syscall_map = (CB_TARGET_DEFS_MAP *) syscall_map; + cb->errno_map = (CB_TARGET_DEFS_MAP *) errno_map; + + /* The kernel stat64 layout. If we see a file > 2G, the "long" + parameter to cb_store_target_endian will make st_size negative. + Similarly for st_ino. FIXME: Find a 64-bit type, and use it + *unsigned*, and/or add syntax for signed-ness. */ + cb->stat_map = stat_map; + cb->open_map = (CB_TARGET_DEFS_MAP *) open_map; + cb->pipe_nonempty = cris_pipe_nonempty; + cb->pipe_empty = cris_pipe_empty; + cb->time = cris_time; +} + +/* Process an address exception. */ + +void +cris_core_signal (SIM_DESC sd, SIM_CPU *current_cpu, sim_cia cia, + unsigned int map, int nr_bytes, address_word addr, + transfer_type transfer, sim_core_signals sig) +{ + sim_core_signal (sd, current_cpu, cia, map, nr_bytes, addr, + transfer, sig); +} |