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),
|
||||
/* 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),
|
||||
|
|
|
@ -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,22 +42,36 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Linux::procMeminfo(Process *process, ThreadContext *tc)
|
||||
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// any target flags left?
|
||||
if (tgtFlags != 0)
|
||||
warn("Syscall: open: cannot decode flags 0x%x", tgtFlags);
|
||||
|
||||
#ifdef __CYGWIN32__
|
||||
hostFlags |= O_BINARY;
|
||||
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 (tgt_flags & OS::openFlagTable[i].tgtFlag) {
|
||||
tgt_flags &= ~OS::openFlagTable[i].tgtFlag;
|
||||
host_flags |= OS::openFlagTable[i].hostFlag;
|
||||
}
|
||||
}
|
||||
if (tgt_flags) {
|
||||
warn("open%s: cannot decode flags 0x%x",
|
||||
isopenat ? "at" : "", tgt_flags);
|
||||
}
|
||||
#ifdef __CYGWIN32__
|
||||
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.
|
||||
|
|
Loading…
Reference in a new issue