syscall_emul.hh revision 1970
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#ifdef __CYGWIN32__
41#include <sys/fcntl.h>	// for O_BINARY
42#endif
43
44#include "base/intmath.hh"	// for RoundUp
45#include "mem/functional/functional.hh"
46#include "targetarch/isa_traits.hh"	// for Addr
47
48#include "base/trace.hh"
49#include "cpu/exec_context.hh"
50#include "sim/process.hh"
51
52///
53/// System call descriptor.
54///
55class SyscallDesc {
56
57  public:
58
59    /// Typedef for target syscall handler functions.
60    typedef SyscallReturn (*FuncPtr)(SyscallDesc *, int num,
61                           Process *, ExecContext *);
62
63    const char *name;	//!< Syscall name (e.g., "open").
64    FuncPtr funcPtr;	//!< Pointer to emulation function.
65    int flags;		//!< Flags (see Flags enum).
66
67    /// Flag values for controlling syscall behavior.
68    enum Flags {
69        /// Don't set return regs according to funcPtr return value.
70        /// Used for syscalls with non-standard return conventions
71        /// that explicitly set the ExecContext regs (e.g.,
72        /// sigreturn).
73        SuppressReturnValue = 1
74    };
75
76    /// Constructor.
77    SyscallDesc(const char *_name, FuncPtr _funcPtr, int _flags = 0)
78        : name(_name), funcPtr(_funcPtr), flags(_flags)
79    {
80    }
81
82    /// Emulate the syscall.  Public interface for calling through funcPtr.
83    void doSyscall(int callnum, Process *proc, ExecContext *xc);
84};
85
86
87class BaseBufferArg {
88
89  public:
90
91    BaseBufferArg(Addr _addr, int _size) : addr(_addr), size(_size)
92    {
93        bufPtr = new uint8_t[size];
94        // clear out buffer: in case we only partially populate this,
95        // and then do a copyOut(), we want to make sure we don't
96        // introduce any random junk into the simulated address space
97        memset(bufPtr, 0, size);
98    }
99
100    virtual ~BaseBufferArg() { delete [] bufPtr; }
101
102    //
103    // copy data into simulator space (read from target memory)
104    //
105    virtual bool copyIn(FunctionalMemory *mem)
106    {
107        mem->access(Read, addr, bufPtr, size);
108        return true;	// no EFAULT detection for now
109    }
110
111    //
112    // copy data out of simulator space (write to target memory)
113    //
114    virtual bool copyOut(FunctionalMemory *mem)
115    {
116        mem->access(Write, addr, bufPtr, size);
117        return true;	// no EFAULT detection for now
118    }
119
120  protected:
121    Addr addr;
122    int size;
123    uint8_t *bufPtr;
124};
125
126
127class BufferArg : public BaseBufferArg
128{
129  public:
130    BufferArg(Addr _addr, int _size) : BaseBufferArg(_addr, _size) { }
131    void *bufferPtr()	{ return bufPtr; }
132};
133
134template <class T>
135class TypedBufferArg : public BaseBufferArg
136{
137  public:
138    // user can optionally specify a specific number of bytes to
139    // allocate to deal with those structs that have variable-size
140    // arrays at the end
141    TypedBufferArg(Addr _addr, int _size = sizeof(T))
142        : BaseBufferArg(_addr, _size)
143    { }
144
145    // type case
146    operator T*() { return (T *)bufPtr; }
147
148    // dereference operators
149    T &operator*()	 { return *((T *)bufPtr); }
150    T* operator->()	 { return (T *)bufPtr; }
151    T &operator[](int i) { return ((T *)bufPtr)[i]; }
152};
153
154//////////////////////////////////////////////////////////////////////
155//
156// The following emulation functions are generic enough that they
157// don't need to be recompiled for different emulated OS's.  They are
158// defined in sim/syscall_emul.cc.
159//
160//////////////////////////////////////////////////////////////////////
161
162
163/// Handler for unimplemented syscalls that we haven't thought about.
164SyscallReturn unimplementedFunc(SyscallDesc *desc, int num,
165                                Process *p, ExecContext *xc);
166
167/// Handler for unimplemented syscalls that we never intend to
168/// implement (signal handling, etc.) and should not affect the correct
169/// behavior of the program.  Print a warning only if the appropriate
170/// trace flag is enabled.  Return success to the target program.
171SyscallReturn ignoreFunc(SyscallDesc *desc, int num,
172                         Process *p, ExecContext *xc);
173
174/// Target exit() handler: terminate simulation.
175SyscallReturn exitFunc(SyscallDesc *desc, int num,
176                       Process *p, ExecContext *xc);
177
178/// Target getpagesize() handler.
179SyscallReturn getpagesizeFunc(SyscallDesc *desc, int num,
180                              Process *p, ExecContext *xc);
181
182/// Target obreak() handler: set brk address.
183SyscallReturn obreakFunc(SyscallDesc *desc, int num,
184                         Process *p, ExecContext *xc);
185
186/// Target close() handler.
187SyscallReturn closeFunc(SyscallDesc *desc, int num,
188                        Process *p, ExecContext *xc);
189
190/// Target read() handler.
191SyscallReturn readFunc(SyscallDesc *desc, int num,
192                       Process *p, ExecContext *xc);
193
194/// Target write() handler.
195SyscallReturn writeFunc(SyscallDesc *desc, int num,
196                        Process *p, ExecContext *xc);
197
198/// Target lseek() handler.
199SyscallReturn lseekFunc(SyscallDesc *desc, int num,
200                        Process *p, ExecContext *xc);
201
202/// Target munmap() handler.
203SyscallReturn munmapFunc(SyscallDesc *desc, int num,
204                         Process *p, ExecContext *xc);
205
206/// Target gethostname() handler.
207SyscallReturn gethostnameFunc(SyscallDesc *desc, int num,
208                              Process *p, ExecContext *xc);
209
210/// Target unlink() handler.
211SyscallReturn unlinkFunc(SyscallDesc *desc, int num,
212                         Process *p, ExecContext *xc);
213
214/// Target rename() handler.
215SyscallReturn renameFunc(SyscallDesc *desc, int num,
216                         Process *p, ExecContext *xc);
217
218
219/// Target truncate() handler.
220SyscallReturn truncateFunc(SyscallDesc *desc, int num,
221                           Process *p, ExecContext *xc);
222
223
224/// Target ftruncate() handler.
225SyscallReturn ftruncateFunc(SyscallDesc *desc, int num,
226                            Process *p, ExecContext *xc);
227
228
229/// This struct is used to build an target-OS-dependent table that
230/// maps the target's open() flags to the host open() flags.
231struct OpenFlagTransTable {
232    int tgtFlag;	//!< Target system flag value.
233    int hostFlag;	//!< Corresponding host system flag value.
234};
235
236
237
238/// A readable name for 1,000,000, for converting microseconds to seconds.
239const int one_million = 1000000;
240
241/// Approximate seconds since the epoch (1/1/1970).  About a billion,
242/// by my reckoning.  We want to keep this a constant (not use the
243/// real-world time) to keep simulations repeatable.
244const unsigned seconds_since_epoch = 1000000000;
245
246/// Helper function to convert current elapsed time to seconds and
247/// microseconds.
248template <class T1, class T2>
249void
250getElapsedTime(T1 &sec, T2 &usec)
251{
252    int elapsed_usecs = curTick / Clock::Int::us;
253    sec = elapsed_usecs / one_million;
254    usec = elapsed_usecs % one_million;
255}
256
257//////////////////////////////////////////////////////////////////////
258//
259// The following emulation functions are generic, but need to be
260// templated to account for differences in types, constants, etc.
261//
262//////////////////////////////////////////////////////////////////////
263
264/// Target ioctl() handler.  For the most part, programs call ioctl()
265/// only to find out if their stdout is a tty, to determine whether to
266/// do line or block buffering.
267template <class OS>
268SyscallReturn
269ioctlFunc(SyscallDesc *desc, int callnum, Process *process,
270          ExecContext *xc)
271{
272    int fd = xc->getSyscallArg(0);
273    unsigned req = xc->getSyscallArg(1);
274
275    DPRINTF(SyscallVerbose, "ioctl(%d, 0x%x, ...)\n", fd, req);
276
277    if (fd < 0 || process->sim_fd(fd) < 0) {
278        // doesn't map to any simulator fd: not a valid target fd
279        return -EBADF;
280    }
281
282    switch (req) {
283      case OS::TIOCISATTY:
284      case OS::TIOCGETP:
285      case OS::TIOCSETP:
286      case OS::TIOCSETN:
287      case OS::TIOCSETC:
288      case OS::TIOCGETC:
289      case OS::TIOCGETS:
290      case OS::TIOCGETA:
291        return -ENOTTY;
292
293      default:
294        fatal("Unsupported ioctl call: ioctl(%d, 0x%x, ...) @ 0x%llx\n",
295              fd, req, xc->readPC());
296    }
297}
298
299/// Target open() handler.
300template <class OS>
301SyscallReturn
302openFunc(SyscallDesc *desc, int callnum, Process *process,
303         ExecContext *xc)
304{
305    std::string path;
306
307    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
308        return -EFAULT;
309
310    if (path == "/dev/sysdev0") {
311        // This is a memory-mapped high-resolution timer device on Alpha.
312        // We don't support it, so just punt.
313        warn("Ignoring open(%s, ...)\n", path);
314        return -ENOENT;
315    }
316
317    int tgtFlags = xc->getSyscallArg(1);
318    int mode = xc->getSyscallArg(2);
319    int hostFlags = 0;
320
321    // translate open flags
322    for (int i = 0; i < OS::NUM_OPEN_FLAGS; i++) {
323        if (tgtFlags & OS::openFlagTable[i].tgtFlag) {
324            tgtFlags &= ~OS::openFlagTable[i].tgtFlag;
325            hostFlags |= OS::openFlagTable[i].hostFlag;
326        }
327    }
328
329    // any target flags left?
330    if (tgtFlags != 0)
331        warn("Syscall: open: cannot decode flags 0x%x", tgtFlags);
332
333#ifdef __CYGWIN32__
334    hostFlags |= O_BINARY;
335#endif
336
337    DPRINTF(SyscallVerbose, "opening file %s\n", path.c_str());
338
339    // open the file
340    int fd = open(path.c_str(), hostFlags, mode);
341
342    return (fd == -1) ? -errno : process->alloc_fd(fd);
343}
344
345
346/// Target stat() handler.
347template <class OS>
348SyscallReturn
349statFunc(SyscallDesc *desc, int callnum, Process *process,
350         ExecContext *xc)
351{
352    std::string path;
353
354    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
355        return -EFAULT;
356
357    struct stat hostBuf;
358    int result = stat(path.c_str(), &hostBuf);
359
360    if (result < 0)
361        return errno;
362
363    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
364
365    return 0;
366}
367
368
369/// Target lstat() handler.
370template <class OS>
371SyscallReturn
372lstatFunc(SyscallDesc *desc, int callnum, Process *process,
373          ExecContext *xc)
374{
375    std::string path;
376
377    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
378        return -EFAULT;
379
380    struct stat hostBuf;
381    int result = lstat(path.c_str(), &hostBuf);
382
383    if (result < 0)
384        return -errno;
385
386    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
387
388    return 0;
389}
390
391/// Target fstat() handler.
392template <class OS>
393SyscallReturn
394fstatFunc(SyscallDesc *desc, int callnum, Process *process,
395          ExecContext *xc)
396{
397    int fd = process->sim_fd(xc->getSyscallArg(0));
398
399    DPRINTF(SyscallVerbose, "fstat(%d, ...)\n", fd);
400
401    if (fd < 0)
402        return -EBADF;
403
404    struct stat hostBuf;
405    int result = fstat(fd, &hostBuf);
406
407    if (result < 0)
408        return -errno;
409
410    OS::copyOutStatBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
411
412    return 0;
413}
414
415
416/// Target statfs() handler.
417template <class OS>
418SyscallReturn
419statfsFunc(SyscallDesc *desc, int callnum, Process *process,
420           ExecContext *xc)
421{
422    std::string path;
423
424    if (xc->mem->readString(path, xc->getSyscallArg(0)) != No_Fault)
425        return -EFAULT;
426
427    struct statfs hostBuf;
428    int result = statfs(path.c_str(), &hostBuf);
429
430    if (result < 0)
431        return errno;
432
433    OS::copyOutStatfsBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
434
435    return 0;
436}
437
438
439/// Target fstatfs() handler.
440template <class OS>
441SyscallReturn
442fstatfsFunc(SyscallDesc *desc, int callnum, Process *process,
443            ExecContext *xc)
444{
445    int fd = process->sim_fd(xc->getSyscallArg(0));
446
447    if (fd < 0)
448        return -EBADF;
449
450    struct statfs hostBuf;
451    int result = fstatfs(fd, &hostBuf);
452
453    if (result < 0)
454        return errno;
455
456    OS::copyOutStatfsBuf(xc->mem, xc->getSyscallArg(1), &hostBuf);
457
458    return 0;
459}
460
461
462/// Target mmap() handler.
463///
464/// We don't really handle mmap().  If the target is mmaping an
465/// anonymous region or /dev/zero, we can get away with doing basically
466/// nothing (since memory is initialized to zero and the simulator
467/// doesn't really check addresses anyway).  Always print a warning,
468/// since this could be seriously broken if we're not mapping
469/// /dev/zero.
470//
471/// Someday we should explicitly check for /dev/zero in open, flag the
472/// file descriptor, and fail (or implement!) a non-anonymous mmap to
473/// anything else.
474template <class OS>
475SyscallReturn
476mmapFunc(SyscallDesc *desc, int num, Process *p, ExecContext *xc)
477{
478    Addr start = xc->getSyscallArg(0);
479    uint64_t length = xc->getSyscallArg(1);
480    // int prot = xc->getSyscallArg(2);
481    int flags = xc->getSyscallArg(3);
482    // int fd = p->sim_fd(xc->getSyscallArg(4));
483    // int offset = xc->getSyscallArg(5);
484
485    if (start == 0) {
486        // user didn't give an address... pick one from our "mmap region"
487        start = p->mmap_end;
488        p->mmap_end += RoundUp<Addr>(length, VMPageSize);
489        if (p->nxm_start != 0) {
490            //If we have an nxm space, make sure we haven't colided
491            assert(p->mmap_end < p->nxm_start);
492        }
493    }
494
495    if (!(flags & OS::TGT_MAP_ANONYMOUS)) {
496        warn("allowing mmap of file @ fd %d. "
497             "This will break if not /dev/zero.", xc->getSyscallArg(4));
498    }
499
500    return start;
501}
502
503/// Target getrlimit() handler.
504template <class OS>
505SyscallReturn
506getrlimitFunc(SyscallDesc *desc, int callnum, Process *process,
507              ExecContext *xc)
508{
509    unsigned resource = xc->getSyscallArg(0);
510    TypedBufferArg<typename OS::rlimit> rlp(xc->getSyscallArg(1));
511
512    switch (resource) {
513      case OS::RLIMIT_STACK:
514        // max stack size in bytes: make up a number (2MB for now)
515        rlp->rlim_cur = rlp->rlim_max = 8 * 1024 * 1024;
516        break;
517
518      default:
519        std::cerr << "getrlimitFunc: unimplemented resource " << resource
520                  << std::endl;
521        abort();
522        break;
523    }
524
525    rlp.copyOut(xc->mem);
526    return 0;
527}
528
529/// Target gettimeofday() handler.
530template <class OS>
531SyscallReturn
532gettimeofdayFunc(SyscallDesc *desc, int callnum, Process *process,
533                 ExecContext *xc)
534{
535    TypedBufferArg<typename OS::timeval> tp(xc->getSyscallArg(0));
536
537    getElapsedTime(tp->tv_sec, tp->tv_usec);
538    tp->tv_sec += seconds_since_epoch;
539
540    tp.copyOut(xc->mem);
541
542    return 0;
543}
544
545
546/// Target getrusage() function.
547template <class OS>
548SyscallReturn
549getrusageFunc(SyscallDesc *desc, int callnum, Process *process,
550              ExecContext *xc)
551{
552    int who = xc->getSyscallArg(0);	// THREAD, SELF, or CHILDREN
553    TypedBufferArg<typename OS::rusage> rup(xc->getSyscallArg(1));
554
555    if (who != OS::RUSAGE_SELF) {
556        // don't really handle THREAD or CHILDREN, but just warn and
557        // plow ahead
558        warn("getrusage() only supports RUSAGE_SELF.  Parameter %d ignored.",
559             who);
560    }
561
562    getElapsedTime(rup->ru_utime.tv_sec, rup->ru_utime.tv_usec);
563    rup->ru_stime.tv_sec = 0;
564    rup->ru_stime.tv_usec = 0;
565    rup->ru_maxrss = 0;
566    rup->ru_ixrss = 0;
567    rup->ru_idrss = 0;
568    rup->ru_isrss = 0;
569    rup->ru_minflt = 0;
570    rup->ru_majflt = 0;
571    rup->ru_nswap = 0;
572    rup->ru_inblock = 0;
573    rup->ru_oublock = 0;
574    rup->ru_msgsnd = 0;
575    rup->ru_msgrcv = 0;
576    rup->ru_nsignals = 0;
577    rup->ru_nvcsw = 0;
578    rup->ru_nivcsw = 0;
579
580    rup.copyOut(xc->mem);
581
582    return 0;
583}
584
585#endif // __SIM_SYSCALL_EMUL_HH__
586