syscall_emul.hh revision 1609
1/*
2 * Copyright (c) 2003-2005 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef __SIM_SYSCALL_EMUL_HH__
30#define __SIM_SYSCALL_EMUL_HH__
31
32///
33/// @file syscall_emul.hh
34///
35/// This file defines objects used to emulate syscalls from the target
36/// application on the host machine.
37
38#include <errno.h>
39#include <string>
40
41#include "base/intmath.hh"	// for RoundUp
42#include "mem/functional_mem/functional_memory.hh"
43#include "targetarch/isa_traits.hh"	// for Addr
44
45#include "base/trace.hh"
46#include "cpu/exec_context.hh"
47#include "sim/process.hh"
48
49///
50/// System call descriptor.
51///
52class SyscallDesc {
53
54  public:
55
56    /// Typedef for target syscall handler functions.
57    typedef SyscallReturn (*FuncPtr)(SyscallDesc *, int num,
58                           Process *, ExecContext *);
59
60    const char *name;	//!< Syscall name (e.g., "open").
61    FuncPtr funcPtr;	//!< Pointer to emulation function.
62    int flags;		//!< Flags (see Flags enum).
63
64    /// Flag values for controlling syscall behavior.
65    enum Flags {
66        /// Don't set return regs according to funcPtr return value.
67        /// Used for syscalls with non-standard return conventions
68        /// that explicitly set the ExecContext regs (e.g.,
69        /// sigreturn).
70        SuppressReturnValue = 1
71    };
72
73    /// Constructor.
74    SyscallDesc(const char *_name, FuncPtr _funcPtr, int _flags = 0)
75        : name(_name), funcPtr(_funcPtr), flags(_flags)
76    {
77    }
78
79    /// Emulate the syscall.  Public interface for calling through funcPtr.
80    void doSyscall(int callnum, Process *proc, ExecContext *xc);
81};
82
83
84class BaseBufferArg {
85
86  public:
87
88    BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size)
89    {
90        bufPtr = new uint8_t[size];
91        // clear out buffer: in case we only partially populate this,
92        // and then do a copyOut(), we want to make sure we don't
93        // introduce any random junk into the simulated address space
94        memset(bufPtr, 0, size);
95    }
96
97    virtual ~BaseBufferArg() { delete [] bufPtr; }
98
99    //
100    // copy data into simulator space (read from target memory)
101    //
102    virtual bool copyIn(FunctionalMemory *mem)
103    {
104        mem->access(Read, addr, bufPtr, size);
105        return true;	// no EFAULT detection for now
106    }
107
108    //
109    // copy data out of simulator space (write to target memory)
110    //
111    virtual bool copyOut(FunctionalMemory *mem)
112    {
113        mem->access(Write, addr, bufPtr, size);
114        return true;	// no EFAULT detection for now
115    }
116
117  protected:
118    Addr addr;
119    int size;
120    uint8_t *bufPtr;
121};
122
123
124class BufferArg : public BaseBufferArg
125{
126  public:
127    BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { }
128    void *bufferPtr()	{ return bufPtr; }
129};
130
131template <class T>
132class TypedBufferArg : public BaseBufferArg
133{
134  public:
135    // user can optionally specify a specific number of bytes to
136    // allocate to deal with those structs that have variable-size
137    // arrays at the end
138    TypedBufferArg(Addr _addr, int _size = sizeof(T))
139        : BaseBufferArg(_addr, _size)
140    { }
141
142    // type case
143    operator T*() { return (T *)bufPtr; }
144
145    // dereference operators
146    T &operator*()	 { return *((T *)bufPtr); }
147    T* operator->()	 { return (T *)bufPtr; }
148    T &operator[](int i) { return ((T *)bufPtr)[i]; }
149};
150
151//////////////////////////////////////////////////////////////////////
152//
153// The following emulation functions are generic enough that they
154// don't need to be recompiled for different emulated OS's.  They are
155// defined in sim/syscall_emul.cc.
156//
157//////////////////////////////////////////////////////////////////////
158
159
160/// Handler for unimplemented syscalls that we haven't thought about.
161SyscallReturn unimplementedFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
162
163/// Handler for unimplemented syscalls that we never intend to
164/// implement (signal handling, etc.) and should not affect the correct
165/// behavior of the program.  Print a warning only if the appropriate
166/// trace flag is enabled.  Return success to the target program.
167SyscallReturn ignoreFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
168
169/// Target exit() handler: terminate simulation.
170SyscallReturn exitFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
171
172/// Target getpagesize() handler.
173SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
174
175/// Target obreak() handler: set brk address.
176SyscallReturn obreakFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
177
178/// Target close() handler.
179SyscallReturn closeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
180
181/// Target read() handler.
182SyscallReturn readFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
183
184/// Target write() handler.
185SyscallReturn writeFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
186
187/// Target lseek() handler.
188SyscallReturn lseekFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
189
190/// Target munmap() handler.
191SyscallReturn munmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
192
193/// Target gethostname() handler.
194SyscallReturn gethostnameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
195
196/// Target unlink() handler.
197SyscallReturn unlinkFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
198
199/// Target rename() handler.
200SyscallReturn renameFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc);
201
202/// This struct is used to build an target-OS-dependent table that
203/// maps the target's open() flags to the host open() flags.
204struct OpenFlagTransTable {
205    int tgtFlag;	//!< Target system flag value.
206    int hostFlag;	//!< Corresponding host system flag value.
207};
208
209
210
211/// A readable name for 1,000,000, for converting microseconds to seconds.
212const int one_million = 1000000;
213
214/// Approximate seconds since the epoch (1/1/1970).  About a billion,
215/// by my reckoning.  We want to keep this a constant (not use the
216/// real-world time) to keep simulations repeatable.
217const unsigned seconds_since_epoch = 1000000000;
218
219/// Helper function to convert current elapsed time to seconds and
220/// microseconds.
221template <class T1, class T2>
222void
223getElapsedTime(T1 &sec, T2 &usec)
224{
225    int elapsed_usecs = curTick / Clock::Int::us;
226    sec = elapsed_usecs / one_million;
227    usec = elapsed_usecs % one_million;
228}
229
230//////////////////////////////////////////////////////////////////////
231//
232// The following emulation functions are generic, but need to be
233// templated to account for differences in types, constants, etc.
234//
235//////////////////////////////////////////////////////////////////////
236
237/// Target ioctl() handler.  For the most part, programs call ioctl()
238/// only to find out if their stdout is a tty, to determine whether to
239/// do line or block buffering.
240template <class OS>
241SyscallReturn
242ioctlFunc(SyscallDesc *desc, int callnum, Process *process,
243          ExecContext *xc)
244{
245    int fd = xc->getSyscallArg(0);
246    unsigned req = xc->getSyscallArg(1);
247
248    // DPRINTFR(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req);
249
250    if (fd < 0 || process->sim_fd(fd) < 0) {
251        // doesn't map to any simulator fd: not a valid target fd
252        return -EBADF;
253    }
254
255    switch (req) {
256      case OS::TIOCISATTY:
257      case OS::TIOCGETP:
258      case OS::TIOCSETP:
259      case OS::TIOCSETN:
260      case OS::TIOCSETC:
261      case OS::TIOCGETC:
262      case OS::TIOCGETS:
263      case OS::TIOCGETA:
264        return -ENOTTY;
265
266      default:
267        fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n", fd, req, xc->readPC());
268    }
269}
270
271/// Target open() handler.
272template <class OS>
273SyscallReturn
274openFunc(SyscallDesc *desc, int callnum, Process *process,
275         ExecContext *xc)
276{
277    std::string path;
278
279    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
280        return -EFAULT;
281
282    if (path == "/dev/sysdev0") {
283        // This is a memory-mapped high-resolution timer device on Alpha.
284        // We don't support it, so just punt.
285        DCOUT(SyscallWarnings) << "Ignoring open(" << path << ", ...)" << std::endl;
286        return -ENOENT;
287    }
288
289    int tgtFlags = xc->getSyscallArg(1);
290    int mode = xc->getSyscallArg(2);
291    int hostFlags = 0;
292
293    // translate open flags
294    for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
295        if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
296            tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
297            hostFlags |= OS::openFlagTable[i].hostFlag;
298        }
299    }
300
301    // any target flags left?
302    if (tgtFlags != 0)
303        std::cerr << "Syscall: open: cannot decode flags: " <<  tgtFlags << std::endl;
304
305#ifdef __CYGWIN32__
306    hostFlags |= O_BINARY;
307#endif
308
309    // open the file
310    int fd = open(path.c_str(), hostFlags, mode);
311
312    return (fd == -1) ? -errno : process->open_fd(fd);
313}
314
315
316/// Target stat() handler.
317template <class OS>
318SyscallReturn
319statFunc(SyscallDesc *desc, int callnum, Process *process,
320         ExecContext *xc)
321{
322    std::string path;
323
324    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
325        return -EFAULT;
326
327    struct stat hostBuf;
328    int result = stat(path.c_str(), &hostBuf);
329
330    if (result < 0)
331        return errno;
332
333    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
334
335    return 0;
336}
337
338
339/// Target lstat() handler.
340template <class OS>
341SyscallReturn
342lstatFunc(SyscallDesc *desc, int callnum, Process *process,
343          ExecContext *xc)
344{
345    std::string path;
346
347    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
348        return -EFAULT;
349
350    struct stat hostBuf;
351    int result = lstat(path.c_str(), &hostBuf);
352
353    if (result < 0)
354        return -errno;
355
356    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
357
358    return 0;
359}
360
361/// Target fstat() handler.
362template <class OS>
363SyscallReturn
364fstatFunc(SyscallDesc *desc, int callnum, Process *process,
365          ExecContext *xc)
366{
367    int fd = process->sim_fd(xc->getSyscallArg(0));
368
369    // DPRINTFR(SyscallVerbose, "fstat(%d, ...)\n", fd);
370
371    if (fd < 0)
372        return -EBADF;
373
374    struct stat hostBuf;
375    int result = fstat(fd, &hostBuf);
376
377    if (result < 0)
378        return -errno;
379
380    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
381
382    return 0;
383}
384
385
386/// Target mmap() handler.
387///
388/// We don't really handle mmap().  If the target is mmaping an
389/// anonymous region or /dev/zero, we can get away with doing basically
390/// nothing (since memory is initialized to zero and the simulator
391/// doesn't really check addresses anyway).  Always print a warning,
392/// since this could be seriously broken if we're not mapping
393/// /dev/zero.
394//
395/// Someday we should explicitly check for /dev/zero in open, flag the
396/// file descriptor, and fail (or implement!) a non-anonymous mmap to
397/// anything else.
398template <class OS>
399SyscallReturn
400mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc)
401{
402    Addr start = xc->getSyscallArg(0);
403    uint64_t length = xc->getSyscallArg(1);
404    // int prot = xc->getSyscallArg(2);
405    int flags = xc->getSyscallArg(3);
406    // int fd = p->sim_fd(xc->getSyscallArg(4));
407    // int offset = xc->getSyscallArg(5);
408
409    if (start == 0) {
410        // user didn't give an address... pick one from our "mmap region"
411        start = p->mmap_end;
412        p->mmap_end += RoundUp<Addr>(length, VMPageSize);
413        if (p->nxm_start != 0) {
414            //If we have an nxm space, make sure we haven't colided
415            assert(p->mmap_end < p->nxm_start);
416        }
417    }
418
419    if (!(flags & OS::TGT_MAP_ANONYMOUS)) {
420        DPRINTF(SyscallWarnings, "Warning: allowing mmap of file @ fd %d.  "
421                "This will break if not /dev/zero.", xc->getSyscallArg(4));
422    }
423
424    return start;
425}
426
427/// Target getrlimit() handler.
428template <class OS>
429SyscallReturn
430getrlimitFunc(SyscallDesc *desc, int callnum, Process *process,
431              ExecContext *xc)
432{
433    unsigned resource = xc->getSyscallArg(0);
434    TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1));
435
436    switch (resource) {
437      case OS::RLIMIT_STACK:
438        // max stack size in bytes: make up a number (2MB for now)
439        rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
440        break;
441
442      default:
443        std::cerr << "getrlimitFunc: unimplemented resource " << resource << std::endl;
444        abort();
445        break;
446    }
447
448    rlp.copyOut(xc->mem);
449    return 0;
450}
451
452/// Target gettimeofday() handler.
453template <class OS>
454SyscallReturn
455gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process,
456                 ExecContext *xc)
457{
458    TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0));
459
460    getElapsedTime(tp->tv_sec, tp->tv_usec);
461    tp->tv_sec += seconds_since_epoch;
462
463    tp.copyOut(xc->mem);
464
465    return 0;
466}
467
468
469/// Target getrusage() function.
470template <class OS>
471SyscallReturn
472getrusageFunc(SyscallDesc *desc, int callnum, Process *process,
473              ExecContext *xc)
474{
475    int who = xc->getSyscallArg(0);	// THREAD, SELF, or CHILDREN
476    TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1));
477
478    if (who != OS::RUSAGE_SELF) {
479        // don't really handle THREAD or CHILDREN, but just warn and
480        // plow ahead
481        DCOUT(SyscallWarnings)
482            << "Warning: getrusage() only supports RUSAGE_SELF."
483            << "  Parameter " << who << " ignored." << std::endl;
484    }
485
486    getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec);
487    rup->ru_stime.tv_sec = 0;
488    rup->ru_stime.tv_usec = 0;
489    rup->ru_maxrss = 0;
490    rup->ru_ixrss = 0;
491    rup->ru_idrss = 0;
492    rup->ru_isrss = 0;
493    rup->ru_minflt = 0;
494    rup->ru_majflt = 0;
495    rup->ru_nswap = 0;
496    rup->ru_inblock = 0;
497    rup->ru_oublock = 0;
498    rup->ru_msgsnd = 0;
499    rup->ru_msgrcv = 0;
500    rup->ru_nsignals = 0;
501    rup->ru_nvcsw = 0;
502    rup->ru_nivcsw = 0;
503
504    rup.copyOut(xc->mem);
505
506    return 0;
507}
508
509#endif // __SIM_SYSCALL_EMUL_HH__
510