80a81
> #include <poll.h>
89a91
> #include <sys/types.h>
165,172d166
< // 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);
<
948a943,950
> 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);
949a952,1017
> 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;
> }
>
1272d1339
<
2160a2228,2624
> 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);
> }
>