syscall-emul: Add functionality to open syscalls

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>
This commit is contained in:
Brandon Potter 2017-03-01 13:24:16 -06:00
parent e5fe2b82b7
commit acce7b0dc0
6 changed files with 151 additions and 75 deletions

View file

@ -477,7 +477,7 @@ static SyscallDesc syscallDescs64[] = {
/* 254 */ SyscallDesc("inotify_add_watch", unimplementedFunc), /* 254 */ SyscallDesc("inotify_add_watch", unimplementedFunc),
/* 255 */ SyscallDesc("inotify_rm_watch", unimplementedFunc), /* 255 */ SyscallDesc("inotify_rm_watch", unimplementedFunc),
/* 256 */ SyscallDesc("migrate_pages", unimplementedFunc), /* 256 */ SyscallDesc("migrate_pages", unimplementedFunc),
/* 257 */ SyscallDesc("openat", unimplementedFunc), /* 257 */ SyscallDesc("openat", openatFunc<X86Linux64>),
/* 258 */ SyscallDesc("mkdirat", unimplementedFunc), /* 258 */ SyscallDesc("mkdirat", unimplementedFunc),
/* 259 */ SyscallDesc("mknodat", unimplementedFunc), /* 259 */ SyscallDesc("mknodat", unimplementedFunc),
/* 260 */ SyscallDesc("fchownat", unimplementedFunc), /* 260 */ SyscallDesc("fchownat", unimplementedFunc),
@ -844,7 +844,7 @@ static SyscallDesc syscallDescs32[] = {
/* 292 */ SyscallDesc("inotify_add_watch", unimplementedFunc), /* 292 */ SyscallDesc("inotify_add_watch", unimplementedFunc),
/* 293 */ SyscallDesc("inotify_rm_watch", unimplementedFunc), /* 293 */ SyscallDesc("inotify_rm_watch", unimplementedFunc),
/* 294 */ SyscallDesc("migrate_pages", unimplementedFunc), /* 294 */ SyscallDesc("migrate_pages", unimplementedFunc),
/* 295 */ SyscallDesc("openat", unimplementedFunc), /* 295 */ SyscallDesc("openat", openatFunc<X86Linux32>),
/* 296 */ SyscallDesc("mkdirat", unimplementedFunc), /* 296 */ SyscallDesc("mkdirat", unimplementedFunc),
/* 297 */ SyscallDesc("mknodat", unimplementedFunc), /* 297 */ SyscallDesc("mknodat", unimplementedFunc),
/* 298 */ SyscallDesc("fchownat", unimplementedFunc), /* 298 */ SyscallDesc("fchownat", unimplementedFunc),

View file

@ -33,6 +33,7 @@
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include "cpu/base.hh"
#include "debug/SyscallVerbose.hh" #include "debug/SyscallVerbose.hh"
#include "sim/process.hh" #include "sim/process.hh"
#include "sim/system.hh" #include "sim/system.hh"
@ -41,21 +42,35 @@ int
Linux::openSpecialFile(std::string path, Process *process, Linux::openSpecialFile(std::string path, Process *process,
ThreadContext *tc) 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) { 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(); FILE *f = tmpfile();
int fd = fileno(f); int fd = fileno(f);
size_t ret M5_VAR_USED = fwrite(data.c_str(), 1, data.size(), f); size_t ret M5_VAR_USED = fwrite(data.c_str(), 1, data.size(), f);
assert(ret == data.size()); assert(ret == data.size());
rewind(f); rewind(f);
return fd; 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 std::string
@ -66,3 +81,9 @@ Linux::procMeminfo(Process *process, ThreadContext *tc)
process->system->freeMemSize() >> 10); 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());
}

View file

@ -226,6 +226,7 @@ class Linux : public OperatingSystem
static int openSpecialFile(std::string path, Process *process, static int openSpecialFile(std::string path, Process *process,
ThreadContext *tc); ThreadContext *tc);
static std::string procMeminfo(Process *process, ThreadContext *tc); static std::string procMeminfo(Process *process, ThreadContext *tc);
static std::string etcPasswd(Process *process, ThreadContext *tc);
// For futex system call // For futex system call
static const unsigned TGT_FUTEX_WAIT = 0; static const unsigned TGT_FUTEX_WAIT = 0;

View file

@ -114,6 +114,9 @@ class Solaris : public OperatingSystem
char machine[_SYS_NMLN]; //!< Machine type. char machine[_SYS_NMLN]; //!< Machine type.
} utsname; } utsname;
// for *at syscalls
static const int TGT_AT_FDCWD = -100;
}; // class Solaris }; // class Solaris
#endif // __SOLARIS_HH__ #endif // __SOLARIS_HH__

View file

@ -365,7 +365,6 @@ getcwdFunc(SyscallDesc *desc, int num, Process *p, ThreadContext *tc)
return (result == -1) ? -errno : result; return (result == -1) ? -errno : result;
} }
/// Target open() handler.
SyscallReturn SyscallReturn
readlinkFunc(SyscallDesc *desc, int callnum, Process *process, readlinkFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc) ThreadContext *tc)

View file

