diff options
Diffstat (limited to 'src/sim/syscall_emul.hh')
-rw-r--r-- | src/sim/syscall_emul.hh | 482 |
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__ |