dev: Use shared_ptr for Arguments::Data
[gem5.git] / src / sim / syscall_emul.hh
index 504add35f85b0b0aeb6e6a02f74a22853c3f4e4d..66c12f07de30b2a0ef85f51bcf9a816de01aad61 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012-2013 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2003-2005 The Regents of The University of Michigan
  * All rights reserved.
  *
@@ -33,7 +45,8 @@
 #define __SIM_SYSCALL_EMUL_HH__
 
 #define NO_STAT64 (defined(__APPLE__) || defined(__OpenBSD__) || \
-                   defined(__FreeBSD__) || defined(__CYGWIN__))
+  defined(__FreeBSD__) || defined(__CYGWIN__) || \
+  defined(__NetBSD__))
 
 ///
 /// @file syscall_emul.hh
@@ -240,7 +253,10 @@ SyscallReturn gethostnameFunc(SyscallDesc *desc, int num,
 SyscallReturn getcwdFunc(SyscallDesc *desc, int num,
                          LiveProcess *p, ThreadContext *tc);
 
-/// Target unlink() handler.
+/// Target readlink() handler.
+SyscallReturn readlinkFunc(SyscallDesc *desc, int num,
+                           LiveProcess *p, ThreadContext *tc,
+                           int index = 0);
 SyscallReturn readlinkFunc(SyscallDesc *desc, int num,
                            LiveProcess *p, ThreadContext *tc);
 
@@ -334,6 +350,95 @@ SyscallReturn getegidFunc(SyscallDesc *desc, int num,
 SyscallReturn cloneFunc(SyscallDesc *desc, int num,
                                LiveProcess *p, ThreadContext *tc);
 
+/// Target access() handler
+SyscallReturn accessFunc(SyscallDesc *desc, int num,
+                               LiveProcess *p, ThreadContext *tc);
+SyscallReturn accessFunc(SyscallDesc *desc, int num,
+                               LiveProcess *p, ThreadContext *tc,
+                               int index);
+
+/// Futex system call
+///  Implemented by Daniel Sanchez
+///  Used by printf's in multi-threaded apps
+template <class OS>
+SyscallReturn
+futexFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
+          ThreadContext *tc)
+{
+    int index_uaddr = 0;
+    int index_op = 1;
+    int index_val = 2;
+    int index_timeout = 3;
+
+    uint64_t uaddr = process->getSyscallArg(tc, index_uaddr);
+    int op = process->getSyscallArg(tc, index_op);
+    int val = process->getSyscallArg(tc, index_val);
+    uint64_t timeout = process->getSyscallArg(tc, index_timeout);
+
+    std::map<uint64_t, std::list<ThreadContext *> * >
+        &futex_map = tc->getSystemPtr()->futexMap;
+
+    DPRINTF(SyscallVerbose, "In sys_futex: Address=%llx, op=%d, val=%d\n",
+            uaddr, op, val);
+
+    op &= ~OS::TGT_FUTEX_PRIVATE_FLAG;
+
+    if (op == OS::TGT_FUTEX_WAIT) {
+        if (timeout != 0) {
+            warn("sys_futex: FUTEX_WAIT with non-null timeout unimplemented;"
+                 "we'll wait indefinitely");
+        }
+
+        uint8_t *buf = new uint8_t[sizeof(int)];
+        tc->getMemProxy().readBlob((Addr)uaddr, buf, (int)sizeof(int));
+        int mem_val = *((int *)buf);
+        delete buf;
+
+        if(val != mem_val) {
+            DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, read: %d, "
+                                    "expected: %d\n", mem_val, val);
+            return -OS::TGT_EWOULDBLOCK;
+        }
+
+        // Queue the thread context
+        std::list<ThreadContext *> * tcWaitList;
+        if (futex_map.count(uaddr)) {
+            tcWaitList = futex_map.find(uaddr)->second;
+        } else {
+            tcWaitList = new std::list<ThreadContext *>();
+            futex_map.insert(std::pair< uint64_t,
+                            std::list<ThreadContext *> * >(uaddr, tcWaitList));
+        }
+        tcWaitList->push_back(tc);
+        DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAIT, suspending calling "
+                                "thread context\n");
+        tc->suspend();
+        return 0;
+    } else if (op == OS::TGT_FUTEX_WAKE){
+        int wokenUp = 0;
+        std::list<ThreadContext *> * tcWaitList;
+        if (futex_map.count(uaddr)) {
+            tcWaitList = futex_map.find(uaddr)->second;
+            while (tcWaitList->size() > 0 && wokenUp < val) {
+                tcWaitList->front()->activate();
+                tcWaitList->pop_front();
+                wokenUp++;
+            }
+            if(tcWaitList->empty()) {
+                futex_map.erase(uaddr);
+                delete tcWaitList;
+            }
+        }
+        DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, activated %d waiting "
+                                "thread contexts\n", wokenUp);
+        return wokenUp;
+    } else {
+        warn("sys_futex: op %d is not implemented, just returning...", op);
+        return 0;
+    }
+
+}
+
 
 /// Pseudo Funcs  - These functions use a different return convension,
 /// returning a second value in a register other than the normal return register
