syscall-emul: Add functionality to open syscalls
authorBrandon Potter <Brandon.Potter@amd.com>
Wed, 1 Mar 2017 19:24:16 +0000 (13:24 -0600)
committerBrandon Potter <Brandon.Potter@amd.com>
Thu, 9 Mar 2017 22:42:45 +0000 (22:42 +0000)
This changeset adds refactors the existing open system call,
adds the openat variant (enabled for x86 builds), and adds
additional "special file" test cases for /proc/meminfo and
/etc/passwd.

Change-Id: I6f429db65bbf2a28ffa3fd12df518c2d0de49663
Reviewed-on: https://gem5-review.googlesource.com/2265
Maintainer: Jason Lowe-Power <jason@lowepower.com>
Reviewed-by: Tony Gutierrez <anthony.gutierrez@amd.com>
Reviewed-by: Michael LeBeane <Michael.Lebeane@amd.com>
src/arch/x86/linux/process.cc
src/kern/linux/linux.cc
src/kern/linux/linux.hh
src/kern/solaris/solaris.hh
src/sim/syscall_emul.cc
src/sim/syscall_emul.hh

index 806d8605b7e57f9a1fa214d84b3487400be267b9..f90022bdd6876c7b3364ac6e6601179610c6a421 100644 (file)
@@ -477,7 +477,7 @@ static SyscallDesc syscallDescs64[] = {
     /* 254 */ SyscallDesc("inotify_add_watch", unimplementedFunc),
     /* 255 */ SyscallDesc("inotify_rm_watch", unimplementedFunc),
     /* 256 */ SyscallDesc("migrate_pages", unimplementedFunc),
-    /* 257 */ SyscallDesc("openat", unimplementedFunc),
+    /* 257 */ SyscallDesc("openat", openatFunc<X86Linux64>),
     /* 258 */ SyscallDesc("mkdirat", unimplementedFunc),
     /* 259 */ SyscallDesc("mknodat", unimplementedFunc),
     /* 260 */ SyscallDesc("fchownat", unimplementedFunc),
@@ -844,7 +844,7 @@ static SyscallDesc syscallDescs32[] = {
     /* 292 */ SyscallDesc("inotify_add_watch", unimplementedFunc),
     /* 293 */ SyscallDesc("inotify_rm_watch", unimplementedFunc),
     /* 294 */ SyscallDesc("migrate_pages", unimplementedFunc),
-    /* 295 */ SyscallDesc("openat", unimplementedFunc),
+    /* 295 */ SyscallDesc("openat", openatFunc<X86Linux32>),
     /* 296 */ SyscallDesc("mkdirat", unimplementedFunc),
     /* 297 */ SyscallDesc("mknodat", unimplementedFunc),
     /* 298 */ SyscallDesc("fchownat", unimplementedFunc),
index 493be9611b952a291d50ab6f406fc31c3f7ff3fd..bd0b4d09a099ed3d57ccf47e059a80c4cfd8c060 100644 (file)
@@ -33,6 +33,7 @@
 #include <cstdio>
 #include <string>
 
+#include "cpu/base.hh"
 #include "debug/SyscallVerbose.hh"
 #include "sim/process.hh"
 #include "sim/system.hh"
@@ -41,21 +42,35 @@ int
 Linux::openSpecialFile(std::string path, Process *process,
                        ThreadContext *tc)
 {
-    DPRINTF(SyscallVerbose, "Opening special file: %s\n", path.c_str());
+    DPRINTFR(SyscallVerbose,
+             "%d: %s: generic-open: opening special file: %s\n",
+             curTick(), tc->getCpuPtr()->name(), path.c_str());
+
+    bool matched = false;
+    std::string data;
+
     if (path.compare(0, 13, "/proc/meminfo") == 0) {
-        std::string data = Linux::procMeminfo(process, tc);
+        data = Linux::procMeminfo(process, tc);
+        matched = true;
+    } else if (path.compare(0, 11, "/etc/passwd") == 0) {
+        data = Linux::etcPasswd(process, tc);
+        matched = true;
+    }
+
+    if (matched) {
         FILE *f = tmpfile();
         int fd = fileno(f);
         size_t ret M5_VAR_USED = fwrite(data.c_str(), 1, data.size(), f);
         assert(ret == data.size());
         rewind(f);
         return fd;
+    } else {
+        warn("Attempting to open special file: %s. Ignoring. Simulation may "
+             "take un-expected code path or be non-deterministic until proper "
+             "handling is implemented.\n", path.c_str());
+        errno = EACCES;
+        return -1;
     }
-
-    warn("Attempting to open special file: %s. Ignoring. Simulation may"
-            " take un-expected code path or be non-deterministic until proper"
-            "  handling is implemented.\n", path.c_str());
-    return -1;
 }
 
 std::string
@@ -66,3 +81,9 @@ Linux::procMeminfo(Process *process, ThreadContext *tc)
             process->system->freeMemSize() >> 10);
 }
 
