summaryrefslogtreecommitdiff
path: root/src/sim/syscall_emul.hh
diff options
context:
space:
mode:
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__