X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=src%2Fsim%2Fsyscall_emul.hh;h=cc430b9492b1c881219f613077887e5bed856594;hb=da0c770943a26cd258fcd9bf608e1c78ff8e6be5;hp=eaec57ef53628d5e72a724b251a4c3a0d96d964b;hpb=6833ca7eedd351596bb1518620af7465f5172fcd;p=gem5.git diff --git a/src/sim/syscall_emul.hh b/src/sim/syscall_emul.hh index eaec57ef5..cc430b949 100644 --- a/src/sim/syscall_emul.hh +++ b/src/sim/syscall_emul.hh @@ -1,4 +1,16 @@ /* + * Copyright (c) 2012-2013 ARM Limited + * All rights reserved + * + * The license below extends only to copyright in the software and shall + * not be construed as granting a license to any other intellectual + * property including but not limited to intellectual property relating + * to a hardware implementation of the functionality of the software + * licensed hereunder. You may use the software subject to the license + * terms below provided that you ensure that this notice is replicated + * unmodified and in its entirety in all distributions of the software, + * modified or unmodified, in source code or in binary form. + * * Copyright (c) 2003-2005 The Regents of The University of Michigan * All rights reserved. * @@ -33,7 +45,8 @@ #define __SIM_SYSCALL_EMUL_HH__ #define NO_STAT64 (defined(__APPLE__) || defined(__OpenBSD__) || \ - defined(__FreeBSD__) || defined(__CYGWIN__)) + defined(__FreeBSD__) || defined(__CYGWIN__) || \ + defined(__NetBSD__)) /// /// @file syscall_emul.hh @@ -45,11 +58,11 @@ #include // for O_BINARY #endif #include -#include -#include -#include #include +#include +#include +#include #include #include "base/chunk_generator.hh" @@ -60,11 +73,14 @@ #include "config/the_isa.hh" #include "cpu/base.hh" #include "cpu/thread_context.hh" -#include "mem/translating_port.hh" +#include "debug/SyscallVerbose.hh" #include "mem/page_table.hh" #include "sim/byteswap.hh" -#include "sim/system.hh" +#include "sim/emul_driver.hh" #include "sim/process.hh" +#include "sim/syscall_emul_buf.hh" +#include "sim/syscallreturn.hh" +#include "sim/system.hh" /// /// System call descriptor. @@ -101,73 +117,6 @@ class SyscallDesc { }; -class BaseBufferArg { - - public: - - BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size) - { - bufPtr = new uint8_t[size]; - // clear out buffer: in case we only partially populate this, - // and then do a copyOut(), we want to make sure we don't - // introduce any random junk into the simulated address space - memset(bufPtr, 0, size); - } - - virtual ~BaseBufferArg() { delete [] bufPtr; } - - // - // copy data into simulator space (read from target memory) - // - virtual bool copyIn(TranslatingPort *memport) - { - memport->readBlob(addr, bufPtr, size); - return true; // no EFAULT detection for now - } - - // - // copy data out of simulator space (write to target memory) - // - virtual bool copyOut(TranslatingPort *memport) - { - memport->writeBlob(addr, bufPtr, size); - return true; // no EFAULT detection for now - } - - protected: - Addr addr; - int size; - uint8_t *bufPtr; -}; - - -class BufferArg : public BaseBufferArg -{ - public: - BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { } - void *bufferPtr() { return bufPtr; } -}; - -template -class TypedBufferArg : public BaseBufferArg -{ - public: - // user can optionally specify a specific number of bytes to - // allocate to deal with those structs that have variable-size - // arrays at the end - TypedBufferArg(Addr _addr, int _size = sizeof(T)) - : BaseBufferArg(_addr, _size) - { } - - // type case - operator T*() { return (T *)bufPtr; } - - // dereference operators - T &operator*() { return *((T *)bufPtr); } - T* operator->() { return (T *)bufPtr; } - T &operator[](int i) { return ((T *)bufPtr)[i]; } -}; - ////////////////////////////////////////////////////////////////////// // // The following emulation functions are generic enough that they @@ -187,6 +136,8 @@ SyscallReturn unimplementedFunc(SyscallDesc *desc, int num, /// trace flag is enabled. Return success to the target program. SyscallReturn ignoreFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc); +SyscallReturn ignoreWarnOnceFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); /// Target exit() handler: terminate current context. SyscallReturn exitFunc(SyscallDesc *desc, int num, @@ -236,11 +187,17 @@ SyscallReturn gethostnameFunc(SyscallDesc *desc, int num, SyscallReturn getcwdFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc); -/// Target unlink() handler. +/// Target readlink() handler. +SyscallReturn readlinkFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc, + int index = 0); SyscallReturn readlinkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc); /// Target unlink() handler. +SyscallReturn unlinkHelper(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc, + int index); SyscallReturn unlinkFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc); @@ -330,6 +287,95 @@ SyscallReturn getegidFunc(SyscallDesc *desc, int num, SyscallReturn cloneFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc); +/// Target access() handler +SyscallReturn accessFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc); +SyscallReturn accessFunc(SyscallDesc *desc, int num, + LiveProcess *p, ThreadContext *tc, + int index); + +/// Futex system call +/// Implemented by Daniel Sanchez +/// Used by printf's in multi-threaded apps +template +SyscallReturn +futexFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int index_uaddr = 0; + int index_op = 1; + int index_val = 2; + int index_timeout = 3; + + uint64_t uaddr = process->getSyscallArg(tc, index_uaddr); + int op = process->getSyscallArg(tc, index_op); + int val = process->getSyscallArg(tc, index_val); + uint64_t timeout = process->getSyscallArg(tc, index_timeout); + + std::map * > + &futex_map = tc->getSystemPtr()->futexMap; + + DPRINTF(SyscallVerbose, "In sys_futex: Address=%llx, op=%d, val=%d\n", + uaddr, op, val); + + op &= ~OS::TGT_FUTEX_PRIVATE_FLAG; + + if (op == OS::TGT_FUTEX_WAIT) { + if (timeout != 0) { + warn("sys_futex: FUTEX_WAIT with non-null timeout unimplemented;" + "we'll wait indefinitely"); + } + + uint8_t *buf = new uint8_t[sizeof(int)]; + tc->getMemProxy().readBlob((Addr)uaddr, buf, (int)sizeof(int)); + int mem_val = *((int *)buf); + delete buf; + + if(val != mem_val) { + DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, read: %d, " + "expected: %d\n", mem_val, val); + return -OS::TGT_EWOULDBLOCK; + } + + // Queue the thread context + std::list * tcWaitList; + if (futex_map.count(uaddr)) { + tcWaitList = futex_map.find(uaddr)->second; + } else { + tcWaitList = new std::list(); + futex_map.insert(std::pair< uint64_t, + std::list * >(uaddr, tcWaitList)); + } + tcWaitList->push_back(tc); + DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAIT, suspending calling " + "thread context\n"); + tc->suspend(); + return 0; + } else if (op == OS::TGT_FUTEX_WAKE){ + int wokenUp = 0; + std::list * tcWaitList; + if (futex_map.count(uaddr)) { + tcWaitList = futex_map.find(uaddr)->second; + while (tcWaitList->size() > 0 && wokenUp < val) { + tcWaitList->front()->activate(); + tcWaitList->pop_front(); + wokenUp++; + } + if(tcWaitList->empty()) { + futex_map.erase(uaddr); + delete tcWaitList; + } + } + DPRINTF(SyscallVerbose, "sys_futex: FUTEX_WAKE, activated %d waiting " + "thread contexts\n", wokenUp); + return wokenUp; + } else { + warn("sys_futex: op %d is not implemented, just returning...", op); + return 0; + } + +} + /// Pseudo Funcs - These functions use a different return convension, /// returning a second value in a register other than the normal return register @@ -363,7 +409,7 @@ template void getElapsedTime(T1 &sec, T2 &usec) { - int elapsed_usecs = curTick / SimClock::Int::us; + int elapsed_usecs = curTick() / SimClock::Int::us; sec = elapsed_usecs / one_million; usec = elapsed_usecs % one_million; } @@ -397,41 +443,41 @@ convertStatBuf(target_stat &tgt, host_stat *host, bool fakeTTY = false) tgt->st_dev = 0xA; else tgt->st_dev = host->st_dev; - tgt->st_dev = htog(tgt->st_dev); + tgt->st_dev = TheISA::htog(tgt->st_dev); tgt->st_ino = host->st_ino; - tgt->st_ino = htog(tgt->st_ino); + tgt->st_ino = TheISA::htog(tgt->st_ino); tgt->st_mode = host->st_mode; if (fakeTTY) { // Claim to be a character device tgt->st_mode &= ~S_IFMT; // Clear S_IFMT tgt->st_mode |= S_IFCHR; // Set S_IFCHR } - tgt->st_mode = htog(tgt->st_mode); + tgt->st_mode = TheISA::htog(tgt->st_mode); tgt->st_nlink = host->st_nlink; - tgt->st_nlink = htog(tgt->st_nlink); + tgt->st_nlink = TheISA::htog(tgt->st_nlink); tgt->st_uid = host->st_uid; - tgt->st_uid = htog(tgt->st_uid); + tgt->st_uid = TheISA::htog(tgt->st_uid); tgt->st_gid = host->st_gid; - tgt->st_gid = htog(tgt->st_gid); + tgt->st_gid = TheISA::htog(tgt->st_gid); if (fakeTTY) tgt->st_rdev = 0x880d; else tgt->st_rdev = host->st_rdev; - tgt->st_rdev = htog(tgt->st_rdev); + tgt->st_rdev = TheISA::htog(tgt->st_rdev); tgt->st_size = host->st_size; - tgt->st_size = htog(tgt->st_size); + tgt->st_size = TheISA::htog(tgt->st_size); tgt->st_atimeX = host->st_atime; - tgt->st_atimeX = htog(tgt->st_atimeX); + tgt->st_atimeX = TheISA::htog(tgt->st_atimeX); tgt->st_mtimeX = host->st_mtime; - tgt->st_mtimeX = htog(tgt->st_mtimeX); + tgt->st_mtimeX = TheISA::htog(tgt->st_mtimeX); tgt->st_ctimeX = host->st_ctime; - tgt->st_ctimeX = htog(tgt->st_ctimeX); + tgt->st_ctimeX = TheISA::htog(tgt->st_ctimeX); // Force the block size to be 8k. This helps to ensure buffered io works // consistently across different hosts. tgt->st_blksize = 0x2000; - tgt->st_blksize = htog(tgt->st_blksize); + tgt->st_blksize = TheISA::htog(tgt->st_blksize); tgt->st_blocks = host->st_blocks; - tgt->st_blocks = htog(tgt->st_blocks); + tgt->st_blocks = TheISA::htog(tgt->st_blocks); } // Same for stat64 @@ -445,11 +491,11 @@ convertStat64Buf(target_stat &tgt, host_stat64 *host, bool fakeTTY = false) convertStatBuf(tgt, host, fakeTTY); #if defined(STAT_HAVE_NSEC) tgt->st_atime_nsec = host->st_atime_nsec; - tgt->st_atime_nsec = htog(tgt->st_atime_nsec); + tgt->st_atime_nsec = TheISA::htog(tgt->st_atime_nsec); tgt->st_mtime_nsec = host->st_mtime_nsec; - tgt->st_mtime_nsec = htog(tgt->st_mtime_nsec); + tgt->st_mtime_nsec = TheISA::htog(tgt->st_mtime_nsec); tgt->st_ctime_nsec = host->st_ctime_nsec; - tgt->st_ctime_nsec = htog(tgt->st_ctime_nsec); + tgt->st_ctime_nsec = TheISA::htog(tgt->st_ctime_nsec); #else tgt->st_atime_nsec = 0; tgt->st_mtime_nsec = 0; @@ -460,7 +506,7 @@ convertStat64Buf(target_stat &tgt, host_stat64 *host, bool fakeTTY = false) //Here are a couple convenience functions template static void -copyOutStatBuf(TranslatingPort * mem, Addr addr, +copyOutStatBuf(SETranslatingPortProxy &mem, Addr addr, hst_stat *host, bool fakeTTY = false) { typedef TypedBufferArg tgt_stat_buf; @@ -471,7 +517,7 @@ copyOutStatBuf(TranslatingPort * mem, Addr addr, template static void -copyOutStat64Buf(TranslatingPort * mem, Addr addr, +copyOutStat64Buf(SETranslatingPortProxy &mem, Addr addr, hst_stat64 *host, bool fakeTTY = false) { typedef TypedBufferArg tgt_stat_buf; @@ -482,7 +528,8 @@ copyOutStat64Buf(TranslatingPort * mem, Addr addr, /// Target ioctl() handler. For the most part, programs call ioctl() /// only to find out if their stdout is a tty, to determine whether to -/// do line or block buffering. +/// do line or block buffering. We always claim that output fds are +/// not TTYs to provide repeatable results. template SyscallReturn ioctlFunc(SyscallDesc *desc, int callnum, LiveProcess *process, @@ -494,49 +541,37 @@ ioctlFunc(SyscallDesc *desc, int callnum, LiveProcess *process, DPRINTF(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req); - if (fd < 0 || process->sim_fd(fd) < 0) { + Process::FdMap *fdObj = process->sim_fd_obj(fd); + + if (fdObj == NULL) { // doesn't map to any simulator fd: not a valid target fd return -EBADF; } - switch (req) { - case OS::TIOCISATTY_: - case OS::TIOCGETP_: - case OS::TIOCSETP_: - case OS::TIOCSETN_: - case OS::TIOCSETC_: - case OS::TIOCGETC_: - case OS::TIOCGETS_: - case OS::TIOCGETA_: - case OS::TCSETAW_: - return -ENOTTY; + if (fdObj->driver != NULL) { + return fdObj->driver->ioctl(process, tc, req); + } - default: - fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n", - fd, req, tc->readPC()); + if (OS::isTtyReq(req)) { + return -ENOTTY; } + + warn("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ \n", + fd, req, tc->pcState()); + return -ENOTTY; } -/// Target open() handler. template -SyscallReturn +static SyscallReturn openFunc(SyscallDesc *desc, int callnum, LiveProcess *process, - ThreadContext *tc) + ThreadContext *tc, int index) { std::string path; - int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) return -EFAULT; - if (path == "/dev/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; - } - int tgtFlags = process->getSyscallArg(tc, index); int mode = process->getSyscallArg(tc, index); int hostFlags = 0; @@ -562,18 +597,105 @@ openFunc(SyscallDesc *desc, int callnum, LiveProcess *process, 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 != NULL) { + // 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 + } + int fd; - if (!path.compare(0, 6, "/proc/") || !path.compare(0, 8, "/system/") || - !path.compare(0, 10, "/platform/") || !path.compare(0, 5, "/sys/")) { - // It's a proc/sys entery and requires special handling + 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); - return (fd == -1) ? -1 : process->alloc_fd(fd,path.c_str(),hostFlags,mode, false); + local_errno = ENOENT; } else { // open the file fd = open(path.c_str(), hostFlags, mode); - return (fd == -1) ? -errno : process->alloc_fd(fd,path.c_str(),hostFlags,mode, false); + local_errno = errno; } + if (fd == -1) + return -local_errno; + + return process->alloc_fd(fd, path.c_str(), hostFlags, mode, false); +} + +/// Target open() handler. +template +SyscallReturn +openFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + return openFunc(desc, callnum, process, tc, 0); +} + +/// Target openat() handler. +template +SyscallReturn +openatFunc(SyscallDesc *desc, int callnum, LiveProcess *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(desc, callnum, process, tc, 1); +} + +/// Target unlinkat() handler. +template +SyscallReturn +unlinkatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int index = 0; + int dirfd = process->getSyscallArg(tc, index); + if (dirfd != OS::TGT_AT_FDCWD) + warn("unlinkat: first argument not AT_FDCWD; unlikely to work"); + + return unlinkHelper(desc, callnum, process, tc, 1); +} + +/// Target facessat() handler +template +SyscallReturn +faccessatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, + ThreadContext *tc) +{ + int index = 0; + int dirfd = process->getSyscallArg(tc, index); + if (dirfd != OS::TGT_AT_FDCWD) + warn("faccessat: first argument not AT_FDCWD; unlikely to work"); + return accessFunc(desc, callnum, process, tc, 1); +} + +/// Target readlinkat() handler +template +SyscallReturn +readlinkatFunc(SyscallDesc *desc, int callnum, LiveProcess *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 readlinkFunc(desc, callnum, process, tc, 1); } /// Target sysinfo() handler. @@ -590,7 +712,7 @@ sysinfoFunc(SyscallDesc *desc, int callnum, LiveProcess *process, sysinfo->uptime=seconds_since_epoch; sysinfo->totalram=process->system->memSize(); - sysinfo.copyOut(tc->getMemPort()); + sysinfo.copyOut(tc->getMemProxy()); return 0; } @@ -604,7 +726,7 @@ chmodFunc(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -664,40 +786,59 @@ mremapFunc(SyscallDesc *desc, int callnum, LiveProcess *process, ThreadContext * uint64_t old_length = process->getSyscallArg(tc, index); uint64_t new_length = process->getSyscallArg(tc, index); uint64_t flags = process->getSyscallArg(tc, index); + uint64_t provided_address = 0; + bool use_provided_address = flags & OS::TGT_MREMAP_FIXED; + + if (use_provided_address) + provided_address = process->getSyscallArg(tc, index); - if ((start % TheISA::VMPageSize != 0) || - (new_length % TheISA::VMPageSize != 0)) { + if ((start % TheISA::PageBytes != 0) || + (provided_address % TheISA::PageBytes != 0)) { warn("mremap failing: arguments not page aligned"); return -EINVAL; } + new_length = roundUp(new_length, TheISA::PageBytes); + if (new_length > old_length) { - if ((start + old_length) == process->mmap_end) { + if ((start + old_length) == process->mmap_end && + (!use_provided_address || provided_address == start)) { uint64_t diff = new_length - old_length; - process->pTable->allocate(process->mmap_end, diff); + process->allocateMem(process->mmap_end, diff); process->mmap_end += diff; return start; } else { - // sys/mman.h defined MREMAP_MAYMOVE - if (!(flags & 1)) { + if (!use_provided_address && !(flags & OS::TGT_MREMAP_MAYMOVE)) { warn("can't remap here and MREMAP_MAYMOVE flag not set\n"); return -ENOMEM; } else { - process->pTable->remap(start, old_length, process->mmap_end); - warn("mremapping to totally new vaddr %08p-%08p, adding %d\n", - process->mmap_end, process->mmap_end + new_length, new_length); - start = process->mmap_end; + uint64_t new_start = use_provided_address ? + provided_address : process->mmap_end; + process->pTable->remap(start, old_length, new_start); + warn("mremapping to new vaddr %08p-%08p, adding %d\n", + new_start, new_start + new_length, + new_length - old_length); // add on the remaining unallocated pages - process->pTable->allocate(start + old_length, new_length - old_length); - process->mmap_end += new_length; - warn("returning %08p as start\n", start); - return start; + process->allocateMem(new_start + old_length, + new_length - old_length, + use_provided_address /* clobber */); + if (!use_provided_address) + process->mmap_end += new_length; + if (use_provided_address && + new_start + new_length > process->mmap_end) { + // something fishy going on here, at least notify the user + // @todo: increase mmap_end? + warn("mmap region limit exceeded with MREMAP_FIXED\n"); + } + warn("returning %08p as start\n", new_start); + return new_start; } } } else { - process->pTable->deallocate(start + new_length, old_length - - new_length); - return start; + if (use_provided_address && provided_address != start) + process->pTable->remap(start, new_length, provided_address); + process->pTable->unmap(start + new_length, old_length - new_length); + return use_provided_address ? provided_address : start; } } @@ -710,7 +851,7 @@ statFunc(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -725,7 +866,7 @@ statFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -740,7 +881,7 @@ stat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) return -EFAULT; Addr bufPtr = process->getSyscallArg(tc, index); @@ -759,7 +900,44 @@ stat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf); + + return 0; +} + + +/// Target fstatat64() handler. +template +SyscallReturn +fstatat64Func(SyscallDesc *desc, int callnum, LiveProcess *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"); + + std::string path; + if (!tc->getMemProxy().tryReadString(path, + process->getSyscallArg(tc, index))) + return -EFAULT; + Addr bufPtr = process->getSyscallArg(tc, index); + + // Adjust path for current working directory + path = process->fullPath(path); + +#if NO_STAT64 + struct stat hostBuf; + int result = stat(path.c_str(), &hostBuf); +#else + struct stat64 hostBuf; + int result = stat64(path.c_str(), &hostBuf); +#endif + + if (result < 0) + return -errno; + + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -790,7 +968,7 @@ fstat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf, (fd == 1)); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf, (fd == 1)); return 0; } @@ -805,7 +983,7 @@ lstatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -820,7 +998,7 @@ lstatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -834,7 +1012,7 @@ lstat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -854,7 +1032,7 @@ lstat64Func(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStat64Buf(tc->getMemPort(), bufPtr, &hostBuf); + copyOutStat64Buf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -880,7 +1058,7 @@ fstatFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - copyOutStatBuf(tc->getMemPort(), bufPtr, &hostBuf, (fd == 1)); + copyOutStatBuf(tc->getMemProxy(), bufPtr, &hostBuf, (fd == 1)); return 0; } @@ -895,7 +1073,7 @@ statfsFunc(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } @@ -910,7 +1088,7 @@ statfsFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(tc->getMemPort(), bufPtr, &hostBuf); + OS::copyOutStatfsBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -935,7 +1113,7 @@ fstatfsFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - OS::copyOutStatfsBuf(tc->getMemPort(), bufPtr, &hostBuf); + OS::copyOutStatfsBuf(tc->getMemProxy(), bufPtr, &hostBuf); return 0; } @@ -954,19 +1132,19 @@ writevFunc(SyscallDesc *desc, int callnum, LiveProcess *process, return -EBADF; } - TranslatingPort *p = tc->getMemPort(); + SETranslatingPortProxy &p = tc->getMemProxy(); uint64_t tiov_base = process->getSyscallArg(tc, index); size_t count = process->getSyscallArg(tc, index); struct iovec hiov[count]; for (size_t i = 0; i < count; ++i) { typename OS::tgt_iovec tiov; - p->readBlob(tiov_base + i*sizeof(typename OS::tgt_iovec), - (uint8_t*)&tiov, sizeof(typename OS::tgt_iovec)); - hiov[i].iov_len = gtoh(tiov.iov_len); + p.readBlob(tiov_base + i*sizeof(typename OS::tgt_iovec), + (uint8_t*)&tiov, sizeof(typename OS::tgt_iovec)); + hiov[i].iov_len = TheISA::gtoh(tiov.iov_len); hiov[i].iov_base = new char [hiov[i].iov_len]; - p->readBlob(gtoh(tiov.iov_base), (uint8_t *)hiov[i].iov_base, - hiov[i].iov_len); + p.readBlob(TheISA::gtoh(tiov.iov_base), (uint8_t *)hiov[i].iov_base, + hiov[i].iov_len); } int result = writev(process->sim_fd(fd), hiov, count); @@ -977,7 +1155,7 @@ writevFunc(SyscallDesc *desc, int callnum, LiveProcess *process, if (result < 0) return -errno; - return 0; + return result; } @@ -986,13 +1164,8 @@ writevFunc(SyscallDesc *desc, int callnum, LiveProcess *process, /// We don't really handle mmap(). If the target is mmaping an /// anonymous region or /dev/zero, we can get away with doing basically /// nothing (since memory is initialized to zero and the simulator -/// doesn't really check addresses anyway). Always print a warning, -/// since this could be seriously broken if we're not mapping -/// /dev/zero. -// -/// Someday we should explicitly check for /dev/zero in open, flag the -/// file descriptor, and fail (or implement!) a non-anonymous mmap to -/// anything else. +/// doesn't really check addresses anyway). +/// template SyscallReturn mmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) @@ -1002,37 +1175,75 @@ mmapFunc(SyscallDesc *desc, int num, LiveProcess *p, ThreadContext *tc) uint64_t length = p->getSyscallArg(tc, index); index++; // int prot = p->getSyscallArg(tc, index); int flags = p->getSyscallArg(tc, index); - int fd = p->sim_fd(p->getSyscallArg(tc, index)); - // int offset = p->getSyscallArg(tc, index); + int tgt_fd = p->getSyscallArg(tc, index); + int offset = p->getSyscallArg(tc, index); + + if (length > 0x100000000ULL) + warn("mmap length argument %#x is unreasonably large.\n", length); + + if (!(flags & OS::TGT_MAP_ANONYMOUS)) { + Process::FdMap *fd_map = p->sim_fd_obj(tgt_fd); + if (!fd_map || fd_map->fd < 0) { + warn("mmap failing: target fd %d is not valid\n", tgt_fd); + return -EBADF; + } + + if (fd_map->filename != "/dev/zero") { + // This is very likely broken, but leave a warning here + // (rather than panic) in case /dev/zero is known by + // another name on some platform + warn("allowing mmap of file %s; mmap not supported on files" + " other than /dev/zero\n", fd_map->filename); + } + } + length = roundUp(length, TheISA::PageBytes); - if ((start % TheISA::VMPageSize) != 0 || - (length % TheISA::VMPageSize) != 0) { + if ((start % TheISA::PageBytes) != 0 || + (offset % TheISA::PageBytes) != 0) { warn("mmap failing: arguments not page-aligned: " - "start 0x%x length 0x%x", - start, length); + "start 0x%x offset 0x%x", + start, offset); return -EINVAL; } - if (start != 0) { - warn("mmap: ignoring suggested map address 0x%x, using 0x%x", - start, p->mmap_end); + // are we ok with clobbering existing mappings? only set this to + // true if the user has been warned. + bool clobber = false; + + // try to use the caller-provided address if there is one + bool use_provided_address = (start != 0); + + if (use_provided_address) { + // check to see if the desired address is already in use + if (!p->pTable->isUnmapped(start, length)) { + // there are existing mappings in the desired range + // whether we clobber them or not depends on whether the caller + // specified MAP_FIXED + if (flags & OS::TGT_MAP_FIXED) { + // MAP_FIXED specified: map attempt fails + return -EINVAL; + } else { + // MAP_FIXED not specified: ignore suggested start address + warn("mmap: ignoring suggested map address 0x%x\n", start); + use_provided_address = false; + } + } } - // pick next address from our "mmap region" - if (OS::mmapGrowsDown()) { - start = p->mmap_end - length; - p->mmap_end = start; - } else { - start = p->mmap_end; - p->mmap_end += length; + if (!use_provided_address) { + // no address provided, or provided address unusable: + // pick next address from our "mmap region" + if (OS::mmapGrowsDown()) { + start = p->mmap_end - length; + p->mmap_end = start; + } else { + start = p->mmap_end; + p->mmap_end += length; + } } - p->pTable->allocate(start, length); - if (!(flags & OS::TGT_MAP_ANONYMOUS)) { - warn("allowing mmap of file @ fd %d. " - "This will break if not /dev/zero.", fd); - } + p->allocateMem(start, length, clobber); return start; } @@ -1051,15 +1262,15 @@ getrlimitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, case OS::TGT_RLIMIT_STACK: // max stack size in bytes: make up a number (8MB for now) rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024; - rlp->rlim_cur = htog(rlp->rlim_cur); - rlp->rlim_max = htog(rlp->rlim_max); + rlp->rlim_cur = TheISA::htog(rlp->rlim_cur); + rlp->rlim_max = TheISA::htog(rlp->rlim_max); break; case OS::TGT_RLIMIT_DATA: // max data segment size in bytes: make up a number rlp->rlim_cur = rlp->rlim_max = 256 * 1024 * 1024; - rlp->rlim_cur = htog(rlp->rlim_cur); - rlp->rlim_max = htog(rlp->rlim_max); + rlp->rlim_cur = TheISA::htog(rlp->rlim_cur); + rlp->rlim_max = TheISA::htog(rlp->rlim_max); break; default: @@ -1069,7 +1280,7 @@ getrlimitFunc(SyscallDesc *desc, int callnum, LiveProcess *process, break; } - rlp.copyOut(tc->getMemPort()); + rlp.copyOut(tc->getMemProxy()); return 0; } @@ -1087,7 +1298,7 @@ gettimeofdayFunc(SyscallDesc *desc, int callnum, LiveProcess *process, tp->tv_sec = TheISA::htog(tp->tv_sec); tp->tv_usec = TheISA::htog(tp->tv_usec); - tp.copyOut(tc->getMemPort()); + tp.copyOut(tc->getMemProxy()); return 0; } @@ -1102,20 +1313,20 @@ utimesFunc(SyscallDesc *desc, int callnum, LiveProcess *process, std::string path; int index = 0; - if (!tc->getMemPort()->tryReadString(path, + if (!tc->getMemProxy().tryReadString(path, process->getSyscallArg(tc, index))) { return -EFAULT; } TypedBufferArg tp(process->getSyscallArg(tc, index)); - tp.copyIn(tc->getMemPort()); + tp.copyIn(tc->getMemProxy()); struct timeval hostTimeval[2]; for (int i = 0; i < 2; ++i) { - hostTimeval[i].tv_sec = gtoh((*tp)[i].tv_sec); - hostTimeval[i].tv_usec = gtoh((*tp)[i].tv_usec); + hostTimeval[i].tv_sec = TheISA::gtoh((*tp)[i].tv_sec); + hostTimeval[i].tv_usec = TheISA::gtoh((*tp)[i].tv_usec); } // Adjust path for current working directory @@ -1160,8 +1371,8 @@ getrusageFunc(SyscallDesc *desc, int callnum, LiveProcess *process, switch (who) { case OS::TGT_RUSAGE_SELF: getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec); - rup->ru_utime.tv_sec = htog(rup->ru_utime.tv_sec); - rup->ru_utime.tv_usec = htog(rup->ru_utime.tv_usec); + rup->ru_utime.tv_sec = TheISA::htog(rup->ru_utime.tv_sec); + rup->ru_utime.tv_usec = TheISA::htog(rup->ru_utime.tv_usec); break; case OS::TGT_RUSAGE_CHILDREN: @@ -1175,7 +1386,7 @@ getrusageFunc(SyscallDesc *desc, int callnum, LiveProcess *process, who); } - rup.copyOut(tc->getMemPort()); + rup.copyOut(tc->getMemProxy()); return 0; } @@ -1190,17 +1401,17 @@ timesFunc(SyscallDesc *desc, int callnum, LiveProcess *process, TypedBufferArg bufp(process->getSyscallArg(tc, index)); // Fill in the time structure (in clocks) - int64_t clocks = curTick * OS::M5_SC_CLK_TCK / SimClock::Int::s; + int64_t clocks = curTick() * OS::M5_SC_CLK_TCK / SimClock::Int::s; bufp->tms_utime = clocks; bufp->tms_stime = 0; bufp->tms_cutime = 0; bufp->tms_cstime = 0; // Convert to host endianness - bufp->tms_utime = htog(bufp->tms_utime); + bufp->tms_utime = TheISA::htog(bufp->tms_utime); // Write back - bufp.copyOut(tc->getMemPort()); + bufp.copyOut(tc->getMemProxy()); // Return clock ticks since system boot return clocks; @@ -1220,9 +1431,9 @@ timeFunc(SyscallDesc *desc, int callnum, LiveProcess *process, Addr taddr = (Addr)process->getSyscallArg(tc, index); if(taddr != 0) { typename OS::time_t t = sec; - t = htog(t); - TranslatingPort *p = tc->getMemPort(); - p->writeBlob(taddr, (uint8_t*)&t, (int)sizeof(typename OS::time_t)); + t = TheISA::htog(t); + SETranslatingPortProxy &p = tc->getMemProxy(); + p.writeBlob(taddr, (uint8_t*)&t, (int)sizeof(typename OS::time_t)); } return sec; }