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 <anthony.gutierrez@amd.com>
Maintainer: Anthony Gutierrez <anthony.gutierrez@amd.com>
diff --git a/src/arch/alpha/linux/process.cc b/src/arch/alpha/linux/process.cc
index dbfbcaf..c1162ba 100644
--- a/src/arch/alpha/linux/process.cc
+++ b/src/arch/alpha/linux/process.cc
@@ -128,8 +128,8 @@
     /*  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<AlphaLinux>),
+    /*  4 */ SyscallDesc("write", writeFunc<AlphaLinux>),
     /*  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 1ecbdd6..e6aa740 100644
--- a/src/arch/arm/freebsd/process.cc
+++ b/src/arch/arm/freebsd/process.cc
@@ -659,8 +659,8 @@
     /*    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<ArmFreebsd64>),
+    /*    4 */ SyscallDesc("write", writeFunc<ArmFreebsd64>),
     /*    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 61caa45..a7ec70e 100644
--- a/src/arch/arm/linux/process.cc
+++ b/src/arch/arm/linux/process.cc
@@ -126,8 +126,8 @@
     /*  0 */ SyscallDesc("syscall", unimplementedFunc),
     /*  1 */ SyscallDesc("exit", exitFunc),
     /*  2 */ SyscallDesc("fork", unimplementedFunc),
-    /*  3 */ SyscallDesc("read", readFunc),
-    /*  4 */ SyscallDesc("write", writeFunc),
+    /*  3 */ SyscallDesc("read", readFunc<ArmLinux32>),
+    /*  4 */ SyscallDesc("write", writeFunc<ArmLinux32>),
     /*  5 */ SyscallDesc("open", openFunc<ArmLinux32>),
     /*  6 */ SyscallDesc("close", closeFunc),
     /*  7 */ SyscallDesc("unused#7", unimplementedFunc),
@@ -567,8 +567,8 @@
     /*   61 */ SyscallDesc("getdents64", unimplementedFunc),
 #endif
     /*   62 */ SyscallDesc("llseek", lseekFunc),
-    /*   63 */ SyscallDesc("read", readFunc),
-    /*   64 */ SyscallDesc("write", writeFunc),
+    /*   63 */ SyscallDesc("read", readFunc<ArmLinux64>),
+    /*   64 */ SyscallDesc("write", writeFunc<ArmLinux64>),
     /*   65 */ SyscallDesc("readv", unimplementedFunc),
     /*   66 */ SyscallDesc("writev", writevFunc<ArmLinux64>),
     /*   67 */ SyscallDesc("pread64", unimplementedFunc),
diff --git a/src/arch/mips/linux/process.cc b/src/arch/mips/linux/process.cc
index b36a26f..b1c09a5 100644
--- a/src/arch/mips/linux/process.cc
+++ b/src/arch/mips/linux/process.cc
@@ -141,8 +141,8 @@
     /*  0 */ SyscallDesc("syscall", unimplementedFunc),
     /*  1 */ SyscallDesc("exit", exitFunc),
     /*  2 */ SyscallDesc("fork", unimplementedFunc),
-    /*  3 */ SyscallDesc("read", readFunc),
-    /*  4 */ SyscallDesc("write", writeFunc),
+    /*  3 */ SyscallDesc("read", readFunc<MipsLinux>),
+    /*  4 */ SyscallDesc("write", writeFunc<MipsLinux>),
     /*  5 */ SyscallDesc("open", openFunc<MipsLinux>),
     /*  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 8012749..f219852 100644
--- a/src/arch/power/linux/process.cc
+++ b/src/arch/power/linux/process.cc
@@ -69,8 +69,8 @@
     /*  0 */ SyscallDesc("syscall", unimplementedFunc),
     /*  1 */ SyscallDesc("exit", exitFunc),
     /*  2 */ SyscallDesc("fork", unimplementedFunc),
-    /*  3 */ SyscallDesc("read", readFunc),
-    /*  4 */ SyscallDesc("write", writeFunc),
+    /*  3 */ SyscallDesc("read", readFunc<PowerLinux>),
+    /*  4 */ SyscallDesc("write", writeFunc<PowerLinux>),
     /*  5 */ SyscallDesc("open", openFunc<PowerLinux>),
     /*  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 0f540af..6806079 100644
--- a/src/arch/riscv/linux/process.cc
+++ b/src/arch/riscv/linux/process.cc
@@ -133,8 +133,8 @@
     {60,   SyscallDesc("quotactl")},
     {61,   SyscallDesc("getdents64")},
     {62,   SyscallDesc("lseek", lseekFunc)},
-    {63,   SyscallDesc("read", readFunc)},
-    {64,   SyscallDesc("write", writeFunc)},
+    {63,   SyscallDesc("read", readFunc<RiscvLinux>)},
+    {64,   SyscallDesc("write", writeFunc<RiscvLinux>)},
     {66,   SyscallDesc("writev", writevFunc<RiscvLinux>)},
     {67,   SyscallDesc("pread64")},
     {68,   SyscallDesc("pwrite64", pwrite64Func<RiscvLinux>)},
diff --git a/src/arch/sparc/linux/syscalls.cc b/src/arch/sparc/linux/syscalls.cc
index 7fdc922..ee8c60c 100644
--- a/src/arch/sparc/linux/syscalls.cc
+++ b/src/arch/sparc/linux/syscalls.cc
@@ -91,8 +91,8 @@
     /*   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<Sparc32Linux>),
+    /*   4 */ SyscallDesc("write", writeFunc<Sparc32Linux>),
     /*   5 */ SyscallDesc("open", openFunc<Sparc32Linux>), // 32 bit
     /*   6 */ SyscallDesc("close", closeFunc),
     /*   7 */ SyscallDesc("wait4", unimplementedFunc), // 32 bit