@ -613,80 +613,136 @@ ioctlFunc(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc)
} }
template <class OS> template <class OS>
static SyscallReturn SyscallReturn
openFunc(SyscallDesc *desc, int callnum, Process *process, openImpl(SyscallDesc *desc, int callnum, Process *p, ThreadContext *tc,
ThreadContext *tc, int index) bool isopenat)
{ {
std::string path; int index = 0;
int tgt_dirfd = -1;
if (!tc->getMemProxy().tryReadString(path, /**
process->getSyscallArg(tc, index))) * If using the openat variant, read in the target directory file
* descriptor from the simulated process.
*/
if (isopenat)
tgt_dirfd = p->getSyscallArg(tc, index);
/**
* 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; return -EFAULT;
int tgtFlags = process->getSyscallArg(tc, index); #ifdef __CYGWIN32__
int mode = process->getSyscallArg(tc, index); int host_flags = O_BINARY;
int hostFlags = 0; #else
int host_flags = 0;
// translate open flags #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++) { for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
if (tgtFlags & OS::openFlagTable[i].tgtFlag) { if (tgt_flags & OS::openFlagTable[i].tgtFlag) {
tgtFlags &= ~OS::openFlagTable[i].tgtFlag; tgt_flags &= ~OS::openFlagTable[i].tgtFlag;
hostFlags |= OS::openFlagTable[i].hostFlag; host_flags |= OS::openFlagTable[i].hostFlag;
} }
} }
if (tgt_flags) {
// any target flags left? warn("open%s: cannot decode flags 0x%x",
if (tgtFlags != 0) isopenat ? "at" : "", tgt_flags);
warn("Syscall: open: cannot decode flags 0x%x", tgtFlags); }
#ifdef __CYGWIN32__ #ifdef __CYGWIN32__
hostFlags |= O_BINARY; host_flags |= O_BINARY;
#endif #endif
// Adjust path for current working directory int mode = p->getSyscallArg(tc, index);
path = process->fullPath(path);
DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str()); /**
* If the simulated process called open or openat with AT_FDCWD specified,
if (startswith(path, "/dev/")) { * take the current working directory value which was passed into the
std::string filename = path.substr(strlen("/dev/")); * process class as a Python parameter and append the current path to
if (filename == "sysdev0") { * create a full path.
// This is a memory-mapped high-resolution timer device on Alpha. * Otherwise, openat with a valid target directory file descriptor has
// We don't support it, so just punt. * been called. If the path option, which was passed in as a parameter,
warn("Ignoring open(%s, ...)\n", path); * is not absolute, retrieve the directory file descriptor's path and
return -ENOENT; * 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.
EmulatedDriver *drv = process->findDriver(filename); */
if (drv) { if (!isopenat || (isopenat && tgt_dirfd == OS::TGT_AT_FDCWD)) {
// the driver's open method will allocate a fd from the path = p->fullPath(path);
// process if necessary. } else if (!startswith(path, "/")) {
return drv->open(process, tc, mode, hostFlags); std::shared_ptr<FDEntry> fdep = ((*p->fds)[tgt_dirfd]);
} auto ffdp = std::dynamic_pointer_cast<FileFDEntry>(fdep);
if (!ffdp)
// fall through here for pass through to host devices, such as return -EBADF;
// /dev/zero path.insert(0, ffdp->getFileName());
} }
int fd; /**
int local_errno; * Since this is an emulated environment, we create pseudo file
if (startswith(path, "/proc/") || startswith(path, "/system/") || * descriptors for device requests that have been registered with
startswith(path, "/platform/") || startswith(path, "/sys/")) { * the process class through Python; this allows us to create a file
// It's a proc/sys entry and requires special handling * descriptor for subsequent ioctl or mmap calls.
fd = OS::openSpecialFile(path, process, tc); */
local_errno = ENOENT; if (startswith(path, "/dev/")) {
} else { std::string filename = path.substr(strlen("/dev/"));
// open the file EmulatedDriver *drv = p->findDriver(filename);
fd = open(path.c_str(), hostFlags, mode); if (drv) {
local_errno = errno; 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
*/
}
if (fd == -1) /**
return -local_errno; * 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;
}
std::shared_ptr<FileFDEntry> ffdp = /**
std::make_shared<FileFDEntry>(fd, hostFlags, path.c_str(), false); * The file was opened successfully and needs to be recorded in the
return process->fds->allocFD(ffdp); * 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. /// Target open() handler.
@ -695,7 +751,7 @@ SyscallReturn
openFunc(SyscallDesc *desc, int callnum, Process *process, openFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc) ThreadContext *tc)
{ {
return openFunc<OS>(desc, callnum, process, tc, 0); return openImpl<OS>(desc, callnum, process, tc, false);
} }
/// Target openat() handler. /// Target openat() handler.
@ -704,11 +760,7 @@ SyscallReturn
openatFunc(SyscallDesc *desc, int callnum, Process *process, openatFunc(SyscallDesc *desc, int callnum, Process *process,
ThreadContext *tc) ThreadContext *tc)
{ {
int index = 0; return openImpl<OS>(desc, callnum, process, tc, true);
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 unlinkat() handler. /// Target unlinkat() handler.