@@ -486,7 +591,8 @@ copyOutStat64Buf(SETranslatingPortProxy &mem, Addr addr,
 
 /// Target ioctl() handler.  For the most part, programs call ioctl()
 /// only to find out if their stdout is a tty, to determine whether to
-/// do line or block buffering.
+/// do line or block buffering.  We always claim that output fds are
+/// not TTYs to provide repeatable results.
 template <class OS>
 SyscallReturn
 ioctlFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
@@ -503,33 +609,22 @@ ioctlFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
         return -EBADF;
     }
 
-    switch (req) {
-      case OS::TIOCISATTY_:
-      case OS::TIOCGETP_:
-      case OS::TIOCSETP_:
-      case OS::TIOCSETN_:
-      case OS::TIOCSETC_:
-      case OS::TIOCGETC_:
-      case OS::TIOCGETS_:
-      case OS::TIOCGETA_:
-      case OS::TCSETAW_:
+    if (OS::isTtyReq(req)) {
         return -ENOTTY;
-
-      default:
-        fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ \n",
-              fd, req, tc->pcState());
     }
+
+    warn("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ \n",
+         fd, req, tc->pcState());
+    return -ENOTTY;
 }
 
-/// Target open() handler.
 template <class OS>
-SyscallReturn
+static SyscallReturn
 openFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
-         ThreadContext *tc)
+         ThreadContext *tc, int index)
 {
     std::string path;
 
-    int index = 0;
     if (!tc->getMemProxy().tryReadString(path,
                 process->getSyscallArg(tc, index)))
         return -EFAULT;
@@ -567,17 +662,70 @@ openFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
     DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str());
 
     int fd;
-    if (!path.compare(0, 6, "/proc/") || !path.compare(0, 8, "/system/") ||
-        !path.compare(0, 10, "/platform/") || !path.compare(0, 5, "/sys/")) {
-        // It's a proc/sys entery and requires special handling
+    int local_errno;
+    if (startswith(path, "/proc/") || startswith(path, "/system/") ||
+        startswith(path, "/platform/") || startswith(path, "/sys/")) {
+        // It's a proc/sys entry and requires special handling
         fd = OS::openSpecialFile(path, process, tc);
-        return (fd == -1) ? -1 : process->alloc_fd(fd,path.c_str(),hostFlags,mode, false);
+        local_errno = ENOENT;
      } else {
         // open the file
         fd = open(path.c_str(), hostFlags, mode);
-        return (fd == -1) ? -errno : process->alloc_fd(fd,path.c_str(),hostFlags,mode, false);
+        local_errno = errno;
      }
 
+    if (fd == -1)
+        return -local_errno;
+
+    return process->alloc_fd(fd, path.c_str(), hostFlags, mode, false);
+}
+
+/// Target open() handler.
+template <class OS>
+SyscallReturn
+openFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
+         ThreadContext *tc)
+{
+    return openFunc<OS>(desc, callnum, process, tc, 0);
+}
+
+/// Target openat() handler.
+template <class OS>
+SyscallReturn
+openatFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
+         ThreadContext *tc)
+{
+    int index = 0;
+    int dirfd = process->getSyscallArg(tc, index);
+    if (dirfd != OS::TGT_AT_FDCWD)
+        warn("openat: first argument not AT_FDCWD; unlikely to work");
+    return openFunc<OS>(desc, callnum, process, tc, 1);
+}
+
+/// Target facessat() handler
+template <class OS>
+SyscallReturn
+faccessatFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
+        ThreadContext *tc)
+{
+    int index = 0;
+    int dirfd = process->getSyscallArg(tc, index);
+    if (dirfd != OS::TGT_AT_FDCWD)
+        warn("faccessat: first argument not AT_FDCWD; unlikely to work");
+    return accessFunc(desc, callnum, process, tc, 1);
+}
+
+/// Target readlinkat() handler
+template <class OS>
+SyscallReturn
+readlinkatFunc(SyscallDesc *desc, int callnum, LiveProcess *process,
+        ThreadContext *tc)
+{
+    int index = 0;
+    int dirfd = process->getSyscallArg(tc, index);
+    if (dirfd != OS::TGT_AT_FDCWD)
+        warn("openat: first argument not AT_FDCWD; unlikely to work");
+    return readlinkFunc(desc, callnum, process, tc, 1);
 }
 
 /// Target sysinfo() handler.