+std::string
+Linux::etcPasswd(Process *process, ThreadContext *tc)
+{
+    return csprintf("gem5-user:x:1000:1000:gem5-user,,,:%s:/bin/bash\n",
+                    process->getcwd());
+}
index e6899a9cae29ff3ee333d1440f259e5d63ed85f1..b24ee389525108ed169475b125c50a9765ef4c75 100644 (file)
@@ -226,6 +226,7 @@ class Linux : public OperatingSystem
     static int openSpecialFile(std::string path, Process *process,
                                ThreadContext *tc);
     static std::string procMeminfo(Process *process, ThreadContext *tc);
+    static std::string etcPasswd(Process *process, ThreadContext *tc);
 
     // For futex system call
     static const unsigned TGT_FUTEX_WAIT  = 0;
index 9cd5af16f7fe2a7a7fb732b5cf65bc337b8fbd74..d875f44027a5a2a176727611bb3f6b00e7a3775e 100644 (file)
@@ -114,6 +114,9 @@ class Solaris : public OperatingSystem
         char machine[_SYS_NMLN];        //!< Machine type.
     } utsname;
 
+    // for *at syscalls
+    static const int TGT_AT_FDCWD   = -100;
+
 };  // class Solaris
 
 #endif // __SOLARIS_HH__
index 2459c801f3d4ecdfeb5385aff8c70d34dda7d030..9680193235091e4254dbd5861db637c57b518ef4 100644 (file)
@@ -365,7 +365,6 @@ getcwdFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc)
     return (result == -1) ? -errno : result;
 }
 
-/// Target open() handler.
 SyscallReturn
 readlinkFunc(SyscallDesc *desc, int callnum, Process *process,
              ThreadContext *tc)
index 099ff58d93bbe709e28bc5f063dba17d53aea251..d379bbe85e89c8aa5aa1a87a2ea09cb26571bd4c 100644 (file)
@@ -613,80 +613,136 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc)
 }
 
 template <class OS>
-static SyscallReturn
-openFunc(SyscallDesc *desc, int callnum, Process *process,
-         ThreadContext *tc, int index)
+SyscallReturn
+openImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
+         bool isopenat)
 {
-    std::string path;
+    int index = 0;
+    int tgt_dirfd = -1;
 
-    if (!tc->getMemProxy().tryReadString(path,
-                process->getSyscallArg(tc, index)))
-        return -EFAULT;
+    /**
+     * If using the openat variant, read in the target directory file
+     * descriptor from the simulated process.
+     */
+    if (isopenat)
+        tgt_dirfd = p->getSyscallArg(tc, index);
 
-    int tgtFlags = process->getSyscallArg(tc, index);
-    int mode = process->getSyscallArg(tc, index);
-    int hostFlags = 0;
+    /**
+     * Retrieve the simulated process' memory proxy and then read in the path
+     * string from that memory space into the host's working memory space.
+     */
+    std::string path;
+    if (!tc->getMemProxy().tryReadString(path, p->getSyscallArg(tc, index)))
+        return -EFAULT;
 
-    // translate open flags
+#ifdef __CYGWIN32__
+    int host_flags = O_BINARY;
+#else
+    int host_flags = 0;
+#endif
+    /**
+     * Translate target flags into host flags. Flags exist which are not
+     * ported between architectures which can cause check failures.
+     */
+    int tgt_flags = p->getSyscallArg(tc, index);
     for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
-        if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
-            tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
-            hostFlags |= OS::openFlagTable[i].hostFlag;
+        if (tgt_flags & OS::openFlagTable[i].tgtFlag) {
+            tgt_flags &= ~OS::openFlagTable[i].tgtFlag;
+            host_flags |= OS::openFlagTable[i].hostFlag;
         }
     }
-
-    // any target flags left?
-    if (tgtFlags != 0)
-        warn("Syscall: open: cannot decode flags 0x%x", tgtFlags);
-
+    if (tgt_flags) {
+        warn("open%s: cannot decode flags 0x%x",
+             isopenat ? "at" : "", tgt_flags);
+    }
 #ifdef __CYGWIN32__
-    hostFlags |= O_BINARY;
+    host_flags |= O_BINARY;
 #endif
 
-    // Adjust path for current working directory
-    path = process->fullPath(path);
+    int mode = p->getSyscallArg(tc, index);
 
