| /* CRIS exception, interrupt, and trap (EIT) support |
| Copyright (C) 2004-2021 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 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/>. */ |
| |
| /* This must come before any other includes. */ |
| #include "defs.h" |
| |
| #include "portability.h" |
| #include "sim-main.h" |
| #include "sim-syscall.h" |
| #include "sim-options.h" |
| #include "sim-signal.h" |
| #include "sim/callback.h" |
| #include "bfd.h" |
| /* FIXME: get rid of targ-vals.h usage everywhere else. */ |
| |
| #include <stdlib.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #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 |
| /* For PATH_MAX, originally. */ |
| #ifdef HAVE_LIMITS_H |
| #include <limits.h> |
| #endif |
| |
| /* From ld/sysdep.h. */ |
| #ifdef PATH_MAX |
| # define SIM_PATHMAX PATH_MAX |
| #else |
| # ifdef MAXPATHLEN |
| # define SIM_PATHMAX MAXPATHLEN |
| # else |
| # define SIM_PATHMAX 1024 |
| # endif |
| #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_access 33 |
| #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_stat 106 |
| #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_writev 146 |
| #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_SYS_set_thread_area 243 |
| #define TARGET_SYS_exit_group 252 |
| |
| #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_MAP_DENYWRITE 0x800 |
| |
| #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 "#7 Thu Jan 1 00:00:00 MET 2009" |
| |
| /* Seconds since 1970-01-01 to the above date + 10 minutes; |
| 'date -d "Thu Jan 1 00:00:10 MET 2009" +%s'. */ |
| #define TARGET_EPOCH 1230764410 |
| |
| /* 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 |
| |
| /* From linux/limits.h. */ |
| #define TARGET_PIPE_BUF 4096 |
| |
| /* From unistd.h. */ |
| #define TARGET_R_OK 4 |
| #define TARGET_W_OK 2 |
| #define TARGET_X_OK 1 |
| #define TARGET_F_OK 0 |
| |
| 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[] = |
| { |
| { "open", CB_SYS_open, TARGET_SYS_open }, |
| { "close", CB_SYS_close, TARGET_SYS_close }, |
| { "read", CB_SYS_read, TARGET_SYS_read }, |
| { "write", CB_SYS_write, TARGET_SYS_write }, |
| { "lseek", CB_SYS_lseek, TARGET_SYS_lseek }, |
| { "unlink", CB_SYS_unlink, TARGET_SYS_unlink }, |
| { "getpid", CB_SYS_getpid, TARGET_SYS_getpid }, |
| { "fstat", CB_SYS_fstat, TARGET_SYS_fstat64 }, |
| { "lstat", CB_SYS_lstat, TARGET_SYS_lstat64 }, |
| { "stat", CB_SYS_stat, TARGET_SYS_stat64 }, |
| { "pipe", CB_SYS_pipe, TARGET_SYS_pipe }, |
| { "rename", CB_SYS_rename, TARGET_SYS_rename }, |
| { "truncate", CB_SYS_truncate, TARGET_SYS_truncate }, |
| { "ftruncate", CB_SYS_ftruncate, TARGET_SYS_ftruncate }, |
| { 0, -1, -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[] = |
| { |
| { "fstat", CB_SYS_fstat, TARGET_SYS_fstat }, |
| { "stat", CB_SYS_stat, TARGET_SYS_stat }, |
| { 0, -1, -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", EPERM, 1 }, |
| #endif |
| #ifdef ENOENT |
| { "ENOENT", ENOENT, 2 }, |
| #endif |
| #ifdef ESRCH |
| { "ESRCH", ESRCH, 3 }, |
| #endif |
| #ifdef EINTR |
| { "EINTR", EINTR, 4 }, |
| #endif |
| #ifdef EIO |
| { "EIO", EIO, 5 }, |
| #endif |
| #ifdef ENXIO |
| { "ENXIO", ENXIO, 6 }, |
| #endif |
| #ifdef E2BIG |
| { "E2BIG", E2BIG, 7 }, |
| #endif |
| #ifdef ENOEXEC |
| { "ENOEXEC", ENOEXEC, 8 }, |
| #endif |
| #ifdef EBADF |
| { "EBADF", EBADF, 9 }, |
| #endif |
| #ifdef ECHILD |
| { "ECHILD", ECHILD, 10 }, |
| #endif |
| #ifdef EAGAIN |
| { "EAGAIN", EAGAIN, 11 }, |
| #endif |
| #ifdef ENOMEM |
| { "ENOMEM", ENOMEM, 12 }, |
| #endif |
| #ifdef EACCES |
| { "EACCES", EACCES, 13 }, |
| #endif |
| #ifdef EFAULT |
| { "EFAULT", EFAULT, 14 }, |
| #endif |
| #ifdef ENOTBLK |
| { "ENOTBLK", ENOTBLK, 15 }, |
| #endif |
| #ifdef EBUSY |
| { "EBUSY", EBUSY, 16 }, |
| #endif |
| #ifdef EEXIST |
| { "EEXIST", EEXIST, 17 }, |
| #endif |
| #ifdef EXDEV |
| { "EXDEV", EXDEV, 18 }, |
| #endif |
| #ifdef ENODEV |
| { "ENODEV", ENODEV, 19 }, |
| #endif |
| #ifdef ENOTDIR |
| { "ENOTDIR", ENOTDIR, 20 }, |
| #endif |
| #ifdef EISDIR |
| { "EISDIR", EISDIR, 21 }, |
| #endif |
| #ifdef EINVAL |
| { "EINVAL", EINVAL, 22 }, |
| #endif |
| #ifdef ENFILE |
| { "ENFILE", ENFILE, 23 }, |
| #endif |
| #ifdef EMFILE |
| { "EMFILE", EMFILE, 24 }, |
| #endif |
| #ifdef ENOTTY |
| { "ENOTTY", ENOTTY, 25 }, |
| #endif |
| #ifdef ETXTBSY |
| { "ETXTBSY", ETXTBSY, 26 }, |
| #endif |
| #ifdef EFBIG |
| { "EFBIG", EFBIG, 27 }, |
| #endif |
| #ifdef ENOSPC |
| { "ENOSPC", ENOSPC, 28 }, |
| #endif |
| #ifdef ESPIPE |
| { "ESPIPE", ESPIPE, 29 }, |
| #endif |
| #ifdef EROFS |
| { "EROFS", EROFS, 30 }, |
| #endif |
| #ifdef EMLINK |
| { "EMLINK", EMLINK, 31 }, |
| #endif |
| #ifdef EPIPE |
| { "EPIPE", EPIPE, 32 }, |
| #endif |
| #ifdef EDOM |
| { "EDOM", EDOM, 33 }, |
| #endif |
| #ifdef ERANGE |
| { "ERANGE", ERANGE, 34 }, |
| #endif |
| #ifdef EDEADLK |
| { "EDEADLK", EDEADLK, 35 }, |
| #endif |
| #ifdef ENAMETOOLONG |
| { "ENAMETOOLONG", ENAMETOOLONG, 36 }, |
| #endif |
| #ifdef ENOLCK |
| { "ENOLCK", ENOLCK, 37 }, |
| #endif |
| #ifdef ENOSYS |
| { "ENOSYS", ENOSYS, 38 }, |
| #endif |
| #ifdef ENOTEMPTY |
| { "ENOTEMPTY", ENOTEMPTY, 39 }, |
| #endif |
| #ifdef ELOOP |
| { "ELOOP", ELOOP, 40 }, |
| #endif |
| #ifdef EWOULDBLOCK |
| { "EWOULDBLOCK", EWOULDBLOCK, 11 }, |
| #endif |
| #ifdef ENOMSG |
| { "ENOMSG", ENOMSG, 42 }, |
| #endif |
| #ifdef EIDRM |
| { "EIDRM", EIDRM, 43 }, |
| #endif |
| #ifdef ECHRNG |
| { "ECHRNG", ECHRNG, 44 }, |
| #endif |
| #ifdef EL2NSYNC |
| { "EL2NSYNC", EL2NSYNC, 45 }, |
| #endif |
| #ifdef EL3HLT |
| { "EL3HLT", EL3HLT, 46 }, |
| #endif |
| #ifdef EL3RST |
| { "EL3RST", EL3RST, 47 }, |
| #endif |
| #ifdef ELNRNG |
| { "ELNRNG", ELNRNG, 48 }, |
| #endif |
| #ifdef EUNATCH |
| { "EUNATCH", EUNATCH, 49 }, |
| #endif |
| #ifdef ENOCSI |
| { "ENOCSI", ENOCSI, 50 }, |
| #endif |
| #ifdef EL2HLT |
| { "EL2HLT", EL2HLT, 51 }, |
| #endif |
| #ifdef EBADE |
| { "EBADE", EBADE, 52 }, |
| #endif |
| #ifdef EBADR |
| { "EBADR", EBADR, 53 }, |
| #endif |
| #ifdef EXFULL |
| { "EXFULL", EXFULL, 54 }, |
| #endif |
| #ifdef ENOANO |
| { "ENOANO", ENOANO, 55 }, |
| #endif |
| #ifdef EBADRQC |
| { "EBADRQC", EBADRQC, 56 }, |
| #endif |
| #ifdef EBADSLT |
| { "EBADSLT", EBADSLT, 57 }, |
| #endif |
| #ifdef EDEADLOCK |
| { "EDEADLOCK", EDEADLOCK, 35 }, |
| #endif |
| #ifdef EBFONT |
| { "EBFONT", EBFONT, 59 }, |
| #endif |
| #ifdef ENOSTR |
| { "ENOSTR", ENOSTR, 60 }, |
| #endif |
| #ifdef ENODATA |
| { "ENODATA", ENODATA, 61 }, |
| #endif |
| #ifdef ETIME |
| { "ETIME", ETIME, 62 }, |
| #endif |
| #ifdef ENOSR |
| { "ENOSR", ENOSR, 63 }, |
| #endif |
| #ifdef ENONET |
| { "ENONET", ENONET, 64 }, |
| #endif |
| #ifdef ENOPKG |
| { "ENOPKG", ENOPKG, 65 }, |
| #endif |
| #ifdef EREMOTE |
| { "EREMOTE", EREMOTE, 66 }, |
| #endif |
| #ifdef ENOLINK |
| { "ENOLINK", ENOLINK, 67 }, |
| #endif |
| #ifdef EADV |
| { "EADV", EADV, 68 }, |
| #endif |
| #ifdef ESRMNT |
| { "ESRMNT", ESRMNT, 69 }, |
| #endif |
| #ifdef ECOMM |
| { "ECOMM", ECOMM, 70 }, |
| #endif |
| #ifdef EPROTO |
| { "EPROTO", EPROTO, 71 }, |
| #endif |
| #ifdef EMULTIHOP |
| { "EMULTIHOP", EMULTIHOP, 72 }, |
| #endif |
| #ifdef EDOTDOT |
| { "EDOTDOT", EDOTDOT, 73 }, |
| #endif |
| #ifdef EBADMSG |
| { "EBADMSG", EBADMSG, 74 }, |
| #endif |
| #ifdef EOVERFLOW |
| { "EOVERFLOW", EOVERFLOW, 75 }, |
| #endif |
| #ifdef ENOTUNIQ |
| { "ENOTUNIQ", ENOTUNIQ, 76 }, |
| #endif |
| #ifdef EBADFD |
| { "EBADFD", EBADFD, 77 }, |
| #endif |
| #ifdef EREMCHG |
| { "EREMCHG", EREMCHG, 78 }, |
| #endif |
| #ifdef ELIBACC |
| { "ELIBACC", ELIBACC, 79 }, |
| #endif |
| #ifdef ELIBBAD |
| { "ELIBBAD", ELIBBAD, 80 }, |
| #endif |
| #ifdef ELIBSCN |
| { "ELIBSCN", ELIBSCN, 81 }, |
| #endif |
| #ifdef ELIBMAX |
| { "ELIBMAX", ELIBMAX, 82 }, |
| #endif |
| #ifdef ELIBEXEC |
| { "ELIBEXEC", ELIBEXEC, 83 }, |
| #endif |
| #ifdef EILSEQ |
| { "EILSEQ", EILSEQ, 84 }, |
| #endif |
| #ifdef ERESTART |
| { "ERESTART", ERESTART, 85 }, |
| #endif |
| #ifdef ESTRPIPE |
| { "ESTRPIPE", ESTRPIPE, 86 }, |
| #endif |
| #ifdef EUSERS |
| { "EUSERS", EUSERS, 87 }, |
| #endif |
| #ifdef ENOTSOCK |
| { "ENOTSOCK", ENOTSOCK, 88 }, |
| #endif |
| #ifdef EDESTADDRREQ |
| { "EDESTADDRREQ", EDESTADDRREQ, 89 }, |
| #endif |
| #ifdef EMSGSIZE |
| { "EMSGSIZE", EMSGSIZE, 90 }, |
| #endif |
| #ifdef EPROTOTYPE |
| { "EPROTOTYPE", EPROTOTYPE, 91 }, |
| #endif |
| #ifdef ENOPROTOOPT |
| { "ENOPROTOOPT", ENOPROTOOPT, 92 }, |
| #endif |
| #ifdef EPROTONOSUPPORT |
| { "EPROTONOSUPPORT", EPROTONOSUPPORT, 93 }, |
| #endif |
| #ifdef ESOCKTNOSUPPORT |
| { "ESOCKTNOSUPPORT", ESOCKTNOSUPPORT, 94 }, |
| #endif |
| #ifdef EOPNOTSUPP |
| { "EOPNOTSUPP", EOPNOTSUPP, 95 }, |
| #endif |
| #ifdef EPFNOSUPPORT |
| { "EPFNOSUPPORT", EPFNOSUPPORT, 96 }, |
| #endif |
| #ifdef EAFNOSUPPORT |
| { "EAFNOSUPPORT", EAFNOSUPPORT, 97 }, |
| #endif |
| #ifdef EADDRINUSE |
| { "EADDRINUSE", EADDRINUSE, 98 }, |
| #endif |
| #ifdef EADDRNOTAVAIL |
| { "EADDRNOTAVAIL", EADDRNOTAVAIL, 99 }, |
| #endif |
| #ifdef ENETDOWN |
| { "ENETDOWN", ENETDOWN, 100 }, |
| #endif |
| #ifdef ENETUNREACH |
| { "ENETUNREACH", ENETUNREACH, 101 }, |
| #endif |
| #ifdef ENETRESET |
| { "ENETRESET", ENETRESET, 102 }, |
| #endif |
| #ifdef ECONNABORTED |
| { "ECONNABORTED", ECONNABORTED, 103 }, |
| #endif |
| #ifdef ECONNRESET |
| { "ECONNRESET", ECONNRESET, 104 }, |
| #endif |
| #ifdef ENOBUFS |
| { "ENOBUFS", ENOBUFS, 105 }, |
| #endif |
| #ifdef EISCONN |
| { "EISCONN", EISCONN, 106 }, |
| #endif |
| #ifdef ENOTCONN |
| { "ENOTCONN", ENOTCONN, 107 }, |
| #endif |
| #ifdef ESHUTDOWN |
| { "ESHUTDOWN", ESHUTDOWN, 108 }, |
| #endif |
| #ifdef ETOOMANYREFS |
| { "ETOOMANYREFS", ETOOMANYREFS, 109 }, |
| #endif |
| #ifdef ETIMEDOUT |
| { "ETIMEDOUT", ETIMEDOUT, 110 }, |
| #endif |
| #ifdef ECONNREFUSED |
| { "ECONNREFUSED", ECONNREFUSED, 111 }, |
| #endif |
| #ifdef EHOSTDOWN |
| { "EHOSTDOWN", EHOSTDOWN, 112 }, |
| #endif |
| #ifdef EHOSTUNREACH |
| { "EHOSTUNREACH", EHOSTUNREACH, 113 }, |
| #endif |
| #ifdef EALREADY |
| { "EALREADY", EALREADY, 114 }, |
| #endif |
| #ifdef EINPROGRESS |
| { "EINPROGRESS", EINPROGRESS, 115 }, |
| #endif |
| #ifdef ESTALE |
| { "ESTALE", ESTALE, 116 }, |
| #endif |
| #ifdef EUCLEAN |
| { "EUCLEAN", EUCLEAN, 117 }, |
| #endif |
| #ifdef ENOTNAM |
| { "ENOTNAM", ENOTNAM, 118 }, |
| #endif |
| #ifdef ENAVAIL |
| { "ENAVAIL", ENAVAIL, 119 }, |
| #endif |
| #ifdef EISNAM |
| { "EISNAM", EISNAM, 120 }, |
| #endif |
| #ifdef EREMOTEIO |
| { "EREMOTEIO", EREMOTEIO, 121 }, |
| #endif |
| #ifdef EDQUOT |
| { "EDQUOT", EDQUOT, 122 }, |
| #endif |
| #ifdef ENOMEDIUM |
| { "ENOMEDIUM", ENOMEDIUM, 123 }, |
| #endif |
| #ifdef EMEDIUMTYPE |
| { "EMEDIUMTYPE", EMEDIUMTYPE, 124 }, |
| #endif |
| { 0, 0, 0 } |
| }; |
| |
| /* 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. */ |
| |
| /* These we treat specially, as they're used in the fcntl F_GETFL |
| syscall. For consistency, open_map is also manually edited to use |
| these macros. */ |
| #define TARGET_O_ACCMODE 0x3 |
| #define TARGET_O_RDONLY 0x0 |
| #define TARGET_O_WRONLY 0x1 |
| |
| static const CB_TARGET_DEFS_MAP open_map[] = { |
| #ifdef O_ACCMODE |
| { "O_ACCMODE", O_ACCMODE, TARGET_O_ACCMODE }, |
| #endif |
| #ifdef O_RDONLY |
| { "O_RDONLY", O_RDONLY, TARGET_O_RDONLY }, |
| #endif |
| #ifdef O_WRONLY |
| { "O_WRONLY", O_WRONLY, TARGET_O_WRONLY }, |
| #endif |
| #ifdef O_RDWR |
| { "O_RDWR", O_RDWR, 0x2 }, |
| #endif |
| #ifdef O_CREAT |
| { "O_CREAT", O_CREAT, 0x40 }, |
| #endif |
| #ifdef O_EXCL |
| { "O_EXCL", O_EXCL, 0x80 }, |
| #endif |
| #ifdef O_NOCTTY |
| { "O_NOCTTY", O_NOCTTY, 0x100 }, |
| #endif |
| #ifdef O_TRUNC |
| { "O_TRUNC", O_TRUNC, 0x200 }, |
| #endif |
| #ifdef O_APPEND |
| { "O_APPEND", O_APPEND, 0x400 }, |
| #endif |
| #ifdef O_NONBLOCK |
| { "O_NONBLOCK", O_NONBLOCK, 0x800 }, |
| #endif |
| #ifdef O_NDELAY |
| { "O_NDELAY", O_NDELAY, 0x0 }, |
| #endif |
| #ifdef O_SYNC |
| { "O_SYNC", O_SYNC, 0x1000 }, |
| #endif |
| #ifdef FASYNC |
| { "FASYNC", FASYNC, 0x2000 }, |
| #endif |
| #ifdef O_DIRECT |
| { "O_DIRECT", O_DIRECT, 0x4000 }, |
| #endif |
| #ifdef O_LARGEFILE |
| { "O_LARGEFILE", O_LARGEFILE, 0x8000 }, |
| #endif |
| #ifdef O_DIRECTORY |
| { "O_DIRECTORY", O_DIRECTORY, 0x10000 }, |
| #endif |
| #ifdef O_NOFOLLOW |
| { "O_NOFOLLOW", O_NOFOLLOW, 0x20000 }, |
| #endif |
| { 0, -1, -1 } |
| }; |
| |
| /* Let's be less drastic and more traceable. FIXME: mark as noreturn. */ |
| #define abort() \ |
| sim_io_error (sd, "simulator unhandled condition at %s:%d", \ |
| __FUNCTION__, __LINE__) |
| |
| /* Needed for the cris_pipe_nonempty and cris_pipe_empty syscalls. */ |
| static SIM_CPU *current_cpu_for_cb_callback; |
| |
| 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); |
| |
| /* 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; |
| |
| /* 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". */ |
| |
| /* 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: |
| sim_io_eprintf (sd, "Basic clock cycles, total @: %" PRIu64 "\n", total); |
| break; |
| |
| case (FLAG_CRIS_MISC_PROFILE_UNALIGNED | FLAG_CRIS_MISC_PROFILE_SIMPLE): |
| total += profp->unaligned_mem_dword_count; |
| sim_io_eprintf (sd, |
| "Clock cycles including stall cycles for unaligned " |
| "accesses @: %" PRIu64 "\n", |
| total); |
| break; |
| |
| case (FLAG_CRIS_MISC_PROFILE_SCHEDULABLE | FLAG_CRIS_MISC_PROFILE_SIMPLE): |
| 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); |
| sim_io_eprintf (sd, "Schedulable clock cycles, total @: %" PRIu64 "\n", |
| total); |
| break; |
| |
| case FLAG_CRIS_MISC_PROFILE_ALL: |
| 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); |
| sim_io_eprintf (sd, "All accounted clock cycles, total @: %" PRIu64 "\n", |
| total); |
| break; |
| |
| default: |
| sim_engine_abort (sd, current_cpu, 0, |
| "Internal inconsistency at %s:%d", |
| __FILE__, __LINE__); |
| } |
| |
| /* 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: %" PRIu64 "\n", |
| profp->memsrc_stall_count + profp->unaligned_mem_dword_count); |
| sim_io_eprintf (sd, "Memory read-after-write stall cycles: %" PRIu64 "\n", |
| profp->memraw_stall_count); |
| sim_io_eprintf (sd, "Movem source stall cycles: %" PRIu64 "\n", |
| profp->movemsrc_stall_count); |
| sim_io_eprintf (sd, "Movem destination stall cycles: %" PRIu64 "\n", |
| profp->movemdst_stall_count); |
| sim_io_eprintf (sd, "Movem address stall cycles: %" PRIu64 "\n", |
| profp->movemaddr_stall_count); |
| sim_io_eprintf (sd, "Multiplication source stall cycles: %" PRIu64 "\n", |
| profp->mulsrc_stall_count); |
| sim_io_eprintf (sd, "Jump source stall cycles: %" PRIu64 "\n", |
| profp->jumpsrc_stall_count); |
| sim_io_eprintf (sd, "Branch misprediction stall cycles: %" PRIu64 "\n", |
| profp->branch_stall_count); |
| sim_io_eprintf (sd, "Jump target stall cycles: %" PRIu64 "\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; |
| } |
| |
| /* Check whether any part of [addr .. addr + len - 1] is *un*mapped. |
| Return 1 if the whole area is mapped, 0 otherwise. */ |
| |
| static USI |
| is_mapped_only (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 |
| lower than the checked area. */ |
| for (mapp = *rootp; mapp != NULL && mapp->addr >= addr; mapp = mapp->prev) |
| if (addr == mapp->addr && len == 8192) |
| return 1; |
| else if (addr + len > mapp->addr) |
| len -= 8192; |
| |
| return 0; |
| } |
| |
| /* Provide a prototype to silence -Wmissing-prototypes. */ |
| void cris_dump_map (SIM_CPU *current_cpu); |
| |
| /* Debug helper; to be run from gdb. */ |
| void |
| cris_dump_map (SIM_CPU *current_cpu) |
| { |
| struct cris_sim_mmapped_page *mapp; |
| USI start, end; |
| |
| for (mapp = current_cpu->highest_mmapped_page, |
| start = mapp == NULL ? 0 : mapp->addr + 8192, |
| end = mapp == NULL ? 0 : mapp->addr + 8191; |
| mapp != NULL; |
| mapp = mapp->prev) |
| { |
| if (mapp->addr != start - 8192) |
| { |
| sim_io_eprintf (CPU_STATE (current_cpu), "0x%x..0x%x\n", start, end); |
| end = mapp->addr + 8191; |
| } |
| |
| start = mapp->addr; |
| } |
| |
| if (current_cpu->highest_mmapped_page != NULL) |
| sim_io_eprintf (CPU_STATE (current_cpu), "0x%x..0x%x\n", start, end); |
| } |
| |
| /* Create mmapped memory. ADDR is -1 if any address will do. Caller |
| must make sure that the address isn't already mapped. */ |
| |
| 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 != (USI) -1) |
| new_addr = addr; |
| else if (*rootp && rootp[0]->addr >= new_addr) |
| 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; |
| |
| /* Assert for consistency that we don't create duplicate maps. */ |
| if (is_mapped (sd, rootp, new_addr, len)) |
| abort (); |
| |
| /* 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; |
| int ret = 0; |
| |
| 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)) |
| ret = EINVAL; |
| |
| if (unmap_pages (sd, rootp, addr, 8192)) |
| ret = EINVAL; |
| |
| return ret; |
| } |
| |
| 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; |
| } |
| |
| /* 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) |
| { |
| SIM_DESC sd = CPU_STATE (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) |
| { |
| SIM_DESC sd = CPU_STATE (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 (); |
| } |
| |
| /* Handle unknown system calls. Returns (if it does) the syscall |
| return value. */ |
| |
| static USI |
| cris_unknown_syscall (SIM_CPU *current_cpu, USI pc, char *s, ...) |
| { |
| SIM_DESC sd = CPU_STATE (current_cpu); |
| host_callback *cb = STATE_CALLBACK (sd); |
| |
| if (cris_unknown_syscall_action == CRIS_USYSC_MSG_STOP |
| || cris_unknown_syscall_action == CRIS_USYSC_MSG_ENOSYS) |
| { |
| va_list ap; |
| |
| va_start (ap, s); |
| sim_io_evprintf (sd, s, ap); |
| va_end (ap); |
| |
| if (cris_unknown_syscall_action == CRIS_USYSC_MSG_STOP) |
| sim_engine_halt (sd, current_cpu, NULL, pc, sim_stopped, SIM_SIGILL); |
| } |
| |
| return -cb_host_to_target_errno (cb, ENOSYS); |
| } |
| |
| /* 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; |
| |
| /* The type of s.arg2 is long, so for hosts with 64-bit longs, we need |
| to sign-extend the lseek offset to be passed as a signed number, |
| else we'll truncate it to something > 2GB on hosts where sizeof |
| long > sizeof USI. We avoid doing it for all syscalls, as arg2 is |
| e.g. an address for some syscalls. */ |
| if (callnum == TARGET_SYS_lseek) |
| s.arg2 = (SI) arg2; |
| |
| if (callnum == TARGET_SYS_exit_group |
| || (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 = sim_syscall_read_mem; |
| s.write_mem = sim_syscall_write_mem; |
| |
| current_cpu_for_cb_callback = current_cpu; |
| |
| if (cb_syscall (cb, &s) != CB_RC_OK) |
| { |
| sim_engine_abort (sd, current_cpu, pc, |
| "Break 13: invalid %d? Returned %ld\n", callnum, |
| s.result); |
| } |
| |
| 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: |
| switch (arg2) |
| { |
| case 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; |
| break; |
| |
| case 2: |
| /* F_SETFD. Just ignore attempts to set the close-on-exec |
| flag. */ |
| retval = 0; |
| break; |
| |
| case 3: |
| /* F_GETFL. Check for the special case for open+fdopen. */ |
| if (current_cpu->last_syscall == TARGET_SYS_open |
| && arg1 == current_cpu->last_open_fd) |
| { |
| retval = current_cpu->last_open_flags & TARGET_O_ACCMODE; |
| break; |
| } |
| else if (arg1 == 0) |
| { |
| /* Because we can't freopen fd:s 0, 1, 2 to mean |
| something else than stdin, stdout and stderr |
| (sim/common/syscall.c:cb_syscall special cases fd |
| 0, 1 and 2), we know what flags that we can |
| sanely return for these fd:s. */ |
| retval = TARGET_O_RDONLY; |
| break; |
| } |
| else if (arg1 == 1 || arg1 == 2) |
| { |
| retval = TARGET_O_WRONLY; |
| break; |
| } |
| /* FALLTHROUGH */ |
| default: |
| /* Nothing else is implemented. */ |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented %s syscall " |
| "(fd: 0x%lx: cmd: 0x%lx arg: " |
| "0x%lx)\n", |
| callnum == TARGET_SYS_fcntl |
| ? "fcntl" : "fcntl64", |
| (unsigned long) (USI) arg1, |
| (unsigned long) (USI) arg2, |
| (unsigned long) (USI) arg3); |
| break; |
| } |
| break; |
| |
| case TARGET_SYS_uname: |
| { |
| /* Fill in a few constants to appease glibc. */ |
| static char sim_utsname[6][65] = |
| { |
| "Linux", |
| "sim-target", |
| "2.6.27", |
| TARGET_UTSNAME, |
| "cris", /* Overwritten below. */ |
| "localdomain" |
| }; |
| |
| /* Having the hardware type in Linux equal to the bfd |
| printable name is deliberate: if you make config.guess |
| work on your Linux-type system the usual way, it |
| probably will; either the bfd printable_name or the |
| ambiguous arch_name. */ |
| strcpy (sim_utsname[4], STATE_ARCHITECTURE (sd)->printable_name); |
| |
| 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; |
| |
| /* At 2.6.27, Linux (many (all?) ports, in the mmap2 syscalls) |
| still masked away this bit, so let's just ignore |
| it. */ |
| flags &= ~TARGET_MAP_DENYWRITE; |
| |
| /* 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 | TARGET_PROT_EXEC)) |
| && prot != TARGET_PROT_READ) |
| || (flags != (TARGET_MAP_ANONYMOUS | TARGET_MAP_PRIVATE) |
| && flags != TARGET_MAP_PRIVATE |
| && flags != (TARGET_MAP_ANONYMOUS |
| | TARGET_MAP_PRIVATE | TARGET_MAP_FIXED) |
| && flags != (TARGET_MAP_PRIVATE | TARGET_MAP_FIXED) |
| && flags != TARGET_MAP_SHARED) |
| || (fd != (USI) -1 |
| && prot != TARGET_PROT_READ |
| && prot != (TARGET_PROT_READ | TARGET_PROT_EXEC) |
| && prot != (TARGET_PROT_READ | TARGET_PROT_WRITE)) |
| || (fd == (USI) -1 && pgoff != 0) |
| || (fd != (USI) -1 && (flags & TARGET_MAP_ANONYMOUS))) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "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); |
| 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, read|exec, and read|write, |
| which we should already have checked. Check again |
| anyway. */ |
| if (prot != TARGET_PROT_READ |
| && prot != (TARGET_PROT_READ | TARGET_PROT_EXEC) |
| && prot != (TARGET_PROT_READ | TARGET_PROT_WRITE)) |
| abort (); |
| |
| if (flags & TARGET_MAP_FIXED) |
| unmap_pages (sd, ¤t_cpu->highest_mmapped_page, |
| addr, newlen); |
| else if (is_mapped (sd, ¤t_cpu->highest_mmapped_page, |
| addr, newlen)) |
| addr = 0; |
| |
| newaddr |
| = create_map (sd, ¤t_cpu->highest_mmapped_page, |
| addr != 0 || (flags & TARGET_MAP_FIXED) |
| ? addr : -1, |
| newlen); |
| |
| if (newaddr >= (USI) -8191) |
| { |
| abort (); |
| retval = -cb_host_to_target_errno (cb, -(SI) newaddr); |
| break; |
| } |
| |
| /* We were asked for MAP_FIXED, but couldn't. */ |
| if ((flags & TARGET_MAP_FIXED) && newaddr != addr) |
| { |
| abort (); |
| unmap_pages (sd, ¤t_cpu->highest_mmapped_page, |
| newaddr, newlen); |
| retval = -cb_host_to_target_errno (cb, EINVAL); |
| 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 (); |
| |
| /* Move to the correct offset in the file. */ |
| s.func = TARGET_SYS_lseek; |
| s.arg1 = fd; |
| s.arg2 = pgoff*8192; |
| s.arg3 = SEEK_SET; |
| if (cb_syscall (cb, &s) != CB_RC_OK) |
| abort (); |
| |
| 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 the result is a page or more lesser than what |
| was requested, something went wrong. */ |
| if (len >= 8192 && (USI) s.result <= len - 8192) |
| 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 newlen = (len + 8191) & ~8191; |
| USI newaddr; |
| |
| if (flags & TARGET_MAP_FIXED) |
| unmap_pages (sd, ¤t_cpu->highest_mmapped_page, |
| addr, newlen); |
| else if (is_mapped (sd, ¤t_cpu->highest_mmapped_page, |
| addr, newlen)) |
| addr = 0; |
| |
| newaddr = create_map (sd, ¤t_cpu->highest_mmapped_page, |
| addr != 0 || (flags & TARGET_MAP_FIXED) |
| ? addr : -1, |
| newlen); |
| |
| if (newaddr >= (USI) -8191) |
| retval = -cb_host_to_target_errno (cb, -(SI) newaddr); |
| else |
| retval = newaddr; |
| |
| if ((flags & TARGET_MAP_FIXED) && newaddr != addr) |
| { |
| abort (); |
| unmap_pages (sd, ¤t_cpu->highest_mmapped_page, |
| newaddr, newlen); |
| retval = -cb_host_to_target_errno (cb, EINVAL); |
| break; |
| } |
| } |
| break; |
| } |
| |
| case TARGET_SYS_mprotect: |
| { |
| /* We only cover the case of linuxthreads mprotecting out |
| its stack guard page and of dynamic loading mprotecting |
| away the data (for some reason the whole library, then |
| mprotects away the data part and mmap-FIX:es it again. */ |
| USI addr = arg1; |
| USI len = arg2; |
| USI prot = arg3; |
| |
| if (prot != TARGET_PROT_NONE |
| || !is_mapped_only (sd, ¤t_cpu->highest_mmapped_page, |
| addr, (len + 8191) & ~8191)) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented mprotect call " |
| "(0x%lx, 0x%lx, 0x%lx)\n", |
| (unsigned long) arg1, |
| (unsigned long) arg2, |
| (unsigned long) arg3); |
| break; |
| } |
| |
| /* Just ignore this. We could make this equal to munmap, |
| but then we'd have to make sure no anon mmaps gets this |
| address before a subsequent MAP_FIXED mmap intended to |
| override it. */ |
| 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) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented wait4 call " |
| "(0x%lx, 0x%lx, 0x%lx, 0x%lx)\n", |
| (unsigned long) arg1, |
| (unsigned long) arg2, |
| (unsigned long) arg3, |
| (unsigned long) arg4); |
| 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 target_sa_handler |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa); |
| USI target_sa_flags |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 4); |
| USI target_sa_restorer |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 8); |
| USI target_sa_mask_low |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, new_sa + 12); |
| USI target_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 ((target_sa_flags != 0 |
| && target_sa_flags != TARGET_SA_RESTART |
| && target_sa_flags != (TARGET_SA_RESTART|TARGET_SA_SIGINFO)) |
| || target_sa_handler == 0) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "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, |
| target_sa_handler, |
| target_sa_flags, |
| target_sa_restorer, |
| target_sa_mask_low, |
| target_sa_mask_high, |
| (unsigned long) arg3); |
| break; |
| } |
| |
| current_cpu->sighandler[signum] = target_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, |
| -1, 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->to_fstat) (cb, fd, &buf) != 0 |
| || (buf.st_mode & S_IFIFO) == 0) |
| || current_cpu->thread_data == NULL) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "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); |
| 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_time: |
| { |
| retval = (int) (*cb->time) (cb); |
| |
| /* At time of this writing, CB_SYSCALL_time doesn't do the |
| part of setting *arg1 to the return value. */ |
| if (arg1) |
| sim_core_write_unaligned_4 (current_cpu, pc, 0, arg1, retval); |
| 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))) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented llseek offset," |
| " fd %d: 0x%x:0x%x\n", |
| fd, (unsigned) arg2, |
| (unsigned) arg3); |
| break; |
| } |
| |
| 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; |
| } |
| |
| /* ssize_t writev(int fd, const struct iovec *iov, int iovcnt); |
| where: |
| struct iovec { |
| void *iov_base; Starting address |
| size_t iov_len; Number of bytes to transfer |
| }; */ |
| case TARGET_SYS_writev: |
| { |
| SI fd = arg1; |
| SI iov = arg2; |
| SI iovcnt = arg3; |
| SI retcnt = 0; |
| int i; |
| |
| /* We'll ignore strict error-handling and just do multiple write calls. */ |
| for (i = 0; i < iovcnt; i++) |
| { |
| int sysret; |
| USI iov_base |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, |
| iov + 8*i); |
| USI iov_len |
| = sim_core_read_unaligned_4 (current_cpu, pc, 0, |
| iov + 8*i + 4); |
| |
| s.func = TARGET_SYS_write; |
| s.arg1 = fd; |
| s.arg2 = iov_base; |
| s.arg3 = iov_len; |
| |
| if (cb_syscall (cb, &s) != CB_RC_OK) |
| abort (); |
| sysret = s.result == -1 ? -s.errcode : s.result; |
| |
| if (sysret != iov_len) |
| { |
| if (i != 0) |
| abort (); |
| retcnt = sysret; |
| break; |
| } |
| |
| retcnt += iov_len; |
| } |
| |
| retval = retcnt; |
| } |
| 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) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented rt_sigprocmask " |
| "syscall (0x%x, 0x%x, 0x%x)\n", |
| arg1, arg2, arg3); |
| 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)) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "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); |
| break; |
| } |
| |
| 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) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented rt_sigsuspend syscall" |
| " arguments (0x%lx, 0x%lx)\n", |
| (unsigned long) arg1, |
| (unsigned long) arg2); |
| break; |
| } |
| |
| /* 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_stat: |
| 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 (SIM_PATHMAX); |
| if (cwd != getcwd (cwd, SIM_PATHMAX)) |
| 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_access: |
| { |
| SI path = arg1; |
| SI mode = arg2; |
| char *pbuf = xmalloc (SIM_PATHMAX); |
| int i; |
| int o = 0; |
| int hmode = 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 < SIM_PATHMAX; i++) |
| { |
| pbuf[i + o] |
| = sim_core_read_unaligned_1 (current_cpu, pc, 0, path + i); |
| if (pbuf[i + o] == 0) |
| break; |
| } |
| |
| if (i + o == SIM_PATHMAX) |
| { |
| retval = -cb_host_to_target_errno (cb, ENAMETOOLONG); |
| break; |
| } |
| |
| /* Assert that we don't get calls for files for which we |
| don't have support. */ |
| if (strncmp (pbuf + strlen (simulator_sysroot), |
| "/proc/", 6) == 0) |
| abort (); |
| #define X_AFLAG(x) if (mode & TARGET_ ## x) hmode |= x |
| X_AFLAG (R_OK); |
| X_AFLAG (W_OK); |
| X_AFLAG (X_OK); |
| X_AFLAG (F_OK); |
| #undef X_AFLAG |
| |
| if (access (pbuf, hmode) != 0) |
| retval = -cb_host_to_target_errno (cb, errno); |
| else |
| retval = 0; |
| |
| free (pbuf); |
| break; |
| } |
| |
| case TARGET_SYS_readlink: |
| { |
| SI path = arg1; |
| SI buf = arg2; |
| SI bufsiz = arg3; |
| char *pbuf = xmalloc (SIM_PATHMAX); |
| char *lbuf = xmalloc (SIM_PATHMAX); |
| 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 < SIM_PATHMAX; i++) |
| { |
| pbuf[i + o] |
| = sim_core_read_unaligned_1 (current_cpu, pc, 0, path + i); |
| if (pbuf[i + o] == 0) |
| break; |
| } |
| |
| if (i + o == SIM_PATHMAX) |
| { |
| 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 == '.') |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented readlink syscall " |
| "(0x%lx: [\"%s\"], 0x%lx)\n", |
| (unsigned long) arg1, pbuf, |
| (unsigned long) arg2); |
| 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, SIM_PATHMAX) != NULL |
| && strlen (lbuf) + 2 + strlen (argv0) < SIM_PATHMAX) |
| { |
| 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, SIM_PATHMAX); |
| |
| /* 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; |
| } |
| |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "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); |
| 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) |
| { |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented clone syscall " |
| "(0x%lx, 0x%lx)\n", |
| (unsigned long) arg1, |
| (unsigned long) arg2); |
| break; |
| } |
| |
| 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; |
| |
| case TARGET_SYS_set_thread_area: |
| /* Do the same error check as Linux. */ |
| if (arg1 & 255) |
| { |
| retval = -cb_host_to_target_errno (cb, EINVAL); |
| break; |
| } |
| (*current_cpu->set_target_thread_data) (current_cpu, arg1); |
| retval = 0; |
| break; |
| |
| unimplemented_syscall: |
| default: |
| retval |
| = cris_unknown_syscall (current_cpu, pc, |
| "Unimplemented syscall: %d " |
| "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n", |
| callnum, arg1, arg2, arg3, arg4, arg5, |
| arg6); |
| } |
| } |
| |
| /* Minimal support for fcntl F_GETFL as used in open+fdopen. */ |
| if (callnum == TARGET_SYS_open) |
| { |
| current_cpu->last_open_fd = retval; |
| current_cpu->last_open_flags = arg2; |