@@ -668,40 +816,58 @@ mremapFunc(SyscallDesc *desc, int callnum, LiveProcess *process, ThreadContext *
     uint64_t old_length = process->getSyscallArg(tc, index);
     uint64_t new_length = process->getSyscallArg(tc, index);
     uint64_t flags = process->getSyscallArg(tc, index);
+    uint64_t provided_address = 0;
+    bool use_provided_address = flags & OS::TGT_MREMAP_FIXED;
 
-    if ((start % TheISA::VMPageSize != 0) ||
-            (new_length % TheISA::VMPageSize != 0)) {
+    if (use_provided_address)
+        provided_address = process->getSyscallArg(tc, index);
+
+    if ((start % TheISA::PageBytes != 0) ||
+        (new_length % TheISA::PageBytes != 0) ||
+        (provided_address % TheISA::PageBytes != 0)) {
         warn("mremap failing: arguments not page aligned");
         return -EINVAL;
     }
 
     if (new_length > old_length) {
-        if ((start + old_length) == process->mmap_end) {
+        if ((start + old_length) == process->mmap_end &&
+            (!use_provided_address || provided_address == start)) {
             uint64_t diff = new_length - old_length;
             process->allocateMem(process->mmap_end, diff);
             process->mmap_end += diff;
             return start;
         } else {
-            // sys/mman.h defined MREMAP_MAYMOVE
-            if (!(flags & 1)) {
+            if (!use_provided_address && !(flags & OS::TGT_MREMAP_MAYMOVE)) {
                 warn("can't remap here and MREMAP_MAYMOVE flag not set\n");
                 return -ENOMEM;
             } else {
-                process->pTable->remap(start, old_length, process->mmap_end);
-                warn("mremapping to totally new vaddr %08p-%08p, adding %d\n", 
-                        process->mmap_end, process->mmap_end + new_length, new_length);
-                start = process->mmap_end;
+                uint64_t new_start = use_provided_address ?
+                    provided_address : process->mmap_end;
+                process->pTable->remap(start, old_length, new_start);
+                warn("mremapping to new vaddr %08p-%08p, adding %d\n",
+                     new_start, new_start + new_length,
+                     new_length - old_length);
                 // add on the remaining unallocated pages
-                process->allocateMem(start + old_length,
-                                     new_length - old_length);
-                process->mmap_end += new_length;
-                warn("returning %08p as start\n", start);
-                return start;
+                process->allocateMem(new_start + old_length,
+                                     new_length - old_length,
+                                     use_provided_address /* clobber */);
+                if (!use_provided_address)
+                    process->mmap_end += new_length;
+                if (use_provided_address &&
+                    new_start + new_length > process->mmap_end) {
+                    // something fishy going on here, at least notify the user
+                    // @todo: increase mmap_end?
+                    warn("mmap region limit exceeded with MREMAP_FIXED\n");
+                }
+                warn("returning %08p as start\n", new_start);
+                return new_start;
             }
         }
     } else {
+        if (use_provided_address && provided_address != start)
+            process->pTable->remap(start, new_length, provided_address);
         process->pTable->unmap(start + new_length, old_length - new_length);
-        return start;
+        return use_provided_address ? provided_address : start;
     }
 }
 
@@ -769,6 +935,43 @@ stat64Func(SyscallDesc *desc, int callnum, LiveProcess *process,
 }
 
 
+/// Target fstatat64() handler.
+template <class OS>
+SyscallReturn
+fstatat64Func(SyscallDesc *desc, int callnum, LiveProcess *process,
+              ThreadContext *tc)
+{
+    int index = 0;
+    int dirfd = process->getSyscallArg(tc, index);
+    if (dirfd != OS::TGT_AT_FDCWD)
+        warn("openat: first argument not AT_FDCWD; unlikely to work");
+
+    std::string path;
+    if (!tc->getMemProxy().tryReadString(path,
+                process->getSyscallArg(tc, index)))
+        return -EFAULT;
+    Addr bufPtr = process->getSyscallArg(tc, index);
+
+    // Adjust path for current working directory
+    path = process->fullPath(path);
+
+#if NO_STAT64
+    struct stat  hostBuf;
+    int result = stat(path.c_str(), &hostBuf);
+#else
+    struct stat64 hostBuf;
+    int result = stat64(path.c_str(), &hostBuf);
+#endif
+
+    if (result < 0)
+        return -errno;
+
+    copyOutStat64Buf<OS>(tc->getMemProxy(), bufPtr, &hostBuf);
+
+    return 0;
+}
+
+
 /// Target fstat64() handler.
 template <class OS>
 SyscallReturn
@@ -1004,6 +1207,9 @@ mmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
     int tgt_fd = p->getSyscallArg(tc, index);
     // int offset = p->getSyscallArg(tc, index);
 
+    if (length > 0x100000000ULL)
+        warn("mmap length argument %#x is unreasonably large.\n", length);
+
     if (!(flags & OS::TGT_MAP_ANONYMOUS)) {
         Process::FdMap *fd_map = p->sim_fd_obj(tgt_fd);
         if (!fd_map || fd_map->fd < 0) {
@@ -1020,8 +1226,8 @@ mmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc)
         }
     }
 
-    if ((start  % TheISA::VMPageSize) != 0 ||
-        (length % TheISA::VMPageSize) != 0) {
+    if ((start  % TheISA::PageBytes) != 0 ||
+        (length % TheISA::PageBytes) != 0) {
         warn("mmap failing: arguments not page-aligned: "
              "start 0x%x length 0x%x",
              start, length);