From bc74c58eaf55005a6a4b2e67657da4121554943c Mon Sep 17 00:00:00 2001 From: Brandon Potter Date: Wed, 18 Apr 2018 14:57:57 -0400 Subject: [PATCH] sim-se: add syscalls related to polling Fix poll so that it will use the syscall retry capability instead of causing a blocking call. Add the accept and wait4 system calls. Add polling to read to remove deadlocks that occur in the event queue that are caused by blocking system calls. Modify the write system call to return an error number in case of error. Change-Id: I0b4091a2e41e4187ebf69d63e0088f988f37d5da Reviewed-on: https://gem5-review.googlesource.com/c/12115 Reviewed-by: Anthony Gutierrez Maintainer: Anthony Gutierrez --- src/arch/alpha/linux/process.cc | 4 +- src/arch/arm/freebsd/process.cc | 4 +- src/arch/arm/linux/process.cc | 8 +- src/arch/mips/linux/process.cc | 4 +- src/arch/power/linux/process.cc | 4 +- src/arch/riscv/linux/process.cc | 4 +- src/arch/sparc/linux/syscalls.cc | 8 +- src/arch/sparc/solaris/process.cc | 4 +- src/arch/x86/linux/process.cc | 22 +- src/kern/linux/linux.hh | 17 ++ src/sim/fd_entry.hh | 1 - src/sim/syscall_emul.cc | 47 --- src/sim/syscall_emul.hh | 482 +++++++++++++++++++++++++++++- 13 files changed, 521 insertions(+), 88 deletions(-) diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc index dbfbcaf6a..c1162bad0 100644 --- a/src/arch/alpha/linux/process.cc +++ b/src/arch/alpha/linux/process.cc @@ -128,8 +128,8 @@ SyscallDesc AlphaLinuxProcess::syscallDescs[] = { /* 0 */ SyscallDesc("osf_syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("osf_old_open", unimplementedFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("osf_wait4", unimplementedFunc), diff --git a/src/arch/arm/freebsd/process.cc b/src/arch/arm/freebsd/process.cc index 1ecbdd678..e6aa74068 100644 --- a/src/arch/arm/freebsd/process.cc +++ b/src/arch/arm/freebsd/process.cc @@ -659,8 +659,8 @@ static SyscallDesc syscallDescs64[] = { /* 0 */ SyscallDesc("unused#000", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("unused#002", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("unused#005", unimplementedFunc), /* 6 */ SyscallDesc("unused#006", unimplementedFunc), /* 7 */ SyscallDesc("unused#007", unimplementedFunc), diff --git a/src/arch/arm/linux/process.cc b/src/arch/arm/linux/process.cc index 61caa456b..a7ec70e25 100644 --- a/src/arch/arm/linux/process.cc +++ b/src/arch/arm/linux/process.cc @@ -126,8 +126,8 @@ static SyscallDesc syscallDescs32[] = { /* 0 */ SyscallDesc("syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("unused#7", unimplementedFunc), @@ -567,8 +567,8 @@ static SyscallDesc syscallDescs64[] = { /* 61 */ SyscallDesc("getdents64", unimplementedFunc), #endif /* 62 */ SyscallDesc("llseek", lseekFunc), - /* 63 */ SyscallDesc("read", readFunc), - /* 64 */ SyscallDesc("write", writeFunc), + /* 63 */ SyscallDesc("read", readFunc), + /* 64 */ SyscallDesc("write", writeFunc), /* 65 */ SyscallDesc("readv", unimplementedFunc), /* 66 */ SyscallDesc("writev", writevFunc), /* 67 */ SyscallDesc("pread64", unimplementedFunc), diff --git a/src/arch/mips/linux/process.cc b/src/arch/mips/linux/process.cc index b36a26fbf..b1c09a5f1 100644 --- a/src/arch/mips/linux/process.cc +++ b/src/arch/mips/linux/process.cc @@ -141,8 +141,8 @@ SyscallDesc MipsLinuxProcess::syscallDescs[] = { /* 0 */ SyscallDesc("syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("waitpid", unimplementedFunc), diff --git a/src/arch/power/linux/process.cc b/src/arch/power/linux/process.cc index 801274969..f219852f8 100644 --- a/src/arch/power/linux/process.cc +++ b/src/arch/power/linux/process.cc @@ -69,8 +69,8 @@ SyscallDesc PowerLinuxProcess::syscallDescs[] = { /* 0 */ SyscallDesc("syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("waitpid", unimplementedFunc), //??? diff --git a/src/arch/riscv/linux/process.cc b/src/arch/riscv/linux/process.cc index 0f540af9d..6806079a3 100644 --- a/src/arch/riscv/linux/process.cc +++ b/src/arch/riscv/linux/process.cc @@ -133,8 +133,8 @@ std::map RiscvLinuxProcess::syscallDescs = { {60, SyscallDesc("quotactl")}, {61, SyscallDesc("getdents64")}, {62, SyscallDesc("lseek", lseekFunc)}, - {63, SyscallDesc("read", readFunc)}, - {64, SyscallDesc("write", writeFunc)}, + {63, SyscallDesc("read", readFunc)}, + {64, SyscallDesc("write", writeFunc)}, {66, SyscallDesc("writev", writevFunc)}, {67, SyscallDesc("pread64")}, {68, SyscallDesc("pwrite64", pwrite64Func)}, diff --git a/src/arch/sparc/linux/syscalls.cc b/src/arch/sparc/linux/syscalls.cc index 7fdc922ef..ee8c60c45 100644 --- a/src/arch/sparc/linux/syscalls.cc +++ b/src/arch/sparc/linux/syscalls.cc @@ -91,8 +91,8 @@ SyscallDesc SparcLinuxProcess::syscall32Descs[] = { /* 0 */ SyscallDesc("restart_syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), // 32 bit /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), // 32 bit /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("wait4", unimplementedFunc), // 32 bit @@ -397,8 +397,8 @@ SyscallDesc SparcLinuxProcess::syscallDescs[] = { /* 0 */ SyscallDesc("restart_syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("wait4", unimplementedFunc), diff --git a/src/arch/sparc/solaris/process.cc b/src/arch/sparc/solaris/process.cc index 1afa35398..bcdd08814 100644 --- a/src/arch/sparc/solaris/process.cc +++ b/src/arch/sparc/solaris/process.cc @@ -67,8 +67,8 @@ SyscallDesc SparcSolarisProcess::syscallDescs[] = { /* 0 */ SyscallDesc("syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("wait", unimplementedFunc), diff --git a/src/arch/x86/linux/process.cc b/src/arch/x86/linux/process.cc index 65d0238f4..03a88fc6e 100644 --- a/src/arch/x86/linux/process.cc +++ b/src/arch/x86/linux/process.cc @@ -222,14 +222,14 @@ setThreadArea32Func(SyscallDesc *desc, int callnum, } static SyscallDesc syscallDescs64[] = { - /* 0 */ SyscallDesc("read", readFunc), - /* 1 */ SyscallDesc("write", writeFunc), + /* 0 */ SyscallDesc("read", readFunc), + /* 1 */ SyscallDesc("write", writeFunc), /* 2 */ SyscallDesc("open", openFunc), /* 3 */ SyscallDesc("close", closeFunc), /* 4 */ SyscallDesc("stat", stat64Func), /* 5 */ SyscallDesc("fstat", fstat64Func), /* 6 */ SyscallDesc("lstat", lstat64Func), - /* 7 */ SyscallDesc("poll", unimplementedFunc), + /* 7 */ SyscallDesc("poll", pollFunc), /* 8 */ SyscallDesc("lseek", lseekFunc), /* 9 */ SyscallDesc("mmap", mmapFunc), /* 10 */ SyscallDesc("mprotect", ignoreFunc), @@ -245,7 +245,7 @@ static SyscallDesc syscallDescs64[] = { /* 20 */ SyscallDesc("writev", writevFunc), /* 21 */ SyscallDesc("access", ignoreFunc), /* 22 */ SyscallDesc("pipe", pipeFunc), - /* 23 */ SyscallDesc("select", unimplementedFunc), + /* 23 */ SyscallDesc("select", selectFunc), /* 24 */ SyscallDesc("sched_yield", ignoreFunc), /* 25 */ SyscallDesc("mremap", mremapFunc), /* 26 */ SyscallDesc("msync", unimplementedFunc), @@ -265,7 +265,7 @@ static SyscallDesc syscallDescs64[] = { /* 40 */ SyscallDesc("sendfile", unimplementedFunc), /* 41 */ SyscallDesc("socket", socketFunc), /* 42 */ SyscallDesc("connect", connectFunc), - /* 43 */ SyscallDesc("accept", unimplementedFunc), + /* 43 */ SyscallDesc("accept", acceptFunc), /* 44 */ SyscallDesc("sendto", sendtoFunc), /* 45 */ SyscallDesc("recvfrom", recvfromFunc), /* 46 */ SyscallDesc("sendmsg", sendmsgFunc), @@ -283,7 +283,7 @@ static SyscallDesc syscallDescs64[] = { /* 58 */ SyscallDesc("vfork", unimplementedFunc), /* 59 */ SyscallDesc("execve", execveFunc), /* 60 */ SyscallDesc("exit", exitFunc), - /* 61 */ SyscallDesc("wait4", unimplementedFunc), + /* 61 */ SyscallDesc("wait4", wait4Func), /* 62 */ SyscallDesc("kill", unimplementedFunc), /* 63 */ SyscallDesc("uname", unameFunc), /* 64 */ SyscallDesc("semget", unimplementedFunc), @@ -558,8 +558,8 @@ static SyscallDesc syscallDescs32[] = { /* 0 */ SyscallDesc("restart_syscall", unimplementedFunc), /* 1 */ SyscallDesc("exit", exitFunc), /* 2 */ SyscallDesc("fork", unimplementedFunc), - /* 3 */ SyscallDesc("read", readFunc), - /* 4 */ SyscallDesc("write", writeFunc), + /* 3 */ SyscallDesc("read", readFunc), + /* 4 */ SyscallDesc("write", writeFunc), /* 5 */ SyscallDesc("open", openFunc), /* 6 */ SyscallDesc("close", closeFunc), /* 7 */ SyscallDesc("waitpid", unimplementedFunc), @@ -637,7 +637,7 @@ static SyscallDesc syscallDescs32[] = { /* 79 */ SyscallDesc("settimeofday", unimplementedFunc), /* 80 */ SyscallDesc("getgroups", unimplementedFunc), /* 81 */ SyscallDesc("setgroups", unimplementedFunc), - /* 82 */ SyscallDesc("select", unimplementedFunc), + /* 82 */ SyscallDesc("select", selectFunc), /* 83 */ SyscallDesc("symlink", unimplementedFunc), /* 84 */ SyscallDesc("oldlstat", unimplementedFunc), /* 85 */ SyscallDesc("readlink", readlinkFunc), @@ -669,7 +669,7 @@ static SyscallDesc syscallDescs32[] = { /* 111 */ SyscallDesc("vhangup", unimplementedFunc), /* 112 */ SyscallDesc("idle", unimplementedFunc), /* 113 */ SyscallDesc("vm86old", unimplementedFunc), - /* 114 */ SyscallDesc("wait4", unimplementedFunc), + /* 114 */ SyscallDesc("wait4", wait4Func), /* 115 */ SyscallDesc("swapoff", unimplementedFunc), /* 116 */ SyscallDesc("sysinfo", sysinfoFunc), /* 117 */ SyscallDesc("ipc", unimplementedFunc), @@ -727,7 +727,7 @@ static SyscallDesc syscallDescs32[] = { /* 165 */ SyscallDesc("getresuid", unimplementedFunc), /* 166 */ SyscallDesc("vm86", unimplementedFunc), /* 167 */ SyscallDesc("query_module", unimplementedFunc), - /* 168 */ SyscallDesc("poll", unimplementedFunc), + /* 168 */ SyscallDesc("poll", pollFunc), /* 169 */ SyscallDesc("nfsservctl", unimplementedFunc), /* 170 */ SyscallDesc("setresgid", unimplementedFunc), /* 171 */ SyscallDesc("getresgid", unimplementedFunc), diff --git a/src/kern/linux/linux.hh b/src/kern/linux/linux.hh index a1df99467..e559e0505 100644 --- a/src/kern/linux/linux.hh +++ b/src/kern/linux/linux.hh @@ -153,6 +153,15 @@ class Linux : public OperatingSystem uint64_t iov_len; }; + // For select(). + // linux-3.14-src/include/uapi/linux/posix_types.h + struct fd_set{ +#ifndef LINUX__FD_SETSIZE +#define LINUX__FD_SETSIZE 1024 + unsigned long fds_bits[LINUX__FD_SETSIZE / (8 * sizeof(long))]; +#endif + }; + //@{ /// ioctl() command codes. static const unsigned TGT_TCGETS = 0x5401; @@ -265,6 +274,14 @@ class Linux : public OperatingSystem static const unsigned TGT_CLONE_NEWPID = 0x20000000; static const unsigned TGT_CLONE_NEWNET = 0x40000000; static const unsigned TGT_CLONE_IO = 0x80000000; + + // linux-3.13-src/include/uapi/linux/wait.h + static const unsigned TGT_WNOHANG = 0x00000001; + static const unsigned TGT_WUNTRACED = 0x00000002; + static const unsigned TGT_WSTOPPED = TGT_WUNTRACED; + static const unsigned TGT_WEXITED = 0x00000004; + static const unsigned TGT_WCONTINUED = 0x00000008; + static const unsigned TGT_WNOWAIT = 0x01000000; }; // class Linux #endif // __LINUX_HH__ diff --git a/src/sim/fd_entry.hh b/src/sim/fd_entry.hh index 6b2b2daa8..15e174ae6 100644 --- a/src/sim/fd_entry.hh +++ b/src/sim/fd_entry.hh @@ -231,7 +231,6 @@ class SocketFDEntry: public HBFDEntry return std::make_shared(*this); } - private: int _domain; int _type; int _protocol; diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc index 89f70a1cb..74ca1e924 100644 --- a/src/sim/syscall_emul.cc +++ b/src/sim/syscall_emul.cc @@ -279,53 +279,6 @@ closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) return p->fds->closeFDEntry(tgt_fd); } - -SyscallReturn -readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) -{ - int index = 0; - int tgt_fd = p->getSyscallArg(tc, index); - Addr buf_ptr = p->getSyscallArg(tc, index); - int nbytes = p->getSyscallArg(tc, index); - - auto hbfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); - if (!hbfdp) - return -EBADF; - int sim_fd = hbfdp->getSimFD(); - - BufferArg bufArg(buf_ptr, nbytes); - int bytes_read = read(sim_fd, bufArg.bufferPtr(), nbytes); - - if (bytes_read > 0) - bufArg.copyOut(tc->getMemProxy()); - - return bytes_read; -} - -SyscallReturn -writeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) -{ - int index = 0; - int tgt_fd = p->getSyscallArg(tc, index); - Addr buf_ptr = p->getSyscallArg(tc, index); - int nbytes = p->getSyscallArg(tc, index); - - auto hbfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); - if (!hbfdp) - return -EBADF; - int sim_fd = hbfdp->getSimFD(); - - BufferArg bufArg(buf_ptr, nbytes); - bufArg.copyIn(tc->getMemProxy()); - - int bytes_written = write(sim_fd, bufArg.bufferPtr(), nbytes); - - fsync(sim_fd); - - return bytes_written; -} - - SyscallReturn lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) { diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index d882cf4fa..67fa9e3d3 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -78,6 +78,7 @@ #endif #include +#include #include #include #include @@ -87,6 +88,7 @@ #include #endif #include +#include #include #include @@ -162,14 +164,6 @@ SyscallReturn brkFunc(SyscallDesc *desc, int num, SyscallReturn closeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); -// Target read() handler. -SyscallReturn readFunc(SyscallDesc *desc, int num, - Process *p, ThreadContext *tc); - -/// Target write() handler. -SyscallReturn writeFunc(SyscallDesc *desc, int num, - Process *p, ThreadContext *tc); - /// Target lseek() handler. SyscallReturn lseekFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc); @@ -946,6 +940,80 @@ chmodFunc(SyscallDesc *desc, int callnum, Process *process, return 0; } +template +SyscallReturn +pollFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) +{ + int index = 0; + Addr fdsPtr = p->getSyscallArg(tc, index); + int nfds = p->getSyscallArg(tc, index); + int tmout = p->getSyscallArg(tc, index); + + BufferArg fdsBuf(fdsPtr, sizeof(struct pollfd) * nfds); + fdsBuf.copyIn(tc->getMemProxy()); + + /** + * Record the target file descriptors in a local variable. We need to + * replace them with host file descriptors but we need a temporary copy + * for later. Afterwards, replace each target file descriptor in the + * poll_fd array with its host_fd. + */ + int temp_tgt_fds[nfds]; + for (index = 0; index < nfds; index++) { + temp_tgt_fds[index] = ((struct pollfd *)fdsBuf.bufferPtr())[index].fd; + auto tgt_fd = temp_tgt_fds[index]; + auto hbfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); + if (!hbfdp) + return -EBADF; + auto host_fd = hbfdp->getSimFD(); + ((struct pollfd *)fdsBuf.bufferPtr())[index].fd = host_fd; + } + + /** + * We cannot allow an infinite poll to occur or it will inevitably cause + * a deadlock in the gem5 simulator with clone. We must pass in tmout with + * a non-negative value, however it also makes no sense to poll on the + * underlying host for any other time than tmout a zero timeout. + */ + int status; + if (tmout < 0) { + status = poll((struct pollfd *)fdsBuf.bufferPtr(), nfds, 0); + if (status == 0) { + /** + * If blocking indefinitely, check the signal list to see if a + * signal would break the poll out of the retry cycle and try + * to return the signal interrupt instead. + */ + System *sysh = tc->getSystemPtr(); + std::list::iterator it; + for (it=sysh->signalList.begin(); it!=sysh->signalList.end(); it++) + if (it->receiver == p) + return -EINTR; + return SyscallReturn::retry(); + } + } else + status = poll((struct pollfd *)fdsBuf.bufferPtr(), nfds, 0); + + if (status == -1) + return -errno; + + /** + * Replace each host_fd in the returned poll_fd array with its original + * target file descriptor. + */ + for (index = 0; index < nfds; index++) { + auto tgt_fd = temp_tgt_fds[index]; + ((struct pollfd *)fdsBuf.bufferPtr())[index].fd = tgt_fd; + } + + /** + * Copy out the pollfd struct because the host may have updated fields + * in the structure. + */ + fdsBuf.copyOut(tc->getMemProxy()); + + return status; +} /// Target fchmod() handler. template @@ -1269,7 +1337,6 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) return 0; } - /// Target statfs() handler. template SyscallReturn @@ -2158,4 +2225,401 @@ socketpairFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) return status; } +template +SyscallReturn +selectFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc) +{ + int retval; + + int index = 0; + int nfds_t = p->getSyscallArg(tc, index); + Addr fds_read_ptr = p->getSyscallArg(tc, index); + Addr fds_writ_ptr = p->getSyscallArg(tc, index); + Addr fds_excp_ptr = p->getSyscallArg(tc, index); + Addr time_val_ptr = p->getSyscallArg(tc, index); + + TypedBufferArg rd_t(fds_read_ptr); + TypedBufferArg wr_t(fds_writ_ptr); + TypedBufferArg ex_t(fds_excp_ptr); + TypedBufferArg tp(time_val_ptr); + + /** + * Host fields. Notice that these use the definitions from the system + * headers instead of the gem5 headers and libraries. If the host and + * target have different header file definitions, this will not work. + */ + fd_set rd_h; + FD_ZERO(&rd_h); + fd_set wr_h; + FD_ZERO(&wr_h); + fd_set ex_h; + FD_ZERO(&ex_h); + + /** + * Copy in the fd_set from the target. + */ + if (fds_read_ptr) + rd_t.copyIn(tc->getMemProxy()); + if (fds_writ_ptr) + wr_t.copyIn(tc->getMemProxy()); + if (fds_excp_ptr) + ex_t.copyIn(tc->getMemProxy()); + + /** + * We need to translate the target file descriptor set into a host file + * descriptor set. This involves both our internal process fd array + * and the fd_set defined in Linux header files. The nfds field also + * needs to be updated as it will be only target specific after + * retrieving it from the target; the nfds value is expected to be the + * highest file descriptor that needs to be checked, so we need to extend + * it out for nfds_h when we do the update. + */ + int nfds_h = 0; + std::map trans_map; + auto try_add_host_set = [&](fd_set *tgt_set_entry, + fd_set *hst_set_entry, + int iter) -> bool + { + /** + * By this point, we know that we are looking at a valid file + * descriptor set on the target. We need to check if the target file + * descriptor value passed in as iter is part of the set. + */ + if (FD_ISSET(iter, tgt_set_entry)) { + /** + * We know that the target file descriptor belongs to the set, + * but we do not yet know if the file descriptor is valid or + * that we have a host mapping. Check that now. + */ + auto hbfdp = std::dynamic_pointer_cast((*p->fds)[iter]); + if (!hbfdp) + return true; + auto sim_fd = hbfdp->getSimFD(); + + /** + * Add the sim_fd to tgt_fd translation into trans_map for use + * later when we need to zero the target fd_set structures and + * then update them with hits returned from the host select call. + */ + trans_map[sim_fd] = iter; + + /** + * We know that the host file descriptor exists so now we check + * if we need to update the max count for nfds_h before passing + * the duplicated structure into the host. + */ + nfds_h = std::max(nfds_h - 1, sim_fd + 1); + + /** + * Add the host file descriptor to the set that we are going to + * pass into the host. + */ + FD_SET(sim_fd, hst_set_entry); + } + return false; + }; + + for (int i = 0; i < nfds_t; i++) { + if (fds_read_ptr) { + bool ebadf = try_add_host_set((fd_set*)&*rd_t, &rd_h, i); + if (ebadf) return -EBADF; + } + if (fds_writ_ptr) { + bool ebadf = try_add_host_set((fd_set*)&*wr_t, &wr_h, i); + if (ebadf) return -EBADF; + } + if (fds_excp_ptr) { + bool ebadf = try_add_host_set((fd_set*)&*ex_t, &ex_h, i); + if (ebadf) return -EBADF; + } + } + + if (time_val_ptr) { + /** + * It might be possible to decrement the timeval based on some + * derivation of wall clock determined from elapsed simulator ticks + * but that seems like overkill. Rather, we just set the timeval with + * zero timeout. (There is no reason to block during the simulation + * as it only decreases simulator performance.) + */ + tp->tv_sec = 0; + tp->tv_usec = 0; + + retval = select(nfds_h, + fds_read_ptr ? &rd_h : nullptr, + fds_writ_ptr ? &wr_h : nullptr, + fds_excp_ptr ? &ex_h : nullptr, + (timeval*)&*tp); + } else { + /** + * If the timeval pointer is null, setup a new timeval structure to + * pass into the host select call. Unfortunately, we will need to + * manually check the return value and throw a retry fault if the + * return value is zero. Allowing the system call to block will + * likely deadlock the event queue. + */ + struct timeval tv = { 0, 0 }; + + retval = select(nfds_h, + fds_read_ptr ? &rd_h : nullptr, + fds_writ_ptr ? &wr_h : nullptr, + fds_excp_ptr ? &ex_h : nullptr, + &tv); + + if (retval == 0) { + /** + * If blocking indefinitely, check the signal list to see if a + * signal would break the poll out of the retry cycle and try to + * return the signal interrupt instead. + */ + for (auto sig : tc->getSystemPtr()->signalList) + if (sig.receiver == p) + return -EINTR; + return SyscallReturn::retry(); + } + } + + if (retval == -1) + return -errno; + + FD_ZERO((fd_set*)&*rd_t); + FD_ZERO((fd_set*)&*wr_t); + FD_ZERO((fd_set*)&*ex_t); + + /** + * We need to translate the host file descriptor set into a target file + * descriptor set. This involves both our internal process fd array + * and the fd_set defined in header files. + */ + for (int i = 0; i < nfds_h; i++) { + if (fds_read_ptr) { + if (FD_ISSET(i, &rd_h)) + FD_SET(trans_map[i], (fd_set*)&*rd_t); + } + + if (fds_writ_ptr) { + if (FD_ISSET(i, &wr_h)) + FD_SET(trans_map[i], (fd_set*)&*wr_t); + } + + if (fds_excp_ptr) { + if (FD_ISSET(i, &ex_h)) + FD_SET(trans_map[i], (fd_set*)&*ex_t); + } + } + + if (fds_read_ptr) + rd_t.copyOut(tc->getMemProxy()); + if (fds_writ_ptr) + wr_t.copyOut(tc->getMemProxy()); + if (fds_excp_ptr) + ex_t.copyOut(tc->getMemProxy()); + if (time_val_ptr) + tp.copyOut(tc->getMemProxy()); + + return retval; +} + +template +SyscallReturn +readFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) +{ + int index = 0; + int tgt_fd = p->getSyscallArg(tc, index); + Addr buf_ptr = p->getSyscallArg(tc, index); + int nbytes = p->getSyscallArg(tc, index); + + auto hbfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); + if (!hbfdp) + return -EBADF; + int sim_fd = hbfdp->getSimFD(); + + struct pollfd pfd; + pfd.fd = sim_fd; + pfd.events = POLLIN | POLLPRI; + if ((poll(&pfd, 1, 0) == 0) + && !(hbfdp->getFlags() & OS::TGT_O_NONBLOCK)) + return SyscallReturn::retry(); + + BufferArg buf_arg(buf_ptr, nbytes); + int bytes_read = read(sim_fd, buf_arg.bufferPtr(), nbytes); + + if (bytes_read > 0) + buf_arg.copyOut(tc->getMemProxy()); + + return (bytes_read == -1) ? -errno : bytes_read; +} + +template +SyscallReturn +writeFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) +{ + int index = 0; + int tgt_fd = p->getSyscallArg(tc, index); + Addr buf_ptr = p->getSyscallArg(tc, index); + int nbytes = p->getSyscallArg(tc, index); + + auto hbfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); + if (!hbfdp) + return -EBADF; + int sim_fd = hbfdp->getSimFD(); + + BufferArg buf_arg(buf_ptr, nbytes); + buf_arg.copyIn(tc->getMemProxy()); + + struct pollfd pfd; + pfd.fd = sim_fd; + pfd.events = POLLOUT; + + /** + * We don't want to poll on /dev/random. The kernel will not enable the + * file descriptor for writing unless the entropy in the system falls + * below write_wakeup_threshold. This is not guaranteed to happen + * depending on host settings. + */ + auto ffdp = std::dynamic_pointer_cast(hbfdp); + if (ffdp && (ffdp->getFileName() != "/dev/random")) { + if (!poll(&pfd, 1, 0) && !(ffdp->getFlags() & OS::TGT_O_NONBLOCK)) + return SyscallReturn::retry(); + } + + int bytes_written = write(sim_fd, buf_arg.bufferPtr(), nbytes); + + if (bytes_written != -1) + fsync(sim_fd); + + return (bytes_written == -1) ? -errno : bytes_written; +} + +template +SyscallReturn +wait4Func(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) +{ + int index = 0; + pid_t pid = p->getSyscallArg(tc, index); + Addr statPtr = p->getSyscallArg(tc, index); + int options = p->getSyscallArg(tc, index); + Addr rusagePtr = p->getSyscallArg(tc, index); + + if (rusagePtr) + DPRINTFR(SyscallVerbose, + "%d: %s: syscall wait4: rusage pointer provided however " + "functionality not supported. Ignoring rusage pointer.\n", + curTick(), tc->getCpuPtr()->name()); + + /** + * Currently, wait4 is only implemented so that it will wait for children + * exit conditions which are denoted by a SIGCHLD signals posted into the + * system signal list. We return no additional information via any of the + * parameters supplied to wait4. If nothing is found in the system signal + * list, we will wait indefinitely for SIGCHLD to post by retrying the + * call. + */ + System *sysh = tc->getSystemPtr(); + std::list::iterator iter; + for (iter=sysh->signalList.begin(); iter!=sysh->signalList.end(); iter++) { + if (iter->receiver == p) { + if (pid < -1) { + if ((iter->sender->pgid() == -pid) + && (iter->signalValue == OS::TGT_SIGCHLD)) + goto success; + } else if (pid == -1) { + if (iter->signalValue == OS::TGT_SIGCHLD) + goto success; + } else if (pid == 0) { + if ((iter->sender->pgid() == p->pgid()) + && (iter->signalValue == OS::TGT_SIGCHLD)) + goto success; + } else { + if ((iter->sender->pid() == pid) + && (iter->signalValue == OS::TGT_SIGCHLD)) + goto success; + } + } + } + + return (options & OS::TGT_WNOHANG) ? 0 : SyscallReturn::retry(); + +success: + // Set status to EXITED for WIFEXITED evaluations. + const int EXITED = 0; + BufferArg statusBuf(statPtr, sizeof(int)); + *(int *)statusBuf.bufferPtr() = EXITED; + statusBuf.copyOut(tc->getMemProxy()); + + // Return the child PID. + pid_t retval = iter->sender->pid(); + sysh->signalList.erase(iter); + return retval; +} + +template +SyscallReturn +acceptFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc) +{ + struct sockaddr sa; + socklen_t addrLen; + int host_fd; + int index = 0; + int tgt_fd = p->getSyscallArg(tc, index); + Addr addrPtr = p->getSyscallArg(tc, index); + Addr lenPtr = p->getSyscallArg(tc, index); + + BufferArg *lenBufPtr = nullptr; + BufferArg *addrBufPtr = nullptr; + + auto sfdp = std::dynamic_pointer_cast((*p->fds)[tgt_fd]); + if (!sfdp) + return -EBADF; + int sim_fd = sfdp->getSimFD(); + + /** + * We poll the socket file descriptor first to guarantee that we do not + * block on our accept call. The socket can be opened without the + * non-blocking flag (it blocks). This will cause deadlocks between + * communicating processes. + */ + struct pollfd pfd; + pfd.fd = sim_fd; + pfd.events = POLLIN | POLLPRI; + if ((poll(&pfd, 1, 0) == 0) + && !(sfdp->getFlags() & OS::TGT_O_NONBLOCK)) + return SyscallReturn::retry(); + + if (lenPtr) { + lenBufPtr = new BufferArg(lenPtr, sizeof(socklen_t)); + lenBufPtr->copyIn(tc->getMemProxy()); + memcpy(&addrLen, (socklen_t *)lenBufPtr->bufferPtr(), + sizeof(socklen_t)); + } + + if (addrPtr) { + addrBufPtr = new BufferArg(addrPtr, sizeof(struct sockaddr)); + addrBufPtr->copyIn(tc->getMemProxy()); + memcpy(&sa, (struct sockaddr *)addrBufPtr->bufferPtr(), + sizeof(struct sockaddr)); + } + + host_fd = accept(sim_fd, &sa, &addrLen); + + if (host_fd == -1) + return -errno; + + if (addrPtr) { + memcpy(addrBufPtr->bufferPtr(), &sa, sizeof(sa)); + addrBufPtr->copyOut(tc->getMemProxy()); + delete(addrBufPtr); + } + + if (lenPtr) { + *(socklen_t *)lenBufPtr->bufferPtr() = addrLen; + lenBufPtr->copyOut(tc->getMemProxy()); + delete(lenBufPtr); + } + + auto afdp = std::make_shared(host_fd, sfdp->_domain, + sfdp->_type, sfdp->_protocol); + return p->fds->allocFD(afdp); +} + #endif // __SIM_SYSCALL_EMUL_HH__ -- 2.30.2