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:
parent
e5fe2b82b7
commit
acce7b0dc0
6 changed files with 151 additions and 75 deletions
|
@ -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),
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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__
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue