summaryrefslogtreecommitdiff
path: root/src/sim/syscall_emul.hh
diff options
context:
space:
mode:
authorBrandon Potter <brandon.potter@amd.com>2018-04-18 14:57:57 -0400
committerAnthony Gutierrez <anthony.gutierrez@amd.com>2019-01-22 02:05:48 +0000
commitbc74c58eaf55005a6a4b2e67657da4121554943c (patch)
tree92b04de877f151c7d5c562a117d695fdf913dc37 /src/sim/syscall_emul.hh
parentc4e67f68377d9b3e909c76412b6ed2fde6a50e01 (diff)
downloadgem5-bc74c58eaf55005a6a4b2e67657da4121554943c.tar.xz
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>
Diffstat (limited to 'src/sim/syscall_emul.hh')
-rw-r--r--src/sim/syscall_emul.hh482
1 files changed, 473 insertions, 9 deletions
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 <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 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 <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 @@ fstatFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc)
return 0;
}
-
/// Target statfs() handler.
template <class OS>
SyscallReturn
@@ -2158,4 +2225,401 @@ socketpairFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc)
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__