-    DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str());
+    /**
+     * If the simulated process called open or openat with AT_FDCWD specified,
+     * take the current working directory value which was passed into the
+     * process class as a Python parameter and append the current path to
+     * create a full path.
+     * Otherwise, openat with a valid target directory file descriptor has
+     * been called. If the path option, which was passed in as a parameter,
+     * is not absolute, retrieve the directory file descriptor's path and
+     * prepend it to the path passed in as a parameter.
+     * In every case, we should have a full path (which is relevant to the
+     * host) to work with after this block has been passed.
+     */
+    if (!isopenat || (isopenat && tgt_dirfd == OS::TGT_AT_FDCWD)) {
+        path = p->fullPath(path);
+    } else if (!startswith(path, "/")) {
+        std::shared_ptr<FDEntry> fdep = ((*p->fds)[tgt_dirfd]);
+        auto ffdp = std::dynamic_pointer_cast<FileFDEntry>(fdep);
+        if (!ffdp)
+            return -EBADF;
+        path.insert(0, ffdp->getFileName());
+    }
 
+    /**
+     * Since this is an emulated environment, we create pseudo file
+     * descriptors for device requests that have been registered with
+     * the process class through Python; this allows us to create a file
+     * descriptor for subsequent ioctl or mmap calls.
+     */
     if (startswith(path, "/dev/")) {
         std::string filename = path.substr(strlen("/dev/"));
-        if (filename == "sysdev0") {
-            // This is a memory-mapped high-resolution timer device on Alpha.
-            // We don't support it, so just punt.
-            warn("Ignoring open(%s, ...)\n", path);
-            return -ENOENT;
-        }
-
-        EmulatedDriver *drv = process->findDriver(filename);
+        EmulatedDriver *drv = p->findDriver(filename);
         if (drv) {
-            // the driver's open method will allocate a fd from the
-            // process if necessary.
-            return drv->open(process, tc, mode, hostFlags);
+            DPRINTF_SYSCALL(Verbose, "open%s: passing call to "
+                            "driver open with path[%s]\n",
+                            isopenat ? "at" : "", path.c_str());
+            return drv->open(p, tc, mode, host_flags);
         }
+        /**
+         * Fall through here for pass through to host devices, such
+         * as /dev/zero
+         */
+    }
 
-        // fall through here for pass through to host devices, such as
-        // /dev/zero
+    /**
+     * Some special paths and files cannot be called on the host and need
+     * to be handled as special cases inside the simulator.
+     * If the full path that was created above does not match any of the
+     * special cases, pass it through to the open call on the host to let
+     * the host open the file on our behalf.
+     * If the host cannot open the file, return the host's error code back
+     * through the system call to the simulated process.
+     */
+    int sim_fd = -1;
+    std::vector<std::string> special_paths =
+            { "/proc/", "/system/", "/sys/", "/platform/", "/etc/passwd" };
+    for (auto entry : special_paths) {
+        if (startswith(path, entry))
+            sim_fd = OS::openSpecialFile(path, p, tc);
+    }
+    if (sim_fd == -1) {
+        sim_fd = open(path.c_str(), host_flags, mode);
+    }
+    if (sim_fd == -1) {
+        int local = -errno;
+        DPRINTF_SYSCALL(Verbose, "open%s: failed -> path:%s\n",
+                        isopenat ? "at" : "", path.c_str());
+        return local;
     }
 
-    int fd;
-    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);
-        local_errno = ENOENT;
-     } else {
-        // open the file
-        fd = open(path.c_str(), hostFlags, mode);
-        local_errno = errno;
-     }
-
-    if (fd == -1)
-        return -local_errno;
-
-    std::shared_ptr<FileFDEntry> ffdp =
-        std::make_shared<FileFDEntry>(fd, hostFlags, path.c_str(), false);
-    return process->fds->allocFD(ffdp);
+    /**
+     * The file was opened successfully and needs to be recorded in the
+     * process' file descriptor array so that it can be retrieved later.
+     * The target file descriptor that is chosen will be the lowest unused
+     * file descriptor.
+     * Return the indirect target file descriptor back to the simulated
+     * process to act as a handle for the opened file.
+     */
+    auto ffdp = std::make_shared<FileFDEntry>(sim_fd, host_flags, path, 0);
+    int tgt_fd = p->fds->allocFD(ffdp);
+    DPRINTF_SYSCALL(Verbose, "open%s: sim_fd[%d], target_fd[%d] -> path:%s\n",
+                    isopenat ? "at" : "", sim_fd, tgt_fd, path.c_str());
+    return tgt_fd;
 }
 
 /// Target open() handler.
@@ -695,7 +751,7 @@ SyscallReturn
 openFunc(SyscallDesc *desc, int callnum, Process *process,
          ThreadContext *tc)
 {
-    return openFunc<OS>(desc, callnum, process, tc, 0);
+    return openImpl<OS>(desc, callnum, process, tc, false);
 }
 
 /// Target openat() handler.
@@ -704,11 +760,7 @@ SyscallReturn
 openatFunc(SyscallDesc *desc, int callnum, Process *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);
+    return openImpl<OS>(desc, callnum, process, tc, true);
 }
 
 /// Target unlinkat() handler.