@@ -397,8 +397,8 @@
     /*  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<SparcLinux>),
+    /*  4 */ SyscallDesc("write", writeFunc<SparcLinux>),
     /*  5 */ SyscallDesc("open", openFunc<SparcLinux>),
     /*  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 1afa353..bcdd088 100644
--- a/src/arch/sparc/solaris/process.cc
+++ b/src/arch/sparc/solaris/process.cc
@@ -67,8 +67,8 @@
     /* 0 */ SyscallDesc("syscall", unimplementedFunc),
     /* 1 */ SyscallDesc("exit", exitFunc),
     /* 2 */ SyscallDesc("fork", unimplementedFunc),
-    /* 3 */ SyscallDesc("read", readFunc),
-    /* 4 */ SyscallDesc("write", writeFunc),
+    /* 3 */ SyscallDesc("read", readFunc<SparcSolaris>),
+    /* 4 */ SyscallDesc("write", writeFunc<SparcSolaris>),
     /* 5 */ SyscallDesc("open", openFunc<SparcSolaris>),
     /* 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 65d0238..03a88fc 100644
--- a/src/arch/x86/linux/process.cc
+++ b/src/arch/x86/linux/process.cc
@@ -222,14 +222,14 @@
 }
 
 static SyscallDesc syscallDescs64[] = {
-    /*   0 */ SyscallDesc("read", readFunc),
-    /*   1 */ SyscallDesc("write", writeFunc),
+    /*   0 */ SyscallDesc("read", readFunc<X86Linux64>),
+    /*   1 */ SyscallDesc("write", writeFunc<X86Linux64>),
     /*   2 */ SyscallDesc("open", openFunc<X86Linux64>),
     /*   3 */ SyscallDesc("close", closeFunc),
     /*   4 */ SyscallDesc("stat", stat64Func<X86Linux64>),
     /*   5 */ SyscallDesc("fstat", fstat64Func<X86Linux64>),
     /*   6 */ SyscallDesc("lstat", lstat64Func<X86Linux64>),
-    /*   7 */ SyscallDesc("poll", unimplementedFunc),
+    /*   7 */ SyscallDesc("poll", pollFunc<X86Linux64>),
     /*   8 */ SyscallDesc("lseek", lseekFunc),
     /*   9 */ SyscallDesc("mmap", mmapFunc<X86Linux64>),
     /*  10 */ SyscallDesc("mprotect", ignoreFunc),
@@ -245,7 +245,7 @@
     /*  20 */ SyscallDesc("writev", writevFunc<X86Linux64>),
     /*  21 */ SyscallDesc("access", ignoreFunc),
     /*  22 */ SyscallDesc("pipe", pipeFunc),
-    /*  23 */ SyscallDesc("select", unimplementedFunc),
+    /*  23 */ SyscallDesc("select", selectFunc<X86Linux64>),
     /*  24 */ SyscallDesc("sched_yield", ignoreFunc),
     /*  25 */ SyscallDesc("mremap", mremapFunc<X86Linux64>),
     /*  26 */ SyscallDesc("msync", unimplementedFunc),
@@ -265,7 +265,7 @@
     /*  40 */ SyscallDesc("sendfile", unimplementedFunc),
     /*  41 */ SyscallDesc("socket", socketFunc<X86Linux64>),
     /*  42 */ SyscallDesc("connect", connectFunc),
-    /*  43 */ SyscallDesc("accept", unimplementedFunc),
+    /*  43 */ SyscallDesc("accept", acceptFunc<X86Linux64>),
     /*  44 */ SyscallDesc("sendto", sendtoFunc),
     /*  45 */ SyscallDesc("recvfrom", recvfromFunc),
     /*  46 */ SyscallDesc("sendmsg", sendmsgFunc),
@@ -283,7 +283,7 @@
     /*  58 */ SyscallDesc("vfork", unimplementedFunc),
     /*  59 */ SyscallDesc("execve", execveFunc<X86Linux64>),
     /*  60 */ SyscallDesc("exit", exitFunc),
