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),
/* 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),

View 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());
}

View 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;

View 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__

View 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)

View 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)))
/**
* 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;
int tgtFlags = process->getSyscallArg(tc, index);
int mode = process->getSyscallArg(tc, index);
int hostFlags = 0;
// 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 (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);
if (drv) {
// the driver's open method will allocate a fd from the
// process if necessary.
return drv->open(process, tc, mode, hostFlags);
}
// fall through here for pass through to host devices, such as
// /dev/zero
/**
* 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());
}
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;
}
/**
* 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/"));
EmulatedDriver *drv = p->findDriver(filename);
if (drv) {
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);
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.