-    /*  61 */ SyscallDesc("wait4", unimplementedFunc),
+    /*  61 */ SyscallDesc("wait4", wait4Func<X86Linux64>),
     /*  62 */ SyscallDesc("kill", unimplementedFunc),
     /*  63 */ SyscallDesc("uname", unameFunc),
     /*  64 */ SyscallDesc("semget", unimplementedFunc),
@@ -558,8 +558,8 @@
     /*   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<X86Linux32>),
+    /*   4 */ SyscallDesc("write", writeFunc<X86Linux32>),
     /*   5 */ SyscallDesc("open", openFunc<X86Linux32>),
     /*   6 */ SyscallDesc("close", closeFunc),
     /*   7 */ SyscallDesc("waitpid", unimplementedFunc),
@@ -637,7 +637,7 @@
     /*  79 */ SyscallDesc("settimeofday", unimplementedFunc),
     /*  80 */ SyscallDesc("getgroups", unimplementedFunc),
     /*  81 */ SyscallDesc("setgroups", unimplementedFunc),
-    /*  82 */ SyscallDesc("select", unimplementedFunc),
+    /*  82 */ SyscallDesc("select", selectFunc<X86Linux32>),
     /*  83 */ SyscallDesc("symlink", unimplementedFunc),
     /*  84 */ SyscallDesc("oldlstat", unimplementedFunc),
     /*  85 */ SyscallDesc("readlink", readlinkFunc),
@@ -669,7 +669,7 @@
     /* 111 */ SyscallDesc("vhangup", unimplementedFunc),
     /* 112 */ SyscallDesc("idle", unimplementedFunc),
     /* 113 */ SyscallDesc("vm86old", unimplementedFunc),
-    /* 114 */ SyscallDesc("wait4", unimplementedFunc),
+    /* 114 */ SyscallDesc("wait4", wait4Func<X86Linux32>),
     /* 115 */ SyscallDesc("swapoff", unimplementedFunc),
     /* 116 */ SyscallDesc("sysinfo", sysinfoFunc<X86Linux32>),
     /* 117 */ SyscallDesc("ipc", unimplementedFunc),
@@ -727,7 +727,7 @@
     /* 165 */ SyscallDesc("getresuid", unimplementedFunc),
     /* 166 */ SyscallDesc("vm86", unimplementedFunc),
     /* 167 */ SyscallDesc("query_module", unimplementedFunc),
-    /* 168 */ SyscallDesc("poll", unimplementedFunc),
+    /* 168 */ SyscallDesc("poll", pollFunc<X86Linux32>),
     /* 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 a1df994..e559e05 100644
--- a/src/kern/linux/linux.hh
+++ b/src/kern/linux/linux.hh
@@ -153,6 +153,15 @@
         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 @@
     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 6b2b2da..15e174a 100644
--- a/src/sim/fd_entry.hh
+++ b/src/sim/fd_entry.hh
@@ -231,7 +231,6 @@
         return std::make_shared<SocketFDEntry>(*this);
     }
 
-  private:
     int _domain;
     int _type;
     int _protocol;
diff --git a/src/sim/syscall_emul.cc b/src/sim/syscall_emul.cc
index 89f70a1..74ca1e9 100644
--- a/src/sim/syscall_emul.cc
+++ b/src/sim/syscall_emul.cc
@@ -279,53 +279,6 @@
     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<HBFDEntry>((*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<HBFDEntry>((*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 d882cf4..67fa9e3 100644
--- a/src/sim/syscall_emul.hh
+++ b/src/sim/syscall_emul.hh
@@ -78,6 +78,7 @@
 
 #endif
 #include <fcntl.h>
+#include <poll.h>
 #include <sys/mman.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -87,6 +88,7 @@
 #include <sys/mount.h>
 #endif
 #include <sys/time.h>
+#include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
@@ -162,14 +164,6 @@
 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 @@
     return 0;
 }
 
+template <class OS>
+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<HBFDEntry>((*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<BasicSignal>::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 <class OS>
@@ -1269,7 +1337,6 @@
     return 0;
 }
 
-
 /// Target statfs() handler.
 template <class OS>
 SyscallReturn
@@ -2158,4 +2225,401 @@
     return status;
 }
 
+template <class OS>
+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<typename OS::fd_set> rd_t(fds_read_ptr);
+    TypedBufferArg<typename OS::fd_set> wr_t(fds_writ_ptr);
+    TypedBufferArg<typename OS::fd_set> ex_t(fds_excp_ptr);
+    TypedBufferArg<typename OS::timeval> 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<int, int> 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<HBFDEntry>((*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 <class OS>
+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<HBFDEntry>((*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 <class OS>
+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<HBFDEntry>((*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<FileFDEntry>(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 <class OS>
+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<BasicSignal>::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 <class OS>
+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<SocketFDEntry>((*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<SocketFDEntry>(host_fd, sfdp->_domain,
+                                                sfdp->_type, sfdp->_protocol);
+    return p->fds->allocFD(afdp);
+}
+
 #endif // __SIM_SYSCALL_EMUL_HH__